不同数据类型会有不同取整行为吗?cv2与PIL图像读取差异解析
旧版Pillow(8.4.0)中cv2与PIL读取图像转numpy数组的类型相关差异解析
问题场景
在使用Pillow 8.4.0版本时,发现以下现象:
- 用
cv2.imread读取图像并转RGB后,与PIL.Image.open读取的同一张图像,转换为默认uint8类型的numpy数组时,两者数值存在可见差异; - 但将两者的数据类型强制转为
int16后,得到的numpy数组结果完全一致。
测试验证
uint8类型测试代码
import cv2 import matplotlib.pyplot as plt import numpy as np from PIL import Image img1 = cv2.cvtColor(cv2.imread('post_5c606e629f934.jpg'), cv2.COLOR_BGR2RGB) img2 = Image.open('post_5c606e629f934.jpg') fig, ax = plt.subplots(1, 3, figsize=(15, 5)) ax[0].imshow(img1) ax[0].set_xlabel("cv2.imread") ax[1].imshow(img2) ax[1].set_xlabel("PIL.Image.open") ax[2].imshow(img2 - img1) ax[2].set_xlabel("difference") plt.show()
运行结果:
int16类型测试代码
img1 = img1.astype(np.int16) img2 = np.array(img2, dtype=np.int16) fig, ax = plt.subplots(1, 3, figsize=(15, 5)) ax[0].imshow(img1) ax[0].set_xlabel("cv2.imread") ax[1].imshow(img2) ax[1].set_xlabel("PIL.Image.open") ax[2].imshow(img2 - img1) ax[2].set_xlabel("difference") plt.show()
运行结果:
原因分析
这是Pillow 8.4.0及更早版本的色彩转换数值处理bug:
- 对于JPEG这类基于YCbCr色彩空间的图像,PIL在将其转换为RGB并输出
uint8数组时,采用了和OpenCV不同的数值取整/截断逻辑——PIL会在中间浮点计算步骤直接截断小数部分得到uint8值,而OpenCV则采用四舍五入的方式处理; - 当指定转换为
int16类型时,PIL会保留色彩转换过程中的完整浮点计算结果,不会提前做uint8的截断处理,因此和OpenCV转int16后的数值完全对齐,差异消失。
修复方案
该问题在Pillow 9.0.0及以上版本中已被官方修复,新版本统一了色彩转换时的数值处理逻辑,和OpenCV等主流图像库的输出结果保持一致,升级Pillow即可解决此差异问题。
内容的提问来源于stack exchange,提问作者Никита Подсеваткин
相关产品推荐
相关产品推荐

