hsv颜色空间

HSV色彩空间从心理学和视觉的角度出发,提出人眼的色彩知觉主要包含三要素:

  • H:色调(Hue,也称为色相)
  • S:饱和度(Saturation)
  • V:亮度(Value)

色调

在HSV色彩空间中,色调H的取值范围是[0,360]。8位图像内每个像素点所能表示的灰度级有28=256个,所以在8位图像内表示HSV图像时,要把色调的角度值映射到[0,255]范围内。在OpenCV中,可以直接把色调的角度值除以2,得到[0,180]之间的值,以适应8位二进制(256个灰度级)的存储和表示范围。

在HSV空间中,色调值为0表示红色,色调值为300表示品红色。

每个色调值对应一个指定的色彩,而与饱和度和亮度无关。在OpenCV中,将色调值除以2之后,会得到如下所示的色调值与对应的颜色。

确定值范围后,就可以直接在图像的H通道内查找对应的值,从而找到特定的颜色。例如,在HSV图像中,H通道内值为120的像素点对应蓝色。查找H通道内值为120的像素点,找到的就是蓝色像素点。在上述基础上,通过分析各种不同对象对应的HSV值,便可以查找不同的对象。

饱和度

进行色彩空间转换后,为了适应8位图的256个像素级,需要将新色彩空间内的数值映射到[0,255]范围内。所以,同样要将饱和度S的值从[0,1]范围映射到[0,255]范围内,通常可以选取100-255。

  • 作为灰度图像显示时,较亮区域对应的颜色具有较高的饱和度。
  • 如果颜色的饱和度很低,那么它计算所得色调就不可靠。

亮度

亮度的范围与饱和度的范围一致,都是[0,1]。同样,亮度值在OpenCV内也将值映射到[0,255]范围内。

亮度值越大,图像越亮;亮度值越低,图像越暗。

当亮度值为0时,图像是纯黑色。同样可以选取100-255范围。

根据hsv分量模型,各种颜色范围分布如下:

红色的范围是:[0, 43, 46]~[10,255,255]∪[156, 43, 46]~[180,255,255]。

红色比较特殊,覆盖了多个范围,处理起来增加了不少难度,可使用以下方法去获取红色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import cv2
import numpy as np

def extract_red(pic):
''''method1:使用inRange方法,拼接mask0,mask1'''

img = io.imread(os.path.join(mask_path, filename)) # RGB
print(img.shape)
img = img[..., ::-1] # 转换成BGR
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
h, w, c = img.shape

# 红黄区间
left = np.array([0, 100, 100])
right = np.array([30, 255, 255])
mask0 = cv.inRange(hsv, left, right)

# 紫-砖红
left = np.array([150, 100, 100])
right = np.array([255, 255, 255])
mask1 = cv.inRange(hsv, left, right)

mask = mask0 + mask1 # 拼接两个区间
color_map = cv.bitwise_and(img, img, mask=mask) # hsv色彩图

得到的颜色结果如下:

仿射变换

图像融合

opencv中的图像融合可以在合成过程中根据图片的权重不同,呈现透明的效果;如果直接叠加图像,又可能改变颜色。但我的需求是不需要它透明,上面的图片直接覆盖底图。

可以使用按位AND,OR,NOT的运算实现。我直接放搜到的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import cv2
import numpy as np

img1 = cv2.imread('img.jpg')
img2 = cv2.imread('img1.jpg')

img2 = cv2.resize(img2,(100,100))
# I want to put logo on top-left corner, So I create a ROI
h,w,c = img2.shape
roi = img1[0:h, 0:w ]

# Now create a mask of logo and create its inverse mask also
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 200, 255, cv2.THRESH_BINARY) # mask的关键部分是0
mask_inv = cv2.bitwise_not(mask) # 反码

# Now black-out the area of logo in ROI
img1_bg = cv2.bitwise_and(roi,roi,mask = mask)

# Take only region of logo from logo image.
img2_fg = cv2.bitwise_and(img2,img2,mask = mask_inv)

# Put logo in ROI and modify the main image
dst = cv2.add(img1_bg,img2_fg)
img1[0:h, 0:w ] = dst

cv2.imshow('res',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()