为什么在使用PIL和OpenCV时同样的像素值会不同?

5

我从互联网上下载了一张随机图片,使用PIL.Image.open()cv2.imread()打开它,然后检查了一些像素的值。问题是,使用PIL和Opencv得到的相同像素的值不同!
这是我尝试过的图片:
enter image description here 这是我所做的:

>>> import cv2
>>> from PIL import Image
>>> img = cv2.imread('img.jpg')
>>> im = Image.open('img.jpg')
>>> img[0][0]
>>> array([142, 152, 146], dtype=uint8)
>>> im.getpixel((0, 0))
>>> (138, 158, 131)

虽然是同一个像素和同一张图片,但是imimg的R、G、B值((138 != 146), (158 != 152), (131 != 142))不匹配!
我查看了 Stack Overflow 的帖子,发现这个帖子讨论了相同的问题,所以我使用了该帖子中提供的代码再次检查了差异:

from PIL import Image
import cv2
import sys
from hashlib import md5
import numpy as np

def hashIm(im):
    imP = np.array(Image.open(im))

    # Convert to BGR and drop alpha channel if it exists
    imP = imP[..., 2::-1]
    # Make the array contiguous again
    imP = np.array(imP)
    im = cv2.imread(im)

    diff = im.astype(int)-imP.astype(int)

    cv2.imshow('cv2', im)
    cv2.imshow('PIL', imP)
    cv2.imshow('diff', np.abs(diff).astype(np.uint8))
    cv2.imshow('diff_overflow', diff.astype(np.uint8))

    with open('dist.csv', 'w') as outfile:
        diff = im-imP
        for i in range(-256, 256):
            outfile.write('{},{}\n'.format(i, np.count_nonzero(diff==i)))

    cv2.waitKey(0)
    cv2.destroyAllWindows()

    return md5(im).hexdigest() + '   ' + md5(imP).hexdigest()

if __name__ == '__main__':
    print(hashIm('img.jpg'))

我得到的哈希值不同,而且两张图片的不同之处不是黑色!

其他信息:
- 操作系统: Ubuntu 18.04
- Python: 3.6
- OpenCV: opencv-python==4.0.0.21
- PIL: Pillow==5.4.1

这是为什么呢?


1
据我所知,OpenCV使用BGR而不是RGB。 - furas
我使用JPG和PNG测试了您的第一个版本(未将BGR转换为RGB),两者都给出了正确的值。Linux Mint 19.1(基于Ubuntu 18.04),Python 3.7,OpenCV 4.1.0,PIL 5.4.1。 - furas
@furas,你认为这是由于软件包版本的原因吗?可能是这样。 - singrium
1
我使用了你的图像进行测试,结果得到了不同的值。看起来这是某些类型的图像存在问题。每个通道的差异在1-2个点之间。 - furas
1
据我所知,JPG算法使用傅里叶变换或类似的方法,可能使用浮点值,这些值在两个模块中可能会以不同的方式四舍五入。我不会使用JPG,而是使用PNG或TIFF这些无损格式。 - furas
显示剩余5条评论
2个回答

5

3

Opencv将图像存储为NumPy ndarray。

import cv2
cv_img = cv2.imread(img_path)
from PIL import Image
pil_img = Image.open(img_path)

当您执行cv_img[x][y]时,您正在访问第y列和第x行,但如果您执行pil_img.getpixel((x,y)),则Pillow会访问第x列和第y行的像素。
另一个因素是Pillow以(R,G,B)格式返回结果,而OpenCV以(B,G,R)格式返回结果。
对于我来说,cv_img[20][10]的结果为array([127, 117, 129], dtype=uint8)。在此示例中,B = 127G = 117R = 129
但是,pil_img[10][20]的结果为(129, 117, 127)。在此示例中,R = 129G = 117B = 127

5
[0][0](0,0) 应该获得相同的像素值——都为 x=0, y=0。我可以在我的 JPG 或 PNG 图像中(将 BGR 转换为 RGB 后)获取正确的值,但是问题中的图像给出了不同的值。每个通道的差异为1-2个点。可能它使用浮点数,在不同的方式下四舍五入。 - furas
@furas 是对的,[0][0]和(0, 0)应该给出相同的结果,我也测试了你的方法,但仍然有不同的值。每个通道的差异最多为+/- 4或+/- 6。 - singrium
@HansHirse,所以问题是因为库的版本问题吗?! - singrium
1
@singrium 可能吧,我不太确定。我只是想支持给出的答案。在ImageJ 1.52a和GIMP 2.10.6中打开提供的图像也会给出x = 0y = 0的值。 - HansHirse
2
我将pil转换为np.array(pil)。pil.max()给出210,但cv.max()只有209。感到困惑... cv.shape(2448, 3264, 3) pil.shape(2448, 3264, 3) cv = cv2.cvtColor(cv, cv2.COLOR_BGR2RGB) cv[1000][1000] array([132, 32, 16], dtype=uint8) pil[1000][1000] array([132, 32, 16], dtype=uint8) (cv==pil).all() False cv.min(), cv.max() (0, 209) pil.min(), pil.max() (0, 210) - jl303
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接