数字水印 | 附彩色图像论文:盲式水印嵌入的方法



本文附属于数字水印 | 彩色图像论文:A blind and robust color image watermarking scheme based on DCT and DWT domains(三)



前言

最初,我看不懂论文 A 中提到的 量化值水印嵌入规则,只觉得莫名其妙。后来看了 盲水印 相关的论文、博客和代码,我突然恍然大悟!

本博客涉及两篇论文:

  • 论文 A:设计的是盲水印方案,即提取水印时不需要使用原始图像;
  • 论文 B:设计的是非盲水印方案,即提取水印时需要使用原始图像;

接下来,我将以人话和 Python 代码来介绍论文 A 的水印嵌入规则。



1 回顾:论文 A 的嵌入规则

这一小节只是回顾一下论文 A 所提出的方案是什么,暂不进行说明😈

步骤五:基于给定的量化值 Q Q Q,通过以下公式构造一个包含 L H 1 LH1 LH1 子带调整系数的新矩阵 S S S

S ( i , j ) = ⌊ L H 1 ( i , j ) Q ⌋ S(i,j)=\left \lfloor \frac{LH1(i,j)}{Q} \right \rfloor S(i,j)=QLH1(i,j)



步骤六:将加密后的水印图像 E W EW EW 按照以下规则嵌入到 L H 1 LH1 LH1 子带中,从而得到 L H 1 ′ LH1' LH1

if mod(S(i, j), 2) = EW(k) then
	LH1(i, j) = S(i, j) * Q + Q / 2
End if

if mod(S(i, j), 2) != EW(k) then
	if LH1(i, j) - S(i, j) * Q ∈ [0, Q/2] then
		LH1(i, j) = (S(i, j) - 1) * Q + Q / 2
	Else
		LH1(i, j) = (S(i, j) + 1) * Q + Q / 2
	End if
End if

其中 i = 0 , 1 , 2 , . . . , M / 16 , j = 0 , 1 , 2 , . . . , N / 16 , k = 0 , 1 , 2 , . . . , R × C i=0,1,2,...,M/16,j=0,1,2,...,N/16,k=0,1,2,...,R\times C i=0,1,2,...,M/16,j=0,1,2,...,N/16,k=0,1,2,...,R×C



2 论文 B 的嵌入规则

这一小节主要介绍非盲的水印嵌入是如何实现的,以及我们为什么需要盲的水印嵌入。

论文 B 所提出的水印嵌入规则如下:

S m a r k = S y + α × S w S_{mark}=S_{y}+\alpha \times S_{w} Smark=Sy+α×Sw

温馨提示:由于这只是一个论文的片段,因此没有必要深究各个变量所代表的含义。只需要知道 S y S_y Sy 是基于原始图像得到的一个值, S w S_{w} Sw 是基于水印得到的一个值,而 S m a r k S_{mark} Smark 将被用于重构出含水印的图像即可。

根据上述水印嵌入规则,易得水印提取规则如下:

S w ′ = ( S m a r k − S y ) / α S'_{w}=(S_{mark}-S_{y})/\alpha Sw=(SmarkSy)/α

其中 S m a r k S_{mark} Smark 根据含水印图像得到, S y S_{y} Sy 根据原始图像得到。

这样的水印嵌入和提取规则是毫无问题的,可是在实际应用中,我们可能并不希望水印的提取过程还需要原始图像的参与。一方面是,提取水印的人可能并不是原始图像的拥有者;另一方面是,不希望因提取水印而需要暴露原始图像。因此,盲水印嵌入技术应运而生。



3 论文 A 的提取规则

这一小节主要介绍论文 A 的提取规则,下一小节再介绍论文 A 的嵌入规则。

首先,我们需要明确的是,论文 A 所针对的水印是二进制的水印,即水印信息是由 01 01 01 二进制数进行表示的。正是这一条件为我们嵌入水印提供了便利!

相较于论文 B,我们不再使用一个直白的数学运算(加减乘除)来嵌入或提取水印信息。而是采用一种类似于零知识证明的方式:在提取水印的过程中,我不需要告诉你原始图像的值是多少,而只需要告诉你 “加密” 后的原始图像的值对 2 2 2 取余的值是多少,而得到的这些余数就是水印信息。

