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

Bug: TypeError exif_transpose() / exif.tobytes() #6882

Closed
kxrob opened this issue Jan 11, 2023 · 3 comments · Fixed by #6890
Closed

Bug: TypeError exif_transpose() / exif.tobytes() #6882

kxrob opened this issue Jan 11, 2023 · 3 comments · Fixed by #6890
Labels

Comments

@kxrob
Copy link

kxrob commented Jan 11, 2023

Doing PIL.ImageOps.exif_transpose(im) for a .jpg I get this error:

  File "C:\Python38\lib\site-packages\PIL\ImageOps.py", line 602, in exif_transpose
    transposed_image.info["exif"] = transposed_exif.tobytes()
  File "C:\Python38\lib\site-packages\PIL\Image.py", line 3628, in tobytes
    return b"Exif\x00\x00" + head + ifd.tobytes(offset)
  File "C:\Python38\lib\site-packages\PIL\TiffImagePlugin.py", line 878, in tobytes
    data = ifd.tobytes(offset)
  File "C:\Python38\lib\site-packages\PIL\TiffImagePlugin.py", line 887, in tobytes
    "<table: %d bytes>" % len(data) if len(data) >= 16 else str(values)
TypeError: object of type 'IFDRational' has no len()

At post-mortem:

>>> PIL.__version__
'9.3.0'
>>> data
2.2
>>> type(data)
<class 'PIL.TiffImagePlugin.IFDRational'>
>>> len(data)
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
TypeError: object of type 'IFDRational' has no len()

# stack frame `data = ifd.tobytes(offset)`
>>> tag, value
(34853, {5: 2.2, 6: 0.0})   # GPSInfo
>>> type(value[5])
<class 'PIL.TiffImagePlugin.IFDRational'>

# stack frame with image:
>>> im.getexif()[274]  # Orientation
6

(Can't publish the image / exif unfortunately, but it should be clear whats going on: IFDRational in GPS exif tag fails to serialize to bytes)

@radarhere
Copy link
Member

radarhere commented Jan 12, 2023

So you've found an image where the GPSAltitudeRef, which should either be a BYTE of 0 (meaning "above sea level") or 1 (meaning "below sea level"), is instead 2.2.

Thanks for trying to be helpful in the absence of an image. Do you know what software created this image? Out of curiosity, would you also be able to let us know what is the value of typ?

@kxrob
Copy link
Author

kxrob commented Jan 12, 2023

Do you know what software created this image? Out of curiosity, would you also be able to let us know what is the value of typ?

It seems to be directly from a phone camera (LG).
typ and other locals():

>>> im._exif.tobytes()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "C:\Python38\lib\site-packages\PIL\Image.py", line 3628, in tobytes
    return b"Exif\x00\x00" + head + ifd.tobytes(offset)
  File "C:\Python38\lib\site-packages\PIL\TiffImagePlugin.py", line 878, in tobytes
    data = ifd.tobytes(offset)
  File "C:\Python38\lib\site-packages\PIL\TiffImagePlugin.py", line 887, in tobytes
    "<table: %d bytes>" % len(data) if len(data) >= 16 else str(values)
TypeError: object of type 'IFDRational' has no len()
>>> pm()
DBPE>>> typ
1
DBPE>>> locals()
{'self': <PIL.TiffImagePlugin.ImageFileDirectory_v2 object at 0x0000022405E20D30>, 'offset': 1328, 'result': b'\x00\x02', 'entries': [], 'stripoffsets': None, 'tag': 5, 'value': 2.2, 'typ': 1, 'is_ifd': False, 'values': (2.2,), 'data': 2.2, 'tagname': 'GPSAltitudeRef', 'typname': 'byte', 'msg': 'save: GPSAltitudeRef (5) - type: byte (1)'}
DBPE>>> self.tagtype
{5: 1, 6: 5}
DBPE>>> self._write_dispatch[typ](self, *values)
2.2
DBPE>>> 

The typ 1 seems to be forced by these lines in _setitem() while self.group is 34853:

        info = TiffTags.lookup(tag, self.group)
        ...
            if info.type:
                self.tagtype[tag] = info.type

( When the data is extracted the same lines were passed for that tag, but self.group then is None, thus auto detection as IFDRational and self.tagtype == {5: 5, 6: 5} )

@radarhere
Copy link
Member

( When the data is extracted the same lines were passed for that tag, but self.group then is None, thus auto detection as IFDRational and self.tagtype == {5: 5, 6: 5} )

self.tagtype would have been populated with those values when reading the tags from the image.

I've created PR #6890 to resolve this.

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.

2 participants