图像加法运算
- 可以通过加号运算符“+”对图像进行加法运算,也可以通过cv2.add()函数对图像进行加法运算。
- 求得的和很可能超过255。上述两种不同的加法运算方式,对超过255的数值的处理方式是不一样的。
加号运算符
“mod(a+b, 256)”表示计算“a+b的和除以256取余数”
通过将数组的数值类型定义为dtype=np.uint8,可以保证数组值的范围在[0,255]之间。
cv2.add
- 计算结果=cv2.add(图像1,图像2),两个参数都是图像,此时参与运算的图像大小和类型必须保持一致。
- 计算结果=cv2.add(数值,图像),第1个参数是数值,第2个参数是图像,此时将超过图像饱和值的数值处理为饱和值(最大值)。
- 计算结果=cv2.add(图像,数值),第1个参数是图像,第2个参数是数值,此时将超过图像饱和值的数值处理为饱和值(最大值)。
图像加权和
在计算两幅图像的像素值之和时,将每幅图像的权重考虑进来
dst=saturate(src1×α+src2×β+γ)
saturate()表示取饱和值(最大值)
图像进行加权和计算时,要求src1和src2必须大小、类型相同,但是对具体是什么类型和通道没有特殊限制。
OpenCV中提供了函数cv2.addWeighted(),用来实现图像的加权和(混合、融合)
dst=cv2.addWeighted(src1, alpha, src2, beta, gamma)
- 参数alpha和beta是src1和src2所对应的系数,它们的和可以等于1,也可以不等于1。该函数实现的功能是dst = src1×alpha + src2×beta + gamma
- 式中参数gamma的值可以是0,但是该参数是必选参数,不能省略
可以将上式理解为“结果图像=图像1×系数1+图像2×系数2+亮度调节量”。
按位逻辑运算
常见的位运算函数
按位与
按位与运算是指将数值转换为二进制值后,在对应的位置上进行与运算。
使用cv2.bitwise_and()函数来实现按位与运算
dst = cv2.bitwise_and( src1, src2[, mask] )
- dst表示与输入值具有同样大小的array输出值
- src1表示第一个array或scalar类型的输入值
- src2表示第二个array或scalar类型的输入值
- mask表示可选操作掩码,8位单通道array
针对BGR模式的彩色图像使用掩模提取指定部分。
由于按位与操作要求参与运算的数据有相同的通道,所以无法直接将彩色图像与单通道的掩模图像进行按位与操作。一般情况下,可以通过将掩模图像转换为BGR模式的彩色图像,让彩色图像与掩模图像进行按位与操作,实现掩模运算
a.shape 获取a图像的维数数据
按位或
dst = cv2.bitwise_or( src1, src2[, mask] )
- dst表示与输入值具有同样大小的array输出值。
- src1表示第一个array或scalar类型的输入值。
- src2表示第二个array或scalar类型的输入值。
- mask表示可选操作掩码,8位单通道array值。
按位非
按位非运算是指将数值转换为二进制值后,在对应的位置上进行非运算
dst = cv2.bitwise_not( src[, mask] )
- dst表示与输入值具有同样大小的array输出值。
- src表示array类型的输入值。
- mask表示可选操作掩码,8位单通道array值。
按位异或
异或运算也叫半加运算,其运算法则与不带进位的二进制加法类似
转换为为二进制值后,在对应的位置上进行异或运算
dst = cv2.bitwise_xor( src1, src2[, mask] )
- dst表示与输入值具有同样大小的array输出值。
- src1表示第一个array或scalar类型的输入值。
- src2表示第二个array或scalar类型的输入值。
- mask表示可选操作掩码,8位单通道array值。
掩膜
例如:
计算结果=cv2.add(参数1,参数2,掩模)
当使用掩模参数时,操作只会在掩模值为非空的像素点上执行,并将其他像素点的值置为0。
在函数中所使用的掩模参数可以是8位单通道图像。所以,可以将掩模图像作为按位与函数cv2.bitwise_and( src1, src2[, mask]] )中参数mask的值,完成掩模运算。
此时,让待处理的彩色图像同时作为函数cv2.bitwise_and( src1, src2[, mask]] )的参数src1和参数src2,使用掩模图像作为掩模参数,完成按位与运算,即可得到由掩模控制的彩色图像。
使用的掩模参数控制的是,在目标图像中,哪些区域的值是彩色图像的值、哪些区域的值是0。
图像与数值的运算
加法运算和按位运算中,参与运算的两个算子(参数)既可以是两幅图像,也可以是一幅图像与一个数值。
img4=cv2.add(img1,6)
位平面分解
将灰度图像中处于同一比特位上的二进制像素值进行组合,得到一幅二进制值图像,该图像被称为灰度图像的一个位平面,这个过程被称为位平面分解
例如,将一幅灰度图像内所有像素点上处于二进制位内最低位上的值进行组合,可以构成“最低有效位”位平面。
图像中全部像素值的ai值所构成的位平面,称为第i个位平面(第i层)
在8位灰度图中,可以组成8个二进制值图像,即可以将原图分解为8个位平面。
像素值中各个ai的权重是不一样的
- a7的权重最高,所构成的位平面与原图像相关性最高,该位平面看起来通常与原图像最类似。
- a0权重最低,所构成的位平面与原图像相关性最低,该平面看起来通常是杂乱无章的。
针对RGB图像,如果将R通道、G通道、B通道中的每一个通道对应的位平面进行合并,即可组成新的RGB彩色图像。例如,针对一幅RGB图像,将其R通道的第3个位平面、G通道的第3个位平面、B通道的第3个位平面进行合并,则可以构成一幅新的RGB彩色图像,我们称之为原始图像的第3个位平面。
位平面分解的具体步骤
- 图像预处理
读取原始图像O,获取原始图像O的宽度M和高度N。 - 构造提取矩阵
使用按位与操作能够很方便地将一个数值指定位上的数字提取出来。
建立一个值均为2n的Mat作为提取矩阵(数组),用来与原始图像进行按位与运算,以提取第n个位平面。 - 提取位平面
将灰度图像与提取矩阵进行按位与运算,得到各个位平面。
将像素值与一个值为2n的数值进行按位与运算,能够使像素值的第n位保持不变,而将其余各位均置零。因此,通过像素值与特定值的按位与运算,能够提取像素值的指定二进制位的值。同理,通过按位与运算,能够提取图像的指定位平面。
提取位平面也可以通过将二进制像素值右移指定位,移到最低位,然后对2取模得到。例如,要提取第n个位平面,则可以将像素向右侧移动n位,然后对2取模,就可以得到第n个位平面。(注意此时得到的 缺少相应的系数) - 阈值处理
如果考虑到不同位平面的权重不同,则不需要进行阈值处理
- 通过计算得到的位平面是一个二值图像,如果直接将上述得到的位平面显示出来,则会得到一张近似黑色的图像。也就是说,每次提取位平面后,要想让二值位平面能够以黑白颜色显示出来,就要将得到的二值位平面进行阈值处理,将其中大于零的值处理为255。例如,将得到的位平面RD进行阈值处理,将其中的大于0的调整为255,具体语句为:
# 借助于 numpy mask=RD[:, :, i]>0 RD[mask]=255
- 首先,使用mask=RD[:, :, i]>0对RD进行处理:
- 将RD中大于0的值处理为逻辑值真(True)。
- 将RD中小于或等于0的值处理为逻辑值假(False)。
- 使用RD[mask]=255,将RD中对应“mask中逻辑值为真”位置上的值替换为255
- 显示图像
例: 矩阵x的8个通道分别用来提取灰度图像的8个位平面。例如,x[:, :,0]用来提取灰度图像的第0个位平面。
第7个位平面是与原始图像最接近的二值图像。
import cv2 import numpy as np lena=cv2.imread("img/lena.jpg",0) # 设置参数为0,即以gray方式读入 cv2.imshow("lena", lena) r, c=lena.shape x=np.zeros((r, c,8), dtype=np.uint8) for i in range(8): x[:, :, i]=2**i x[:, :, i]=cv2.bitwise_and(lena, x[:, :, i]) # mask=x[:, :, i]>0 # x[mask]=255 cv2.imshow(str(i), x[:, :, i]) cv2.waitKey() cv2.destroyAllWindows()
图像加密和解密
通过按位异或运算可以实现图像的加密和解密。
#使用掩模和按位运算方式实现的对脸部打码、解码 import cv2 import numpy as np #读取原始载体图像 lena=cv2.imread("img/lena.jpg",0) #读取原始载体图像的shape值 r, c=lena.shape mask=np.zeros((r, c), dtype=np.uint8) mask[220:400,250:350]=1 #获取一个key,打码、解码所使用的密钥 key=np.random.randint(0,256, size=[r, c], dtype=np.uint8) #============获取打码脸============ #使用密钥key对原始图像lena加密 lenaXorKey=cv2.bitwise_xor(lena, key) #获取加密图像的脸部信息encryptFace encryptFace=cv2.bitwise_and(lenaXorKey, mask*255) #将图像lena内的脸部值设置为0,得到noFace1 noFace1=cv2.bitwise_and(lena, (1-mask)*255) #得到打码的lena图像 maskFace=encryptFace+noFace1 #============将打码脸解码============ #将脸部打码的lena与密钥key进行异或运算,得到脸部的原始信息 extractOriginal=cv2.bitwise_xor(maskFace, key) #将解码的脸部信息extractOriginal提取出来,得到extractFace extractFace=cv2.bitwise_and(extractOriginal, mask*255) #从脸部打码的lena内提取没有脸部信息的lena图像,得到noFace2 noFace2=cv2.bitwise_and(maskFace, (1-mask)*255) #得到解码的lena图像 extractLena=noFace2+extractFace #============显示图像============ cv2.imshow("lena", lena) cv2.imshow("mask", mask*255) cv2.imshow("1-mask", (1-mask)*255) cv2.imshow("key", key) cv2.imshow("lenaXorKey", lenaXorKey) cv2.imshow("encryptFace", encryptFace) cv2.imshow("noFace1", noFace1) cv2.imshow("maskFace", maskFace) cv2.imshow("extractOriginal", extractOriginal) cv2.imshow("extractFace", extractFace) cv2.imshow("noFace2", noFace2) cv2.imshow("extractLena", extractLena) cv2.waitKey() cv2.destroyAllWindows()
数字水印
最低有效位(Least Significant Bit, LSB)指的是一个二进制数中的第0位(即最低位)。
最低有效位信息隐藏指的是,将一个需要隐藏的二值图像信息嵌入载体图像的最低有效位,即将载体图像的最低有效位层替换为当前需要隐藏的二值图像,从而实现将二值图像隐藏的目的。
由于二值图像处于载体图像的最低有效位上,所以对于载体图像的影响非常不明显,其具有较高的隐蔽性。
在必要时直接将载体图像的最低有效位层提取出来,即可得到嵌入在该位上的二值图像,达到提取秘密信息的目的。
这种信息隐藏也被称为数字水印,通过该方式可以实现信息隐藏、版权认证、身份认证等功能
在灰度二值图像中,像素值只有0和255两种类型值,分别用来表示黑色和白色。
由于信息的最低有效位对值的大小影响有限,因此,将载体图像最低有效位的值用水印信息替换后,载体图像像素的值并没有发生太大变化,人眼不足以看出区别,水印具有较高的隐蔽性。
要保留图像的高七位,还有一种实现方法,即先对像素进行右移一位的位操作,再进行左移一位的位操作。
将像素值对2取模,可以获取像素值的最低有效位。因此,可以通过让含水印载体图像对2取模的方式,获取图像的“最低有效位”位平面,提取到的位平面即为水印信息。
import cv2 import numpy as np #读取原始载体图像 lena=cv2.imread("img/lena.jpg",0) #读取水印图像 watermark=cv2.imread("img/watermark.jpg",0) #将水印图像内的值255处理为1,以方便嵌入 #后续章节会介绍使用threshold处理 w=watermark[:, :]>0 watermark[w]=1 #读取原始载体图像的shape值 r, c=lena.shape #============嵌入过程============ #生成元素值都是254的数组 t254=np.ones((r, c), dtype=np.uint8)*254 #获取lena图像的高七位 lenaH7=cv2.bitwise_and(lena, t254) #将watermark嵌入lenaH7内 e=cv2.bitwise_or(lenaH7, watermark) #============提取过程============ #生成元素值都是1的数组 t1=np.ones((r, c), dtype=np.uint8) #从载体图像内提取水印图像 wm=cv2.bitwise_and(e, t1) print(wm) #将水印图像内的值1处理为255,以方便显示 #后续章节会介绍使用threshold实现 w=wm[:, :]>0 wm[w]=255 #============显示============ cv2.imshow("lena", lena) cv2.imshow("watermark", watermark*255) #当前watermark内最大值为1 cv2.imshow("e", e) cv2.imshow("wm", wm) cv2.waitKey() cv2.destroyAllWindows()