数字水印 | Python 基于离散小波变换 DWT 的图像水印嵌入(上)

🍍原文: 基于 dwt (离散小波变换) 实现彩色图像水印嵌入部分_1.0

🍍写在前面: 本文在原文的基础上进行了代码补全。



正文

本文的内容主要为:水印图像经过 A r n o l d \mathsf{Arnold} Arnold 置乱算法后,通过离散小波变换进行嵌入。

在这里插入图片描述

c A \mathsf{cA} cA 等为原始图像的小波系数, c a \mathsf{ca} ca 等为水印图像的小波系数,嵌入公式如下:

{ c A 3 ′ = c A 3 + a 1 × c a 1 c H 3 ′ = c H 3 + a 2 × c h 1 c V 3 ′ = c V 3 + a 3 × c v 1 c D 3 ′ = c D 3 + a 4 × c d 1 \left\{\begin{matrix} cA'_3 = cA_3 + a_1 \times ca_1\\ cH'_3 = cH_3 + a_2 \times ch_1 \\ cV'_3 = cV_3 + a_3 \times cv_1 \\ cD'_3 = cD_3 + a_4 \times cd_1 \end{matrix}\right. cA3=cA3+a1×ca1cH3=cH3+a2×ch1cV3=cV3+a3×cv1cD3=cD3+a4×cd1

为了验证方便,后文代码将上述公式中的三级小波 c A 3 \mathsf{cA_3} cA3 变换与一级小波 c a 1 \mathsf{ca_1} ca1 变换的嵌入,简化为了一级小波 c A 1 \mathsf{cA_1} cA1 变换与一级小波 c a 1 \mathsf{ca_1} ca1 变换的嵌入。当然,嵌入效果不会很好。

看得出来嵌入效果不是很好😇

为了使结果仍然为彩色图像,本文在小波变换前将原始图像的 R , G , B \mathsf{R,G,B} R,G,B 通道分离,仅在 B ( b l u e ) \mathsf{B(blue)} B(blue) 通道中嵌入水印。嵌入完成后,再将 R , G , B \mathsf{R,G,B} R,G,B 三个通道合并。



1 代码说明

1.1 Arnold

A r n o l d \mathsf{Arnold} Arnold 置乱算法如下,即粘即用:

def arnold(img, s):  # 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

以上代码就是对 A r n o l d \mathsf{Arnold} Arnold 公式的实现。如果不知道 A r n o l d \mathsf{Arnold} Arnold 公式是什么,那么自然是看不懂的😇

参考自博客:Python 基于位平面的信息隐藏算法 阿诺德置乱算法



1.2 读取图像

分别读取 I m g \mathsf{Img} Img 原始图像和 w a t e r I m g \mathsf{waterImg} waterImg 水印图像:

Img = cv2.imread('white_bear.jpg')
# 原始图像调序
b, g, r = cv2.split(Img)
Img = cv2.merge([r, g, b])

waterImg = cv2.imread('uestc_logo.jpg')
# 水印图像调序
b, g, r = cv2.split(waterImg)
waterImg = cv2.merge([r, g, b])

cv2 读取图片时的通道顺序为 B、G、R,而 PIL 显示图片时的通道顺序为 R、G、B,因此显示出来的图片颜色会改变,需要对图像通道进行调序。

参考自博客:CV2 读取图片,图片颜色发生改变解决方案



1.3 小波变换

首先修剪原始图像为水印图像的大小,然后分离出原始图像的 B \mathsf{B} B 通道:

img = cv2.resize(Img, (r, c))  # 修剪原始图像
(b, g, r) = cv2.split(img)  # 分离通道

接着,直接调用库函数 pywt.dwt2 实现小波变换:

# 水印图像一级小波变换
coeffs1 = pywt.dwt2(waterImg, 'haar')
ca1, (ch1, cv1, cd1) = coeffs1

# 原始图像B通道一级小波变换
coeffs2 = pywt.dwt2(b, 'haar')
ca2, (ch2, cv2, cd2) = coeffs2

如果没有 p y w t \mathsf{pywt} pywt 库则需要进行安装:

pip install PyWavelets


1.4 嵌入水印

就是对文首的嵌入公式的实现:

# 自定义嵌入系数,可用随机数处理
a1 = 0.1
a2 = 0.2
a3 = 0.1
a4 = 0.1

ca2 = ca2 + ca1 * a1
ch2 = ch2 + ch1 * a2
cv2 = cv2 + cv1 * a3
cd2 = cd2 + cd1 * a4


1.5 小波逆变换

第一句代码才是小波逆变换:

newImg = pywt.idwt2((ca2, (ch2, cv2, cd2)), "haar")
merged = np.ones(img.shape, dtype=np.uint8)
merged[:, :, 0] = r
merged[:, :, 1] = g
merged[:, :, 2] = newImg

后面的代码是在对通道进行合并。可以看出,嵌入了水印的 n e w I m g \mathsf{newImg} newImg 被我们放在了 B \mathsf{B} B 通道,而 R , G \mathsf{R,G} R,G 通道保持原样。



2 完整代码

import cv2
import pywt
import numpy as np
import matplotlib.pyplot as plt


def arnold(img, s):  # 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


Img = cv2.imread('white_bear.jpg')  # 原始图像
b, g, r = cv2.split(Img)
Img = cv2.merge([r, g, b])

water = cv2.imread('uestc_logo.jpg')  # 只是为了后面的展示
b, g, r = cv2.split(water)
water = cv2.merge([r, g, b])

waterImg = cv2.imread('uestc_logo.jpg')  # 水印图像
b, g, r = cv2.split(waterImg)
waterImg = cv2.merge([r, g, b])
waterImg = arnold(waterImg, 5)  # Arnold置乱

# 原始图像尺寸数据
R = Img.shape[0]
C = Img.shape[1]

# 水印图像尺寸数据
r = waterImg.shape[0]
c = waterImg.shape[1]

img = cv2.resize(Img, (r, c))  # 修剪原始图像
(b, g, r) = cv2.split(img)  # 分离通道

# 水印图像一级小波变换
coeffs1 = pywt.dwt2(waterImg, 'haar')
ca1, (ch1, cv1, cd1) = coeffs1

# 原始图像B通道一级小波变换
coeffs2 = pywt.dwt2(b, 'haar')
ca2, (ch2, cv2, cd2) = coeffs2

# 自定义嵌入系数,可用随机数处理
a1 = 0.1
a2 = 0.2
a3 = 0.1
a4 = 0.1

ca2 = ca2 + ca1 * a1
ch2 = ch2 + ch1 * a2
cv2 = cv2 + cv1 * a3
cd2 = cd2 + cd1 * a4

# 对小波系数进行逆变换
newImg = pywt.idwt2((ca2, (ch2, cv2, cd2)), "haar")
merged = np.ones(img.shape, dtype=np.uint8)
merged[:, :, 0] = r
merged[:, :, 1] = g
merged[:, :, 2] = newImg

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()


  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值