Opencv与Pillow图片操作差异对深度学习的影响

目前在使用Pytorch训练的深度学习模型算法,大部分由于pillow与torchvision中transforms的优异兼容都会采用Image.open from pillow的方式进行图像数据的读取和crop or resize。

这种方案在纯学术环境下不会暴露其问题,但是用到工业部署环境下就会暴露其致命的问题。由于目前C++工业部署大部分使用的还是opencv框架进行图像数据的处理,但是我们发现opencv读取图像进行resize操作时,其模型的预测精度比pillow前处理版本将下降10%到60%,这巨大的gap不由得引起我们的关注。为什么会造成这种情况。
于是我们对同一种图像分别用两个框架进行读取、resize这两个操作判断其像素间的差异。

import cv2
from PIL import Image
import numpy as np

# pillow读取并转换为int8格式数组
pil_img = Image.open("test.jpg").convert("RGB")
pil_arr = np.uint8(np.array(pil_img))

# opencv读取并转换为int8格式数组
cv_img = cv2.imread("test.jpg")
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
cv_arr = np.uint8(np.array(cv_img))

# 计算其读取误差
mean_error = np.mean(np.absolute(pil_arr - cv_arr))

读取平均像素误差

在这里我们就发现这两种框架在读取上就会造成误差,但是opencv和pillow读取有误差已经算是一个常识了,并且在未归一化的前提下只有0.0003像素值的平均误差已经很低了,应该不是造成巨大gap的原因。

归一化后的读取误差可以忽略不计

为了进一步的验证不是读取造成的模型推理精度误差,我们使用opencv读取的图片转换为pillow格式后,用pillow前处理然后推理得到的结果和pillow本身读取推理的结果是一致的。

接下来我们进一步对resize操作进行验证,两个框架均使用同一种resize算法:

# pillow resize
pil_img = pil_img.resize((300, 300), Image.BICUBIC)
pil_arr = np.uint8(np.array(pil_img))

# opencv读取并转换为int8格式数组
cv_img = cv2.resize(cv_img, (300, 300), cv2.INTER_CUBIC)
cv_arr = np.uint8(np.array(cv_img))

# 计算其resize误差
mean_error = np.mean(np.absolute(pil_arr - cv_arr))

resize平均像素误差

77的平均像素误差!要知道像素值的范围也只有0到255,将近三分之一的误差,说明两者在同一种resize的实现逻辑一定有巨大差异,这种误差将在使用opencv部署pillow训练模型的时候造成巨大的精度gap。

后续对此情况进行调研,发现这个问题在2017年的时候,就已经有人在pillow的官方仓库下提出。

pillow仓库问题链接

pillow框架在resize算法实现上和opencv、matlab均有巨大差异,但是pillow并不认为这是他们的问题,所以也一直没有修改。

从开发者的角度上来说,为了工业部署时对精度的良好把控,建议在训练时不要使用pillow框架和torchvision自带的前处理方案,保证训练和部署对数据处理的一致性,避免这种由框架不同导致的巨大gap。

相关解决方案

posted @ 2023-05-05 13:11  萧霍之  阅读(55)  评论(1编辑  收藏  举报