【CV2】计算机视觉的图像处理库
cv2.add()输入图像尺寸和通道数必须相同,使用cv2.resize来实现,cv2.resize中的dsize参数是指目标图像的大小,其格式应为 (宽度, 高度)途径:生成一张`掩膜`,把图像中需要去掉(或者说,变成黑色)的像素点标记为0、把图像中需要保留的像素点标记为`“1”``cv2.add`用于两个图像的逐像素加法操作,或者一个图像与一个标量的加法操作,与直接使用 + 运算符不同,它提供
目录
# pip install opencv-python matplotlib numpy -i Simple Index
import cv2
import matplotlib.pyplot as plt
import numpy as np
独立窗口显示图片,直到按下任意键关闭图片
Parameters:
- img: Image to display.
- title: Window title (default 'Image').
def show_image(img,title='Image'):
cv2.imshow(title, img) # Display the image in a window with the given title.
cv2.waitKey(0) # 等待任意键按下
cv2.destroyAllWindows() # Close all OpenCV windows.
image_path = 'D:\lene.png' # 需修改为自己电脑上的图片路径
位运算和掩码图像mask
位运算
- 任意8位二进制数与0做`&`运算,结果为`0`
- 任意8位二进制数与<b>255</b>`&`运算,结果为自身
在计算机中,位运算是对二进制位进行操作的运算。对于 8 位二进制数,常见的位运算规则如下:
按位与(&):两个对应位都为 1 时,结果位才为 1,否则为 0。任意 8 位二进制数与 0(二进制 00000000)做 & 运算,结果为 0。因为 0 与任何位进行与运算结果都是 0。
任意 8 位二进制数与 255(二进制 11111111)做 & 运算,结果为自身。因为 1 与任何位进行与运算都保持该位不变。
图像像素的按位与操作:在图像处理中,图像通常以多维数组的形式存储,每个像素由一个或多个通道的值表示(如 BGR 模式下每个像素由三个通道表示)。按位与操作可以逐像素、逐通道地对图像进行处理。
思路:修改图像的颜色实际上是修改表示图像的数组的数值。由于黑色的BGR三元组是`(0,0,0)`,可以使用数组的加法实现图像层叠。
掩码图像(Mask)的概念
掩码图像是一个单通道的图像,其尺寸与原图像相同。掩码图像中的每个像素值代表对原图像对应像素的操作规则:
像素值为 0 表示在处理后将原图像对应像素置为黑色(即 (0, 0, 0))。
像素值为 255 表示保留原图像对应像素的值不变。
需要实现的功能:保留图像中某些像素点不变,把某些像素点的颜色变成黑色。
途径:生成一张`掩膜`,把图像中需要去掉(或者说,变成黑色)的像素点标记为0、把图像中需要保留的像素点标记为`“1”`
可否借助位运算实现对图像的修改?
bitwise_and
img = cv2.imread(image_path)
mask = np.zeros(img.shape[:2], dtype = np.uint8) # 创建一个与图像大小相同的掩码矩阵,各元素的掩码初值是0
mask[200:400, 350:500] = 255 # 把200:400行, 200:380列的区域的掩码设成255
print('mask的尺寸是', mask.shape)
# 用图像的形式显示mask,可看到周围一圈是0(黑色),中间一块是255(白色)
plt.imshow(mask, cmap = 'gray') # mask是单通道灰度图,用plt显示要添加参数cmap='gray'

dst = bitwise_and(src1, src2[, dst[, mask]])`
dst = src1 & src2`
* src1:第一个输入图像,通常是想要处理的原始图像
* src2:第二个输入图像,通常是一个掩膜(mask),其尺寸和通道数应该与 src1 相同
* mask:操作掩码,单通道,用于指定函数操作的区域
把灰度图转成BGR三通道得到mask3
# 把灰度图转成BGR三通道得到mask3
mask3 = cv2.cvtColor(mask,cv2.COLOR_GRAY2BGR)
print('mask3的尺寸是', mask3.shape)
plt.imshow(mask3[:, :, ::-1]) #::-1是Python的切片语法,用于反转数组或列表,用于将图像从BGR格式转换为RGB格式

