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

Support for a "blank" exif #6123

Closed
leakyH opened this issue Mar 11, 2022 · 10 comments · Fixed by #6124
Closed

Support for a "blank" exif #6123

leakyH opened this issue Mar 11, 2022 · 10 comments · Fixed by #6124
Labels

Comments

@leakyH
Copy link

leakyH commented Mar 11, 2022

What did you do?

load a image and its exif is b'Exif\x00\x00'

im=Image.open(/path/to/img)
im._exif is None
True
im.info
{'jfif': 257,
 'jfif_version': (1, 1),
 'dpi': (1, 1),
 'jfif_unit': 1,
 'jfif_density': (1, 1),
 'exif': b'Exif\x00\x00'}
im.getexif()
Traceback (most recent call last):

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3444, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)

  File "/tmp/ipykernel_15028/2749469980.py", line 1, in <module>
    im.getexif()

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/PIL/Image.py", line 1391, in getexif
    self._exif.load(exif_info)

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/PIL/Image.py", line 3422, in load
    self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head)

  File "/home/me/anaconda3/envs/ppdetection213/lib/python3.9/site-packages/PIL/TiffImagePlugin.py", line 491, in __init__
    raise SyntaxError(f"not a TIFF file (header {repr(ifh)} not valid)")

  File "<string>", line unknown
SyntaxError: not a TIFF file (header b'' not valid)

What did you expect to happen?

Nothing to be done. Since functions like ImageOps.exif_transpose calls im.getexif(), so I will expect that no errors should be raised.

What actually happened?

the getexif() function will call Exif() to initiate a blank Exif instance, since the im._exif is None. And then the Exif() instance will load the im.info.get('exif'), which is b'Exif\x00\x00'. In the load function, the b'Exif\x00\x00' will be dropped by this line. So a b'' is passed to TiffImagePlugin.ImageFileDirectory_v2(self.head). And since b'' is not accepted by TiffImagePlugin.ImageFileDirectory_v2, an error will be raised.

BTW, these three lines in Exif.load():

if data == self._loaded_exif:
        return
self._loaded_exif = data

make the code work when calling im.getexif() the second time.

What are your OS, Python and Pillow versions?

  • OS: Linux
  • Python: 3.9.7
  • Pillow: 8.4.0 and 9.0.1
im=Image.open(/path/to/img)
im.getexif()

testimage

I wonder if this repo is considering returning a blank Exif in this case, instead of raising an error. I have no idea whether this would lead to other unknown damages or not.

@radarhere
Copy link
Member

radarhere commented Mar 11, 2022

Hi. We have actually had this report before, in #4852. Like that issue, if I inspect the image with exiftool, part of the output is

Warning                         : Malformed APP1 EXIF segment

Even though the image is malformed, I've created #6124 to resolve this by handling the data anyway.

@leakyH
Copy link
Author

leakyH commented Mar 12, 2022

Thanks for your reply and PR!! 👏👏👏👏

@a-l-e-x-d-s-9
Copy link

I have an error "Error: not a TIFF file (header b"b'Exif\x" not valid)" with Pillow 9.2.0.
I'm not sure if it is this problem or something new.

@a-l-e-x-d-s-9
Copy link

@leakyH
My images were processed with WebUI upscaling, looks like those that had Exif info from Photoshop and other editing software, have this issue, but most of them are fine.
Here is an example of such an image, first I printed the Image.info, then the Image.getexif() throwing an exception.
Here is a link to the image itself to recreate the problem.

the_699518.png, info: {'exif': b"b'Exif\\x00\\x00II*\\x00\\x08\\x00\\x00\\x00\\x04\\x00\\x12\\x01\\x03\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x001\\x01\\x02\\x00\\x11\\x00\\x00\\x00>\\x00\\x00\\x00\\x13\\x02\\x03\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00i\\x87\\x04\\x00\\x01\\x00\\x00\\x00P\\x00\\x00\\x00\\x00\\x00\\x00\\x00Shotwell 0.30.16\\x00\\x00\\x03\\x00\\x90\\x92\\x02\\x00\\x04\\x00\\x00\\x00442\\x00\\x02\\xa0\\t\\x00\\x01\\x00\\x00\\x00u\\x02\\x00\\x00\\x03\\xa0\\t\\x00\\x01\\x00\\x00\\x00\\xd5\\x03\\x00\\x00\\x00\\x00\\x00\\x00'", 'postprocessing': 'Postprocess upscale to: 2560x2560, Postprocess upscaler: 4x-AnimeSharp, Postprocess upscaler 2: RealESRGAN_x4plus_anime_6B', 'extras': 'Postprocess upscale to: 2560x2560, Postprocess upscaler: 4x-AnimeSharp, Postprocess upscaler 2: RealESRGAN_x4plus_anime_6B'}
Error occurred while processing the_699518.png. Error: not a TIFF file (header b"b'Exif\\x" not valid), Traceback (most recent call last):
  File "/home/username/Documents/stable_diffusion/Training/images_find_bad.py", line 23, in check_image_integrity
    image_exif = img.getexif()
  File "/home/username/Training/lib/python3.10/site-packages/PIL/PngImagePlugin.py", line 1040, in getexif
    return super().getexif()
  File "/home/username/Training/lib/python3.10/site-packages/PIL/Image.py", line 1454, in getexif
    self._exif.load(exif_info)
  File "/home/username/Training/lib/python3.10/site-packages/PIL/Image.py", line 3670, in load
    self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head)
  File "/home/username/Training/lib/python3.10/site-packages/PIL/TiffImagePlugin.py", line 504, in __init__
    raise SyntaxError(msg)
