Pillow: Image exif_transpose not working with Pillow 7

Created on 8 Jan 2020  路  11Comments  路  Source: python-pillow/Pillow

Hi!
First, sorry if my question is not fully documented, i'm not exactly used to work with pillow.

I'm working on a ERP called "odoo". When uploading a picture, it gets treated by pillow then saved into a filestore.
One of our client had a problem when trying to upload some of its picture. Some of then did work fine, others provoke this error:

  File "[...]parts/odoo/odoo/tools/image.py", line 383, in image_fix_orientation
    return ImageOps.exif_transpose(image)
  File "[...]lib/python3.6/site-packages/PIL/ImageOps.py", line 549, in exif_transpose
    transposed_image.info["exif"] = exif.tobytes()
  File "[...]lib/python3.6/site-packages/PIL/Image.py", line 3258, in tobytes
    return b"Exif\x00\x00" + head + ifd.tobytes(offset)
  File "[...]lib/python3.6/site-packages/PIL/TiffImagePlugin.py", line 808, in tobytes
    data = self._write_dispatch[typ](self, *values)
TypeError: write_undefined() takes 2 positional arguments but 5 were given

The Pillow version used is 7.0.0. (6.0.0 gave me another error, while 4.0.0 works fine!)

When i tried to modify the image (simply by rotating it in the default MacOs app: Preview), some exif information got deleted and then i was able to upload those pictures (i compared before/after exif using a website).

I tried to use an exif editor to check exactly what metadata caused this issue, but i wasnt able to find an editor that allowed me to edit the metadata "Orientation", which i highly suspect causes the issues.

In the original picture, the orientation metadata was: Rotate 90 CW
In the modified picture, the orientation metadata is: Horizontal (normal)

You'll find attached both image, if it can help you diagnose where the problem lies.

The first image works fine, the second one does not work.

WORK

DOES NOT WORK

Most helpful comment

I'm having problems with this as well.
If you don't care about the other exif tags you can solve this by deleting them.

exif = image.getexif()
# Remove all exif tags
for k in exif.keys():
    if k != 0x0112:
        exif[k] = None # If I don't set it to None first (or print it) the del fails for some reason. 
        del exif[k]
# Put the new exif object in the original image
new_exif = exif.tobytes()
image.info["exif"] = new_exif
# Rotate the image
transposed = ImageOps.exif_transpose(image)

This removed all the exif tags except for the one that contains the orientation information (0x0112), and rotates the image.

All 11 comments

Here is the tag that seems to make it fail:

Tag 42034, Type: 7, Value: ((4, 1), (6, 1), (9, 5), (12, 5))

it seems that tag 42034 refer to LensSpecification.

I'm having problems with this as well.
If you don't care about the other exif tags you can solve this by deleting them.

exif = image.getexif()
# Remove all exif tags
for k in exif.keys():
    if k != 0x0112:
        exif[k] = None # If I don't set it to None first (or print it) the del fails for some reason. 
        del exif[k]
# Put the new exif object in the original image
new_exif = exif.tobytes()
image.info["exif"] = new_exif
# Rotate the image
transposed = ImageOps.exif_transpose(image)

This removed all the exif tags except for the one that contains the orientation information (0x0112), and rotates the image.

To add a little context.
The issue is happening because for tag 42034 it's expected to have 4 rationals. Right now, Pillow only supports writing 1 Rational/SRational value per tag. Other types should be fine, looking at the code.

It's unclear what is meant when the original post states that this worked in Pillow 4.0, when ImageOps.exif_transpose was only added in Pillow 6.0.

Indeed, i understood afterward what was going on. Basically, odoo uses that method or not, depending on pillow's version :)

Also failed when value of tag is empty as "{}".

Relevant code for PIL.TiffImagePluginImageFileDirectory_v2.to_bytes is problematic [line 584-592]. The line dest[tag] = values[0] failed for many cases.


try:
     (dest[tag],) = values
except ValueError:
     # We've got a builtin tag with 1 expected entry
     warnings.warn( 
     "Metadata Warning, tag %s had too many entries: %s, expected 1"
            % (tag, len(values))
     )
     dest[tag] = values[0]

@mikesongming could you give a self-contained example of your situation?

I also encountered the original issue with tag 42034, not using exif_transpose though. Here is an example of what I'm doing if it helps:

image = Image.open(path)
exif = image.getexif()

# Workaround for tag 42034
if 42034 in exif:
  exif.pop(42034, 0)

image.save(path, quality=100, exif=exif)

Without using the workaround image.save() will fail.

PR #4637 resolves the original error here.

@Kr4ken-9 your code produces an error when run over the image provided in this issue, and testing, I find that my PR also fixes it. However, if you'd like to be absolutely certain that your image is also fixed, you can upload a copy here. Another option would be for you to compile and test the branch in the PR.

Thank you for solving this error @radarhere. I was not previously aware of this PR but I am confident that it fixes my issue.

Tag 42034 (also known as 0xa432) should not be UNDEFINED, but is instead RATIONAL. With the merging of PR #4637, the correct type should now be identified.

Was this page helpful?
0 / 5 - 0 ratings