翻译转自jdhao's blog
前几天,我在工作中遇到了一个奇怪的错误。我的同事给了我一些用智能手机拍摄的照片。在他们的计算机上(Windows 7),所有照片均以横向模式正确显示。但是,当我查看这些照片时,发现其中一些照片是以纵向模式(旋转90或270度)或上下颠倒(旋转180度)显示的。
我很好奇发生了什么,并了解了 Exif及其所有相关内容。
Exif定向如何工作
什么是Exif
Exif(可交换图像文件格式)是一种协议,用于存储有关数码相机拍摄的图像的各种元信息。Exif与实际图像数据一起存储。Exif中的一些元信息包括相机制造商,快门速度,焦距,方向,拍摄时间等。这些元信息称为标签,每个标签都有一个由Exif格式标准决定的特定标签号。标签的完整列表及其相关信息可在此处找到。
Exif方向标志
在这里,我们对方向元信息感兴趣。用相机拍摄照片时,可能并不总是将相机保持在相机顶部与场景顶部相对应的位置。该博客下面的图片 清楚地说明了这个想法:
但是,无论如何握持相机,如果您在计算机上查看图像,图像都将以正确的方向显示。这与Exif方向标志有关。当您以非直立姿势握持相机时,所拍摄的原始照片将存储为旋转图像。数字设备(可以是智能手机或数码相机)具有传感器,可以记录相机的方向,并将该信息写入Exif中的方向标志。
Exif方向标记可以具有1到9 的9个不同值。下图显示了其中的八个:
通常,对于数码照片,您只会获得标志1、8、3、6。标志2、7、4、5代表镜像和旋转的图像版本。
为什么我的图像显示错误?
当您使用照片查看器检查计算机上的图像时,如果照片查看器可以读取Exif信息并尊重该信息,它将基于方向信息自动旋转原始图像。最终结果是,无论实际存储方式如何,您都可以看到方向正确的照片。
现在在本文开头讨论这个问题。根据 本文,Windows 8之前的Windows系统不会考虑Exif方向标志并按原样显示图像,即,显示原始的未旋转图像,而不是正确旋转的图像。由于同事们给我的图像可以在Windows 7计算机上正确显示,因此我们可以得出结论,原始图像的方向正确。不知何故,智能手机为某些照片报告了错误的方向标记。当我在Windows 10机器上显示这些照片时,由于Windows 10遵循方向标记,由于错误的方向标记,某些图像将显示为旋转。
另一方面,如果您在Windows 10上看到正确的照片但在Windows 7机器上旋转了一张照片,那是因为原始图像处于旋转位置,并且Windows 7不遵守Exif中的方向信息。
读取和写入Exif信息
IrfanView
IrfanView是Windows上出色的图像查看器,它尊重图像Exif信息。要查看图像Exif信息,请打开图像,然后单击Image -> Information
。如果图像包含Exif信息,则可以单击EXIF info
弹出窗口左下方的按钮以检查图像Exif信息。
禁用IrfanView的自动旋转
默认情况下,IrfanView会遵守Exif信息,并将根据其方向标记自动旋转图像。要禁用此行为,请转到Options -> Properties/Settings
,单击JPG/PCD/GIF
并取消选中该框Auto-rotate image according to EXIF info (if available)
。
Pillow和Exif信息
如果您使用Pillow读取图像并显示或再次保存,则Pillow将不遵守Exif方向标签。您可能在这里 和这里看到这样的问题。有一个 拉取请求可以解决此问题,该请求现已合并。
Pillow能够读取图像的Exif信息,但无法编辑Exif信息。下面显示了显示图像Exif信息的示例脚本:
from PIL import Image
from PIL. from PIL.ExifTags import TAGS
img = Image.open('test.jpg')
exif = img.getexif()
for k, v in exif.items():
print('{}: {}'.format(TAGS[k], v))
在上面的脚本中,我们使用 Image.getexif()
3检索图像Exif信息。 TAGS
是将标签号映射到描述性名称的词典。
Piexif
Python程序包piexif可用于读取和写入图像Exif信息。
基于 其文档站点上的 示例,我展示了一个更改图像Exif方向标记并使用新的Exif信息保存新图像的示例。
from PIL import Image
import piexif
img = Image.open('test.jpg')
if "exif" in img.info:
exif_dict = piexif.load(img.info['exif'])
if piexif.ImageIFD.Orientation in exif_dict['0th']:
exif_dict['0th'][pixeif.ImageIFD.Orientation] = 3
# quick and dirty work around to avoid type error
exif_dict['Exif'][41729] = b'1'
exif_bytes = piexif.dump(exif_dict)
img.save('new_img.jpg', exif=exif_bytes)
Exif的主要信息存储在的0th
键中exif_dict
,该键也是Python字典。似乎piexif并未检查Exif dict的值类型,因此 当我们尝试转储exif_dict时可能会 遇到ValueError。阅读piexif的源代码之后,我现在使用下面的代码行来解决此问题。
exif_dict['Exif'][41729] = b'1'
之后,您应该能够正确地转储exif_dict
。