почему opencv конвертирует цветовое пространство, отличное от pil? - PullRequest
1 голос
/ 17 октября 2019

Я пытаюсь преобразовать обычное изображение в серое и цветовое пространство hsv с помощью opencv и PIL, однако я обнаружил, что результаты не совпадают:

## 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))

Откуда берется разница? Могу ли я сделать два результата одинаковыми?

1 Ответ

4 голосов
/ 18 октября 2019

Как подсказывает @HansHirse в комментариях, разница в преобразовании оттенков серого между OpenCV и PIL заключается в том, что OpenCV использует округление до ближайшего целого числа (nint()), тогда как PIL использует округление до (int()).

Следующая программа демонстрирует, что путем генерации 1x1 пикселей изображения чистого красного цвета с 0..255, затем чистого зеленого с 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)))

Если вы хотите, чтобы OpenCV пришел к тому же результату, что и PIL , возможно, самый простой способ - OpenCV выполнить вычисление для значения с плавающей запятой(вместо np.uint8), чтобы получить дополнительную точность, а затем округлите ее так же, как PIL . Таким образом, вместо:

grey = cv2.cvtColor(uint8im,cv2.COLOR_BGR2GRAY)

вы бы использовали:

grey = cv2.cvtColor(uint8im.astype(np.float32),cv2.COLOR_BGR2GRAY).astype(np.uint8)

Если вы измените формулы в коде в начале моего ответа на этот формат, значения шкалы серого, рассчитанные как OpenCV совпадают с PIL .

...