图像加法运算
- 加号运算符“+”
- cv2.add()函数
-
通常情况下,在灰度图像中,像素用8个比特位(一个字节)来表示,像素值的范围是[0, 255]。两个像素值在进行加法运算时,求得的和很可能超过255.上述两种不同的加法运算方式,对超过255的数值的处理方式是不一样的。
加号运算符
- 式中,mod()是取模运算,表示计算“a+b的和除以256取余数”
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)
img1
array([[ 84, 0, 138],
[ 86, 82, 191],
[ 42, 14, 182]], dtype=uint8)
img2
array([[202, 39, 180],
[ 22, 222, 180],
[196, 207, 191]], dtype=uint8)
img1+img2
array([[ 30, 39, 62],
[108, 48, 115],
[238, 221, 117]], dtype=uint8)
cv2.add()函数
- 当像素值的和大于255时统一赋值为255
import cv2
cv2.add(img1, img2)
array([[255, 39, 255],
[108, 255, 255],
[238, 221, 255]], dtype=uint8)
案例:分别使用加号运算符和函数cv2.add()计算两幅灰度图像的像素值之和。
import cv2
import numpy as np
a = cv2.imread('data/dog.jpg', flags=cv2.IMREAD_GRAYSCALE)
b = a
c = a+b
d = cv2.add(a, b)
cv2.imshow('dog', np.hstack([a, c, d]))
cv2.waitKey()
cv2.destroyAllWindows()
# 使用plt进行批量图像绘制
def get_show(*args):
import matplotlib.pyplot as plt
img_list = []
# 彩色图像
if len(args[0].shape) == 3:
for i in args:
img_list.append(i[:,:,::-1])
plt.figure(figsize=[20,20])
plt.xticks([])
plt.yticks([])
plt.imshow(np.hstack(img_list))
plt.show()
# 黑白图像
else:
img_list = args
plt.figure(figsize=[20,20])
plt.xticks([])
plt.yticks([])
plt.imshow(np.hstack(img_list), cmap='gray')
plt.show()
图像加权和
- 所谓图像加权和,就是在计算两幅图像的像素值之和时,将每幅图像的权重考虑进来
- 用公式表示为dst=saturate(src1×α+src2×β+γ)
- 式中:
- saturate()表示取饱和值(最大值)。
- 图像进行加权和计算时,要求src1和src2必须大小、类型相同,但是对具体是什么类型和通道没有特殊限制。它们可以是任意数据类型,也可以有任意数量的通道(灰度图像或彩色图像),只要二者相同即可
cv2.addWeighted(src1, alpha, src2, beta, gamma)函数
案例1:使用数组演示函数cv2.addWeighted()的使用
import cv2
import numpy as np
img1 = np.ones((3,4), dtype=np.uint8)*100
img2 = np.ones((3,4), dtype=np.uint8)*10
gamma = 3
img3 = cv2.addWeighted(img1, 0.6, img2, 5, gamma)
img3
array([[113, 113, 113, 113],
[113, 113, 113, 113],
[113, 113, 113, 113]], dtype=uint8)
案例2:使用函数cv2.addWeighted()对两幅图像进行加权混合,观察处理结果
import cv2
import numpy as np
a = cv2.imread('data/dog.jpg')
b = np.zeros(a.shape, dtype=np.uint8)
b[:,:,0]=255
c = cv2.addWeighted(a,0.5, b, 0.9, 0.5)
get_show(a,b,c)
按位逻辑运算
按位与运算
- dst=cv2.bitwise_and(src1, src2[,mask])
- 式中:
- dst:表示与输入值具有同样大小的array输出值
- src1:表示第一个array或scalar类型的输入值
- src2:表示第二个array或scalar类型的输入值
- mask:表示可选操作掩码,8位单通道array
- 任何数值与0进行按位与操作,都会得到0;任何数值与255进行按位与操作都会得到其本身
案例1:使用数组演示与掩膜图像的按位与运算
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)
a=
[[ 84 160 87 219 74]
[ 39 218 132 80 0]
[ 4 211 49 22 27]
[ 0 122 195 76 13]
[131 143 210 71 244]]
b=
[[255 255 255 0 0]
[255 255 255 0 0]
[255 255 255 0 0]
[ 0 0 0 0 0]
[ 0 0 0 0 255]]
c=
[[ 84 160 87 0 0]
[ 39 218 132 0 0]
[ 4 211 49 0 0]
[ 0 0 0 0 0]
[ 0 0 0 0 244]]
案例2:构造一个掩模图像,使用按位与运算保留图像中被掩膜指定的部分
import cv2
import numpy as np
img = cv2.imread('data/dog.jpg')
a = np.zeros(img.shape, dtype=np.uint8)
a[200:300, 250:350,:]=255
b = cv2.bitwise_and(img,a)
get_show(img,a,b)
按位或运算
- 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
掩膜
- opencv中很多函数都会指定一个掩膜,也被称为掩码,例如:
- 计算结果=cv2.add(参数1, 参数2, 掩膜)
- 当使用掩膜参数时,操作只会在掩膜值为非空的像素点上执行,并将其他像素点的值置为0.
import cv2
import numpy as np
img = cv2.imread('data/dog.jpg')
a = np.zeros(img.shape[:2], dtype=np.uint8)
print('掩膜的尺寸为:', a.shape)
a[200:300, 250:350]=255
b = cv2.bitwise_and(img,img, mask=a) # 设定掩膜参数
get_show(img,b)
掩膜的尺寸为: (300, 534)
图像与数值的运算
- 在上述加法运算和按位运算中,参与运算的两个算子(参数)既可以是两幅图像,也可以是一幅图像与一个数值
- 例如,如果想增加图像的整体亮度,可以将每一个像素值都加上一个特定值。在具体实现时,可以给图像加上一个统一像素值的图像,也可以给图像加上一个固定值。但是针对彩色图像只能对第一个维度进行处理
import numpy as np
import cv2
a = np.random.randint(0, 255, (5,5), dtype=np.uint8)
print('a=\n', a)
b = cv2.add(a, 5) # 每个个像素点加5,但保持最大值为255
print('b=\n', b)
a=
[[223 18 28 73 52]
[184 232 223 182 254]
[166 239 98 0 211]
[ 96 158 239 31 230]
[ 26 228 100 247 67]]
b=
[[228 23 33 78 57]
[189 237 228 187 255]
[171 244 103 5 216]
[101 163 244 36 235]
[ 31 233 105 252 72]]
import numpy as np
import cv2
img = cv2.imread('data/dog.jpg', cv2.IMREAD_GRAYSCALE)
light_img = cv2.add(img, 100)
get_show(img, light_img) # 提高图像的亮度
位平面分解
- 将灰度图像中位于同一比特位上的二进制像素值进行组合,得到一幅二进制值图像,该图像被称为灰度图像的一个位平面,这个过程被称为位平面分解
- 建立一个值均为2^n的Mat作为提取矩阵(数组),用来与原始图像进行按位与运算,可提取对应的第n个位平面。
- 位平面的位数越高说明其在原始图像中的权重越高,越接近原始图像。
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('data/dog.jpg', 0)
h,w = img.shape
x = np.zeros((h,w,8), dtype=np.uint8) # 初始化用于位平面分解的矩阵
for i in range(8):
x[:,:,i] = np.power(2, i) # 对矩阵进行相应的赋值
img_bit_split = np.zeros((h,w,8), dtype=np.uint8)
for i in range(8):
img_bit_split[:,:,i] = cv2.bitwise_and(img, x[:,:,i])
plt.rcParams['font.sans-serif']='SimHei'
plt.figure(figsize=(10,10))
plt.subplot(3,3,1)
plt.title('原始图像')
plt.xticks([])
plt.yticks([])
plt.imshow(img, cmap='gray')
for i in range(1,9):
plt.subplot(3,3,i+1)
plt.title('第%d位位平面图'%i)
plt.xticks([])
plt.yticks([])
plt.imshow(img_bit_split[:,:,i-1], cmap='gray')
图像的加密和解密
- 通过按位异或运算可以实现图像的加密和解密
- 通过对原始图像与密钥图像进行按位异或,可以实现加密;将加密后的图像与密钥图像再次进行按位异或,可以实现解密
- 根据上述按位异或运算的规则,假设:
- xor(a,b)=c
- 则可以得到:
- xor(c,b)=a
- xor(c,a)=b
- 根据上述规则:
- a----->明文
- b----->密钥
- c----->密文
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('data/dog.jpg', 0)
w,h = img.shape
key_img = cv2.resize(cv2.imread('data/car.jpg', 0), (h,w))
# 图像加密
img2entr = cv2.bitwise_xor(img, key_img)
# 图像解密
entr2img = cv2.bitwise_xor(img2entr, key_img)
get_show(img, key_img, img2entr, entr2img)
数字水印
- 最低有效位信息隐藏指的是,将一个需要隐藏的二值图像信息嵌入载体图像的最低有效位,即将载体图像的最低有效位层替换位当前需要隐藏的二值图像,从而实现将二值图像隐藏的目的。由于二值图像处于载体图像的最低有效位上,所以对于载体图像的影响非常不明显,其具有较高的隐蔽性。
- 在必要时直接将载体图像的最低有效位层提取出来,即可得到嵌入在该位上的二值图像,达到提取秘密信息的目的。
- 这种信息隐藏也被称为数字水印,通过该方式可以实现信息隐藏、版权认证、身份认证等功能。例如,如果嵌入载体图像内的信息是版权信息,就能够实现版权认证;如果嵌入载体图像内的是身份信息,就可以实现数字签名,等等。所以,被嵌入载体图像内的信息也被称为数字水印信息。
- 数字水印信息可以是文本、视频、音频等多种形式,这里我们仅讨论数字水印信息是二值图像的情况。
案例:将猫的最高有效位嵌入到狗的最低有效位
import cv2
dog = cv2.imread('data/dog.jpg', 0) # 读取原始载体图像
cat = cv2.imread('data/cat.jpg', 0) # 读取水印图像
w,h = dog.shape
cat = cv2.resize(cat, (h,w)) # 进行形状同一处理
# 提取猫的最高有效位
bit_max = np.zeros((w,h), dtype=np.uint8)
bit_max[:,:] = 2**7 # 构建提取最高有效位的矩阵
cat_max_bit = cv2.bitwise_and(cat, bit_max) # 提取出猫的最高有效位
ind = cat_max_bit==2**7
cat_max_bit[ind] = 1 # 将最高有效位处理为最大值为1
bit_min = np.ones((w,h), dtype=np.uint8) # 构建提取最低有效位的矩阵
dog_min_bit = cv2.bitwise_and(dog, bit_min) # 提取狗的最低有效位
dog_new = dog-dog_min_bit+cat_max_bit # 进行相关操作
### 第二种方法
bit_tmp = np.zeros((w,h), dtype=np.uint8)
bit_tmp[:,:] = 254 # 构建提取后七位位平面的矩阵
dog_temp = cv2.bitwise_and(dog, bit_tmp) # 采用按位与运算得到后七位位平面,第一个位平面全位0
dog_tem = cv2.bitwise_or(dog_temp, cat_max_bit) # 采用按位或运算(因为上方操作已经将第一位位平面全置为了0)
get_show(dog,cat,cat_max_bit*255,dog_new)
# 提取过程
dog_new_min_bit = cv2.bitwise_and(dog_new,bit_min) # 提取水印(新图形的第一位位平面)
dog_new_other_bit = cv2.bitwise_and(dog_new,bit_tmp) # 提取后七位位平面
get_show(dog_new_min_bit*255,dog_new_other_bit) # 展示水印
脸部打码及解码
import cv2
import numpy
# 脸部打码
dog = cv2.imread('data/dog.jpg', 0)
w, h = dog.shape
face_mask = np.zeros((w,h), dtype=np.uint8)
face_mask[200:300, 250:350]=1
entr_key = np.random.randint(0, 255, size=(w,h), dtype=np.uint8) # 生成加密密钥
entr_dog = cv2.bitwise_xor(dog, entr_key) # 加密
entr_face = cv2.bitwise_and(entr_dog,entr_dog, mask=face_mask) # 根据mask提取加密图像的脸部信息
entr_no_face = cv2.bitwise_and(dog,dog,mask=1-face_mask) # 根据mask提取原始图像的非脸部信息
entr_face_dog = entr_face+entr_no_face # 将两个信息进行加和
get_show(dog,entr_dog,entr_no_face,entr_face_dog)
# 脸部解码
get_entr_face = cv2.bitwise_and(entr_face_dog, entr_face_dog, mask=face_mask)
get_face = cv2.bitwise_xor(get_entr_face, entr_key, mask=face_mask)
get_no_face = cv2.bitwise_and(entr_face_dog, entr_face_dog, mask=1-face_mask)
return_dog = get_no_face + get_face
get_show(entr_face_dog,get_entr_face,get_face,get_no_face,return_dog)