在图像加载API中,我们可以看到imread()有一个参数flags ,这个参数有一个选项是cv.IMREAD_GRAYSCALE,意思是将原图像转换为灰度图后返回。
官方文档解释是:
If set, always convert image to the single channel grayscale image (codec internal conversion).
如果设置,总是将图像转换成单通道的灰度图(采取内部转换编码)。
并且在imread()的API文档中也有一个注释
When using IMREAD_GRAYSCALE, the codec's internal grayscale conversion will be used, if available. Results may differ to the output of cvtColor().
当用参数IMREAD_GRAYSCALE时,如果内部灰度转换编码可获得,那么它将被使用。结果与cvtColor()的有差异。cvtColor()
可以看出来,在读取图像时直接转换成灰度图和先读取全色再用cvtColor()转成灰度,结果是不一样的。
这个区别虽然存在,但是非常小,仅有部分区域、一些像素点不同;具体可以参考Stack Overflow的文章,测试代码如下
- import cv2
- import numpy as np
-
- path = 'some/path/to/color/image.jpg'
-
- # Load color image (BGR) and convert to gray
- img = cv2.imread(path)
- img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
-
- # Load in grayscale mode
- img_gray_mode = cv2.imread(path, 0)
-
- # diff = img_gray_mode - img_gray
- diff = cv2.bitwise_xor(img_gray,img_gray_mode)
- print(np.sum(diff ))
- # 输出结果不为0,对于一个494 x 750=370500的图像,结果为6143,不是很大
-
-
- cv2.imshow('diff', diff)
- # 最后会发现图像不全是黑色,说明img_gray和img_gray_mode有像素点不一样
-
- cv2.waitKey()
cvtColor()不仅可以将三通道转成灰度单通道,也可将BGR的三通道转成其他类型的三通道,比如HSV。
以下摘录于官网cvtColor()文档。
- cv::COLOR_BGR2BGRA = 0,
- cv::COLOR_RGB2RGBA = COLOR_BGR2BGRA,
- cv::COLOR_BGRA2BGR = 1,
- cv::COLOR_RGBA2RGB = COLOR_BGRA2BGR,
- cv::COLOR_BGR2RGBA = 2,
- cv::COLOR_RGB2BGRA = COLOR_BGR2RGBA,
- cv::COLOR_RGBA2BGR = 3,
- cv::COLOR_BGRA2RGB = COLOR_RGBA2BGR,
- cv::COLOR_BGR2RGB = 4,
- cv::COLOR_RGB2BGR = COLOR_BGR2RGB,
- cv::COLOR_BGRA2RGBA = 5,
- cv::COLOR_RGBA2BGRA = COLOR_BGRA2RGBA,
- cv::COLOR_BGR2GRAY = 6,
- cv::COLOR_RGB2GRAY = 7,
- cv::COLOR_GRAY2BGR = 8,
- cv::COLOR_GRAY2RGB = COLOR_GRAY2BGR,
- cv::COLOR_GRAY2BGRA = 9,
- cv::COLOR_GRAY2RGBA = COLOR_GRAY2BGRA,
- cv::COLOR_BGRA2GRAY = 10,
- cv::COLOR_RGBA2GRAY = 11,
- cv::COLOR_BGR2BGR565 = 12,
- cv::COLOR_RGB2BGR565 = 13,
- cv::COLOR_BGR5652BGR = 14,
- cv::COLOR_BGR5652RGB = 15,
- cv::COLOR_BGRA2BGR565 = 16,
- cv::COLOR_RGBA2BGR565 = 17,
- cv::COLOR_BGR5652BGRA = 18,
- cv::COLOR_BGR5652RGBA = 19,
- cv::COLOR_GRAY2BGR565 = 20,
- cv::COLOR_BGR5652GRAY = 21,
- cv::COLOR_BGR2BGR555 = 22,
- cv::COLOR_RGB2BGR555 = 23,
- cv::COLOR_BGR5552BGR = 24,
- cv::COLOR_BGR5552RGB = 25,
- cv::COLOR_BGRA2BGR555 = 26,
- cv::COLOR_RGBA2BGR555 = 27,
- cv::COLOR_BGR5552BGRA = 28,
- cv::COLOR_BGR5552RGBA = 29,
- cv::COLOR_GRAY2BGR555 = 30,
- cv::COLOR_BGR5552GRAY = 31,
- cv::COLOR_BGR2XYZ = 32,
- cv::COLOR_RGB2XYZ = 33,
- cv::COLOR_XYZ2BGR = 34,
- cv::COLOR_XYZ2RGB = 35,
- cv::COLOR_BGR2YCrCb = 36,
- cv::COLOR_RGB2YCrCb = 37,
- cv::COLOR_YCrCb2BGR = 38,
- cv::COLOR_YCrCb2RGB = 39,
- cv::COLOR_BGR2HSV = 40,
- cv::COLOR_RGB2HSV = 41,
- cv::COLOR_BGR2Lab = 44,
- cv::COLOR_RGB2Lab = 45,
- cv::COLOR_BGR2Luv = 50,
最常见的是COLOR_BGR2GRAY=6、COLOR_GRAY2BGR=8、COLOR_BGR2HLS=52、COLOR_RGB2HLS=53、COLOR_BGR2HSV=40、COLOR_RGB2HSV=41。
语法很简单,就是cv2.cvtColor(src, code[, dst[, dstCn]])。
参数src用来指定图片路径。
参数code就是用来指定颜色空间转换规则。
本章节讨论cv中的split,而不是numpy中的。
cv中的split是分离图像通道,而numpy中的split是分割数组。
假设img是一个BGR颜色图,使用split()分离通道后,由b, g, r接收。
- import cv2 as cv
-
- img = cv.imread('Snipaste_5.png')
-
- print(img.shape)
- b, g, r = cv.split(img)
- print(b.shape)
- print(g.shape)
- print(r.shape)
- cv.imshow("b", b)
- cv.imshow("g", g)
- cv.imshow("r", r)
- cv.waitKey(0)
- cv.destroyAllWindows()
输出如下
- (769, 188, 3)
- (769, 188)
- (769, 188)
- (769, 188)
- (769, 188)
原图如下

