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

Wrong file format for the tmp file #7510

Closed
Mimi-py opened this issue Oct 31, 2023 · 9 comments
Closed

Wrong file format for the tmp file #7510

Mimi-py opened this issue Oct 31, 2023 · 9 comments

Comments

@Mimi-py
Copy link

Mimi-py commented Oct 31, 2023

Hi everyone. I found a weird bug. When I try to use "show()" method to display images (that was opened with Image.open) it do not work. I found the problem (but not solution). When the "show()" actually do not open the image it self but rather creates a tmp file and opens it, but the tmp file is created with wrong format / extension (i.e image format is JPEG, but created tmp file is PNG)
what is also weird when I use the "format" function to check if the object has right format it displays right format (JPEG). Nevertheless the tmp file has PNG format.

PS. When I change the tmp file to the right format (via desktop env) then I can normally open the image via Desktop env. Also I can open the png files normally via desktop env but not the ones created by the "show()" function.
And I did tried to manually set the right format for the image but didn't work (which is not surprising because the "format" function shows the right format)

What did you do?

Tried to open images using "show()" function

What did you expect to happen?

I expected that the images will be displayed using default image viewer.

What actually happened?

The image was not displayed.

What are your OS, Python and Pillow versions?

  • OS: Arch Linux x86_64
  • Kernel: 6.5.9-arch2-1
  • Python: 3.11.5
  • Pillow: 10.1.0
    *Default image viewer: gwenview version: 23.08.2-1

CODE that I used

from PIL import Image

i = Image.open("images/img1.jpeg")

print(i.format)

i.show()

IMAGES (actual img, tmp img and visual presentation that tmp files are created with different format)
The opened image: (JPEG)
img1

The tmp image what is created when execute the code: (PNG)
tmp0cclb1xd

The visual view of tmp folder vs images folder
dir-comapre

@radarhere
Copy link
Member

Hi. https://pillow.readthedocs.io/en/stable/reference/ImageShow.html states that

All default viewers convert the image to be shown to PNG format.

The fact that the PNG format is used is intended as a feature, not a bug. Pillow supports some obscure formats, and not all applications support opening them. For example, on my Mac, Preview can't display MIC files, but it can display PNG files. By standardising the format, the support becomes better. 'But wait', you say, 'JPEG isn't obscure!' - true, but JPEG is a lossy format. This means that when an image is saved, it doesn't come out exactly the same. PNG, however, is lossless, meaning it does. https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.show says im.show() is

This method is mainly intended for debugging purposes.

If I'm debugging an image, I want the saved file to be exactly the same, not slightly different.

As to why show() isn't currently working,
when you say "The image was not displayed", does anything happen at all?
If you run print(i.show()) what value does it returned? If it returns False, that means that Pillow can't find a program that it knows about to open the image with. https://pillow.readthedocs.io/en/stable/reference/ImageShow.html#PIL.ImageShow.UnixViewer lists xdg-open, display, gm display, eog and xv as supported viewers. Do you not have any of those programs?

If we can find a viewer that is supported on your machine, and you do want to resave the image as a JPEG file before showing it, then you can change the ImageShow format with something like

from PIL import Image, ImageShow
im = Image.open("Tests/images/hopper.jpg")

viewer = ImageShow.MacViewer()
del viewer.options["save_all"]
viewer.format = "JPEG"
viewer.show(im)

@Mimi-py
Copy link
Author

Mimi-py commented Oct 31, 2023

Hmm u are right.
But still dont understand it. I mean even if the tmp file are png my image viewer (gwenview) normally can open png files (via desktop env or via command line kde-open / xdg-open)
The problem only appears when I try to open it via PIL.

In this image I marked 3 things, can u take a look?
1.Shows that my image viewer can open png files
2.The output of print(i.show()) is: None
3.Some errors (which also appear when I open the photo directly via clt but it works there anyway)

Screenshot_20231031_221552

Do u have any idea how to fix it?

@radarhere
Copy link
Member

Here is the code that Pillow is using to detect a program it can open images with.

Pillow/src/PIL/ImageShow.py

Lines 289 to 299 in c9f7a82

if sys.platform not in ("win32", "darwin"): # unixoids
if shutil.which("xdg-open"):
register(XDGViewer)
if shutil.which("display"):
register(DisplayViewer)
if shutil.which("gm"):
register(GmDisplayViewer)
if shutil.which("eog"):
register(EogViewer)
if shutil.which("xv"):
register(XVViewer)

If xdg-open, display, gm, eog or xv are not found on your Linux machine, then it won't work at the moment.

Sorry, when I mentioned detecting False, I was looking at a different function. I expect the following will print False, indicating that none of the programs can be found.

from PIL import ImageShow
print(ImageShow.show(i))

@Mimi-py
Copy link
Author

Mimi-py commented Oct 31, 2023

Is there a way to point Python / PIL to the xdg-open?
I mean I have it on my machine I can use this (works in shell) and it is in the sys path. (In shell $PATH)

@radarhere
Copy link
Member

If you believe that subprocess.Popen(["xdg-open", path]) will work, but Pillow isn't finding xdg-open with shutil.which("xdg-open"), then you can run

from PIL import ImageShow

viewer = ImageShow.XDGViewer()
viewer.show(i)

@Mimi-py
Copy link
Author

Mimi-py commented Nov 1, 2023

Okay I have found the real problem here.
When I call the "show()" function it creates the tmp file in png format, at least it should from what I understand right? But instead it creates file with APNG format with extension PNG. gwenviewer do not support APNG files so it cannot be displayed.

Look I marked the img crated in tmp folder (by show() func.) and it's real image format.
true-problem-PIL

So I guess for now I just need to open the real images with subprocess instead the tmp using show().So If I want to check any changes that I made to the opened image I need to save it as new file and the open it with sub-process. (or build func that does that.)
Or if u have better idea pls tell
(Yeah I know I could just use another image viewer that supports apng)

@radarhere
Copy link
Member

radarhere commented Nov 1, 2023

Alternatively, you could run

from PIL import ImageShow
del ImageShow.UnixViewer.options["save_all"]
img.show()

@Mimi-py
Copy link
Author

Mimi-py commented Nov 1, 2023

Cool that works! Thank you!
Do u know / if u could explain why the issue appeared in the first place and why this line solves it?

del ImageShow.UnixViewer.options["save_all"]

@Mimi-py Mimi-py closed this as completed Nov 2, 2023
@radarhere
Copy link
Member

#6611 changed ImageShow so that if you are showing an animated image, all frames are visible. However, this has also had the effect of changing all still images from PNGs to APNGs - and you have said that gwenview does not support APNGs.

So the code that I gave you stops ImageShow from saving animated images.

I've created #7522 as a long-term solution, to save still images not as APNGs, but as PNGs instead.

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

No branches or pull requests

2 participants