Q1:为什么是对 2 2 2 取余?
A1:因为对 2 2 2 取余的余数只能是 0 0 0 1 1 1,所以可以完美地表示我们的二进制水印信息。如果水印信息采用的是三进制,那么就是对 3 3 3 取余了。
Q2:什么是 “加密” 后的原始图像的值?
A2:本质上就是通过数学运算对原始图像的值进行了一些变换,我个人把这样的变换过程称为 “加密” 过程。

论文 A 提取水印的方式是:

S ′ ( i , j ) = ⌊ L H 1 ′ ( i , j ) Q ⌋ E W ′ ( k ) = m o d ( S ′ ( i , j ) , 2 ) \begin{alignat}{2} S'(i,j) =& \left \lfloor \frac{LH1'(i,j)}{Q} \right \rfloor \\ EW'(k) =& \mathrm{mod}(S'(i,j),2) \end{alignat}{} S(i,j)=EW(k)=QLH1(i,j)mod(S(i,j),2)

说明: L H 1 ( i , j ) LH1(i,j) LH1(i,j) 是根据原始图像得到的值。由于 L H 1 ′ ( i , j ) LH1'(i,j) LH1(i,j) L H 1 ( i , j ) LH1(i,j) LH1(i,j) 经过一些变换得到的,因此我们可以认为 L H 1 ′ ( i , j ) LH1'(i,j) LH1(i,j) 是 “加密” 后的原始图像的值。

再来看上述水印提取的过程,我们只需要通过一个和水印嵌入过程相同的变换 —— L H 1 ′ ( i , j ) LH1'(i,j) LH1(i,j) 整除 Q Q Q,再对 2 2 2 取余即可获得水印信息。可以看出,我们全程都没有使用到原始图像的值,因此这是一个盲水印方案。那么这到底是如何实现的呢?如何保证整除后取余一定等于水印信息呢?请看下一小节。

说明:上标的那一撇是为了表明这不是原始的信息,而是经过了调整或者是被提取出的信息。不过可以证明的是,嵌入和提取水印的规则保证了 E W ( k ) = E W ′ ( k ) EW(k)=EW'(k) EW(k)=EW(k) 的成立。



4 论文 A 的嵌入规则

步骤五:基于给定的量化值 Q Q Q,通过以下公式构造一个包含 L H 1 LH1 LH1 子带调整系数的新矩阵 S S S

S ( i , j ) = ⌊ L H 1 ( i , j ) Q ⌋ S(i,j)=\left \lfloor \frac{LH1(i,j)}{Q} \right \rfloor S(i,j)=QLH1(i,j)

其实我到现在还是不知道量化值的作用,但是在 Python 中通过以下代码即可实现:

for i in range(length):
	S[i] = LH1[i] // Q

S ( i , j ) S(i,j) S(i,j) 等于 L H 1 ( i , j ) LH1(i,j) LH1(i,j) 整除 Q Q Q。由于这只是一个测试代码,因此我定义的都是一维数组。




步骤六:将加密后的水印图像 E W EW EW 按照以下规则嵌入到 L H 1 LH1 LH1 子带中,从而得到 L H 1 ′ LH1' LH1

① 针对第一种情况

if mod(S(i, j), 2) = EW(k) then
	LH1(i, j) = S(i, j) * Q + Q / 2
End if

如果 S ( i , j ) S(i,j) S(i,j) 2 2 2 取余本来就等于对应位置的水印信息 E W ( k ) EW(k) EW(k),那么

L H 1 ′ ( i , j ) = S ( i , j ) × Q + Q 2 LH1'(i,j)=S(i,j)\times Q+\frac{Q}{2} LH1(i,j)=S(i,j)×Q+2Q

Q:为什么需要 L H 1 ′ ( i , j ) LH1'(i,j) LH1(i,j) 且它的值和 L H 1 ( i , j ) LH1(i,j) LH1(i,j) 差不多?
A:因为我们希望嵌入水印后的图像和原始图像长得差不多。

提取水印时有

S ′ ( i , j ) = ⌊ L H 1 ′ ( i , j ) Q ⌋ = ⌊ S ( i , j ) × Q + Q / 2 Q ⌋ = ⌊ S ( i , j ) + 1 2 ⌋ = S ( i , j ) \begin{alignat}{2} S'(i,j) =& \left \lfloor \frac{LH1'(i,j)}{Q} \right \rfloor=\left \lfloor \frac{S(i,j)\times Q+Q/2}{Q} \right \rfloor \\ =& \left \lfloor S(i,j)+\frac{1}{2} \right \rfloor=S(i,j) \end{alignat}{} S(i,j)==QLH1(i,j)=QS(i,j)×Q+Q/2S(i,j)+21=S(i,j)

