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

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

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

正文

修改了尺寸变换导致的图像失真问题,同时简化了部分代码。

效果确实很好😉

1 通道调序的简化

将之前的两句代码简化为了一句代码:

python 复制代码
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 多级小波变换

python 复制代码
# 水印图像一级小波变换
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 等的形状相同,否则无法相加:

python 复制代码
cA = cA + ca * a1
cH3 = cH3 + ch1 * a2
cV3 = cV3 + cv1 * a3
cD3 = cD3 + cd1 * a4

而每做一次小波变换,图像的尺寸都会减小到原本的 1 / 2 1/2 1/2:

python 复制代码
[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 函数比较原始图像的长度和宽度,并返回其中的较大值作为原始图像的新尺寸:

python 复制代码
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 倍:

python 复制代码
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 函数应该还可以简化,名字也是取得莫名其妙,可以自己改一下😇

python 复制代码
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()
相关推荐
18你磊哥25 分钟前
chromedriver.exe的使用和python基本处理
开发语言·python
闲人编程1 小时前
Python的抽象基类(ABC):定义接口契约的艺术
开发语言·python·接口·抽象类·基类·abc·codecapsule
vx_dmxq2111 小时前
【微信小程序学习交流平台】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·python·mysql·微信小程序·小程序·idea
无垠的广袤1 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
艾莉丝努力练剑2 小时前
【Python基础:语法第一课】Python 基础语法详解:变量、类型、动态特性与运算符实战,构建完整的编程基础认知体系
大数据·人工智能·爬虫·python·pycharm·编辑器
gCode Teacher 格码致知2 小时前
Python基础教学:如何拼接字符串?-由Deepseek产生
python
还债大湿兄2 小时前
阿里通义千问调用图像大模型生成轮动漫风格 python调用
开发语言·前端·python
blank@l2 小时前
python测开小工具--日志查询分析工具
python·python接口自动化测试基础·python测试开发·日志查询分析·日志分析统计查询·软件测试工具·argparse模块
hu_nil3 小时前
LLMOps-第十三周
python·vllm
空影星3 小时前
轻量日记神器RedNotebook,高效记录每一天
python·数据挖掘·数据分析·音视频