为什么opencv转换颜色空间与pil不同?

coi*_*ung 1 python opencv image-processing python-imaging-library

我正在尝试使用 opencv 和 PIL 将普通图像转换为灰色和 hsv 颜色空间,但是,我发现结果不一样:

## convert to gray
im_pil = np.array(Image.open(imgpth).convert("L"))
im_cv = cv2.cvtColor(cv2.imread(imgpth), cv2.COLOR_BGR2GRAY)
print(np.sum(im_pil - im_cv))

## convert to hsv
im_pil = np.array(Image.open(imgpth).convert("HSV").split()[0])
im_cv = cv2.cvtColor(cv2.imread(imgpth), cv2.COLOR_BGR2HSV)[:, :, 0]
print(np.sum(im_pil - im_cv))
Run Code Online (Sandbox Code Playgroud)

差异从何而来?我可以使两个结果相同吗?

Mar*_*ell 5

正如@HansHirse 在评论中所暗示的那样,OpenCV 和 PIL 之间灰度转换的区别在于 OpenCV 使用最接近的整数舍入 ( nint()) 而 PIL 使用向下舍入 ( int())。

下面的程序演示了通过从 0..255 生成纯红色的 1x1 像素图像,然后从 0..255 生成纯绿色,然后从 0..255 生成纯蓝色,并使用 OpenCV 和 PIL 转换它们。

#!/usr/bin/env python3

import cv2
from PIL import Image
import numpy as np

print("Varying red through 0..255, showing red component, OpenCV grey, PIL grey")
for r in range(256):
    ocvim = np.zeros((1,1,3), dtype=np.uint8) 
    ocvim[0,0,0] = r
    ocvgrey = cv2.cvtColor(ocvim,cv2.COLOR_RGB2GRAY)
    pilim = Image.new('RGB',(1,1),(r,0,0)).convert('L')
    print(r,ocvgrey[0,0],pilim.getpixel((0,0)))

print("Varying green through 0..255, showing green component, OpenCV grey, PIL grey")
for g in range(256):
    ocvim = np.zeros((1,1,3), dtype=np.uint8) 
    ocvim[0,0,1] = g
    ocvgrey = cv2.cvtColor(ocvim,cv2.COLOR_RGB2GRAY)
    pilim = Image.new('RGB',(1,1),(0,g,0)).convert('L')
    print(g,ocvgrey[0,0],pilim.getpixel((0,0)))

print("Varying blue through 0..255, showing blue component, OpenCV grey, PIL grey")
for b in range(256):
    ocvim = np.zeros((1,1,3), dtype=np.uint8) 
    ocvim[0,0,2] = b
    ocvgrey = cv2.cvtColor(ocvim,cv2.COLOR_RGB2GRAY)
    pilim = Image.new('RGB',(1,1),(0,0,b)).convert('L')
    print(b,ocvgrey[0,0],pilim.getpixel((0,0)))
Run Code Online (Sandbox Code Playgroud)

如果你想让OpenCV得到与PIL相同的结果,最简单的方法可能是让OpenCV对浮点值(而不是 a np.uint8)进行计算以获得额外的精度,然后用与PIL相同的方式将其舍入做。所以,而不是:

grey = cv2.cvtColor(uint8im,cv2.COLOR_BGR2GRAY)
Run Code Online (Sandbox Code Playgroud)

你会使用:

grey = cv2.cvtColor(uint8im.astype(np.float32),cv2.COLOR_BGR2GRAY).astype(np.uint8)
Run Code Online (Sandbox Code Playgroud)

如果您在我对此格式的回答开始时更改代码中的公式,则OpenCV计算的灰度值与PIL 的值相匹配。