使用 Python 旋转具有 EXIF 中指定方向的图像,而不使用 PIL,包括缩略图

Posted

技术标签:

【中文标题】使用 Python 旋转具有 EXIF 中指定方向的图像,而不使用 PIL,包括缩略图【英文标题】:Rotating an image with orientation specified in EXIF using Python without PIL including the thumbnail 【发布时间】:2012-12-02 01:54:56 【问题描述】:

我有以下场景:

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

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

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

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

问题:

EXIF 内的缩略图不旋转。所以,图片和缩略图有不同的方向。

问题?

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

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

有没有一种方法可以让我单独旋转缩略图?

感谢您的帮助...

【问题讨论】:

如果您查看Exif 2.2 spec 的第 56 页,您会看到 Orientation 是一个可选标签,可以附加到文件的第一个 IFD 中的缩略图上。我对pyexiv2没有任何经验,但如果你可以通过库在缩略图上设置标签,我敢打赌你可以设置这个。 @BenTrofatter 谢谢伙计...我已经尝试过了,但大多数观众在显示图像/缩略图之前甚至不会看 EXIF。这就是我旋转图像并重置 EXIF 的全部原因。 【参考方案1】:

这个解决方案对我有用: 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 = 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

之前:

之后:

【讨论】:

它是否也旋转缩略图? deg 用于将 PIL 图像 im0 旋转为非常不符合标准的单线:deg = 3:180,6:270,8:90.get(im0._getexif().get(274,0),0) if hasattr(im0,'_getexif') else 0 exif=dict(image._getexif().items()) 你为什么要编一个字典?只需 exif=image.getexif() 即可。【参考方案2】:

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

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

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

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

我用了这条线

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

【讨论】:

【参考方案3】:

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

from PIL import ImageOps

image = ImageOps.exif_transpose(image)

【讨论】:

我希望使用这个超级简单的解决方案,但我得到了这个错误:AttributeError: module 'PIL.ImageOps' has no attribute 'exif_transpose' @DrK,我更新了答案以反映使用 exif_t​​ranspose 函数需要 Pillow 6.0.0+。 此功能不适用于 tif 图像。参考here 太棒了。拯救了我的一天。 如果使用 .png (PIL 7.0) 不起作用,请检查 plhn 评论链接【参考方案4】:

由于这是“python exif rotate”的最佳答案,我想添加一个附录以防您只需要旋转值而不需要 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

【讨论】:

感谢您修复我错误复制的self。我直接使用274 而不是piexif.ImageIFD.Orientation 的原因是为了节省查找时间。【参考方案5】:

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

【讨论】:

这看起来很不错,而且效果很好,谢谢,唯一我找不到如何将 expand 设置为 False,有没有办法用 transpose 做到这一点? @Hyagoro:我认为如果您只是在 scabbiaza 的答案中添加评论而不是完全列出不同的答案会更好 @Georges 很抱歉,我刚刚看到您的评论,我不再想细节了 :( @aspiring1 是的,你可能是对的,我会在下次干预时记住这一点;)【参考方案6】:
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

【讨论】:

以上是关于使用 Python 旋转具有 EXIF 中指定方向的图像,而不使用 PIL,包括缩略图的主要内容,如果未能解决你的问题,请参考以下文章

JS 客户端 Exif 方向:旋转和镜像 JPEG 图像

JQuery-File-Upload BlueImp文件上传EXIF方向旋转

EXIF 方向参数 Orientation

exif-js 方向在 android 上总是返回 0 但在桌面上工作

exif获取图片旋转角度

使用 EXIF 或光标的图像方向始终为 0