Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iPython cannot display RGBA image after upgrade to 10.0.0 #7283

Closed
braindevices opened this issue Jul 13, 2023 · 12 comments · Fixed by #7266
Closed

iPython cannot display RGBA image after upgrade to 10.0.0 #7283

braindevices opened this issue Jul 13, 2023 · 12 comments · Fixed by #7266

Comments

@braindevices
Copy link

Previously we only have _repr_png_, so we can always display RGBA in ipython.

now we also have _repr_jpg_ for some reason, the default way to display it is this jpg function, thus when we have RGBA image we cannot display it.

What did you do?

in ipython qtconsole, I try to display RGBA image

from PIL import Image
im = Image.new("RGBA", (126, 187))
display(im)

What did you expect to happen?

show the image without error.

The default repr should be png or at least for RGBA image it should be png.

What actually happened?

display default to choose the jpeg formatter.

SError: cannot write mode RGBA as JPEG

What are your OS, Python and Pillow versions?

  • OS: ubuntu 22.04
  • Python: 3.10.6
  • Pillow: 10.0.0
from PIL import Image
im = Image.new("RGBA", (126, 187))
display(im)
@radarhere
Copy link
Member

Hi. This has been raised in another issue already - #7259 (comment)

I found that https://ipython.readthedocs.io/en/stable/config/integrating.html#custom-methods states

In general, all available formatters will be called when an object is displayed, and it is up to the UI to select which to display. A given formatter should not generally change its output based on what other formats are available

So I think IPython's deliberate plan is to request both JPEG and PNG. A conversation is trying to be started at ipython/ipython#14109 to find out more about this.

@smason
Copy link
Contributor

smason commented Jul 13, 2023

You asked about a work around in the IPython list. Which doesn't seem like the right place, but you can replace your import with:

from PIL import Image
try:
    del Image._repr_jpeg_
except AttributeError:
    pass

which will remove this new code.

I feel as though there should be some magic thing that can be done to get_ipython().display_formatter but I'm not sure what it should be.

@braindevices
Copy link
Author

@smason This workaround seems nice. I asked in ipython because I thought I can choose disable/prioritize some of the repr, I cannot find it in documentation though.

@radarhere radarhere changed the title iPython cannot display RGBA image after upgrade to 10.0 iPython cannot display RGBA image after upgrade to 10.0.0 Jul 21, 2023
@radarhere
Copy link
Member

#7266 would resolve this.

@radarhere
Copy link
Member

Pillow 10.1.0 has now been released with the fix for this.

@raphaelquast
Copy link

raphaelquast commented Dec 1, 2023

I came here looking for a fix that also works with 10.0.0 for compatibility reasons but the above mentioned didn't work...

However, forcing the image to be recognized as png by passing an in-memory png seems to do the job nicely with jupyter notebooks!

from PIL import Image
from IPython.display import display, Image as IpythonImage
from io import BytesIO

temp = BytesIO()
Image.fromarray(image data, "RGBA").save(temp, format="png")

display(IpythonImage(temp.getvalue()))

@smason
Copy link
Contributor

smason commented Dec 1, 2023

I came here looking for a fix that also works with 10.0.0 for compatibility reasons but the above mentioned didn't work...

What are you actually trying to accomplish?

If you want to force PNG only output (as older versions of Pillow only knew about) then my del Image._repr_jpeg_ hack should work, and the try/except should ignore this for older versions that don't have this method.

@raphaelquast
Copy link

Hey @smason thanks for the response!

I am working on improving some things in EOmaps when working in jupyter-notebooks.

A stripped-down example of what I'm trying to do would be the following:

  1. create a matplotlib figure in a cell
  2. get the image buffer of the figure and display it in a different cell
    (note this is not what I'm actually doing but somewhat similar and it results in the same error)

Import stuff, create a figure and get the buffer

from IPython.display import display, Image as IpythonImage
from io import BytesIO
import numpy as np
import matplotlib.pyplot as plt

f, ax = plt.subplots()
ax.plot([1,2,3])

buf = f.canvas.print_to_buffer()
image_data = np.frombuffer(buf[0], dtype=np.uint8).reshape(buf[1][1], buf[1][0], 4)

show the figure

This is my current fix that works well in most recent jupyter-lab version with Pillow 10.0.0

from PIL import Image

temp = BytesIO()
Image.fromarray(image_data, "RGBA").save(temp, format="png")

display(IpythonImage(temp.getvalue()))

This would be your fix... but it does still result in ValueError: Could not save to JPEG for display

from PIL import Image
try:
    del Image._repr_jpeg_
except AttributeError:
    pass

img = Image.fromarray(image_data, "RGBA")

display(img)

Maybe this is a problem with jupyter lab... not sure... but anyways... that was the origin of why I came up with this

@radarhere
Copy link
Member

I came here looking for a fix that also works with 10.0.0 for compatibility reasons

Out of curiosity, could you elaborate on this? Is there something about Pillow 10.1.0 that doesn't work for you?

@raphaelquast
Copy link

No, not particularly. I ran into this with an existing conda environment and upgrading to 10.1.0 would have required a lot of changes which I wanted to avoid. I just thought I'll put the fix here since the mentioned one didn't work for me.

@smason
Copy link
Contributor

smason commented Dec 3, 2023

A stripped-down example of what I'm trying to do would be the following:

  1. create a matplotlib figure in a cell

  2. get the image buffer of the figure and display it in a different cell
    (note this is not what I'm actually doing but somewhat similar and it results in the same error)

[...code...]

That looks very complicated! Wouldn't it be easier to fig.save(filedes, format='png') and display the result?

Something like:

from io import BytesIO
import matplotlib.pyplot as plt
from IPython.display import display_png

fig, ax = plt.subplots()
ax.plot([0, 1])
with BytesIO() as fd:
    fig.savefig(fd, format='png')
    display_png(fd.getvalue(), raw=True)

or even just use the default, non-raw, variant on the figure directly:

display_png(fig)

@raphaelquast
Copy link

raphaelquast commented Dec 4, 2023

@smason thanks for the suggestions!
The example I created is not the full story of what I'm trying to do... (in my case I already have cached image buffers that I'd like to show) I just used this method to create an example that works similar.

Anyways, your suggestion of using display_png directly is great!
So finally, this does the job nice and clean without having to fiddle around with in-memory images:

from IPython.display import display_png
display_png(Image.fromarray(image_data, "RGBA"), raw=False)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants