使用不包含缩略图的 PIL 使用 Python 旋转 EXIF 中指定的方向的图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13872331/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 09:48:37  来源:igfitidea点击:

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

pythoniosimageexif

提问by ATOzTOA

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.
  • 我正在从 iPhone 发送图像以及 EXIF 信息到我的 Pyhon 套接字服务器。
  • 我需要根据拍摄图像时的实际方向正确定向图像。我知道 IOS 总是将图像保存为横向左侧并将实际方向添加为 EXIF 字段 (EXIF.Image.Orientation)。
  • 我正在阅读 EXIF 字段以查看实际方向。然后我使用 wxpython 将图像旋转到正确的方向。

I am using pyexiv2 for EXIF manipulation.

我正在使用 pyexiv2 进行 EXIF 操作。

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

问题:EXIF 信息包括使用 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.

我做了什么:我在旋转图像之前阅读 EXIF。我重置了 EXIF 中的方向字段。然后我在旋转后把它放回去。

The problem:

问题:

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

EXIF 内的缩略图不会旋转。因此,图像和缩略图具有不同的方向。

Questions?

问题?

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

除了 PIL 之外,还有其他模块可以旋转图像以保留其 EXIF 信息吗?

Is there a separate EXIF field for thumbnail orientation?

缩略图方向是否有单独的 EXIF 字段?

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

有没有办法我可以单独旋转缩略图?

Thanks for your help...

谢谢你的帮助...

采纳答案by ATOzTOA

I have found a solution... check this out... http://www.atoztoa.com/2012/12/rotate-images-along-with-thumbnails-in.html

我找到了一个解决方案......看看这个...... http://www.atoztoa.com/2012/12/rotate-images-along-with-thumbnails-in.html

'''
Rotate Image
'''
import pyexiv2
import wx
import cStringIO
import os

def rotateImage(infile, device):
    try:
        # Read Metadata from the image
        metadata = pyexiv2.metadata.ImageMetadata(infile)
        metadata.read();

        # Let's get the orientation
        orientation = metadata.__getitem__("Exif.Image.Orientation")
        orientation = int(str(orientation).split("=")[1][1:-1])

        # Extract thumbnail
        thumb = metadata.exif_thumbnail

        angle = 0

        # Check the orientation field in EXIF and rotate image accordingly
        if device == "iPhone" or device == "iPad":
            # Landscape Left : Do nothing
            if orientation == ORIENTATION_NORMAL:
                angle = 0
            # Portrait Normal : Rotate Right
            elif orientation == ORIENTATION_LEFT:
                angle = -90
            # Landscape Right : Rotate Right Twice
            elif orientation == ORIENTATION_DOWN:
                angle = 180
            # Portrait Upside Down : Rotate Left
            elif orientation == ORIENTATION_RIGHT:
                angle = 90

            # Resetting Exif field to normal
            print "Resetting exif..."
            orientation = 1
            metadata.__setitem__("Exif.Image.Orientation", orientation)

        # Rotate
        if angle != 0:
            # Just rotating the image based on the angle
            print "Rotating image..."
            angle = math.radians(angle)
            img = wx.Image(infile, wx.BITMAP_TYPE_ANY)
            img_centre = wx.Point( img.GetWidth()/2, img.GetHeight()/2 )
            img = img.Rotate( angle, img_centre, True )
            img.SaveFile(infile, wx.BITMAP_TYPE_JPEG)

            # Create a stream out of the thumbnail and rotate it using wx
            # Save the rotated image to a temporary file
            print "Rotating thumbnail..."
            t = wx.EmptyImage(100, 100)
            thumbStream = cStringIO.StringIO(thumb.data)
            t.LoadStream(thumbStream, wx.BITMAP_TYPE_ANY)
            t_centre = wx.Point( t.GetWidth()/2, t.GetHeight()/2 )
            t = t.Rotate( angle, t_centre, True )
            t.SaveFile(infile + ".jpg", wx.BITMAP_TYPE_JPEG)
            thumbStream.close()

            # Read the rotated thumbnail and put it back in the rotated image
            thumb.data = open(infile + ".jpg", "rb").read();
            # Remove temporary file
            os.remove(infile + ".jpg")

        # Write back metadata
        metadata.write();

    except Exception, e:
        print "Error rotating image... : " + str(e)

回答by scabbiaza

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

此解决方案适用于我: PIL 缩略图正在旋转我的图像?

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

不需要检查它是 iPhone 还是 iPad:如果照片有方向标签 - 旋转它。

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.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

后: 后

回答by joelvarma

https://medium.com/@giovanni_cortes/rotate-image-in-django-when-saved-in-a-model-8fd98aac8f2a

https://medium.com/@giovanni_cortes/rotate-image-in-django-when-saved-in-a-model-8fd98aac8f2a

This blog post explains it clearly. Just make sure you try keeping the @receiver..code in forms.pyor models.pyas I got cannot import model/viewerrors .

这篇博文解释得很清楚。只要确保您尝试将@receiver..代码保留在forms.pymodels.py当我遇到cannot import model/view错误时。

keep the rotate_imagemethod in models.py& @receiver..code also in models.py.

rotate_image方法保留在models.py& @receiver..代码中也保留在models.py.

I also got errors like No such directory. Just make sure full_pathis set correctly to media folder.

我也有类似没有这样的目录的错误。只要确保full_path正确设置到媒体文件夹。

I used this line

我用过这条线

fullpath = os.path.join(os.path.dirname(BASE_DIR)) + instance.fimage.url

fullpath = os.path.join(os.path.dirname(BASE_DIR)) + instance.fimage.url

回答by GaretJax

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

如果您使用Pillow >= 6.0.0,则可以使用内置ImageOps.exif_transpose函数根据其 exif 标签正确旋转图像:

from PIL import ImageOps

image = ImageOps.exif_transpose(image)

回答by user136036

Since this is the top answer for "python exif rotate" I'd like to add an addendum in case you only need the rotation value and not have PIL rotate the image- in my case I used QPixmap to rotate the image on a QGraphicsView, so I only needed the angle for the QPixmap transformation.
The answerabove using PIL to get exif is rather slow. In my test it took 6x the time as the piexiflibrary did (6 ms vs 1 ms) because creating/opening the PIL.Image takes a lot of time. So I'd recommend using piexifin this case.

由于这是“python exif rotate”的最佳答案,我想添加一个附录,以防您只需要旋转值而不需要 PIL 旋转图像- 在我的情况下,我使用 QPixmap 在 QGraphicsView 上旋转图像,所以我只需要 QPixmap 转换的角度。上面使用PIL 获取 exif
答案相当慢。在我的测试中,它花费的时间是piexif库的6 倍(6 毫秒 vs 1 毫秒),因为创建/打开 PIL.Image 需要很多时间。所以我建议piexif在这种情况下使用。

import piexif

def get_exif_rotation_angle(picture)

    exif_dict = piexif.load(picture)
    if piexif.ImageIFD.Orientation in exif_dict["0th"]:
        orientation = exif_dict["0th"][piexif.ImageIFD.Orientation]
        if orientation == 3:
            return 180
        elif orientation == 6:
            return 90
        elif orientation == 8:
            return 270
        else:
            return None
    else:
        return None

picturecan befile path or bytes object.

picture可以是文件路径或字节对象。

Ref: https://piexif.readthedocs.io/en/latest/sample.html#rotate-image-by-exif-orientation

参考:https: //piexif.readthedocs.io/en/latest/sample.html#rotate-image-by-exif-orientation

回答by Hyagoro

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

与@scabbiaza 的答案几乎相同,但使用转置而不是旋转(出于性能目的)。

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