为什么相同的像素使用PIL和Opencv有不同的值?

sin*_*ium 6 python opencv python-imaging-library

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

>>> 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)
Run Code Online (Sandbox Code Playgroud)

两者的 R、G、B 值((138!= 146)、(158!= 152)、(131!= 142))im不匹配,尽管它们img是相同的像素和相同的图像!
我查看了SO帖子,发现这篇文章讨论了同样的问题,所以我使用发布的代码再次检查差异:

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'))
Run Code Online (Sandbox Code Playgroud)

我得到的哈希值不同,图像之间的差异也不是黑色的!

附加信息:
-操作系统: Ubuntu 18.04
- Python: 3.6
- Opencv: opencv-python==4.0.0.21
- PIL: Pillow==5.4.1

对此有什么解释吗?

小智 5

这可能不是 numpy / 舍入问题,而是 jpg 解码变体:https://github.com/python-pillow/Pillow/issues/3833

尤其:

JPEG 解码器可以产生稍微不同的结果,根据维基百科,“每个像素分量最多有一位差异”。

https://github.com/python-pillow/Pillow/issues/3833#issuecomment-585211263


Ark*_*tev 3

Opencv 将图像存储为 numpy ndarray。

import cv2
cv_img = cv2.imread(img_path)
from PIL import Image
pil_img = Image.open(img_path)
Run Code Online (Sandbox Code Playgroud)

当你这样做时,cv_img[x][y]你正在访问y第列和x第行,但如果你这样做,你将访问第列和第行pil_img.getpixel((x,y))的像素。xy

另一个因素是pillow返回的(R, G, B)格式,而opencv返回的(B, G, R)格式。

对我来说cv_img[20][10]给予array([127, 117, 129], dtype=uint8)。检查这里B = 127,,G = 117R = 129

pil_img[10][20]给出(129, 117, 127). 检查这里R = 129,,G = 117B = 127

  • `[0][0]` 和 `(0,0)` 应该在 `x=0, y=0` 中获得相同的像素。我可以毫无问题地在 JPG 或 PNG 图像中获取正确的值(将 BGR 转换为 RGB 后),但问题中的图像给出不同的值。每个通道的差异为 1-2 个点。也许它使用浮点数并且它们以不同的方式舍入。 (5认同)
  • 我将 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]` 数组([132, 32, 16], dtype=uint8) `pil[1000][1000]` 数组([132, 32, 16], dtype=uint8) `(cv==pil).all()` False `cv.min(), cv.max()` (0, 209) `pil.min(), pil.max() ` (0, 210) (2认同)