-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Addition of _repr_jpeg_ causes both JPEG and PNG format images to be attached to Jupyter cells #7259
Comments
Particularly for those of us not familiar with IPython, could you explain how to replicate this? |
Yup, sorry! In the shell you can run (in a new venv if needed): pip install -U pillow notebook
jupyter notebook This should result in your web browser opening the Jupyter home page. You can then click on the "New" button in the top right and create a new notebook (i.e. from PIL import Image
Image.open('something.png') and either hit the Run button at the top, or press Ctrl+Enter to evaluate this block. Running this should cause the image to appear below the code. If you then press the "Save and Checkpoint" icon (top left toolbar) it'll save the file. Switching back to the shell, you can look at the resulting JSON encoded file. Something like: less -S Untitled.ipynb would do the job. Under a |
Thanks for that. https://ipython.readthedocs.io/en/stable/config/integrating.html#MyObject states that
https://ipython.readthedocs.io/en/stable/config/integrating.html#custom-methods states
Correct me if I'm wrong, but it would seem that IPython has made a decision to include all supported representations when saving. "Pillow supports iPython's API too well" doesn't sound like a problem to be solved. Are you not fighting against IPython's decision to include all formats? If this is how they think their software should behave, why should we try and override that decision? |
Yup, I was wondering the same when writing this code but when I noticed That said, I think it would be helpful if something similar to my above code ran somewhere so more images have a chance of appearing by default. Thinking about it now, it might be worth using the |
I think this is also causing failure in certain scenarios. Specifically with the release of 10.0.0 the rustworkx project's CI has started failing because the jpeg output from a In that case the docs (running via jupyter) are writing a png image to disk by calling out to graphviz and loading that file with |
Since the recent release of Pillow 10.0.0 the docs CI job has started failing due to an error in Pillow when trying to run the jupyter-execute cell in the `.to_dot()` docstring. It looks like a bug that was introduced in the new release which is being tracked in python-pillow/Pillow#7259 where it's trying to return a jpeg representation of the object from the RGBA data loaded from a PNG. Until the issue is resolved upstream in pillow this commit just caps the version we run in CI via the constraints file. While pillow is an optional dependency and we could cap the version in the extras, this issue isn't severe enough to warrant that, and the typical pillow usage, especially via the rustworkx api (i.e. graphviz_draw() which returns a PIL.Image object) will continue to work as expected with pillow 10.0.0 there isn't a reason to cap more broadly. This is just needed as a workaround to unblock CI.
Rather than a change in Pillow, wouldn't a more general solution be to ask IPython to change their code, so that if some representation formats pass and others fail, then the exceptions are caught? |
Since the recent release of Pillow 10.0.0 the docs CI job has started failing due to an error in Pillow when trying to run the jupyter-execute cell in the `.to_dot()` docstring. It looks like a bug that was introduced in the new release which is being tracked in python-pillow/Pillow#7259 where it's trying to return a jpeg representation of the object from the RGBA data loaded from a PNG. Until the issue is resolved upstream in pillow this commit just caps the version we run in CI via the constraints file. While pillow is an optional dependency and we could cap the version in the extras, this issue isn't severe enough to warrant that, and the typical pillow usage, especially via the rustworkx api (i.e. graphviz_draw() which returns a PIL.Image object) will continue to work as expected with pillow 10.0.0 there isn't a reason to cap more broadly. This is just needed as a workaround to unblock CI.
In 10.0.0 a _repr_jpeg_ implementation was added to the Image object to enable the use of display_jpeg() in IPython environments. However, in some cases the implementation of this method could result in an exception being raised while trying to generate the jpeg data. The best example is if the image data is in an RGBA format as a result of the object being created by opening a PNG file. In this case trying to save the Image object as a jpeg will error because the jpeg format can't represent the transparency in the alpha channel. This results in an exception being raised in the IPython/Jupyter context when outputing the image object. However, in cases like this IPython allows the repr methods to return None to indicate there is no representation in that format available. [1] This commit updates the _repr_png_ and _repr_jpeg_ methods to catch any exception that might be raised while trying to generate the image data. If an exception is raised we treat that as not being able to generate image data in that format and return None instead. Related to python-pillow#7259 [1] https://ipython.readthedocs.io/en/stable/config/integrating.html#custom-methods
Sure, it's arguable where the bug actually lies. But, raising an exception in this case where the I opened a pull request to adjust the behavior of the repr methods so they return None in case saving the image data fails for any reason: #7266 |
I'm pretty sure that throwing an exception is the wrong thing to be doing as well. My code at the top does this mode conversion and causes the endpoint to return either PNG or JPEG data depending on which format produces the smaller output. It also resizes the image as it's assumed that the image is for displaying in a moderately sized window as working with PNG images is relatively slow (i.e. several seconds for encoding, transferring, decoding in a browser for a 10 megapixel image). |
Could anyone explain why Pillow needs |
@homm We probably don't have to support it, but I agree with the original intention of trying to support it based on user feedback, and it should become clear over time if more folks than @smason have an issue, we could consider removing it. But it seems like the current implementation can stand for now, based on everything I've read. |
I'll add myself as +1 on this issue. I've pinned pillow<10 until this is fixed. Either in Pillow or in Jupyter, I don't much care. But currently, the following very simple bit of code in a Jupyter cell doesn't work. import PIL
from IPython import display
display.display(PIL.Image.open('image.png')) Especially if the png file has transparency, which is common in matplotlib plots. I get errors like
So I need either a fix from IPython or PIL, or a workaround. Until then I'm pinning pillow<10. |
Since the recent release of Pillow 10.0.0 the docs CI job has started failing due to an error in Pillow when trying to run the jupyter-execute cell in the `.to_dot()` docstring. It looks like a bug that was introduced in the new release which is being tracked in python-pillow/Pillow#7259 where it's trying to return a jpeg representation of the object from the RGBA data loaded from a PNG. Until the issue is resolved upstream in pillow this commit just caps the version we run in CI via the constraints file. While pillow is an optional dependency and we could cap the version in the extras, this issue isn't severe enough to warrant that, and the typical pillow usage, especially via the rustworkx api (i.e. graphviz_draw() which returns a PIL.Image object) will continue to work as expected with pillow 10.0.0 there isn't a reason to cap more broadly. This is just needed as a workaround to unblock CI.
) (#931) * Adding fix that clippy warns for (#930) Because of a new Rust update, Clippy now warned for a line of code in cycle_basis.rs in rustworkx-core. This PR fixes that one line so that Clippy passes. (cherry picked from commit d88f18b) * Pin pillow<10 in CI (#922) Since the recent release of Pillow 10.0.0 the docs CI job has started failing due to an error in Pillow when trying to run the jupyter-execute cell in the `.to_dot()` docstring. It looks like a bug that was introduced in the new release which is being tracked in python-pillow/Pillow#7259 where it's trying to return a jpeg representation of the object from the RGBA data loaded from a PNG. Until the issue is resolved upstream in pillow this commit just caps the version we run in CI via the constraints file. While pillow is an optional dependency and we could cap the version in the extras, this issue isn't severe enough to warrant that, and the typical pillow usage, especially via the rustworkx api (i.e. graphviz_draw() which returns a PIL.Image object) will continue to work as expected with pillow 10.0.0 there isn't a reason to cap more broadly. This is just needed as a workaround to unblock CI. --------- Co-authored-by: danielleodigie <97267313+danielleodigie@users.noreply.github.com> Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
#7266 has been merged. In the next version of Pillow, that should resolve the matter of "Could not save to JPEG for display". |
Over in the issue asking IPython about whether we're correctly including all formats, and the file size is not something that needs to be solved by Pillow, ipython/ipython#14109 (comment)
My response was
So I'm somewhat confused now. If @smason no longer thinks there is a problem, then #7266 has fixed the errors reported by others here, and this issue can be closed. |
Closing this issue as no feedback has been received. |
@radarhere I'm monkey patching Pillow with my own code in to fix the issue in a way that works for me (i.e. similar to #7268). I'm not sure if there's enough interest to maintain the relevant state in Pillow directly. Specifically, I'm dealing with raw camera data (i.e. lots of pixels at 12 or 14bpc) and Pillow doesn't seem to track the numeric range from the source data in a way that would help me. I'm also interested in just previewing images while I'm exploring the data in Jupyter (hence my code downsampling images) and returning both PNG and JPEG versions of the image seems wasteful. When I've got a workflow/code that's mostly correct I'll turn it into a proper Python module that's tested to process things correctly, but, e.g., while working out whether I'm correctly debayering an image correctly I just want a quick preview that things are on track. In my experience this is a good way to use Jupyter notebooks; exploratory work goes into notebooks where it's very quick to iterate on changes (no need to rerun Python and/or reload large datasets), then the code is tidied up for production into a module (or a package for large chunks of code). You can close as Wont Fix if you think that's relevant, I'm not sure how many other people use Pillow in the way I am. |
As has been discussed, I don't think this is a Pillow problem, but an IPython decision. I've left a comment on #7268. Thanks for your thoughts, and for providing regular feedback along the journey of this issue. |
For me "is not Pillow problem" is not appropriate here, since this part is not just Pillow internals, this is intentional integration with iPython API. It would be correct to follow the clarification from IPython how to use this API. It is very sad that there are none of them still. |
Yeah I agree with @homm or at least the standard for feature requests, as far as I'm concerned, should always be "can we support this feature request without creating issues for ourselves or others and if so, do it". There are plenty of times when we can't support a feature request for whatever reason, so I think that if something works for @smason and one other person and doesn't create issues for anyone else, then it should go in. If there are any issues to be worked through, then it could go in but not until the issues are resolved. |
Sorry, to clarify, I meant "I don't think this is a Pillow bug, but a decision that IPython has made". As far as I can tell, we are following https://ipython.readthedocs.io/en/stable/config/integrating.html#custom-methods
From my reading of that, IPython made a decision that saving all formats is the best thing to do. I closed this issue because the creator of the issue felt like it was no longer necessary to question that decision. |
In a notebook I want to display a PNG image, resized: Still a similar issue as posted before, although the image is displayed in the notebook.
Why with code in a notebook? |
What version of Pillow? |
Hi @gsgxnet. Your problem should be resolved by #7266 on October 15 with the release of Pillow 10.1.0. Until then, a suggested workaround is from PIL import Image
del Image._repr_jpeg_
Image.open("img/PCA_all_features_2D.png").resize(size=(600,500)) |
Pillow 10.1.0 has now been released. |
I've had a go at improving the interaction between Jupyter and
Image
s. As I commented on in #7135, that change causes the image to be saved in JPEG and PNG format and both files attached to the invoking cell.This behavior seems redundant, and given that no resizing is done can result in very large
.ipynb
files if the user isn't careful when working with high resolution images. I've had a look at how to avoid this, and the nicest way I could see to do this was by adding aImage._repr_mimebundle_
method that would do the "right thing" in more circumstances.Specifically, it would know whether the user has called
display_jpeg
ordisplay_png
and could return the appropriate encoding. If neither format is specifically requested it could return something appropriate for the data.I've had a go at implementing this and it currently looks like:
This can be tested with something like:
Could turn this into a pull-request if this seems like the right way to go.
Note the
if include is not None or exclude is not None
code at the top would mean that explicit calls like:would cause Jupyter to end up calling
Image._repr_jpeg_
directly, bypassing this code. If called with a large image, this would cause a full-res JPEG to be returned. Not sure if this is the right thing, or even what the "correct" way of handling these arguments should be.I've only seen
exclude=None
in my testing, so am going on description inIPython/core/formatters.py
and whatgraphviz
does.The text was updated successfully, but these errors were encountered: