yolov5踩坑记录:标签错位(PIL读取图片方向异常)

PIL踩坑记录:读取图片方向异常(yolov5标签错位)

奇怪的现象

今天用 YOLOv5 做项目时,对数据集的标记出现了奇怪的现象,在下述测试用例中可明显看到,标记框偏离了物体,故发文阐述原因和解决方法. ——©️ Sylvan Ding 转载需注明出处

在这里插入图片描述

下载了一份数据集UNIMIB2016,使用 PIL 读取其中一张照片并标记 bbox.

from PIL import Image

img_name = '20151127_120643.jpg'
img = Image.open(img_name)
img.show()

在这里插入图片描述

from PIL import ImageDraw
from PIL import ImageFont

label_name = '20151127_120643.txt'
with open(label_name) as file:
    items = file.readlines()
    
draw = ImageDraw.Draw(img)
for item in items:
  
    item = item.split()
    
    # convert str to int
    item[1:] = list(map(int, item[1:]))
    
    # draw bbox
    draw.rectangle([item[1], item[2], item[5], item[6]],
                       outline="red", width=20)
    
    # add text about order of the points
    for j, i in enumerate([1, 3, 5, 7]):
        j = j + 1
        draw.text([item[i], item[i + 1]], str(j))

img.show()
        
# items
# ['pane 2008 404 2713 404 2713 915 2008 915\n',
#  'pizzoccheri 1502 1104 2500 1104 2500 1984 1502 1984\n',
#  'arrosto 634 602 1193 602 1193 1371 634 1371\n',
#  'patate/pure 271 539 708 539 708 1408 271 1408\n']

# the lash item, whose form is [cls, x1, y1, x2, y2, x3, y3, x4, y4]
# ['patate/pure', 271, 539, 708, 539, 708, 1408, 271, 1408]

在这里插入图片描述

bbox 正确,但整张图片好似被旋转了一样。接着使用 cv2 进行相同操作,并观察结果。

import cv2
from matplotlib import pyplot as plt

img = cv2.imread(img_name)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

for item in items:

    item = item.split()

    # convert str to int
    item[1:] = list(map(int, item[1:]))

    # draw bbox
    cv2.rectangle( img,
                  (item[1], item[2]),
                  (item[5], item[6]),
                  (0, 255, 0), 30)

plt.imshow(img)
plt.show()

在这里插入图片描述

不难发现,draw bbox 中使用的坐标均为 [x1,y1], [x3, y3],即对角坐标(left-top、right-bottom),但 PILcv2 的结果不同。cv2 中 bbox 错位了,而 PIL 中 text 发生了旋转.

😯 为什么会产生这种现象呢?从 EXIT 说起:

什么是EXIF?

EXIF(Exchangeable Image File)是"可交换图像文件"的缩写,当中包含了专门为数码相机的照片而定制的元数据,可以记录数码照片的拍摄参数、缩略图及其他属性信息。Exif 文件实际是JPEG文件的一种,遵从JPEG标准,只是在文件头信息中增加了有关拍摄信息的内容和索引图。

简单来说,Exif 信息就是由数码相机在拍摄过程中采集一系列的信息,然后把信息放置在我们熟知的 JPEG/TIFF 文件的头部,也就是说 Exif信息是镶嵌在 JPEG/TIFF 图像文件格式内的一组拍摄参数,它就好像是傻瓜相机的日期打印功能一样,只不过 Exif信息所记录的资讯更为详尽和完备。

EXIF Orientation tag

EXIF Orientation tag(EXIF方向参数)让你随便照像但都可以看到正确方向的照片而无需手动旋转(前提要图片浏览器支持,Windows 自带的不支持).

在这里插入图片描述

⭐️ 原因解释

造成第一目中“奇怪现象”的原因,我将其归纳为:在标记数据集时,不关注图像的 EXIF Orientation tag,而图像本身是含有 EXIF Orientation tag 的。在 PIL 读取图片时,创建了 Image 对象,其中存有 EXIF Orientation tag,根据该标记,不仅旋转了图像,还旋转了图像的参考系。而 cv2 读取图片时,直接生成 numpy.ndarray 数组,只根据 EXIF Orientation tag 对图像进行了旋转,但并没有旋转图像的参考系

PIL

在这里插入图片描述

在这里插入图片描述

cv2

在这里插入图片描述

在绘制矩形框时,二者依赖的参考系不同,导致标记的错位。而 yolov5 使用的正是 cv2,故读取含 EXIF Orientation tag 的图片时,会造成 labels 和 图片的错位。

原因证实

PIL 的 PIL.ImageOps.exif_transpose(image)方法,可以在读取图片后,清除 Image 对象内的 EXIF Orientation tag,从而让坐标系不受该 tag 的影响.

from PIL import ImageOps

img = Image.open(img_name)
img = ImageOps.exif_transpose(img)

# draw bbox
...

1

可见,清除 EXIF 旋转信息后,PIL 所得结果和 cv2 结果一致!

⭐️ 解决方法

❤️ 当然,如果你有更好的方法欢迎在评论区留言告诉我哦!

from os import listdir
from PIL import Image
import numpy as np

img = './images/'
if __name__ == '__main__':
    for img_name in listdir(img_path):
        img = Image.open(img_path + img_name)
        img_rectified = Image.fromarray(np.asarray(img))
        img_rectified.save(img_path + img_name)

上述代码对 ./images/文件夹下所有图片进行了“修正”,核心是 img_rectified = Image.fromarray(np.asarray(img)),(1). 将 img 转化为 ndarray,(2). 再将该ndarray转化为Image并保存.

(1)、(2) 等同于去除了图片所有的 EXIF 信息,这样图片就不会再发生“自动旋转”的现象了.

在这里插入图片描述

转载请注明出处:©️ Sylvan Ding

参考文献

  1. PIL thumbnail is rotating my image?
  2. 图片元信息Exif,给你详细讲讲
  3. EXIF 方向参数 Orientation
  • 13
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值