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

🍍原文: 基于 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=31=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()


  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值