可以看到mask3尺寸为三维的尺寸
把`img`的各像素与`mask3`(掩码)各像素做`and`操作。
- 指定切片区域的像素与`(255,255,255)`做`and`操作,像素值保持不变。
- 其余区域的像素都与`(0, 0, 0)`做`and`操作,像素值变成`(0, 0, 0)`
利用cv2.bitwise_and对img和mask3进行按位与运算得到result
result = cv2.bitwise_and(img,mask3)
plt.subplot(1, 3, 1) # 1行3列显示三张图,画第1张
plt.imshow(img[:, :, ::-1])
plt.subplot(1, 3, 2) # 1行3列显示三张图,画第2张
plt.imshow(mask3[:, :, ::-1])
plt.subplot(1, 3, 3) # 1行3列显示三张图,画第3张
plt.imshow(result[:, :, ::-1])

换一种写法,mask以灰度图也就是单通道的方式
img = cv2.imread(image_path)
mask = np.zeros(img.shape[:2], dtype = np.uint8) # 创建一个与图像大小相同的掩码矩阵,各元素的掩码初值是0
mask[200:400, 350:500] = 255 # 把200:400行, 200:380列的区域的掩码设成255
# 仅对mask=1的位进行img & img运算,即保持img的数值不变。
# 把mask=0的位变成0
result = cv2.bitwise_and(img,img,mask=mask)
plt.subplot(1, 3, 1) # 1行3列显示三张图,画第1张
plt.xticks([]), plt.yticks([]) # 不显示坐标
plt.imshow(img[:, :, ::-1])
plt.subplot(1, 3, 2) # 1行3列显示三张图,画第2张
plt.xticks([]), plt.yticks([])
plt.imshow(mask, cmap = 'gray') # mask是单通道,只能用灰度图显示
plt.subplot(1, 3, 3) # 1行3列显示三张图,画第3张
plt.xticks([]), plt.yticks([])
plt.imshow(result[:, :, ::-1])

可以看到输出结果其实没什么变化,因为mask
plt.imshow(mask, cmap = 'gray') # mask是单通道,只能用灰度图显示
只让第二张图以灰度的方式显示
使用掩码获得Lena的脸部区域
交互式获取roi
img = cv2.imread(image_path)
# 交互式获取roi
# 用鼠标框选roi区域后,按空格或回车键确认!按c键取消!
r = cv2.selectROI(img) # 返回一个元组(x, y, w, h)
cv2.destroyAllWindows()
mask = np.zeros(img.shape[:2], dtype = np.uint8)
mask[int(r[1]):int(r[1] + r[3]), int(r[0]):int(r[0] + r[2])] = 255 # 将选择的ROI区域变为255,即8位全是1
# TODO: result = cv2.bitwise_and()
result = cv2.bitwise_and(img,img,mask=mask)
plt.subplot(1, 3, 1) # 1行3列显示三张图,画第1张
plt.xticks([]), plt.yticks([]) # 不显示坐标
plt.imshow(img[:,:,::-1])
plt.subplot(1, 3, 2) # 1行3列显示三张图,画第2张
plt.xticks([]), plt.yticks([])
plt.imshow(mask, cmap='gray') # mask是单通道,只能用灰度图显示
plt.subplot(1, 3, 3) # 1行3列显示三张图,画第3张
plt.xticks([]), plt.yticks([])
plt.imshow(result[:,:,::-1])
运行后会有python的cv窗口弹出,手动框出需要的位置

图像相加
图像叠加(直接+)
示例
img1 = np.ones((100, 200, 3), dtype = np.uint8) * 100 # 生成尺寸为100 * 200 * 3的数组,元素值是100
img2 = np.ones((100, 200, 3), dtype = np.uint8) * 200 # 生成尺寸为100 * 200 * 3的数组,元素值是200
# TODO:将img1和img2直接相加+得到img3
img3= img1+img2
imgs = np.hstack([img1, img2, img3]) # 把三张图片横向拼接,生成的imgs高(行)是100,宽(列)是600
plt.imshow(imgs[:, :, ::-1])

