目录
4、制作掩膜(cv2.inRange(参数1.参数2,参数3))
5、应用掩膜(cv2.bitwise_and(参数1,参数2,参数3))
———————————————————————————————————————————
1、RGB颜色空间
在图像处理中,最常见的就是RGB颜色空间。RGB颜色空间是我们接触最多的颜色空间,是一种用于表示和显示彩色图像的一种颜色模型,R代表红色(Red),G代表绿色(Green),B代表蓝色(Blue),这三种颜色通过不同强度的光的组合来创建其他颜色,广泛应用于我们的生活中,比如电视、电脑显示屏等。
RGB颜色模型基于笛卡尔坐标系,如下图所示,原色值位于3个角上,二次色青色、红色和黄色位于另外三个角上,黑色位于原点处,白色位于离原点最远的角上。因为黑色在RGB三通道中表现为(0,0,0),所以映射到这里就是原点;而白色是(255,255,255),所以映射到这里就是三个坐标为最大值的点。
这里解释一下,彩色图是三通道图像,即每个像素点包含三个值,可以想象成一个像素点就是一个小盒子,里面叠放了三张不同颜色的玻璃纸,最终所折射出来的颜色就是该像素点的颜色,所以彩色图的每个像素点的颜色是由三个数值共同定义的(本人之前对这里有点疑惑所以整理笔记的时候还是顺带写一下)
但是注意:在opencv中,颜色是以BGR的形式存储的,即纯红色的像素值应该是(0,0,255)
2、颜色加法
就是两幅图或多幅图的颜色通道叠加,有多种方式,比如普通加法、加权加法、通道分离等,这里只介绍前两种。
2.1 普通加法(饱和与模运算)
有两种方法:
1、利用 opencv中的 cv2.add(参数1,参数2) 进行饱和加法,参数直接填图像或数组名
饱和加法会将运算后超过255的值进行截断使其变为255。
代码示例如下:
数组展示:
def test1():
'''普通加法-cv2.add()'''
img1 = np.array([[1,2,3],
[40,50,60]],dtype = np.uint8)
img2 = np.array([[100,200,30],
[240,150,66]],dtype = np.uint8)
img3 = cv2.add(img1,img2)
print(img3)
# [[101 202 33]
# [255 200 126]]
可以看到img3的打印结果中第二行第一列的值本来是 40+240=280,但是被截断成了255。
图像展示:(注意相加的两个图像尺寸要一致)
def test2():
'''普通加法-cv2.add() 图像展示'''
img1=cv2.imread("./src/cao.png")
img2 = cv2.imread("./src/pig.png")
img3 = cv2.add(img1,img2)
cv2.imshow("img1",img1)
cv2.imshow("img2",img2)
cv2.imshow("img3",img3)
cv2.waitKey(0)
运行结果:
就是两个图像叠加在一起
2、利用numpy库里的 np.add(参数1,参数2) ,参数同样直接填图像或数组名,或者直接 用“+”进行模运算
模运算会将超过255的值与256相除然后取余。(因为 unit8 的范围是 0-255,共256个值),这种运算方式会导致亮区域变暗。
代码示例如下:
数组展示:
def test3():
'''普通加法-numpy 数组展示'''
img1 = np.array([[1,2,3],
[40,50,60]],dtype = np.uint8)
img2 = np.array([[100,200,30],
[240,250,66]],dtype = np.uint8)
img3 = np.add(img1,img2)
print(img3)
# [[101 202 33]
# [ 24 44 126]]
可以看到img3的打印结果中第二行第一列的值本来是 40+240=280,与256经过模运算后变为24,同理旁边那个值本来应该是300,与256经过模运算后变为44。
图像展示:
def test4():
'''普通加法-np.add() 图像展示'''
img1=cv2.imread("./src/cao.png")
img2 = cv2.imread("./src/pig.png")
img3 = np.add(img1,img2)
cv2.imshow("img1",img1)
cv2.imshow("img2",img2)
cv2.imshow("img3",img3)
cv2.waitKey(0)
运行结果:
可以看到效果挺奇特的,所以一般不会采用这种方法去叠加图像。
2.2 加权加法
与普通加法的区别在于两张图像的权重不同,这就会给人一种混合或者透明的感觉,API为 cv2.addWeighted(图像1,a,图像2,b,y),这里的a和b分别是两张图像各自的权重,不一定加起来要为1,最终结果会按公式 dst = a⋅img1 + b⋅img2 + y 计算并自动处理溢出,这里只介绍权重和=1的标准混合。
代码展示:
def test5():
'''加权加法'''
img1 = cv2.imread("./src/cao.png")
img2 = cv2.imread("./src/pig.png")
img3 = cv2.addWeighted(img1,0.6,img2,0.4,0)
cv2.imshow("img1",img1)
cv2.imshow("img2",img2)
cv2.imshow("img3",img3)
cv2.waitKey(0)
运行结果:
效果比之前好了很多,可以继续调整权重以达到更好的效果。
3、HSV颜色空间
HSV颜色空间指的是HSV颜色模型,这是一种与RGB颜色模型并列的颜色空间表示法。RGB颜色模型使用红、绿、蓝三原色的强度来表示颜色,是一种加色法模型,即颜色的混合是添加三原色的强度。而HSV颜色空间使用色调(Hue)、饱和度(Saturation)和亮度(Value)三个参数来表示颜色,色调H表示颜色的种类,如红色、绿色、蓝色等;饱和度表示颜色的纯度或强度,如红色越纯,饱和度就越高;亮度表示颜色的明暗程度,如黑色比白色亮度低。
HSV颜色模型是一种六角锥体模型,如下图所示:
用更直观的话来说,
-
H 决定“是什么颜色”(如红色还是蓝色)。
-
S 决定“有多浓”(如深红还是浅红)。
-
V 决定“有多亮”(如亮红还是暗红)。
然后hsv颜色空间有一张基本分量图用来表示颜色,当然这是通过实验计算出的模糊范围(此处把部分红色归为了紫色):
至于为什么有了RGB颜色空间还需要HSV,可以理解为它在处理颜色识别以及颜色分割相关任务时更优秀,具体原因这里不详解,接下来介绍使用HSV颜色空间最多的操作:制作掩膜。
当然将RGB图像转换为HSV图像的方法如下:
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
4、制作掩膜(cv2.inRange(参数1.参数2,参数3))
掩膜(Mask)是一种在图像处理中常见的操作,它选择性地遮挡图像的某些部分,以实现特定任务的目标,通常是一个二值化图像,并且与原图像的大小相同,其中目标区域被设置为255(白色),而其他区域被设置为0(黑色),并且目标区域可以根据HSV的颜色范围进行修改。
通俗的话来说,就是拿一张与目标图像大小相同的纸,将要提取的目标颜色对应的区域变成白色,其余区域全都为黑色。 API中的参数分别为目标图像转为HSV颜色空间后的图像:img_hsv, 以及提取的目标颜色在HSV空间中的极值:color_low, color_high
使用示例如下,比如我想提取这张图像的紫色区域:
代码如下:
def test6():
'''掩膜制作'''
img1 = cv2.imread("./src/masktest.png")
img1_hsv = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)#转成hsv空间
#找出紫色在hsv空间中的范围
color_low = np.array([125,43,46])
color_high = np.array([155,255,255])
img_mask = cv2.inRange(img1_hsv,color_low,color_high)#制作掩膜
cv2.imshow("img1", img1)
cv2.imshow("img_mask",img_mask)
cv2.waitKey(0)
运行结果如下:
可以看到掩膜是制作成功了,但是我的目标是提取紫色区域,即新图像应该只有紫色,所以接下来还要进行一步操作就是:应用掩膜。
5、应用掩膜(cv2.bitwise_and(参数1,参数2,参数3))
参数1=参数2=img(原图),参数3=img_mask(掩膜)
API是cv2.bitwise_and(),这个方法就像“图像界的魔法剪刀”,能按掩膜精准裁剪或组合图像,但是其中的原理有点复杂,我们一步一步来:
先复习一下按位与运算:将输入数值转化为二进制并进行逐位比较,有0为0,无0为1,例如
-
像素A: 150 → 二进制表示为
10010110
-
像素B:
200
→ 二进制表示为11001000
-
按位与运算结果:逐位比较 →
10000000
(转化为十进制是128)
当然,A&A=A 是基本性质,因为每一位都和自己比较,保持不变。
再解释原理:参数1和参数2都是填原图,然后原图与原图进行按位与运算,当然结果是原图本身,参数3就是掩膜。至于为什么要让原图与原图进行按位与运算,是因为掩膜本身没有对原图进行任何操作的能力,它就像一张筛子只能通过控制原图与原图是否进行按位与运算,来决定结果图中哪些区域显示,哪些区域隐藏。在opencv内部就是:
-
mask=255 的地方:执行 img 和 img 的按位与运算(即保留原值)
-
mask=0 的地方:不执行,直接把输出像素置为0(黑色)
所以,不是掩膜本身去修改图像,而是在操作过程中干预了结果产生,是在原图与原图按位与计算过程中,控制哪些地方产生结果,哪些地方归零。
原理理解完之后就接着我们上一步的操作提取紫色区域吧,代码如下:
def test7():
'''应用掩膜'''
img1 = cv2.imread("./src/masktest.png")
img1_hsv = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)#转成hsv空间
#找出紫色在hsv空间中的范围
color_low = np.array([125,43,46])
color_high = np.array([155,255,255])
img_mask = cv2.inRange(img1_hsv,color_low,color_high)#制作掩膜
img_bit = cv2.bitwise_and(img1,img1,mask=img_mask)#应用掩膜
cv2.imshow("img1", img1)
cv2.imshow("img_bit", img_bit)
cv2.waitKey(0)
运行结果:
可以看到,此时我们已经成功提取了紫色区域。
所以现在我们有了这个方法之后,就可以对抠出来的图像做进一步操作,比如常见的颜色替换。
6、图片颜色替换
为什么有了掩膜之后就可以对指定颜色区域进行颜色替换呢,是因为由于掩膜与原图的大小相同,并且像素位置一一对应,所以我们就得到了掩膜中白色(也就是像素值=255)区域,再将其进行赋值即可,比如我想将刚刚提取的紫色区域变成蓝色,就可以这样:
def test8():
'''图片颜色替换'''
img1 = cv2.imread("./src/masktest.png")
img1_hsv = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)#转成hsv空间
#找出紫色在hsv空间中的范围
color_low = np.array([125,43,46])
color_high = np.array([155,255,255])
img_mask = cv2.inRange(img1_hsv,color_low,color_high)#制作掩膜
img_change = img1.copy() #复制原图,增加对比
img_change[img_mask==255]=(255,0,0)#图片颜色改变,将紫色对应区域改成蓝色
cv2.imshow("img1", img1)
cv2.imshow("img_change", img_change)
cv2.waitKey(0)
运行结果:
可以看到,左边是原图,右边是修改颜色后的图像。
以上就是图片颜色识别与替换相关,下一篇是添加水印以及ROI切割。