3.1图像的加法运算
3.1.1加号运算符
使用加号运算符"+"对图像 a(像素值为 a)和图像 b(像素值为 b)进行求和运算 时,遵循以下规则:
a+b={a+b,if a+b≤255mod(a+b,256),if a+b>255 a + b = \begin{cases} a + b, & \text{if } a + b \leq 255 \\ \operatorname{mod}(a + b, 256), & \text{if } a + b > 255 \end{cases} a+b={a+b,mod(a+b,256),if a+b≤255if a+b>255
矩阵对位相加
如果两个图像对应像素值的和小于或等于 255,则直接相加得到运算结果。例如,像素值 28 和像素值 36 相加,得到计算结果 64。
如果两个图像对应像素值的和大于 255,则将运算结果对 256 取模。例如, 255+58=313,大于 255,则计算(255+58)%256=57,得到计算结果57。
python
import numpy as np
img1=np.random.randint(0,256,size=[3,3],dtype=np.uint8)
img2=np.random.randint(0,256,size=[3,3],dtype=np.uint8)
print("img1=\n",img1)
print("img2=\n",img2)
print("img1+img2=\n",img1+img2)
3.1.2cv2.add()函数
函数 cv2.add()可以用来计算图像像素值相加的和,其语法格式为:
计算结果=cv2.add(像素值 a,像素值 b)
需要注意,函数 cv2.add()中的参数可能有如下三种形式。
- 形式 1:计算结果=cv2.add(图像 1,图像 2),两个参数都是图像,此时参与运算的图像大小和类型必须保持一致。
- 形式 2:计算结果=cv2.add(数值,图像),第 1 个参数是数值,第 2 个参数是图像,此时将超过 图像饱和值的数值处理为饱和值(最大值)。
- 形式 3:计算结果=cv2.add(图像,数值),第 1 个参数是图像,第 2 个参数是数值,此时将超过图像饱和值的数值处理为饱和值(最大值)。
python
#形式1
import numpy as np
import cv2
img1=np.random.randint(0,256,size=[3,3],dtype=np.uint8)
img2=np.random.randint(0,256,size=[3,3],dtype=np.uint8)
print("img1=\n",img1)
print("img2=\n",img2)
img3=cv2.add(img1,img2)
print("cv2.add(img1,img2)=\n",img3)
3.2图像加权和
所谓图像加权和,就是在计算两幅图像的像素值之和时,将每幅图像的权重 考虑进来,可以用公式表示为:
dst = saturate(src1 × 𝛼 + src2 × 𝛽 + 𝛾)
saturate()表示取饱和值(最大值) 。图像进行加权和计算时,要求 src1 和 src2 必须大小、类型相同。
函数 cv2.addWeighted()
,用来实现图像的加权和(混合、融合) ,该函数的语法格式为:
dst=cv2.addWeighted(src1, alpha, src2, beta, gamma)
数组演示函数 cv2.addWeighted()的使用。
python
import numpy as np
import cv2
img1 = np.ones((3,4),dtype=np.uint8)*100
img2 = np.ones((3,4),dtype=np.uint8)*10
gamma=3
#将调节亮度参数 gamma 的值设置为 3。
img3 = cv2.addWeighted(img1,0.6,img2,5,gamma)
#计算"img1×0.6+img2×5+3"的混合值。
print(img3)
使用函数 cv2.addWeighted()对两幅图像进行加权混合
python
import cv2
import numpy as np
a=cv2.imread("duo.webp")
b=cv2.imread("duo.webp")
result=cv2.addWeighted(a,0.6,b,0.4,0)
cv2.imshow("suo",a)
cv2.imshow("duo",b)
cv2.imshow("result",result)
cv2.waitKey()
cv2.destroyAllWindows()

使用函数 cv2.addWeighted()将一幅图像的 ROI 混合在另外一幅图像内。
python
import cv2
lena=cv2.imread("lena.jpg",cv2.IMREAD_UNCHANGED)
dollar=cv2.imread("lan.jpg",cv2.IMREAD_UNCHANGED)
cv2.imshow("lena",lena)
cv2.imshow("dollar",dollar)
face1=lena[220:400,250:350]
face2=dollar[160:340,200:300]
add=cv2.addWeighted(face1,0.6,face2,0.4,0)
dollar[160:340,200:300]=add
cv2.imshow("result",dollar)
cv2.waitKey()
cv2.destroyAllWindows()

3.3按位逻辑运算
3.3.1按位与运算<且运算>
在 OpenCV 中,可以使用 cv2.bitwise_and()函数来实现按位与运算,其语法格式为:
dst = cv2.bitwise_and( src1, src2[, mask]] )
dst 表示与输入值具有同样大小的 array 输出值。
src1 表示第一个 array 或 scalar 类型的输入值。
src2 表示第二个 array 或 scalar 类型的输入值。
mask 表示可选操作掩码,8 位单通道 array。
与运算:


使用数组演示与掩模图像的按位与运算
python
import cv2
import numpy as np
a=np.random.randint(0,255,(5,5),dtype=np.uint8)
b=np.zeros((5,5),dtype=np.uint8)
b[0:3,0:3]=255
b[4,4]=255
c=cv2.bitwise_and(a,b)
print("a=\n",a)
print("b=\n",b)
print("c=\n",c)
构造一个掩模图像,使用按位与运算保留图像中被掩模指定的部分。
python
import cv2
import numpy as np
a=cv2.imread("lan.jpg",0)
b=np.zeros(a.shape,dtype=np.uint8)
b[100:400,200:400]=255
b[100:500,100:200]=255
c=cv2.bitwise_and(a,b)
cv2.imshow("a",a)
cv2.imshow("b",b)
cv2.imshow("c",c)
cv2.waitKey()
cv2.destroyAllWindows()

3.3.2 按位或运算<或运算>
OpenCV 中,可以使用 cv2.bitwise_or()函数来实现按位或运算,其语法格式为:
dst = cv2.bitwise_or( src1, src2[, mask]] )
式中:
dst 表示与输入值具有同样大小的 array 输出值。
src1 表示第一个 array 或 scalar 类型的输入值。
src2 表示第二个 array 或 scalar 类型的输入值。
mask 表示可选操作掩码,8 位单通道 array 值。
或运算:


3.3.3 按位非运算<非运算(取反)>
在 OpenCV 中,可以使用函数 cv2.bitwise_not()来实现按位取反操作,其语法格式为:
dst = cv2.bitwise_not( src[, mask]] )
式中:
dst 表示与输入值具有同样大小的 array 输出值。
src 表示 array 类型的输入值。
mask 表示可选操作掩码,8 位单通道 array 值。
非运算:


3.3.4 按位异或运算<异或运算>
在 OpenCV 中,可以使用函数 cv2.bitwise_xor()来实现按位异或运算,其语法格式为:
dst = cv2.bitwise_xor( src1, src2[, mask]] )
式中:
dst 表示与输入值具有同样大小的 array 输出值。
src1 表示第一个 array 或 scalar 类型的输入值。
src2 表示第二个 array 或 scalar 类型的输入值。
mask 表示可选操作掩码,8 位单通道 array 值。


3.4掩膜
OpenCV 中的很多函数都会指定一个掩模,也被称为掩码,例如:
计算结果=cv2.add(参数 1 , 参数 2 , 掩模)
掩码的使用
python
import cv2
import numpy as np
img1=np.ones((4,4),dtype=np.uint8)*3
img2=np.ones((4,4),dtype=np.uint8)*5
mask=np.zeros((4,4),dtype=np.uint8) #zeros
mask[2:4,2:4]=1
img3=np.ones((4,4),dtype=np.uint8)*66
print("img1=\n",img1)
print("img2=\n",img2)
print("mask=\n",mask)
print("初始值 img3=\n",img3)
img3=cv2.add(img1,img2,mask=mask)
print("求和后 img3=\n",img3)
3.5 图像与数值的运算
图像与数值的运算
python
import numpy as np
import cv2
img1=np.ones((4,4),dtype=np.uint8)*3
img2=np.ones((4,4),dtype=np.uint8)*5
print("img1=\n",img1)
print("img2=\n",img2)
img3=cv2.add(img1,img2)
print("cv2.add(img1,img2)=\n",img3)
img4=cv2.add(img1,6)
print("cv2.add(img1,6)\n",img4)
img5=cv2.add(6,img2)
print("cv2.add(6,img2)=\n",img5)
3.6 位平面分解
位平面分解:
将灰度图像中处于同一比特位上的二进制像素值进行组合,得到一幅二进制值图像,该图像被称为灰度图像的一个位平面。
在 8 位灰度图中,每一个像素使用 8 位二进制值来表示,其值的范围在[0,255]之间。可以将其中的值表示为:
value=a7⋅27+a6⋅26+a5⋅25+a4⋅24+a3⋅23+a2⋅22+a1⋅21+a0⋅20value = a_7 \cdot 2^{7} + a_6 \cdot 2^{6} + a_5 \cdot 2^{5} + a_4 \cdot 2^{4} + a_3 \cdot 2^{3} + a_2 \cdot 2^{2} + a_1 \cdot 2^{1} + a_0 \cdot 2^{0}value=a7⋅27+a6⋅26+a5⋅25+a4⋅24+a3⋅23+a2⋅22+a1⋅21+a0⋅20
aia_iai的可能值为 0 或 1。可以看出,各个aia_iai的权重是不一样的,a7a_7a7的权重最高,a0a_0a0的权重最低。这代表a7a_7a7的值对图像的影响最大,而a0a_0a0的值对图像的影响最小。

