🍍原文: 基于 dwt (离散小波变换) 实现彩色图像水印嵌入部分_2.0
🍍写在前面: 本文在原文的基础上进行了代码补全。
正文
修改了尺寸变换导致的图像失真问题,同时简化了部分代码。
效果确实很好😉
1 通道调序的简化
将之前的两句代码简化为了一句代码:
Img_path = 'white_bear.jpg'
Img = cv2.imread(Img_path)
Img = Img[:, :, [2, 1, 0]] # 调整通道顺序
这里的 I m g \mathsf{Img} Img 是一个三维数组,其中每个维度分别代表:
- 第一个维度是图像的高度(或行数)
- 第二个维度是图像的宽度(或列数)
- 第三个维度是图像的通道数
由于 c v 2 \mathsf{cv2} cv2 读取图像通道的顺序是 B , G , R \mathsf{B,G,R} B,G,R,因此上述代码将其更改为 R , G , B \mathsf{R,G,B} R,G,B 顺序。
2 多级小波变换
# 水印图像一级小波变换
coeffs1 = pywt.wavedec2(waterImg_new, 'db2', level=1)
[ca, (ch1, cv1, cd1)] = coeffs1
# 原始图像B通道三级小波变换
coeffs2 = pywt.wavedec2(b, 'db2', level=3)
[cA, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)] = coeffs2
'db2'
:是选定的小波类型。这里是 D a u b e c h i e s \mathsf{Daubechies} Daubechies 长度为 2 的整数系小波。level=3
:是小波分解的级别。级别决定了分解的深度,也就是小波变换的层数。
3 图像尺寸裁剪
由嵌入公式可以看出, c A \mathsf{cA} cA 等的形状要和 c a \mathsf{ca} ca 等的形状相同,否则无法相加:
cA = cA + ca * a1
cH3 = cH3 + ch1 * a2
cV3 = cV3 + cv1 * a3
cD3 = cD3 + cd1 * a4
而每做一次小波变换,图像的尺寸都会减小到原本的 1 / 2 1/2 1/2:
[cA, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)] = coeffs2
其中 c H 3 \mathsf{cH3} cH3 是 c H 2 \mathsf{cH2} cH2 的 1 / 2 1/2 1/2, c H 2 \mathsf{cH2} cH2 是 c H 1 \mathsf{cH1} cH1 的 1 / 2 1/2 1/2。因此,定义了以下三个函数为图像计算尺寸。
m a x R C \mathsf{maxRC} maxRC 函数比较原始图像的长度和宽度,并返回其中的较大值作为原始图像的新尺寸:
def maxRC(Img_path):
Img = cv2.imread(Img_path, 0)
R = Img.shape[0]
C = Img.shape[1]
RC_new = max(R, C)
return RC_new
裁剪后的原始图像比例为 1 : 1 1:1 1:1,且边长为 m a x ( R , C ) \mathsf{max(R, C)} max(R,C)。
i n c h e s 3 \mathsf{inches3} inches3 函数计算水印图像的新尺寸,它需要是原始图像的 ( 1 / 2 ) t i m e s \mathsf{(1/2)^{times}} (1/2)times 倍:
def inches3(num, times):
for i in range(times):
num = round((num/2 + 1))
return num
def Icm(RC_new):
rc_new = inches3(RC_new, 2)
return rc_new
其中, t i m e s = 3 − 1 = 2 \mathsf{times}=3-1=2 times=3−1=2 即原始图像的小波变换级别减去水印图像的小波变换级别。
个人理解:原始图像的小波变换级别是 3 3 3,水印图像的小波变换级别是 1 1 1。要使 c A \mathsf{cA} cA 等和 c a \mathsf{ca} ca 等能够相加,那么需要它们的尺寸相同。参考下图,根据 c A \mathsf{cA} cA 的尺寸,以及水印图像的小波变换级别是 1 1 1 这一条件,去倒推水印图像的新尺寸应该是多少。
4 完整代码
i n c h e s 3 \mathsf{inches3} inches3 函数和 I c m \mathsf{Icm} Icm 函数应该还可以简化,名字也是取得莫名其妙,可以自己改一下😇
import cv2
import pywt
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
def arnold(img, s):
r, c, d = img.shape
img = img[:, :, 0]
p = np.zeros((r, c), np.uint8)
a = 1
b = 1
for _s in range(s):
for i in range(r):
for j in range(c):
x = (i + b * j) % r
y = (a * i + (a * b + 1) * j) % c
p[x, y] = img[i, j]
img = np.copy(p)
return p
def inches3(num, times):
for i in range(times):
num = round((num/2 + 1))
return num
def Icm(RC_new):
rc_new = inches3(RC_new, 2)
return rc_new
def maxRC(Img_path):
Img = cv2.imread(Img_path, 0)
R = Img.shape[0]
C = Img.shape[1]
RC_new = max(R, C)
return RC_new
# 读取图像
Img_path = 'white_bear.jpg'
waterImg_path = 'uestc_logo.jpg'
Img = cv2.imread(Img_path)
Img = Img[:, :, [2, 1, 0]]
water = cv2.imread(waterImg_path) # 只是为了后面展示用
water = water[:, :, [2, 1, 0]]
waterImg = cv2.imread(waterImg_path)
waterImg = waterImg[:, :, [2, 1, 0]]
waterImg = arnold(waterImg, 5)
# 修改原始图像的尺寸
RC_new = maxRC(Img_path)
Img_new = cv2.resize(Img, (RC_new, RC_new))
(r, g, b) = cv2.split(Img_new)
# 修改水印图像的尺寸
rc_new = Icm(RC_new)
waterImg_new = cv2.resize(waterImg, (rc_new, rc_new))
# 水印图像一级小波变换
coeffs1 = pywt.wavedec2(waterImg_new, 'db2', level=1)
[ca, (ch1, cv1, cd1)] = coeffs1
# 原始图像B通道三级小波变换
coeffs2 = pywt.wavedec2(b, 'db2', level=3)
[cA, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)] = coeffs2
# 自定义嵌入系数
a1 = 0.1
a2 = 0.2
a3 = 0.1
a4 = 0.1
cA = cA + ca * a1
cH3 = cH3 + ch1 * a2
cV3 = cV3 + cv1 * a3
cD3 = cD3 + cd1 * a4
# 对小波系数进行逆变换
newImg = pywt.waverec2([cA, (cH3, cV3, cD3), (cH2, cV2, cD2), (cH1, cV1, cD1)], 'db2')
merged = np.ones(Img_new.shape, dtype=np.uint8)
merged[:, :, 0] = r
merged[:, :, 1] = g
merged[:, :, 2] = newImg
Img_water = Image.fromarray(merged)
Img_water = Img_water.resize((808, 808), Image.LANCZOS) # 重新调整大小,抗锯齿
# 画图
plt.subplot(2, 2, 1)
plt.title("Watermark", fontsize=12, loc="center")
plt.axis('off')
plt.imshow(water)
plt.subplot(2, 2, 2)
plt.title("Arnold Watermark", fontsize=12, loc="center")
plt.axis('off')
plt.imshow(waterImg)
plt.subplot(2, 2, 3)
plt.title("Original", fontsize=12, loc="center")
plt.axis('off')
plt.imshow(Img)
plt.subplot(2, 2, 4)
plt.title("Watermarked", fontsize=12, loc="center")
plt.axis('off')
plt.imshow(merged)
plt.savefig('test.jpg', dpi=400)
plt.show()