由于 S ( i , j ) S(i,j) S(i,j) 是由整除得到的,因此 S ( i , j ) S(i,j) S(i,j) 一定是一个整数。一个整数加上 0.5 0.5 0.5 后再向下取整,一定等于自身。那岂不是显得 0.5 0.5 0.5 很多余?还有必要加 0.5 0.5 0.5 吗?

从而有

E W ′ ( k ) = m o d ( S ′ ( i , j ) , 2 ) = m o d ( S ( i , j ) , 2 ) = E W ( k ) EW'(k) = \mathrm{mod}(S'(i,j),2) = \mathrm{mod}(S(i,j),2) = EW(k) EW(k)=mod(S(i,j),2)=mod(S(i,j),2)=EW(k)



② 针对第二种情况

if mod(S(i, j), 2) != EW(k) then
	if LH1(i, j) - S(i, j) * Q ∈ [0, Q/2] then
		LH1(i, j) = (S(i, j) - 1) * Q + Q / 2
	Else
		LH1(i, j) = (S(i, j) + 1) * Q + Q / 2
	End if
End if

如果 S ( i , j ) S(i,j) S(i,j) 2 2 2 取余不等于对应位置的水印信息 E W ( k ) EW(k) EW(k),那么我们需要分情况讨论。

0 ≤ L H 1 ( i , j ) − S ( i , j ) × Q ≤ Q 2 0 \le LH1(i, j) - S(i, j) \times Q \le \frac{Q}{2} 0LH1(i,j)S(i,j)×Q2Q

那么

L H 1 ′ ( i , j ) = ( S ( i , j ) − 1 ) × Q + Q 2 LH1'(i,j)=(S(i,j)-1)\times Q+\frac{Q}{2} LH1(i,j)=(S(i,j)1)×Q+2Q

提取水印时有

S ′ ( i , j ) = ⌊ L H 1 ′ ( i , j ) Q ⌋ = ⌊ ( S ( i , j ) − 1 ) × Q + Q / 2 Q ⌋ = ⌊ S ( i , j ) − 1 2 ⌋ = S ( i , j ) − 1 \begin{alignat}{2} S'(i,j) =& \left \lfloor \frac{LH1'(i,j)}{Q} \right \rfloor=\left \lfloor \frac{(S(i,j)-1)\times Q+Q/2}{Q} \right \rfloor \\ =& \left \lfloor S(i,j)-\frac{1}{2} \right \rfloor=S(i,j)-1 \end{alignat}{} S(i,j)==QLH1(i,j)=Q(S(i,j)1)×Q+Q/2S(i,j)21=S(i,j)1

由于 S ( i , j ) S(i,j) S(i,j) 2 2 2 取余不等于对应位置的水印信息 E W ( k ) EW(k) EW(k),因此 S ( i , j ) − 1 S(i,j)-1 S(i,j)1 2 2 2 取余一定等于对应位置的水印信息 E W ( k ) EW(k) EW(k)。这就是二进制的好处!



5 代码实现

import numpy as np

Q = 3.56  # 对应论文中的Q,即量化值
EW1 = [1, 0, 0, 1, 1, 0]  # 原始的EW
LH1 = [255, 204, 230, 198, 160, 98]  # 原始的LH
LH1 = np.array(LH1)

length = len(EW1)
S = np.zeros(length)  # 对应论文中的S
LH2 = np.zeros(length)  # 调整后的LH
EW2 = np.zeros(length)  # 提取出的EW


# 嵌入水印:获取LH2
def get_new_lh(lh1, s, ew):
    if s % 2 == ew:
        return s * Q + Q / 2
    else:
        if 0 <= lh1 - s * Q <= Q / 2:
            return (s - 1) * Q + Q / 2
        else:
            return (s + 1) * Q + Q / 2


for i in range(length):
    S[i] = LH1[i] // Q

for i in range(length):
    LH2[i] = get_new_lh(LH1[i], S[i], EW1[i])

print(LH2)


# 提取水印:获取EW2
def get_ew(s):
    return s % 2


for i in range(length):
    S[i] = LH2[i] // Q

for i in range(length):
    EW2[i] = get_ew(S[i])

print("原始的水印信息:", EW1)
print("提取的水印信息:", EW2)

按理来说 S S S L H LH LH 都应该是二维数组,但因为这只是一个简单的测试代码,因此我定义成了一维数组。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值