使用Python而不使用PIL(包括缩略图)在EXIF中指定方向旋转图像

时间:2012-12-14 03:43:13

标签: python ios image exif

我有以下情况:

  • 我正在将iPhone中的图像连同EXIF信息一起发送到我的Pyhon套接字服务器。
  • 我需要根据拍摄图像时的实际方向正确定位图像。我知道IOS总是将图像保存为Landscape Left并将实际方向添加为EXIF字段(EXIF.Image.Orientation)。
  • 我正在阅读EXIF字段以查看实际方向。然后我使用wxpython将图像旋转到正确的方向。

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

问题:EXIF信息包括使用wxpython旋转图像时丢失的缩略图。

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

问题:

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

有问题吗?

是否有除PIL之外的任何模块来旋转保留其EXIF信息的图像?

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

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

感谢您的帮助......

6 个答案:

答案 0 :(得分:53)

此解决方案适合我: PIL thumbnail is rotating my image?

不需要检查它是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

在: After

答案 1 :(得分:11)

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

from PIL import ImageOps

image = ImageOps.exif_transpose(image)

答案 2 :(得分:5)

我找到了一个解决方案......请检查一下... 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)

答案 3 :(得分:2)

答案与@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

答案 4 :(得分:0)

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

这篇博客文章清楚地解释了这一点。请确保在@receiver..forms.py中保留models.py代码,因为我遇到了cannot import model/view错误。

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

我也遇到了像No这样的目录的错误。只需确保full_path已正确设置为媒体文件夹。

我用过这一行

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

答案 5 :(得分:0)

因为这是“ python exif旋转”的最佳答案,所以我想添加一个附录,以防您仅需要旋转值而无需PIL旋转图像-在我的情况下,使用QPixmap在QGraphicsView上旋转图像,因此我只需要QPixmap转换的角度即可。
上面的answer使用 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

picture can be文件路径或字节对象。

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