当使用 OpenCV 完成图像加载和调整大小时,Resnet50 会产生不同的预测
Posted
技术标签:
【中文标题】当使用 OpenCV 完成图像加载和调整大小时,Resnet50 会产生不同的预测【英文标题】:Resnet50 produces different prediction when image loading and resizing is done with OpenCV 【发布时间】:2021-01-15 20:18:22 【问题描述】:我想使用使用 OpenCV 的 Keras Resnet50 模型来读取和调整输入图像的大小。 我正在使用来自 Keras 的相同预处理代码(使用 OpenCV,我需要转换为 RGB,因为这是 preprocess_input() 所期望的格式)。 我使用 OpenCV 和 Keras 图像加载得到的预测略有不同。我不明白为什么预测不一样。
这是我的代码:
import numpy as np
import json
from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
import cv2
model = ResNet50(weights='imagenet')
img_path = '/home/me/squirle.jpg'
# Keras prediction
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
preds = model.predict(x)
print('Predicted Keras:', decode_predictions(preds, top=3)[0])
# OpenCV prediction
imgcv = cv2.imread(img_path)
dim = (224, 224)
imgcv_resized = cv2.resize(imgcv, dim, interpolation=cv2.INTER_LINEAR)
x = cv2.cvtColor(imgcv_resized , cv2.COLOR_BGR2RGB)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
preds = model.predict(x)
print('Predicted OpenCV:', decode_predictions(preds, top=3)[0])
Predicted Keras: [('n02490219', 'marmoset', 0.28250763), ('n02356798', 'fox_squirrel', 0.25657368), ('n02494079', 'squirrel_monkey', 0.19992349)]
Predicted OpenCV: [('n02356798', 'fox_squirrel', 0.5161952), ('n02490219', 'marmoset', 0.21953616), ('n02494079', 'squirrel_monkey', 0.1160824)]
如何使用 OpenCV imread()
和 resize()
获得与 Keras 图像加载相同的预测?
【问题讨论】:
【参考方案1】:# Keras prediction
img = image.load_img(img_path, target_size=(224, 224))
# OpenCV prediction
imgcv = cv2.imread(img_path)
dim = (224, 224)
imgcv_resized = cv2.resize(imgcv, dim, interpolation=cv2.INTER_LINEAR)
如果你仔细看,你在案例中指定的插值
cv2 的值为cv2.INTER_LINEAR
(双线性插值);但是,默认情况下,
image.load_img()
使用INTER_NEAREST
插值方法。
img_to_array(img)
。 dtype
这里的参数是:无
默认为无,在这种情况下全局设置 tf.keras.backend.floatx() 被使用(除非你改变它,它默认 到“float32”)
因此,在img_to_array(img)
中,您有一个由float32
值组成的图像,而cv2.imread(img)
返回一个由uint8
值组成的numpy 数组。
-
确保从 BGR 转换为 RGB,因为 OpenCV 直接加载为 BGR 格式。您可以使用
image = image[:,:,::-1]
或image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
;否则 R 和 B 通道会颠倒,导致比较不正确。
由于您应用的预处理在两种情况下都是相同的,因此唯一的区别是我上面提到的那些;适应这些变化应确保可重复性。
我想做一个观察:假设使用一个库(在这种情况下为cv2
)自动(并且可以说只加载整数)而不是浮点数,唯一正确的方法是投射第一个预测数组(Keras)到uint8
,因为通过将后者转换为float32
,可能的信息差异丢失了。例如,使用cv2
加载到uint8
,通过转换而不是233
得到233.0
。但是,可能初始像素值是233,3
,但由于第一次转换而丢失了。
【讨论】:
谢谢 Timbus。确实更改为 INTER_NEAREST 并添加 resized_image = resized_image.astype(np.float32) 改进了推理:预测的 opencv: [('n02356798', 'fox_squirrel', 0.57044667)。预测的 keras:[('n02356798', 'fox_squirrel', 0.5161952) 有趣的是它们仍然不一样。我认为唯一正确的方法是将第一个 Keras 数组转换为 uint8,因为通过将后者转换为 float32,可能会丢失信息上的差异。例如,使用 cv2 加载到 uint8,通过转换而不是 233 得到 233.0。但是,初始值可能是 233,3,但由于第一次转换而丢失了。 我的意思是(在我看来)唯一正确的测试方法是将第一个 keras 数组转换为 uint8 而不是将第二个 cv2 转换为 float32。 我怀疑这就是现在较小差异的来源,转换了一些整数值而不是浮点数。 是的。将两者都转换为 uint8 是有意义的。这使得两个预测相同但不太准确:(【参考方案2】:Keras 以 RGB 格式加载图像,而 OpenCV 以 BGR 格式加载图像。
ResNet50 的 preprocessing function 使用 caffe 设置,它期望图像为 RGB 格式,并应用以下内容:
-
反转通道 (RGB -> BGR)
从各自的值中减去 [103.939, 116.779, 123.68]
由于每个通道要减去的值不同,因此通道顺序很重要。此外,预训练 ResNet50 模型的层已经按照特定顺序进行了训练。
因此,在使用 OpenCV 加载图像时,您必须将通道的顺序从 BGR 反转为 RGB
imgcv_resized = imgcv_resized[:,:,::-1]
【讨论】:
以上是关于当使用 OpenCV 完成图像加载和调整大小时,Resnet50 会产生不同的预测的主要内容,如果未能解决你的问题,请参考以下文章