前言
Exif 规范 定义了方向标签,用于指示相机相对于所捕获场景的方向。相机可以使用该标签通过方向传感器自动指示方向,也可以让用户通过菜单开关手动指示方向,而无需实际转换图像数据本身。
在图像处理过程中,若是原图文件包含了方向 Orientation
信息,会导致输出的图片在方向上有些许偏差。一般我们需要在处理图像之前将方向信息去掉,并将图像处理成正确的展示形式。
Orientation说明
拍摄图像时相机相对于场景的方向。“第 0 行”和“第 0 列”与视觉位置的关系如下所示。
值 | 第 0 行 | 第 0 列 | 描述 |
---|---|---|---|
1 | 顶部 | 左边 | 0度:正确方向,无需调整 |
2 | 顶部 | 右边 | 水平翻转 |
3 | 底部 | 右边 | 180度旋转 |
4 | 底部 | 左边 | 水平翻转+180度旋转 (垂直翻转) |
5 | 左边 | 顶部 | 水平翻转+顺时针270度 |
6 | 右边 | 顶部 | 顺时针270度 |
7 | 右边 | 底部 | 水平翻转+顺时针90度 |
8 | 左边 | 底部 | 顺时针90度 |
图例说明:
如何查看
系统自带的 preview
的显示检查器可直接查看:
通过命令行工具
Mac可以安装 brew install exiftool
后使用 exiftool
工具进行查看:
处理方式
既然知道了方向定义的含义,就按照相反的方式就行处理即可。
自己通过 Pillow
库实现了一个简单的方法:
from PIL import Image
def reset_image_rotate(im: Image) -> Image:
# 0x0112 EXIF tags: Orientation ,see PIL.ExifTags.TAGS
orientation_code = im.getexif().get_ifd(0x0112)
if orientation_code == 2:
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
elif orientation_code == 3:
im = im.transpose(Image.Transpose.ROTATE_180)
elif orientation_code == 4:
im = im.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
elif orientation_code == 5:
im = im.transpose(Image.Transpose.TRANSPOSE) # 矩阵转置
elif orientation_code == 6:
im = im.transpose(Image.Transpose.ROTATE_270) # 逆时针270度
elif orientation_code == 7:
im = im.transpose(Image.Transpose.TRANSVERSE)
elif orientation_code == 8:
im = im.transpose(Image.Transpose.ROTATE_90) # 逆时针90度
return im
注意
:后来查看Pillow官方文档时发现,库中已提供了现成的方法。
from PIL import ImageOps
img = ImageOps.exif_transpose(img)
源码如下:
def exif_transpose(image):
"""
If an image has an EXIF Orientation tag, return a new image that is
transposed accordingly. Otherwise, return a copy of the image.
:param image: The image to transpose.
:return: An image.
"""
exif = image.getexif()
orientation = exif.get(0x0112)
method = {
2: Image.FLIP_LEFT_RIGHT,
3: Image.ROTATE_180,
4: Image.FLIP_TOP_BOTTOM,
5: Image.TRANSPOSE,
6: Image.ROTATE_270,
7: Image.TRANSVERSE,
8: Image.ROTATE_90,
}.get(orientation)
if method is not None:
transposed_image = image.transpose(method)
transposed_exif = transposed_image.getexif()
if 0x0112 in transposed_exif:
del transposed_exif[0x0112]
if "exif" in transposed_image.info:
transposed_image.info["exif"] = transposed_exif.tobytes()
elif "Raw profile type exif" in transposed_image.info:
transposed_image.info[
"Raw profile type exif"
] = transposed_exif.tobytes().hex()
elif "XML:com.adobe.xmp" in transposed_image.info:
transposed_image.info["XML:com.adobe.xmp"] = re.sub(
r'tiff:Orientation="([0-9])"',
"",
transposed_image.info["XML:com.adobe.xmp"],
)
return transposed_image
return image.copy()
参考
- Exif Orientation Tag (Feb 17 2002)
- PIL.ImageOps.exif_transpose