【问题标题】:Why would cv2.COLOR_RGB2GRAY and cv2.COLOR_BGR2GRAY give different results?为什么 cv2.COLOR_RGB2GRAY 和 cv2.COLOR_BGR2GRAY 会给出不同的结果?
【发布时间】:2020-07-11 23:44:51
【问题描述】:

我一直认为将图像从彩色转换为灰度很简单:每个像素的强度将是每个颜色通道强度的平均值。但我注意到cv2.COLOR_RGB2GRAYcv2.COLOR_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

但当显示为图像时

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

【问题讨论】:

    标签: python-3.x opencv image-processing


    【解决方案1】:
    1. RGB->灰度转换实际上并不是一个平均值——不同通道的权重不同。具体来说:
    gray_pixel = 0.114 * blue_pixel + 0.299 * red_pixel + 0.587 * green_pixel
    

    documentation 中也提到了这一点。因此,预计 RGB2GRAY 和 BGR2GRAY 会给出不同的结果。

    1. 关于 sum-then-divide 和 division-then-sum 方法之间的差异,即在
    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 数组。因此,后一种操作(在除法之前将所有通道组合在一起)会导致溢出(实际上,在这种情况下,ipython3 会给我一个运行时警告)。在标记为channel_avg_div_togetherchannel_sum 的图像中也可以看到类似溢出的伪影。

    【讨论】:

      【解决方案2】:

      嗯,首先转换不是简单的平均,更不用说线性变换了。计算公式为RGB[A] to Gray:Y←0.299⋅R+0.587⋅G+0.114⋅B(OpenCV Docs)

      显然 OpenCV 使用相同的公式转换为灰度,无论其 BGR 还是 RGB 用作输入,但使用公式时会保留通道顺序,因此转换传递的错误顺序会导致错误的结果。

      一个很好的例子是我最近遇到的forum post,作者比较了从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:如果你传递了错误的通道顺序,转换后的输出会有所不同。

      【讨论】:

        【解决方案3】:

        我认为问题出在 MatPlotLib .imshow() 方法上。在我的实验中:

        img_read_as_grayscale = cv2.imread(sample, cv2.IMREAD_GRAYSCALE)
        

        img_BGR_to_grayscale = cv2.cvtColor(img_read_as_color, cv2.COLOR_BGR2GRAY)
        

        使用 cv2.imshow() 时将正确显示为灰度图像 但使用 plt.imshow() 会显示为奇怪的蓝色和绿色。

        您可以通过指定 cmap='gray'

        来解决此问题
        plt.imshow(img_BGR_to_grayscale, cmap='gray')
        

        【讨论】:

        • “奇怪的蓝绿色”是pyplot默认使用的viridis颜色图。
        • 在使用 Pyplot 时遇到了同样的问题,谢谢
        猜你喜欢
        • 2019-08-25
        • 1970-01-01
        • 2020-01-21
        • 1970-01-01
        • 2021-09-07
        • 2019-09-20
        • 2014-03-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多