SealHuang +

计算机视觉应用开发中踩到EXIF的坑

最近开发了一个人脸个性信息提取的小应用,中间利用到了人脸检测等模型;想到系统在线下测试中表现还不错,就找了几个朋友试玩体验一下,谁知好几个人和我说,自己的自拍照上传完没有任何反馈,是不是出了什么问题?

我赶紧做了检查,发现出问题的图片都来源于手机自拍,同时程序在读取图片数据后,获取的图像高宽数值正好颠倒过来(如一张照片的高宽为700 X 350,程序读取出来成了350 X 700),隐约感觉问题可能出在了这个地方。想到我的人脸检测模块都是用正立面孔做的训练(没有加rotation等augmentation),如果输入图像的人脸旋转了90/270度,其结果不好倒是可以预期了。

有了这个方向,便开始搜索相关的信息,终于找到了问题的根源:相机的EXIF信息。

EXIF信息是什么

可交换图像文件格式常被简称为EXIF(Exchangeable Image File Format),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据… EXIF可以附加于JPEG、TIFF、RIFF等文件之中。

相机的旋转方向

EXIF涵盖的各种信息之中,其中有一个叫做Orientation的标签,用于记录图像的方向,这便是相机写入方向信息的最终位置。它总共定义了八个值:

Orientation定义

注意:对于上面的八种方向中,加了*的并不常见,因为它们代表的是镜像方向,如果不做任何的处理,不管相机以任何角度拍摄,都无法出现镜像的情况。

这个表格定义了相机拍照角度的信息。如第一行中,值为1时,右边两列的值分别为:Row #0 is Top,Column #0 is Left side,它表示照片的第一行位于顶端,而第一列位于左侧,那么这张照片自然就是以正常角度拍摄的。

那么在相机处于不同的拍摄角度时,由于相机都是以其自身的坐标系来保存照片,因此每张照片对应的第一行和第一列的位置和EXIFOrientation的值如下所示:

相机不同角度时的orientation值

由于相机加上了方向传感器的缘故,可以非常容易的检测出以上几种拍摄角度,并将角度对应的Orientation值保存至图像中。

iPhone上的情况

作为智能手机的重要组成部分,形形色色的传感器自然必不可少。在iOS的设备中也是包含了这样的方向传感器,它也采用了同样的方式来保存照片的方向信息到EXIF中。但是它默认的照片方向并不是竖着拿手机时的情况,而是横向,即Home键在右侧,如下:

iPhone默认拍照方向

如此一来,如果竖着拿手机拍摄时,就相当于对手机顺时针旋转了90度,也即上面相机图片中的最后一幅,那么它的Orientation值为6。

iPhone正立拍照方向

EXIF信息是如何影响数据处理的

EXIF原先是用在TIFF图像格式上,后来才加入到JPEG图像格式中,而图像数据集中的图片大多是JPEG格式。大多数训练图像中则不含EXIF信息(PNG图像无法保存EXIF信息)。

一些程序为了保持向后兼容性,不会去解析EXIF信息。大多数用于处理图像数据的Python库(如NumPy、SciPy,TensorFlow,Keras等)就是这样的。

这意味着当你使用这些工具导入图像时,都将获得原始的未旋转图像数据。如果把这些侧躺着或上下颠倒的图像输入到CV模型中,会得到错误的检测结果;如在我现在描述的场景中,图像就旋转了270度,导致图像的宽高颠倒了。据说很多大厂的应用或API也会因为忽略EXIF信息而产生错误。

因此在模型线上应用时,还是要注意用户自主上传的含有EXIF信息的图片,根据Orientation信息对图像进行“矫正”,才能让模型正常工作。

如何用Python的PIL包解决这个问题

Pillow>=6.0.0的版本里,已经有一个现成的工具帮助用户根据EXIFOrientation信息旋转图像,使用很简单:

from PIL import ImageOps
image = ImageOps.exif_transpose(image)

它的具体处理见这里

在我的程序中加入这部分代码后,后续处理就都正常了。现在想想自己真的是犯了一个挺“愚蠢”的错误。

扩展:为什么要有EXIF信息

一切都得从相机的发展开始说起。

胶片时代

一般相机拍摄出来的画面都是长方形,在拍摄的那一瞬间,它会将取景器中的场景对应的颜色值存到对应的像素位置。相机本身并没有任何方向的概念,只是使用者想要拍摄的场景在他期望的照片中显示的方式与实际存在差异时,才有了方向一说。如下图,对一个场景F进行拍摄,相机的方向可能会有这样四个常见的角度:

相机的拍摄角度

传统相机并不知道自己的旋转角度,由于相机仅反应真实的场景,它不理解拍摄的内容,因此照片都以相机的坐标系保存,于是上面四种情形实际拍摄出来的照片会像这样:

相机保存的图像

最初的卡片机时代,照片都会经由底片洗出来。那时不存在照片的方向问题,因为不管我们以何种角度拍摄,最终洗出来的照片,它本身非常容易旋转,所以我们总可以通过简单的旋转来观看照片或者保存照片。比如这张照片墙中的照片,你能否说哪些照片是横着?哪些颠倒着?你甚至都无法判断每张照片相机是以何种角度拍摄的,因为每张都已经旋转至适合观看的角度。

照片墙

数据时代

可是到了数码时代,不再需要底片,照片需要被存成一个图像文件。对于上面的拍摄角度,存储方式并没有变化,所有的场景仍然是以相机的坐标系来保存。于是这些照片仍像上面一样,原封不动的保存了下来:

相机保存的图像

虽然存储方式不变,和卡机机时代的实体相片不同的是,由于电脑屏幕可没洗出来的照片那么容易旋转,所以照片只能够以它存储于磁盘中的方向来展示。这便是为何照片传到电脑上之后,会出现横了,或者颠倒的情况。正因为这样,我们只有利用工具来旋转照片才能够正常观看。

方向传感器

为了克服这一情况,让照片可以真实的反应人们拍摄时看到的场景,现在很多相机中就加入了方向传感器,它能够记录下拍摄时相机的方向,并将这一信息保存在照片中。照片的存储方式还是没有任何改变,它仍然是以相机的坐标系来保存,只是当相机来浏览这些照片时,相机可以根据照片中的方向信息,结合此时相机的方向,对照片进行旋转,从而转到适合人们观看的角度。

注:本文部分内容摘抄自如何处理iOS中照片的方向

Blog

Technique

Theory