三张图像进行了横向叠加
直接相加后的图片跟相加前有什么变化?
直接相加后的图片(img3)在亮度和颜色的灰度值上与相加前的图片(img1 和 img2)有明显不同,这是由于 np.uint8 数据类型的溢出截断导致的。在实际图像处理中,如果需要正确处理这种相加操作,可考虑使用更大数据类型(如 np.int16),然后再进行合适的范围调整。
cv2.add方法
为什么img1、img2叠加后得到的img3的颜色更深?输出img3可看到元素值是44。为什么不是`100+200=300`呢?
因为img1、img2、img3的元素都是`np.uint8`类型,取值范围是`0~255`。当数值超出范围就会对`256`求余数。`300`除以`256`的余数是`300 % 256=44`
`cv2.add`用于两个图像的逐像素加法操作,或者一个图像与一个标量的加法操作,与直接使用 + 运算符不同,它提供了饱和操作,即如果计算的像素值超过了255,则自动设置为255。这是为了防止数据溢出。
img1 = np.ones((100, 200, 3), dtype = np.uint8) * 100 # 生成尺寸为100 * 200 * 3的数组,元素值是100
img2 = np.ones((100, 200, 3), dtype = np.uint8) * 200 # 生成尺寸为100 * 200 * 3的数组,元素值是100
# TODO:利用cv2.add进行相加得到img3
img3 = cv2.add(img1,img2)
imgs = np.hstack([img1, img2, img3]) # 把三张图片横向拼接,生成的imgs高(行)是100,宽(列)是600
plt.imshow(imgs[:, :, ::-1])

可以看到第三张图明显与上面的结果不同,发生了取余的操作
dog = cv2.imread('D:\dog.png') # 改为自己电脑上的图片路径
cat = cv2.imread('D:\cat.png')
dog.shape, cat.shape # (h,w)
# ((1600, 2560, 3), (713, 725, 3))
cv2.add()输入图像尺寸和通道数必须相同,使用cv2.resize来实现,cv2.resize中的dsize参数是指目标图像的大小,其格式应为 (宽度, 高度)
# 将dog和cat的尺寸调整为宽1000, 高700
dog = cv2.resize(dog,(1000,700)) # (w,h)重新调整尺寸为宽1000, 高700
cat = cv2.resize(cat,(1000,700))
print(dog.shape, cat.shape)
# (700, 1000, 3) (700, 1000, 3)
利用cv2.add进行相加的结果用dog_cat定义
dog_cat = cv2.add(dog,cat)
plt.subplot(1, 3, 1) # 1行3列显示三张图,画第1张
plt.xticks([]), plt.yticks([])
plt.imshow(dog[:, :, ::-1])
plt.subplot(1, 3, 2) # 1行3列显示三张图,画第2张
plt.xticks([]), plt.yticks([])
plt.imshow(cat[:,:,::-1])
plt.subplot(1, 3, 3) # 1行3列显示三张图,画第3张
plt.xticks([]), plt.yticks([])
plt.imshow(dog_cat[:, :, ::-1])

用到的图片:


cv2.addWeighted方法
`addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) -> dst`
`cv2.addWeighted`函数用于按权重对两个图像进行加权和,这在进行图像融合时特别有用。可以控制每个图像贡献的比重,以及结果图像的全局亮度调整。第五个参数gamma为加到最终加权和上的标量,用于调整亮度。
利用cv2.addWeighted实现dog_cat = dog * 0.3 + cat * 0.6
dog_cat = cv2.addWeighted(dog,0.5,cat,0.5,0.3)
plt.subplot(1, 3, 1) # 1行3列显示三张图,画第1张
plt.xticks([]), plt.yticks([])
plt.imshow(dog[:, :, ::-1])
plt.subplot(1, 3, 2) # 1行3列显示三张图,画第2张
plt.xticks([]), plt.yticks([])
plt.imshow(cat[:,:,::-1])
plt.subplot(1, 3, 3) # 1行3列显示三张图,画第3张
plt.xticks([]), plt.yticks([])
plt.imshow(dog_cat[:, :, ::-1])

实践-给图片右下角加opencv-logo
读取大图和logo原图
lena = cv2.imread('D:\lene02.png',1) # 选择了一张分辨率更大的图片,更改图片路径
logo = cv2.imread('D:\cv-log.png',1)
用到的图片:
裁剪过的lena

提取大图右下角ROI
h1, w1, c1 = lena.shape
h2, w2, c2 = logo.shape
# 定位大图右下角区域roi,使其具有logo的尺寸
roi = lena[h1-h2:h1,w1-w2:w1]
# 注意:对roi的操作就是对lena原图的操作
cv2.imwrite('D:\lene02.png',roi)
plt.imshow(roi[:,:,::-1])

把logo原图转换为灰度图
图片:
opencv的logo

利用cv2.cvtColor得到灰度图gray
gray = cv2.cvtColor(logo, cv2.COLOR_BGR2GRAY)
cv2.imwrite('D:\cv-log.png',gray)
plt.imshow(gray, cmap='gray')