输出的三个通道图片,是灰度图;这是因为b、g、r这三个图片变量的维度只有2维,而imshow()只所以能够顺利执行,就是因为会给三个图片变量额外扩充一维,第三维的大小为1,这时候各个通道的对应蓝、绿、红的数值自然就被视作灰度值了(或者看成第三维大小为3,拷贝性填充3个同样的值,同样也是灰度图)。
比如假如通道b的某点取值为255,那么单独imshow()这个通道,这一点和(255, 255, 255)的显示相同,得以验证上面的结论。

分离之后,我们可以对各个通道进行单独操作(加减乘除等),单独操作完成了就可以合并成一个完整的三色图。
比如下面的代码,进一步对各个通道同时加上100(饱和运算),可以提高整体的亮度水平(虽然可能用不到),然后再用merge()融合在一起。
- import cv2 as cv
-
- img = cv.imread('Snipaste_5.png')
- print(img)
- print(img.shape)
- b, g, r = cv.split(img)
- b = cv.add(b, 100)
- g = cv.add(g, 100)
- r = cv.add(r, 100)
- cv.merge([b, g, r], img)
- print(b.shape)
- print(g.shape)
- print(r.shape)
- cv.imshow("b", b)
- cv.imshow("g", g)
- cv.imshow("r", r)
- cv.imshow("img", img)
- cv.waitKey(0)
- cv.destroyAllWindows()
如果您学过Numpy的数组切片、索引方面的知识,应该能够很简单地看出下面这两条语句等价,都是用来进行通道的分离。
b, g, r = img[:, :, 0], img[:, :, 1], img[:, :, 2]
b, g, r = cv2.split(img)
同理,下面两条都是用来进行通道的合并。
img[:, :, 0], img[:, :, 1], img[:, :, 2] = b, g, r
cv2.merge([b, g, r], img)
因此,我们执行下面的代码
- import cv2 as cv
-
- img = cv.imread('Snipaste_5.png')
- copy = img.copy()
-
- b, g, r = cv.split(img)
- b1, g1, r1 = copy[:, :, 0], copy[:, :, 1], copy[:, :, 2]
- print((b == b1).all())
- print((g == g1).all())
- print((r == r1).all())
- b = cv.add(b, 100)
- g = cv.add(g, 100)
- r = cv.add(r, 100)
- b1 = cv.add(b1, 100)
- g1 = cv.add(g1, 100)
- r1 = cv.add(r1, 100)
- cv.merge([b, g, r], img)
- copy[:, :, 0], copy[:, :, 1], copy[:, :, 2] = b1, g1, r1
- cv.imshow("img", img)
- cv.imshow("copy", copy)
- cv.waitKey(0)
- cv.destroyAllWindows()
最后输出的两张图片,完全一模一样!
