Почему cv2.COLOR_RGB2GRAY и cv2.COLOR_BGR2GRAY дают разные результаты? - PullRequest
2 голосов
/ 12 июля 2020

Я всегда думал, что преобразовать изображение из цветного в оттенки серого просто: интенсивность каждого пикселя будет средней интенсивностью каждого цветового канала. Но я заметил, что cv2.COLOR_RGB2GRAY и cv2.COLOR_BGR2GRAY дают разные результаты. Когда я экспериментировал с ними, я также обнаружил, что оно также будет отличаться от среднего значения интенсивности каждого цветового канала.

PS Я был полностью сбит с толку, когда обнаружил, что

img_read_as_color[:,:,0]/3+img_read_as_color[:,:,1]/3+img_read_as_color[:,:,2]/3 == (img_read_as_color[:,:,0]+img_read_as_color[:,:,1]+img_read_as_color[:,:,2])/3

, но при отображении как изображение

(img_read_as_color[:,:,0]+img_read_as_color[:,:,1]+img_read_as_color[:,:,2])/3

будет выглядеть как

img_read_as_color[:,:,0]+img_read_as_color[:,:,1]+img_read_as_color[:,:,2]

Может кто-нибудь объяснить мне, почему это происходит?

Мой полный код:

import matplotlib.pyplot as plt
import cv2

sample = r'G:\Python\knight-mare\screenshots\2020-07-12-02-40-44.jpg'
img_read_as_grayscale = cv2.imread(sample, cv2.IMREAD_GRAYSCALE)
img_read_as_color = cv2.imread(sample, cv2.IMREAD_COLOR)
img_RGB_to_grayscale = cv2.cvtColor(img_read_as_color, cv2.COLOR_RGB2GRAY)
img_BGR_to_grayscale = cv2.cvtColor(img_read_as_color, cv2.COLOR_BGR2GRAY)
plt.imshow(img_read_as_grayscale)
plt.title('img_read_as_grayscale')
plt.show()
plt.imshow(img_read_as_color)
plt.title('img_read_as_color')
plt.show()
plt.imshow(img_RGB_to_grayscale)
plt.title('img_RGB_to_grayscale')
plt.show()
plt.imshow(img_BGR_to_grayscale)
plt.title('img_BGR_to_grayscale')
plt.show()

channel_avg_div_separately = img_read_as_color[:,:,0]/3+img_read_as_color[:,:,1]/3+img_read_as_color[:,:,2]/3
channel_avg_div_together = (img_read_as_color[:,:,0]+img_read_as_color[:,:,1]+img_read_as_color[:,:,2])/3
channel_sum = img_read_as_color[:,:,0]+img_read_as_color[:,:,1]+img_read_as_color[:,:,2]
plt.imshow(channel_avg_div_separately)
plt.title('channel_avg_div_separately')
plt.show()
plt.imshow(channel_avg_div_together)
plt.title('channel_avg_div_together')
plt.show()
plt.imshow(channel_sum)
plt.title('channel_sum')
plt.show()

image read as grayscaleimage read as coloredimage read as colored transformed by RGB2GRAYimage read as colored transformed by BGR2GRAYimage read as colored, each channel divided by 3 than added togetherimage read as colored, all channels added together, then divided by 3 изображение читается как цветное, просто добавляются каналы

Ответы [ 2 ]

4 голосов
/ 12 июля 2020
  1. Преобразование RGB-> серого на самом деле не является средним - разные каналы имеют разный вес. В частности:
gray_pixel = 0.114 * blue_pixel + 0.299 * red_pixel + 0.587 * green_pixel

Это также упоминается в документации . Таким образом, ожидается, что RGB2GRAY и BGR2GRAY дадут разные результаты.

Что касается несоответствия между подходами «сумма, затем деление» и «разделение, затем сумма», то есть между
img_read_as_color[:,:,0]/3+img_read_as_color[:,:,1]/3+img_read_as_color[:,:,2]/3

и

(img_read_as_color[:,:,0]+img_read_as_color[:,:,1]+img_read_as_color[:,:,2])/3

Напомним, что cv2.imread возвращает массив uint8 numpy. Таким образом, последняя операция (когда все каналы объединяются вместе перед разделением) приводит к переполнению (фактически, i python3 выдает мне предупреждение во время выполнения в этом случае). Артефакты, похожие на переполнение, также видны на изображениях, помеченных channel_avg_div_together и channel_sum.

3 голосов
/ 12 июля 2020

Ну, во-первых, преобразование - это не просто среднее, не говоря уже о линейном преобразовании. Формула для расчета: RGB[A] to Gray:Y←0.299⋅R+0.587⋅G+0.114⋅B ( OpenCV Docs )

По-видимому, OpenCV использует ту же формулу для преобразования в оттенки серого независимо от того, используется ли его BGR или RGB в качестве входных данных, но порядок каналов сохраняется, когда используя формулу, поэтому неверный порядок, переданный для преобразования, приведет к неверным результатам. преобразование из RGB и BGR. Пиксель нижнего правого угла имеет следующие значения. (Изображение предоставлено связанной публикацией / автором)

CV_BGR2GRAY: Lower right corner: 0.1140 * 163 + 0.5870 * 182 + 0.2989 * 203 ≈ 186

CV_RGB2GRAY: Lower right corner: 0.1140 * 203 + 0.5870 * 182 + 0.2989 * 163 ≈ 179

Итак, tl; dr: если вы передадите неправильный порядок каналов, преобразованный результат будет другим.

CV_BGR2GRAY

CV_RGB2GRAY

...