针对 RGB 图像,如果将 R 通道、G 通道、B 通道中的每一个通道对应的位平面进行合并,即可组成新的 RGB 彩色图像。例如,针对一幅 RGB 图像,将其 R 通道的第 3 个位平面、G 通道的第 3 个位平面、B 通道的第 3 个位平面进行合并,则可以构成一幅新的 RGB 彩色图像,我们称之为原始图像的第 3 个位平面。
位平面分解的具体步骤:
- 1.图像预处理
读取原始图像 O,获取原始图像 O 的宽度 M 和高度 N。 - 2.构造提取矩阵
建立一个值均为 2n2^{n}2n的 Mat 作为提取矩阵(数组),用来与原始图像进行按位与运算,以提取第 nnn个位平面。

- 3.提取位平面
将灰度图像与提取矩阵进行按位与运算,得到各个位平面。 - 4.阈值处理
通过计算得到的位平面是一个二值图像,也就是说,每次提取位平面后,要想让二值位平面能够以黑白颜色显示出来,就要将得到的二值位平面进行阈值处理,将其中大于零的值处理为 255。 - 5.显示图像
灰度图像的各个位平面。
python
import cv2
import numpy as np
lena=cv2.imread("lan.jpg",0)
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
r=np.zeros((r,c,8),dtype=np.uint8)
for i in range(8):
r[:,:,i]=cv2.bitwise_and(lena, x[:,:,i])
mask=r[:,:,i]>0
r[mask]=255
cv2.imshow(str(i),r[:,:,i])
cv2.waitKey()
cv2.destroyAllWindows()、

- 图(a)是原始 lena 图像。
- 图(b)是第 0 个位平面,第 0 个位平面位于 8 位二进制值的最低位,其权重最低,对像素值的影响最小,与 lena 图像的相关度也最低,所以显示出来的是一幅杂乱无章的图像。
- 图©是第 1 个位平面。
- 图(d)是第 2 个位平面。
- 图(e)是第 3 个位平面。
- 图(f)是第 4 个位平面。
- 图(g)是第 5 个位平面。
- 图(h)是第 6 个位平面。
- 图(i)是第 7 个位平面,第 7 个位平面位于 8 位二进制值的最高位,其对像素值的影响最大。第 7 位二进制值在 8 位二进制数中权重最高,与 lena 图像的相关度最高。所以,第 7 个位平面是与原始图像最接近的二值图像。
3.7 图像加密和解密
通过对原始图像与密钥图像进行按位异或,可以实现加密;将加密后的图像与密钥图像再次进行按位异或,可以实现解密。
根据按位异或运算的规则,假设:
xor(a,b)=c
则可以得到:
xor(c,b)=a
xor(c,a)=b

- a:明文,原始数据。
- b:密钥。
- c:密文,通过
xor(a,b)
实现。 - 加密过程 :将明文 a 与密钥 b 进行按位异或
xor(a,b)
,完成加密,得到密文 c。 - 解密过程 :将密文 c 与密钥 b 进行按位异或
xor(c,b)
,完成解密,得到明文 a。
python
import cv2
import numpy as np
lena = cv2.imread("lan.jpg",0)
r,c=lena.shape
key = np.random.randint(0,256,size=[r,c],dtype=np.uint8)
encryption = cv2.bitwise_xor(lena,key)
decryption = cv2.bitwise_xor(key,encryption)
cv2.imshow("lena",lena)
cv2.imshow("key",key)
cv2.imshow("encryption",encryption)
cv2.imshow("decryption",decryption)
cv2.waitKey()
cv2.destroyAllWindows()

3.8数字水印
最低有效位(Least Significant Bit,LSB) 指的是一个二进制数中的第 0 位(即最低位)。最低有效位信息隐藏 指的是,将一个需要隐藏的二值图像信息嵌入载体图像的最低有效位,即将载体图像的最低有效位层替换为当前需要隐藏的二值图像,从而实现将二值图像隐藏的目的 。由于二值图像处于载体图像的最低有效位上,所以对于载体图像的影响非常不明显,其具有较高的隐蔽性。在必要时直接将载体图像的最低有效位层提取出来,即可得到嵌入在该位上的二值图像,达到提取秘密信息的目的。这种信息隐藏也被称为数字水印。
数字水印的处理过程分为下面两步:
- 嵌入过程 :将载体图像的第 0 个位平面替换为数字水印信息(一幅二值图像)。
- 原始载体图像预处理
将载体图像处理为二进制形式,并标记出最低有效位。 - 水印图像处理
在嵌入水印前,需要将水印信息处理为二值图像。 - 嵌入水印
将载体图像的最低有效位替换为二进制水印图像,完成水印的嵌入。
- 原始载体图像预处理
- 提取过程 :
将载体图像的最低有效位所构成的第 0 个位平面提取出来,得到数字水印信息。将水印信息从包含水印信息的载体图像内提取出来的过程。提取水印时,先将含水印载体图像的像素值转换为二进制形式,然后从其最低有效位提取出水印信息即可。因此,可以通过提取含水印载体图像的"最低有效位"位平面的方式来得到水印信息。
模拟数字水印的嵌入和提取过程。
python
import cv2
import numpy as np
#读取原始载体图像
lena=cv2.imread("lena.bmp",0)
#读取水印图像
watermark=cv2.imread("watermark.bmp",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()
