掩膜
外科医生在给患者做手术时,会为患者盖上手术洞巾,这样医生就可以只在这个预设好的孔洞部位进行手术。手术洞巾不仅有利于医生定位患处、暴露手术视野,还可以对非患处起到隔离、防止污染的作用
同样地,当计算机处理图像时,图像也如同一名“患者”一样,有些内容需要处理,有些内容不需要处理。通常计算机处理图像时会把所有像素都处理一遍,但如果想让计算机像外科大夫那样仅处理某一小块区域,那就要为图像盖上一张仅暴露一小块区域的“手术洞巾”。像“手术洞巾”那样能够覆盖原始图像、仅暴露原始图像感兴趣区域(ROI)的模板图像就被叫做掩膜
掩膜,也叫掩码(mask),在程序中用二值图像来表示:
0值(纯黑)区域
表示被遮盖的部分255值(纯白)区域
表示暴露的部分- 某些场景下也会用
0
和1
当作掩膜的值
在使用OpenCV
处理图像时,通常使用Numpy
库提供的方法来创建掩膜图像,下面通过一个实例来演示:
利用Numpy
库的np.zeros()
方法来创建一幅掩膜图像,感兴趣区域为在该图像中横坐标为20、纵坐标为50、宽为60、高为50的矩形
。先展示该掩膜图像,后调换该掩膜图像的感兴趣区域和不感兴趣区域,再次展示掩膜图像
# 掩膜
import cv2
import numpy as np
# 创建宽150、高150、3通道、像素类型为无符号8位数字的零值(纯黑)图像
mask = np.zeros((150,150,3),np.uint8)
##---------------------------------------------------
# 将50~100行、20~80列的像素改为纯白像素
mask[50:100,20:80,:] = 255
# 展示掩膜
cv2.imshow('mask1',mask)
##---------------------------------------------------
# 全部改成纯白像素
mask[:,:,:] = 255
# 将50~100行、20~80列的像素改为纯黑像素
mask[50:100,20:80,:] = 0
# 展示掩膜
cv2.imshow('mask2',mask)
##---------------------------------------------------
# 退出
cv2.waitKey()
cv2.destroyAllWindows()
结果:
掩膜在图像运算过程中充当了重要角色,通过掩膜才能看到最直观的运算结果,接下来将详细介绍图像运算的相关内容
图像的算数运算
加法运算
关键API:dst = cv2.add(src1, src2[, dst[, mask[, dtype]]])
其中:
src1
:第一幅图像src2
:第二幅图像mask
:可选参数,掩膜(建议使用默认值)dtype
:可选参数,图像深度(建议使用默认值)
返回值:
dst
:相加之后的结果图像。如果相加之后值的结果大于255,则取255
cv2.add()
的规则就是两个图对应的元素相加,如果超过255(像素点的大小0-255),则全部变成255
我们可以用cv2.add()
方法和正常的相加+
做比较
代码如下:
# 加法运算
import cv2
import numpy as np
# 读取图片
img = cv2.imread('./beach.jpg')
# 展示
cv2.imshow('img',img) # 原图
##----------------------------------------
# 使用 "+" 运算符增加
sum1 = img + img
# 展示
cv2.imshow('using_+',sum1)
##----------------------------------------
# 使用cv2.add()方法相加
sum2 = cv2.add(img,img)
# 展示
cv2.imshow('using_cv2.add()',sum2)
##----------------------------------------
cv2.waitKey()
cv2.destroyAllWindows()
上述代码运行结果如下:
原图:
从这个结果可以看出:
" + "运算符
的计算结果如果超出了255,就会取相加的和除以255的余数,也就是取模运算,像素值相加后反而变得更小了,由浅色变成了深色cv2.add()方法
的计算结果如果超过了255,就取值为255,很多浅颜色像素彻底变成了白色
不同的图相加:
# 加法运算
import cv2
import numpy as np
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# cv2.add()方法要求两个图的长、宽、通道数都相等,否则会报错
# add的规则就是两个图对应的元素相加,如果超过255(像素点的大小0-255),则全部变成255
print(cat.shape)
print(dog.shape) # 检查两图的长宽及通道数
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
print(new_dog.shape)
# 将两幅图片相加
new_img = cv2.add(cat,new_dog)
cv2.imshow('new_img',new_img)
cv2.waitKey()
cv2.destroyAllWindows()
图片还可以和单个数字进行运算,如dog += 100
每个和100进行加法运算,超过255的数字,会被截断,相当于%256
# 加法运算
import cv2
import numpy as np
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# cv2.add()方法要求两个图的长、宽、通道数都相等,否则会报错
# add的规则就是两个图对应的元素相加,如果超过255(像素点的大小0-255),则全部变成255
# print(cat.shape)
# print(dog.shape) # 检查两图的长宽及通道数
# 图片还可以和单个数字进行运算
# 每个和100进行加法运算,超出255的数字,会被截断,相当于%256
dog += 100
print(dog[:3,:3])
cv2.waitKey()
cv2.destroyAllWindows()
利用掩膜遮盖相加结果
图像的加运算也可以使用掩膜,下面通过一个实例介绍掩膜的使用方法
创建纯蓝和纯红这两幅图像,使用add()方法对这两幅图像进行加运算,并在方法中添加一个掩膜,查看计算结果
具体代码如下:
import cv2
import numpy as np
# 创建150*150的0值(纯黑)图像
img1 = np.zeros((150,150,3),np.uint8)
img2 = np.zeros((150,150,3),np.uint8)
##--------------------------------------------------------------------
# 将图片变成蓝色 ——> 设置第三维度的BGR通道:蓝色通道赋予最大值
img1[:,:,0] = 255
# cv2.imshow('new_img',img1)
# 将图片变成红色 ——> 设置第三维度的BGR通道:红色通道赋予最大值
img2[:,:,2] = 255
# cv2.imshow('img2',img2)
# 蓝色 + 红色 = 洋红色
img = cv2.add(img1,img2)
cv2.imshow('img',img)
##--------------------------------------------------------------------
# 创建掩膜
mask = np.zeros((150,150,1),np.uint8)
mask[50:100,50:100,:] = 255
cv2.imshow('mask',mask)
# 相加时使用掩膜
img = cv2.add(img1,img2,mask = mask)
cv2.imshow('using_mask',img)
cv2.waitKey()
cv2.destroyAllWindows()
结果:
原图 、 掩膜 、 原图使用了掩膜后的效果
减法运算
关键API:cv2.subtract(img1,img2)
subtract
的规则就是两个图对应的元素相减,如果小于0(像素点的大小0-255),则全部变成0
# 减法运算
import cv2
import numpy as np
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# cv2.subtract()方法要求两个图的长、宽、通道数都相等,否则会报错
# subtract的规则就是两个图对应的元素相减,如果小于0(像素点的大小0-255),则全部变成0
print(cat.shape)
print(dog.shape) # 检查两图的长宽及通道数
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
print(new_dog.shape)
# 将两幅图片相减 ——> 对应图片的元素(像素)相减,减完小于0,则全部为0
new_img = cv2.subtract(cat,new_dog)
cv2.imshow('new_img',new_img)
cv2.waitKey()
cv2.destroyAllWindows()
乘法运算
关键API:cv2.multiply(img1,img2)
乘法的规则和加法一样:两个图对应的元素相乘,如果超过255(像素点的大小0-255),则全部变成255
# 乘法运算
import cv2
import numpy as np
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# cv2.multiply()方法要求两个图的长、宽、通道数都相等,否则会报错
# multiply的规则就是两个图对应的元素相乘,如果超过255(像素点的大小0-255),则全部变成255
print(cat.shape)
print(dog.shape) # 检查两图的长宽及通道数
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
print(new_dog.shape)
# 将两幅图片相乘 multiply——> 对应图片的元素(像素)相减,减完小于0,则全部为0
new_img = cv2.multiply(cat,new_dog)
cv2.imshow('new_img',new_img)
cv2.waitKey()
cv2.destroyAllWindows()
除法运算
关键API:cv2.divide(img1,img2
除法的规则和减法一样:两个图对应的元素相除,如果小于0(像素点的大小0-255),则全部变成0
# 加法运算
import cv2
import numpy as np
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# cv2.divide()方法要求两个图的长、宽、通道数都相等,否则会报错
# divide的规则就是两个图对应的元素相除,如果小于0(像素点的大小0-255),则全部变成0
print(cat.shape)
print(dog.shape) # 检查两图的长宽及通道数
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
print(new_dog.shape)
# 将两幅图片相除 divide——> 对应图片的元素(像素)相减,减完小于0,则全部为0
new_img = cv2.divide(cat,new_dog)
cv2.imshow('new_img',new_img)
cv2.waitKey()
cv2.destroyAllWindows()
图片的融合
关键API:addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])
其中:
src1
:第一幅图片alpha
:第一幅图片的权重scr2
:第二幅图片beta
:第二幅图片的权重gamma
:偏差
# 图像的融合
# 不是简单的加法,相当于拿图片进行了线性运算:new_img = img1 * w1 +img2 * w2 + bias(w1、w1为权重,bias为偏差)
#我们可以通过控制权重,来调整最终融合之后的图片里面哪一张图片所占比重更大
#比如猫和狗融合的图片,我想让狗显示的更清晰,那我就可以加大狗的比重,减少猫的比重
# 关键API:addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) ——> src1:第一幅图片 alpha:第一幅图片的权重 scr2:第二幅图片 beta:第二幅图片的权重 gamma:偏差
import cv2
import numpy as np
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
new_img = cv2.addWeighted(cat,0.3,new_dog,1,0) # 如果想把图片调暗,那么可以把gamma改成负数
cv2.imshow('new_img',new_img)
cv2.waitKey()
cv2.destroyAllWindows()
OpenCV的逻辑运算(位运算)
位运算是二进制数特有的运算特性。图像由像素组成,每个像素可以用十进制数表示,十进制整数又可以转化为二进制数,所以图像也可以进行为位运算,并且位运算在图像数字化技术是一项重要的运算操作
OpenCV提供了几种常用的位运算方法
方 法 | 含 义 |
---|---|
cv2.bitwise_and() |
按 位 与 |
cv2.bitwise_or() |
按 位 或 |
cv2.bitwise_not() |
按 位 取反 |
cv2.bitwise_xor() |
按 位 异或 |
与运算&
关键API:cv2.bitwise_and(src1,src2,mask)
其中:
src1
:第一幅图像src2
:第二幅图像mask
:可选参数,掩膜
返回值:
图像做与运算时,会把每一个像素值都转换为二进制数,然后让两幅图像相同位置的两个像素值做与运算,最后把运算结果保存在新图像的相同位置上
dst
:与运算后的结果图像
如204 & 213 = 196
,先将204和213转化为二进制,再把结果的二进制转化为十进制,即为196
两个数与运算后会变小,如204 & 213 = 196 ; 150 & 140 = 132
与运算有两个特点:
- 如果某像素与纯白色像素(255)做与运算,结果仍然是某像素的原值,即不变
00101011 & 11111111 = 00101011
- 如果某像素与纯黑色像素(0)做与运算,结果为纯黑像素,即归零
00101011 & 00000000 = 00000000
由此可以得出:如果原图像与掩膜
进行与运算,原图像仅仅会保留掩膜中白色区域所覆盖的内容,其他区域全部变成黑色。下面通过一个实例来演示掩膜在与运算过程中的作用,
花图像与十字掩膜做与运算
创建一个掩膜,在掩膜中央保留一个十字形的白色区域,让掩膜与❀做与运算
,查看运算后的结果
代码如下:
import cv2
import numpy as np
# 导入❀的图像
img = cv2.imread('./amygdalus triloba.png')
print(img.shape)
# 创建一个和❀大小一样的掩膜
mask = np.zeros(img.shape,np.uint8)
# 在掩膜中画出十字
mask[120:180,:,:] = 255 # 横着的白色区域
mask[:,90:150,:] = 255 # 竖着的白色区域
# 将图像❀和掩膜相加
dst = cv2.bitwise_and(img, mask)
# 展示图片
cv2.imshow('img',img)
cv2.imshow('mask',mask)
cv2.imshow('dst',dst)
# cv2.imshow('img and mask',np.hstack((img,mask,dst)))
cv2.waitKey()
cv2.destroyAllWindows()
结果:
示例代码:
两张图片进行与运算
# 与运算
import cv2
import numpy as np
# 创建窗口
cv2.namedWindow('new_img',cv2.WINDOW_NORMAL)
# 更改窗口大小—>在窗口属性为‘WINDOW_AUTOSIZE’自动设置时无效果
cv2.resizeWindow('new_img',800,600) # resizeWindow(winname, width, height)
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
# 与操作 ——> 要求两幅图形状相同
cat_and = cv2.bitwise_and(cat,new_dog)
# 观察像素变换结果
print(cat[:2,:2])
print('---------------------')
print(dog[:2,:2])
print('---------------------')
print(cat_and[:2,:2])
# 展示图片
cv2.imshow('new_img',np.hstack((cat,cat_and)))
cv2.waitKey()
cv2.destroyAllWindows()
结果:
cat:
[[[195 216 244]
[194 215 243]]
[[194 215 243]
[193 214 242]]]
---------------------
dog:
[[[ 84 102 125]
[ 79 100 122]]
[[ 85 102 123]
[ 84 103 124]]]
---------------------
cat_and
[[[ 64 64 116]
[ 66 68 114]]
[[ 64 70 115]
[ 64 70 112]]]
或运算|
关键API:dst = cv2.bitwise_or(src1,src2,mask)
src1
:第一幅图像src2
:第二幅图像mask
:可选参数,掩膜
返回值:
dst
:或运算之后的结果图像
或运算也有两个特点:
- 如果某像素与纯白色像素(255)做或运算,结果为纯白色像素(255)
00101011 | 11111111 = 11111111
- 如果某像素与纯黑色像素(0)做或运算,结果仍然为某像素的原值(不变)
00101011 | 00000000 = 00101011
由此可以得出:如果原图像与掩膜
进行或运算,原图像仅会保存掩膜中黑色区域所覆盖的内容,其他区域全部变成白色。下面通过一个实例来掩饰掩膜在或运算中的作用
花图像与十字掩膜做或运算
创建一个掩膜,在掩膜中央保留一个十字形的白色区域,让掩膜与❀做或运算
,查看运算后的结果
代码如下:
import cv2
import numpy as np
# 导入❀的图像
img = cv2.imread('./amygdalus triloba.png')
print(img.shape)
# 创建一个和❀大小一样的掩膜
mask = np.zeros(img.shape,np.uint8)
# 在掩膜中画出十字
mask[120:180,:,:] = 255 # 横着的白色区域
mask[:,90:150,:] = 255 # 竖着的白色区域
# 将图像❀和掩膜相加
dst = cv2.bitwise_or(img, mask)
# 展示图片
cv2.imshow('img',img)
cv2.imshow('mask',mask)
cv2.imshow('dst',dst)
# cv2.imshow('img and mask',np.hstack((img,mask,dst)))
cv2.waitKey()
cv2.destroyAllWindows()
结果:
示例代码:
两个数进行或运算
# 或运算
import cv2
import numpy as np
# 创建窗口
cv2.namedWindow('new_img',cv2.WINDOW_NORMAL)
# 更改窗口大小—>在窗口属性为‘WINDOW_AUTOSIZE’自动设置时无效果
cv2.resizeWindow('new_img',800,600) # resizeWindow(winname, width, height)
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
# 或操作 ——> 要求两幅图形状相同
cat_or = cv2.bitwise_or(cat,new_dog)
# 观察像素变换结果
print(cat[:2,:2])
print('---------------------')
print(dog[:2,:2])
print('---------------------')
print(cat_or[:2,:2])
# 展示图片
cv2.imshow('new_img',np.hstack((cat,new_dog,cat_and)))
cv2.waitKey()
cv2.destroyAllWindows()
结果:
[[[195 216 244]
[194 215 243]]
[[194 215 243]
[193 214 242]]]
---------------------
[[[ 84 102 125]
[ 79 100 122]]
[[ 85 102 123]
[ 84 103 124]]]
---------------------
[[[215 254 253]
[207 247 251]]
[[215 247 251]
[213 247 254]]]
非运算~
关键API:dst = cv2.bitwise_not(src,mask)
其中:
src
:参与运算的图像mask
:可选参数,掩膜
返回值:
dst
:取反运算之后的结果图像
如~255 = 0, ~195 = 60
,在opencv中取非就是相当于用255减去取非的数
# 非运算
import cv2
import numpy as np
# 创建窗口
cv2.namedWindow('new_img',cv2.WINDOW_NORMAL)
# 更改窗口大小—>在窗口属性为‘WINDOW_AUTOSIZE’自动设置时无效果
cv2.resizeWindow('new_img',800,600) # resizeWindow(winname, width, height)
# 读取图片
cat = cv2.imread('./cat.jpeg')
# 非操作 ——> 相当于255 - cat 并且会返回运算的结果
cat_not = cv2.bitwise_not(cat)
# 观察像素变换结果
print(cat[:2,:2])
print('---------------------')
print(cat_not[:2,:2])
# 展示图片
cv2.imshow('new_img',np.hstack((cat,cat_not)))
cv2.waitKey()
cv2.destroyAllWindows()
结果:
cat:
[[[195 216 244]
[194 215 243]]
[[194 215 243]
[193 214 242]]]
---------------------
cat_not:
[[[60 39 11]
[61 40 12]]
[[61 40 12]
[62 41 13]]]
异或运算~
关键API:dst = cv2.bitwise_xor(src,mask)
其中:
src
:参与运算的图像mask
:可选参数,掩膜
返回值:
dst
:取反运算之后的结果图像
图像做异或运算的过程:
一样的为0,不一样为1
异或运算也有两个特点:
- 如果某像素和纯白色像素(255)做异或运算,结果为原像素的取反结果:
00101011 ^ 11111111 = 11010100
- 如果某像素和纯黑色像素(0)做异或运算,结果仍为该像素的原值(不变):
00101011 ^ 00000000 = 00101011
由此可以看出:如果原图像与掩膜
进行异或运算,掩膜白色区域所覆盖的内容呈现取反效果,黑色区域覆盖内容保持不变。下面通过一个实例来演示掩膜在异或运算过程中的应用
花图象与十字掩膜做异或运算
创建一个掩膜,在掩膜中央保留一个十字形的白色区域,让掩膜与❀做异或运算
,查看运算后的结果
代码如下:
import cv2
import numpy as np
# 导入❀的图像
img = cv2.imread('./amygdalus triloba.png')
print(img.shape)
# 创建一个和❀大小一样的掩膜
mask = np.zeros(img.shape,np.uint8)
# 在掩膜中画出十字
mask[120:180,:,:] = 255 # 横着的白色区域
mask[:,90:150,:] = 255 # 竖着的白色区域
# 将图像❀和掩膜相加
dst = cv2.bitwise_xor(img, mask)
# 展示图片
cv2.imshow('img',img)
cv2.imshow('mask',mask)
cv2.imshow('dst',dst)
# cv2.imshow('img and mask',np.hstack((img,mask,dst)))
cv2.waitKey()
cv2.destroyAllWindows()
结果:
其他实例:
# 异或运算
import cv2
import numpy as np
# 创建窗口
cv2.namedWindow('new_img',cv2.WINDOW_NORMAL)
# 更改窗口大小—>在窗口属性为‘WINDOW_AUTOSIZE’自动设置时无效果
cv2.resizeWindow('new_img',800,600) # resizeWindow(winname, width, height)
# 读取图片
cat = cv2.imread('./cat.jpeg')
dog = cv2.imread('./dog.jpeg')
# 由于dog的大小比cat大,因此我们使用numpy切片的方法把dog切成和cat一样的大小
new_dog = dog[0:1078,0:1095] # numpy的切片方式
# 或操作 ——> 要求两幅图形状相同
cat_xor = cv2.bitwise_xor(cat,new_dog)
# 观察像素变换结果
print(cat[:2,:2])
print('---------------------')
print(dog[:2,:2])
print('---------------------')
print(cat_xor[:2,:2])
# 展示图片
cv2.imshow('new_img',np.hstack((cat,new_dog,cat_xor)))
cv2.waitKey()
cv2.destroyAllWindows()
结果:
[[[195 216 244]
[194 215 243]]
[[194 215 243]
[193 214 242]]]
---------------------
[[[ 84 102 125]
[ 79 100 122]]
[[ 85 102 123]
[ 84 103 124]]]
---------------------
[[[151 190 137]
[141 179 137]]
[[151 177 136]
[149 177 142]]]
以上的 位运算API
中,还有一个可选参数 ——> 掩码mask
掩码mask
——> 我们的bitewise_
操作(无论是and
、or
、not
、xor
操作都有的参数)
以与运算bitwise_and(src1, src2[, dst[, mask]])
为例:
作用:前面的两个参数src1
和src2
正常地做与运算,将结果和掩码mask
再做与运算(无论是什么位运算,都统统和掩码mask
做与运算)
按位运算:如果是True
,则显示原来的值(返回原图);如果是False
,则为0(黑色)
可参考 作业 ——> Version2 ——>3
以上例程中,我们想要对两幅不一样的图片进行位运算时,都需要将大的图片"切片"成和小图一样的大小,用到的是numpy
中的ndarray
方法,其实略为繁琐,在下一章我们将使用图像变换的cv2.resize()
方法,会更加简便操作!
对图像进行加密、解密
异或运算还有一个特点:
- 执行一次
异或运算
得到一个结果,再对这个结果执行第二次异或运算
,则又会还原成最初的值。
利用这个特点可以实现对图像内容的加密和解密
利用Numpy
库中的np.random.randint()
方法创建一个随机像素值图像作为密钥图像
让密钥图像与原始图像做异或运算得出加密图像,再使用密钥图像对加密图像进行解密
具体代码如下:
import cv2
import numpy as np
# 用于加密、解密的函数
def encode(img,img_key):
result = img = cv2.bitwise_xor(img,img_key)
return result
# 导入❀的图像
img = cv2.imread('./amygdalus triloba.png')
# 原图像的列数、行数、通道数
rows,colmns,channel = img.shape
##================================================================
# 创建与❀图像大小相等的随机像素图像,作为密钥图像
img_key = np.random.randint(0,256,img.shape,np.uint8)
# 展示❀图像
cv2.imshow('1',img)
# 展示密钥图像
cv2.imshow('2',img_key)
##================================================================
# 对❀图像进行加密
result = encode(img,img_key)
# 展示加密图像
cv2.imshow('3',result)
##================================================================
# 对加密图像进行解密
result = encode(result,img_key)
# 展示解密图像
cv2.imshow('4',result)
##================================================================
cv2.waitKey()
cv2.destroyAllWindows()
结果:
- 原图
- 密钥图像
- 加密后的图像
- 解密后的图像
合并图像
在处理图像时经常会遇到需要将两幅图像合并成一幅图像的需求
合并图像分两种情况:
- 两幅图像融合在一起
- 每幅图像提供一部分内容,将这些内容拼接成一幅图像
OpenCV
分别用加权和和覆盖两种方式来满足上述需求
加权和
OpenCV
通过计算加权和的方式,按照不同的权重取两幅图像的像素之和,最后组成两幅新图像。
加权和不会像纯加法那样让图像丢失信息,而是在尽量保留原有图像信息的基础上把两幅图像融合到一起
关键API:dst = cv2.addWeighted(src1,alpha,src2,beta,gamma)
其中:
src1
:第一幅图像alpha
:第一幅图像的权重src2
:第二幅图像beta
:第二幅图象的权重gamma
:在相加的结果上添加的标量。- 该值越大,图像越亮
- 该值越小,图像越暗(可以是负数)
返回值:
dst
:叠加之后的图像
下面通过一个实例来演示cv2.addWeighted()
方法的效果
读取两幅不同的风景照片,使用cv2.addWeighted()
方法计算两幅图像的加权和,查看处理之后的图像是否为多次曝光效果
具体代码如下:
import cv2
import numpy as np
sun = cv2.imread('./sunset.jpg')
beach = cv2.imread('./beach.jpg')
# 获取beach图像的大小
rows,colmns,channel = sun.shape
# 由于两幅图像的大小不一样,因此我们要缩放成同样大小
beach = cv2.resize(beach,(colmns,rows))
# 计算两幅图片的加权和
img = cv2.addWeighted(sun,0.6,beach,0.6,0)
# 展示
cv2.imshow('sun',sun)
cv2.imshow('beach',beach)
cv2.imshow('sun_beach',img)
cv2.waitKey()
cv2.destroyAllWindows()
结果:
可以看到最后得到的图像中包含两幅图像的信息
覆盖
覆盖图像就是直接把前景图像显示在背景图像中,前景图像会挡住背景图像。覆盖之后的背景图像会丢失信息,不会出现加权和那样的效果
OpenCV
没有提供覆盖操作的方法,开发者可以直接用修改图像像素值的方式来实现图像的覆盖、拼接效果:从A图像中取像素值,直接赋值给B图像中的像素,这样就能在B图像中看到A图像的信息了
下面通过一个实例演示如何从前景图像中抠图,再将抠出的图像覆盖在背景图像中
将小猫图像贴在沙滩
- 读取小猫原始图像,将原始图像中的75行至400行、120列到260列的像素单独保存成一幅小猫图像,并且将小猫图像缩放成70*60大小
- 读取沙滩图像,将小猫图像覆盖到沙滩图像 (100,260) 的坐标位置
- 覆盖过程中将小猫图像的像素逐个赋值给沙滩图像中对应位置的像素
具体代码如下:
import cv2
import numpy as np
beach = cv2.imread('./beach.jpg')
cat_img = cv2.imread('./cat.jpg')
cat = cat_img[75:400,120:260,:]
cat = cv2.resize(cat,(70,160))
# 记录截取下的猫的大小
rows,colmns,channel = cat.shape
# 将沙滩中从(100,260)开始的和猫大小一样的图像区域改成猫
beach[100:(100 + rows),260:(260 + colmns),:] = cat
# 展示
cv2.imshow('cat_orign',cat_img)
cv2.imshow('cat_cut',cat)
cv2.imshow('beach',beach)
cv2.waitKey()
cv2.destroyAllWindows()
结果:
作业
Version 1
在图片上加上自己设计的Logo,建议Logo简单一点
我们把logo设计为白底,红蓝绿三个颜色的三角形
用到图片的融合API:cv2.addWeighted()
import cv2
import numpy as np
# 导入背景图
cat = cv2.imread('./cat.jpeg')
# 创建一个纯白的背景图 用于画logo
logo = np.ones((480,640,3),np.uint8) * 255 # 行(宽):640 列(高):480
# 绘制直线——> 三角形的边
logo = cv2.line(img,(160,320),(480,320),(0,0,255),5,16)
logo = cv2.line(img,(160,320),(320,100),(0,255,0),5,16)
logo = cv2.line(img,(480,320),(320,100),(255,0,0),5,16)
new_cat = cv2.resize(cat , (640,480))
print(new_cat.shape)
print(logo.shape)
# 融合两张图片 ——> 前提是两张图片大小必须相等!
new_img = cv2.addWeighted(new_cat,0.7,logo,0.3,0)
cv2.imshow('logo',new_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
其中:
画出的logo:
我们用的底图:
结果:
Version 2
在图片上加上自己设计的Logo,建议Logo简单一点
我们把logo设计为红绿两个颜色的正方形拼接
用到的图像处理API:cv2.bitwise_and(roi, roi,mask = m)
;cv2.add(src1,src2) # 要求两幅图大小一样
操作步骤:
- 引入图片
- 设计一个logo图片
- 规划一下你的logo需要放在哪块位置,在添加的位置变成黑色
- 利用add方法,把logo和图片叠加在一起
具体操作:
1.引入图片
:
# 导入图片
cat = cv2.imread('./cat.jpeg')
2.设计一个logo图片
:
# 创建logo
logo = np.zeros((200,200,3),np.uint8)
# 绘制logo ——> 对图像进行切片,换成另一种颜色
logo[20:120,20:120] = [0,0,255] # 红色
logo[80:180,80:180] = [0,255,0] # 绿色
3.规划一下你的logo需要放在哪块位置,在添加的位置变成黑色
:
用到了掩码mask
掩码mask ——> 我们的bitewise_
操作(无论是and
、or
、not
、xor
操作都有的参数)
以与运算bitewise_and
为例:
作用:前面的两个参数src1
和src2
正常地做与运算,将结果和掩码mask
再做与运算(无论是什么位运算,都统统和掩码mask
做与运算)
按位运算:如果是True
,则显示原来的值(返回原图);如果是False
,则为0(黑色)
# 掩码mask ——> 我们的bitewise_操作(无论是and、or、not、xor操作都有的参数) 以与运算and为例
# 作用:前面的两个参数src1和src2正常地做与运算,将结果和掩码mask再做与运算(无论是什么位运算,都统统和掩码mask做与运算)
#按位运算:如果是True,则显示原来的值(返回原图);如果是False,则为0(黑色)
# 我们用掩码的目的是:把logo中不必要的像素(纯黑的部分)剔除,使和背景融合的只有有用的部分
mask = np.zeros((200,200),np.uint8) # 需要设置为和logo一样的大小才能做掩码 注意:掩码是黑白的!(做与运算时要求对象是二维的,不能是三维的)
mask[20:120,20:120] = 255 # 改成白色
mask[80:180,80:180] = 255 # 改成白色 # 白色"与"上任何东西都是白色,做mask运算时这部分就是原来的颜色
cv2.imshow('mask',mask)
# 取反
m = cv2.bitwise_not(mask) # 将logo部分变成黑色 ——> 用该图去和logo做与运算,就可以把logo中有用的部分抠出来
cv2.imshow('m',m)
4.利用add方法,把logo和图片叠加在一起
:
# 选择cat中添加logo的位置 ——> 感兴趣区域roi
roi = cat[0:200,0:200] # 为浅拷贝:相当于从cat中截取了一块图片出来,改变这一小块中的值,原始的cat也会改变
# roi 与 m 进行与操作 ——> 相当于在roi中抠出了logo的位置(变成黑色)
temp = cv2.bitwise_and(roi, roi,mask = m) # 自己和自己进行"与"操作还是自己
# 做个加法:temp中间是黑的,logo周围是黑的,两个图片相加:黑的地方值为0,相加不影响像素,即把两张图完好地叠在一起了!
dst = cv2.add(temp,logo)
cv2.imshow('temp',temp)
# 在cat上还原 ——> 把图片覆盖上去即可
cat[0:200,0:200] = dst
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
整段代码:
# 写代码前思路要清晰
# 1. 引入图片
# 2. 设计一个logo图片
# 3. 规划一下你的logo需要放在哪块位置,在添加的位置变成黑色
# 4. 利用add方法,把logo和图片叠加在一起
# 导入图片
cat = cv2.imread('./cat.jpeg')
# 创建logo
logo = np.zeros((200,200,3),np.uint8)
# 绘制logo ——> 对图像进行切片,换成另一种颜色
logo[20:120,20:120] = [0,0,255] # 红色
logo[80:180,80:180] = [0,255,0] # 绿色
cv2.imshow('logo',logo)
cv2.imshow('cat',cat)
# 掩码mask ——> 我们的bitewise_操作(无论是and、or、not、xor操作都有的参数) 以与运算and为例
# 作用:前面的两个参数src1和src2正常地做与运算,将结果和掩码mask再做与运算(无论是什么位运算,都统统和掩码mask做与运算)
#按位运算:如果是True,则显示原来的值(返回原图);如果是False,则为0(黑色)
# 我们用掩码的目的是:把logo中不必要的像素(纯黑的部分)剔除,使和背景融合的只有有用的部分
mask = np.zeros((200,200),np.uint8) # 需要设置为和logo一样的大小才能做掩码 注意:掩码是黑白的!(做与运算时要求对象是二维的,不能是三维的)
mask[20:120,20:120] = 255 # 改成白色
mask[80:180,80:180] = 255 # 改成白色 # 白色"与"上任何东西都是白色,做mask运算时这部分就是原来的颜色
cv2.imshow('mask',mask)
# 取反
m = cv2.bitwise_not(mask) # 将logo部分变成黑色 ——> 用该图去和logo做与运算,就可以把logo中有用的部分抠出来
cv2.imshow('m',m)
# 选择cat中添加logo的位置 ——> 感兴趣区域roi
roi = cat[0:200,0:200] # 为浅拷贝:相当于从cat中截取了一块图片出来,改变这一小块中的值,原始的cat也会改变
# roi 与 m 进行与操作 ——> 相当于在roi中抠出了logo的位置(变成黑色)
temp = cv2.bitwise_and(roi, roi,mask = m) # 自己和自己进行"与"操作还是自己
# 做个加法:temp中间是黑的,logo周围是黑的,两个图片相加:黑的地方值为0,相加不影响像素,即把两张图完好地叠在一起了!
dst = cv2.add(temp,logo)
cv2.imshow('temp',temp)
# 在cat上还原 ——> 把图片覆盖上去即可
cat[0:200,0:200] = dst
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()