Rotating an image with orientation specified in EXIF using Python without PIL including the thumbnail

46,586

Solution 1

This solution works for me: PIL thumbnail is rotating my image?

Don't need to check if it's iPhone or iPad: if photo has orientation tag – rotate it.

from PIL import Image, ExifTags

try:
    image=Image.open(filepath)

    for orientation in ExifTags.TAGS.keys():
        if ExifTags.TAGS[orientation]=='Orientation':
            break
    
    exif = image._getexif()

    if exif[orientation] == 3:
        image=image.rotate(180, expand=True)
    elif exif[orientation] == 6:
        image=image.rotate(270, expand=True)
    elif exif[orientation] == 8:
        image=image.rotate(90, expand=True)

    image.save(filepath)
    image.close()
except (AttributeError, KeyError, IndexError):
    # cases: image don't have getexif
    pass

Before:

Before

After: After

Solution 2

If you're using Pillow >= 6.0.0, you can use the built-in ImageOps.exif_transpose function do correctly rotate an image according to its exif tag:

from PIL import ImageOps

image = ImageOps.exif_transpose(image)

Solution 3

Pretty much the same answer than @scabbiaza, but using transpose instead of rotate (for performance purposes).

from PIL import Image, ExifTags

try:
    image=Image.open(filepath)
    for orientation in ExifTags.TAGS.keys():
        if ExifTags.TAGS[orientation]=='Orientation':
            break
    exif=dict(image._getexif().items())

    if exif[orientation] == 3:
        image=image.transpose(Image.ROTATE_180)
    elif exif[orientation] == 6:
        image=image.transpose(Image.ROTATE_270)
    elif exif[orientation] == 8:
        image=image.transpose(Image.ROTATE_90)
    image.save(filepath)
    image.close()

except (AttributeError, KeyError, IndexError):
    # cases: image don't have getexif
    pass

Solution 4

from PIL import Image

def reorient_img(pil_img):
    img_exif = pil_img.getexif()

    if len(img_exif):
        if img_exif[274] == 3:
            pil_img = pil_img.transpose(Image.ROTATE_180)
        elif img_exif[274] == 6:
            pil_img = pil_img.transpose(Image.ROTATE_270)
        elif img_exif[274] == 8:
            pil_img = pil_img.transpose(Image.ROTATE_90)

    return pil_img
Share:
46,586
ATOzTOA
Author by

ATOzTOA

Programming is my passion... any programming language, you name it, I have used it... well except SmallTalk... Site: www.atoztoa.info Email: [email protected] SOreadytohelp

Updated on December 13, 2021

Comments

  • ATOzTOA
    ATOzTOA over 2 years

    I have the following scenario:

    • I am sending an image from iPhone along with the EXIF information to my Pyhon socket server.
    • I need the image to be properly oriented based on the actual orientation when the image was taken. I know IOS always saves the image as Landscape Left and adds the actual orientation as EXIF field (EXIF.Image.Orientation).
    • I am reading the EXIF field to see the actual orientation. Then I am rotating the image using wxpython to the proper orientation.

    I am using pyexiv2 for EXIF manipulation.

    Issue: The EXIF information incluiding the thumbnails lost while rotating the image using wxpython.

    What I did: I am reading the EXIF before rotating the image. I reset the orientation field in the EXIF. Then I am putting it back after rotation.

    The problem:

    The thumbnail inside the EXIF is not rotated. So, the image and thumbnail have different orientations.

    Questions?

    Is there any module other than PIL to rotate an image keeping its EXIF info?

    Is there a separate EXIF field for thumbnail orientation?

    Is there a way I can just rotate the Thumbnail alone?

    Thanks for your help...

  • ATOzTOA
    ATOzTOA over 9 years
    Does it rotate the thumbnail too?
  • Oliver Zendel
    Oliver Zendel about 5 years
    deg for rotation of PIL image im0 as a very unpythonic one-liner: deg = {3:180,6:270,8:90}.get(im0._getexif().get(274,0),0) if hasattr(im0,'_getexif') else 0
  • Dr K
    Dr K over 4 years
    I was hoping to use this super simple solution but I get this error: AttributeError: module 'PIL.ImageOps' has no attribute 'exif_transpose'
  • GaretJax
    GaretJax over 4 years
    @DrK, I updated the answer to reflect that Pillow 6.0.0+ is required to use the exif_transpose function.
  • user136036
    user136036 about 4 years
    exif=dict(image._getexif().items()) why do you dict a dict? Just exif=image.getexif() works fine.
  • user136036
    user136036 about 4 years
    Thank you for fixing the self I incorrectly copied.. The reason I used 274 directly instead of piexif.ImageIFD.Orientation is to save lookup time.
  • plhn
    plhn about 4 years
    This function does not works well for tif images. Refere here
  • Georges D
    Georges D almost 4 years
    This looks very good, and it works perfectly, thank you, the only thing I couldn't find how to set expand to False, is there a way to do that with transpose?
  • aspiring1
    aspiring1 over 3 years
    @Hyagoro: I think it'd have been better if you would've just added a comment to scabbiaza's answer, instead of listing a different answer altogether
  • Hyagoro
    Hyagoro over 3 years
    @Georges I'm sorry I just saw your comment and I don't have the details in mind anymore :(
  • Hyagoro
    Hyagoro over 3 years
    @aspiring1 Yeah, you're probably right, I'll keep that in mind for my next intervention ;)
  • jerome
    jerome almost 3 years
    check the plhn comment link if this does not work using .png (PIL 7.0)
  • Salvatore Iovene
    Salvatore Iovene about 2 years
    When I do this, the resulting image is now rotated, but the EXIF rotation information is still in the file, so when I open it with Safari the rotation is applied twice (the first time it was hardcoded in the transposed image, and the second time because Safari rotated it again after reading the EXIF). Should I drop the EXIF rotation when I transpose the image? How? Thanks!
  • Louis Yang
    Louis Yang almost 2 years
    I got AttributeError: _getexif. I think you need to change to image.getexif() instead now.