通过二值化获取抛弃logo前景的掩码图
使用OTSU二值化方法进行二值化ret, mask1 =
ret, mask1 = cv2. threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite('D:\cv-log.png',mask1)
plt.imshow(mask1, cmap='gray')

二值化也就是保留黑白
通过按位与获得只有大图背景而logo前景挖空的局部图
mask1与roi进行按位与,fg1 = cv2.bitwise_and()
fg1 = cv2.bitwise_and(roi,roi,mask=mask1)
cv2.imwrite('D:\cv-log.png',fg1)
plt.imshow(fg1[:,:,::-1])

其实也就是图片的像素叠层,logo的前景像素覆盖掉lena图片的对应像素
通过二值化获取保留logo前景的掩码图
使用BINARY_INV方法进行二值化ret, mask2 =
ret, mask2 = cv2.threshold(gray,220,255,cv2.THRESH_BINARY_INV)
cv2.imwrite('D:\cv-log.png',mask2)
plt.imshow(mask2, cmap='gray')

通过按位与获得只有logo前景而背景挖空的局部图
mask2与logo进行按位与,fg2 = cv2.bitwise_and
fg2 = cv2.bitwise_and(logo,logo,mask=mask2)
cv2.imwrite('D:\cv-log.png',fg2)
plt.imshow(fg2[:,:,::-1])

提取过后的logo
通过add()函数把两个局部图相加
roi[:]=cv2.add
roi[:]=cv2.add(fg1,fg2) #加上“:”是为了确保原图的提取
cv2.imwrite('D:\cv-log.png',roi)
plt.imshow(roi[:,:,::-1])

提取后的roi视图
把合成照片放回Lena照片的右下角
lena[h1-h2:h1,w1-w2:w1] = roi
plt.imshow(lena[:, :, ::-1])
plt.xticks([]), plt.yticks([])

不打印坐标,这是原图视图
使用掩码对lena图像的脸部进行打码、解码
lena = cv2.imread(image_path)
h,w, _ = lena.shape # 图片的行(高)h、列(宽)w
只显示lena脸部,其他部分打码
mask1 = np.zeros((h,w), dtype=np.uint8) #生成一个掩膜图像(任何一个数与0与运算都是0,任何一个数与1与运算都是这个数本身)
mask1[200:400, 200:380]=255 #这个掩膜图像大小是lena.shape的大小,像素值是0或255
# # 或者用cv2.selectROI自己选取ROI区域
# TODO: lena_face = cv2.bitwise_and()
lena_face = cv2.bitwise_and(lena,lena,mask = mask1)
imgs = np.hstack([lena, lena_face])
plt.imshow(imgs[:, :, ::-1])

去掉Lena脸部
mask2 = np.ones((h,w), dtype=np.uint8)*255
mask2[200:400, 200:380]=0
# TODO:lena_no_face = cv2.bitwise_and()
lena_no_face = cv2.bitwise_and(lena ,lena , mask=mask2)
imgs = np.hstack([lena, lena_no_face])
plt.imshow(imgs[:, :, ::-1])

给lena脸部打彩色码
lena_no_face_color = lena.copy()
ROI =np.random.randint(0,
256,
size=(400-200,380-200,3),
dtype = np.uint8)
lena_no_face_color[200:400, 200:380] = ROI
imgs = np.hstack([lena, lena_no_face_color])
plt.imshow(imgs[:, :, ::-1])
原理是随机的生成0-255的像素值(点)
ROI =np.random.randint(0,
256,
size=(400-200,380-200,3),
dtype = np.uint8)

给图像加密/解密
随机生成密钥图像,尺寸跟lena图像相同
#生成一个随机密钥图像
key = np.random.randint(0,
256,
size=(h,w,3),
dtype = np.uint8)
#1、使用密钥key对lena整体加密
lena_encryption = cv2.bitwise_xor(lena,key) # 在逻辑异或中,如果两个比特位相同,则结果为0;如果不同,则结果为1
plt.imshow(lena_encryption[:, :, ::-1])

只对lena脸部应用密钥进行解密(逻辑异或操作)
face_only = lena_encryption.copy()
# face_only[200:400, 200:380] = lena_face[200:400, 200:380]
face_only[200:400, 200:380] = cv2.bitwise_xor(face_only[200:400, 200:380],key[200:400, 200:380])
plt.imshow(face_only[:, :, ::-1])

使用密钥key对lena整体解密
lena_decryption = cv2.bitwise_xor(lena_encryption,key)
plt.imshow(lena_decryption[:, :, ::-1])

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)