数字水印 | 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()
相关推荐
cdut_suye几秒前
C++11新特性探索:Lambda表达式与函数包装器的实用指南
开发语言·数据库·c++·人工智能·python·机器学习·华为
weixin_543662862 分钟前
BERT的中文问答系统36-1
人工智能·python·bert
weixin_431470864 分钟前
人名分类器(nlp)
人工智能·pytorch·python·深度学习·自然语言处理
努力更新中15 分钟前
Python浪漫之画一个音符♪
开发语言·python
泰山小张只吃荷园21 分钟前
期末Python复习-输入输出
java·前端·spring boot·python·spring cloud·docker·容器
凤枭香33 分钟前
Python Selenium介绍(二)
开发语言·爬虫·python·selenium
工业互联网专业1 小时前
Python毕业设计选题:基于django+vue的期货交易模拟系统的设计与实现
vue.js·python·django·毕业设计·源码·课程设计
java_python源码1 小时前
[含文档+PPT+源码等]精品大数据项目-Django基于大数据实现的游戏用户行为分析与个性化推荐系统
python·游戏
Srlua1 小时前
周期性移动模式地铁乘客流量预测
python·数据分析
易辰君1 小时前
【Python爬虫实战】深入解析 Scrapy:从阻塞与非阻塞到高效爬取的实战指南
开发语言·python