我们常看到各种颜色空间类型,有RGB色彩空间, Gray(灰度)色彩空间,XYZ色彩空间,YCrCb色彩空间,HSV色彩空间,HLS色彩空间,Bayer色彩空间等。
不同的色彩空间都有其擅长处理的区域。所以就有了转换的需求。
1. 色彩空间的介绍:
1.1: Gray色彩空间:
Gray通常指8位灰度图,像素取值范围[0-255], 当图像由RG颜色空间转换为Gray色彩空间时,其处理方法如下:
Gray= 0.299*R + 0.587*G + .114*R
当Gray转换成RGB色彩空间时,则:
R= Gray
B = Gray
B = Gray
1.2: YCrCb色彩空间:
人眼对颜色敏感度要低于对亮度的敏感度,在传统的RGB色彩空间,RGB三原色具有相同的重要性,当忽略了亮度信息。
YCrCb色彩空间中,Y代表光源的亮度,色彩信息保存在Cr(红色分量信息)和Cb(蓝色分量信息)中。
1.3:HSV色彩空间:
HSV是一种面向视觉感知的颜色模型。它从心理学和视觉的角度出发,之处人眼的色彩知觉主要包涵三要素:色调(Hue),饱和度(Saturation),亮度(Value).
这个色彩空间比较重要,后续会仔细讨论。
1.4: HLS色彩空间:
H(Hue), 光亮度 L(Lightness), 饱和度S(Saturation).
1.5:Bayer色彩空间:
被广泛应用于CCD和CMOS相机。
2.颜色空间的转换:
dst = cv2.cvtColor(src, code)
dst为输出图像.
src为输入图像。
code:色彩空间转换码。指出从何种色彩空间转换到何种色彩空间。
import cv2 data_bgr = cv2.imread("images/test1.webp", cv2.IMREAD_COLOR) cv2.imshow("BGR", data_bgr) rgb = cv2.cvtColor(data_bgr, cv2.COLOR_BGR2RGB) cv2.imshow("RGB", rgb) data_gray = cv2.cvtColor(data_bgr, cv2.COLOR_BGR2GRAY) cv2.imshow("GRAY", data_gray) cv2.waitKey() cv2.destroyAllWindows()
3. Alpha通道:
在RGB色彩空间三通道的基础上,还可以增加一个A通道(alpha通道),表示透明度。
它叫做RBGA色彩空间。
alpha通道的取值范围是:[0,255]. 表示从透明到不透明。
img_bgr = cv2.imread("images/people.webp", cv2.IMREAD_COLOR) img_BGRA = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2BGRA) print(img_BGRA[:,:,3]) b,g,r,a = cv2.split(img_BGRA) a[:,:] = 125 img_BGRA_125 = cv2.merge([b,g,r,a]) a[:,:] = 0 img_BGRA_0 = cv2.merge([b,g,r,a]) cv2.imshow("BGR", img_bgr) cv2.imshow("255", img_BGRA) cv2.imshow("125", img_BGRA_125) cv2.imshow("0", img_BGRA_0) cv2.waitKey() cv2.destroyAllWindows() cv2.imwrite("images/bgra_0.jpg", img_BGRA_0) cv2.imwrite("images/bgra_125.jpg", img_BGRA_125) cv2.imwrite("images/bgra_255.jpg", img_BGRA)
4. HSV色彩空间的使用:
HSV色彩空间提供了通过其值直观的感知具体色彩的方式。
HSV色彩空间从心理学和视觉的角度出发,提出人类眼睛的色彩感知主要包括的三要素:
H:Hue, 色调。
S:Saturation. 饱和度
V: Value. 亮度。
4.1:HSV介绍:
H: Hue,色调:
在HSV色彩空间中,色调H的取值范围是[0,360].
用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;
为了能够在1个byte内存储,OpenCV把它的取值范围映射到[0,180].
0:红色。
30:黄色。
60:绿色
90:青色
120:蓝色
150:品红色。
(灰度图的H为0)
S: Saturation, 饱和度:
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
灰度颜色所包含的R,G,B的成分相同相当于一种极不饱和的颜色。所以灰度颜色的饱和度为0.
如果颜色的饱和度很低,则它计算出的色调就不可靠。(如上图靠近圆锥轴心的地方)。
同样为了适应1byte的存储空间,OpenCV把其取值范围从[0,1]映射到[0,255].
V: Value,亮度:
亮度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
4.2: HSV具体使用:
4.2.1:查看指定颜色的HSV值。
主要是看H色调。
blue_point = np.zeros([1,1,3], dtype=np.uint8) blue_point[0,0,0] = 255 print("Blue:\n", blue_point) blue_hsv = cv2.cvtColor(blue_point, cv2.COLOR_BGR2HSV) print("HSV:\n", blue_hsv) green_point = np.zeros([1,1,3], dtype=np.uint8) green_point[0,0,1] = 255 green_hsv = cv2.cvtColor(green_point, cv2.COLOR_BGR2HSV) print("Green:\n", green_point) print("Green HSV:\n", green_hsv) red_point = np.zeros([1, 1, 3], dtype=np.uint8) red_point[0, 0, 2] = 255 red_hsv = cv2.cvtColor(red_point, cv2.COLOR_BGR2HSV) print("Red:\n", red_point) print("Red HSV:\n", red_hsv)
创建了Blue, Green, Red三个点,并查看其HSV值。
Blue:
[[[255 0 0]]]
HSV:
[[[120 255 255]]]
Green:
[[[ 0 255 0]]]
Green HSV:
[[[ 60 255 255]]]
Red:
[[[ 0 0 255]]]
Red HSV:
[[[ 0 255 255]]]
可以看到。对应颜色的H值和之前定义的相同。
4.2.2:利用HSV筛选指定颜色:
在HSV色彩空间中,H通道对应的是不同的颜色。所以可以通过对H通道进行筛选,以找出特定的颜色的区域。
A. 利用cv2.inRange()判断像素的像素值是否处于指定的范围内. 获取掩码mask.
B. 利用位运算与cv2.bitwise_and(img, img, mask)把需要的像素提取出来。其它点置0.
C. 筛选颜色时,并不是只找H为指定值的,而是找一个区间。例如:蓝色H为120. 则找110-130之间的值。
S,V通道则通常取50-255之间,因为若饱和度和亮度太低时,色调就不可靠了。
例如:分别采集一张图像中的Red, Blue, Green元素并显示出来。
data_bgr = cv2.imread("images/opencv.webp", cv2.IMREAD_COLOR) if(data_bgr is None): print("Read Image File Error.") sys.exit() data_hsv = cv2.cvtColor(data_bgr, cv2.COLOR_BGR2HSV) blue_min = np.array([110, 50, 50]) blue_max = np.array([130, 255, 255]) blue_mask = cv2.inRange(data_hsv, blue_min, blue_max) blue_bgr = cv2.bitwise_and(data_bgr,data_bgr, mask=blue_mask) green_mix = np.array([50, 50, 50]) green_max = np.array([70, 255, 255]) green_mask = cv2.inRange(data_hsv, green_mix, green_max) green_bgr = cv2.bitwise_and(data_bgr, data_bgr, mask=green_mask) red_mix = np.array([0, 50, 50]) red_max = np.array([30, 255, 255]) red_mask = cv2.inRange(data_hsv, red_mix, red_max) red_bgr = cv2.bitwise_and(data_bgr, data_bgr, mask=red_mask) cv2.imshow("BGR", data_bgr) cv2.imshow('Blue', blue_bgr) cv2.imshow("Green", green_bgr) cv2.imshow("Red", red_bgr) cv2.waitKey() cv2.destroyAllWindows()
找肤色:
假定肤色区间为: H:[5-170] S:[25,166]
#找肤色 data_bgr = cv2.imread("images/people.webp", cv2.IMREAD_COLOR) if(data_bgr is None): print("Read File Error") sys.exit() print("Image shape:", data_bgr.shape) cv2.imshow("Orz", data_bgr) data_hsv = cv2.cvtColor(data_bgr, cv2.COLOR_BGR2HSV) p_min = np.array([5, 25, 50], dtype=np.uint8) p_max = np.array([170, 166, 255], dtype=np.uint8) p_mask = cv2.inRange(data_hsv, p_min, p_max) p_roi = cv2.bitwise_and(data_bgr, data_bgr, mask=p_mask) cv2.imshow("ROI", p_roi) cv2.waitKey() cv2.destroyAllWindows()
更改亮度:
#修改图像亮度 img_bgr= cv2.imread("images/people.webp", cv2.IMREAD_COLOR) img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV) for i in range(img_bgr.shape[0]): for j in range(img_bgr.shape[1]): img_hsv[i,j,2] = 255 img_after = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR) cv2.imshow("Before", img_bgr) cv2.imshow("After", img_after) cv2.waitKey() cv2.destroyAllWindows()
再以找红色为例,理解HSV和cv2.inRange()
原图如下:
若想把所有红色的像素提取出来。则可以利用cv2.inRange()
但红色的区域比较特殊,它的Hue以0为中心。所以可以把区域定为两个部分。
import cv2 import numpy as np import sys if __name__ == '__main__': img = cv2.imread("../images/red.png") if(img is None): sys.exit() img_hsv = cv2.cvtColor(img,code=cv2.COLOR_BGR2HSV) low_red1 = np.array([0, 150, 150]) high_red1 = np.array([20, 255, 255]) low_red2 = np.array([160, 150, 150]) high_red2 = np.array([180, 255, 255]) mask1 = cv2.inRange(img_hsv, low_red1, high_red1) mask2 = cv2.inRange(img_hsv, low_red2, high_red2) mask = cv2.bitwise_or(mask1, mask2) # print("mask shape:",mask.shape) # print("Image shape:", img_hsv.shape) cv2.imshow("Img", img) cv2.imshow("mask1", mask1) cv2.imshow("mask2", mask2) cv2.imshow("mask", mask) img_red = cv2.bitwise_and(img, img, mask=mask) cv2.imshow("Red", img_red) cv2.waitKey(0) cv2.destroyAllWindows()
产生两个mask, 利用cv2.bitwise_or()运算把他们合并到一起。
import cv2 import numpy as np import sys if __name__ == '__main__': img = cv2.imread("../images/red.png") if(img is None): sys.exit() img_hsv = cv2.cvtColor(img,code=cv2.COLOR_BGR2HSV) low_red1 = np.array([0, 150, 150]) high_red1 = np.array([20, 255, 255]) low_red2 = np.array([160, 150, 150]) high_red2 = np.array([180, 255, 255]) mask1 = cv2.inRange(img_hsv, low_red1, high_red1) mask2 = cv2.inRange(img_hsv, low_red2, high_red2) mask = cv2.bitwise_or(mask1, mask2) # print("mask shape:",mask.shape) # print("Image shape:", img_hsv.shape) cv2.imshow("Img", img) cv2.imshow("mask1", mask1) cv2.imshow("mask2", mask2) cv2.imshow("mask", mask) img_red = cv2.bitwise_and(img, img, mask=mask) cv2.imshow("Red", img_red) cv2.waitKey(0) cv2.destroyAllWindows()
结果如下:
可以看到,mask1, mask2都没能取全。只有把他们俩or起来。才算取全了红色。
附录:
1. cv2.inRange()
dst = cv2.inRange(src, lowerb, upperb)
src: 输入的array, 表示要检查的数组或图像。
lowerb: 下边界。 ( array or a scalar)
upperb:上边界。( array or a scalar)
dst: 输出array. 和src相同。 CV_8U type。在lowerb~upperb之间的值变成255。 否则变为0.
2.按位逻辑运算:
cv2.bitwise_and()
cv2.bitwise_or()
cv2.bitwise_xor()
cv2.bitwise_not()
dst = cv2.bitwise_and(src1,src2 [, mask] )
src1和src2按位与。
如果由mask, 操作只会在掩码值为非空的像素点上执行。 并将其它像素点置0.