SyntaxError: not a TIFF file (header b"b'Exif\\x" not valid)

@leakyH
Copy link
Author

leakyH commented Mar 28, 2023

I have an error "Error: not a TIFF file (header b"b'Exif\x" not valid)" with Pillow 9.2.0. I'm not sure if it is this problem or something new.

hi @a-l-e-x-d-s-9 , I got a 404 error when open your image link, but I found that in this issue AUTOMATIC1111/stable-diffusion-webui#7034 (comment), a b"b'Exif\x" was also met and seemed to be corrected

This looks like it is now corrected with the new upgrade dependencies of pytorch and xformers that was rolled out today.

. Maybe you come here from this issue LOL.
In Pillow 9.2.x, the line was:

self.head = self.fp.read(8)

So I think the extra b' in b"b'Exif\x" was added in somewhere else by mistake(maybe the mentioned webui repo or xformers? ), and then read by Pillow.

@leakyH
Copy link
Author

leakyH commented Mar 28, 2023

Pillow/src/PIL/Image.py

Lines 3521 to 3532 in 58acec3

if data and data.startswith(b"Exif\x00\x00"):
data = data[6:]
if not data:
self._info = None
return
self.fp = io.BytesIO(data)
self.head = self.fp.read(8)
# process dictionary
from . import TiffImagePlugin
self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head)

@a-l-e-x-d-s-9 , in the first two lines, if the data starts with b"Exif\x00\x00", it will be stripped. But your image seems to have a extra b' , so the first 8 byte in data was then passed to TiffImagePlugin.ImageFileDirectory_v2 function. So it's not accepted
PREFIXES = [
b"MM\x00\x2A", # Valid TIFF header with big-endian byte order
b"II\x2A\x00", # Valid TIFF header with little-endian byte order
b"MM\x2A\x00", # Invalid TIFF header, assume big-endian
b"II\x00\x2A", # Invalid TIFF header, assume little-endian
b"MM\x00\x2B", # BigTIFF with big-endian byte order
b"II\x2B\x00", # BigTIFF with little-endian byte order
]
def _accept(prefix):
return prefix[:4] in PREFIXES

@a-l-e-x-d-s-9
Copy link

a-l-e-x-d-s-9 commented Mar 28, 2023

@leakyH
Thank you very much for your response.
The link was probably blocked by your internet service provider or other people who definitely know what is best for us...
I found another file-sharing site on Google, but I never tried it before, maybe this link will work, just for testing...
I saw the issue that you have linked, it's from a couple of months ago, so my issue is a new one.
I reported it also stable-diffusion-webui/discussions/8142.
I don't really need the Exif data for these files. Can you please advise if I can remove the Exif data without triggering the exception?

@a-l-e-x-d-s-9
Copy link

Ok, I'm not sure if it's the best way to remove exif, but this is what I have found, and it is working:

            img = Image.open(open(file_path, 'rb'))

            image_data = list(img.getdata())
            image_without_exif = Image.new(img.mode, img.size)
            image_without_exif.putdata(image_data)

            img.close()
            image_without_exif.save(file_path)
            image_without_exif.close()

@VitaliyAT
Copy link

Ok, I'm not sure if it's the best way to remove exif, but this is what I have found, and it is working:

            img = Image.open(open(file_path, 'rb'))

            image_data = list(img.getdata())
            image_without_exif = Image.new(img.mode, img.size)
            image_without_exif.putdata(image_data)

            img.close()
            image_without_exif.save(file_path)
            image_without_exif.close()

where did you fund it? where we shoud correct code?

@radarhere
Copy link
Member

I don't think that EXIF data should be saved by default with Pillow, so the question of how to remove it is strange to me. If you have an example where it is automatically present in a saved file, feel free to share.

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

Successfully merging a pull request may close this issue.

4 participants