目录
本文附属于 :数字水印 | 彩色图像论文:A blind and robust color image watermarking scheme based on DCT and DWT domains(三)
前言
最初,我看不懂论文 A 中提到的 量化值 和 水印嵌入规则 ,只觉得莫名其妙。后来看了 盲水印 相关的论文、博客和代码,我突然恍然大悟!
本博客涉及两篇论文:
接下来,我将以人话和 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′=(Smark−Sy)/α
其中 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 中通过以下代码即可实现:
pythonfor 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/2⌋⌊S(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} 0≤LH1(i,j)−S(i,j)×Q≤2Q
那么
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/2⌋⌊S(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 代码实现
python
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 都应该是二维数组,但因为这只是一个简单的测试代码,因此我定义成了一维数组。