一直对各大互联网大厂传说中揪出内鬼的能力感到好奇,机缘巧合下了解到其中一种数字水印的方法,在员工操作的设备上添加隐藏的水印,通过解密可以得到附加的信息,因此学习复现一下。
原理
现阶段常见的,使用最多的水印技术,就是用水印图片覆盖原有像素,虽然简洁明了,但容易被PS干掉,也不可逆地破坏了原图片的内容,因此隐藏数字水印的技术只能作用于频域中
为了获得图像的频域,需要使用二维的快速傅立叶变换(FFT2),将灰度图转化为频率的分布
一张频谱图如下所示:
其中,频谱图反映了图像中突变、边缘的变化等信息,对其进行细微修改不会显著地变化原图
因此,将一张水印直接添加到待加密的图片的频谱上,再进行傅立叶逆变换,恢复到空间域的图像,得到一张加密过的图像
解密时,将图像进行傅立叶变换,减去原图的频谱,得到隐藏在其中的信息
代码验证
待加密图 |
水印 |
|
|
52pojie.jpg |
watermark.jpg |
代码:需要opencv-python
+ matplotlib
import cv2
import random
import numpy as np
import matplotlib.pyplot as plt
# 随机序列表
key1 = []
key2 = []
def read(name):
img0 = cv2.imread(name)
return cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
# 读取图片和水印
raw_pic = read("52pojie.jpg")
watermark = read("watermark.jpg")
if raw_pic.shape[0] != watermark.shape[0] & raw_pic.shape[1] != watermark.shape[1]:
print("水印尺寸错误")
# 水印随机序列编码
if key1 == key2 == []:
key1 = random.sample(range(watermark.shape[0]), watermark.shape[0])
key2 = random.sample(range(watermark.shape[1]), watermark.shape[1])
with open("key.txt", "w") as f:
f.write(str(key1) + "\n" + str(key2))
temp = np.zeros((watermark.shape[0], watermark.shape[1]))
for i in range(watermark.shape[0]):
for j in range(watermark.shape[1]):
temp[i][j] = watermark[key1[i]][key2[j]]
watermark_encoded = temp
cv2.imwrite("watermark_encoded.jpg", watermark_encoded)
# 添加水印
FR = np.fft.fft2(raw_pic)
FA = FR + watermark_encoded
fshift = np.fft.fftshift(FA)
s = np.log(np.abs(fshift))
fimg = np.abs(s)
# 从频域恢复图片
ifshift = np.fft.ifftshift(fshift)
back = np.fft.ifft2(ifshift)
back = np.abs(back)
cv2.imwrite("picture.jpg", back)
# 提取水印
F_encoded = np.fft.fft2(back)
F_watermark = (F_encoded - FR)
plt.savefig("watermark_extracted.jpg")
# 水印解码
FF = np.zeros((F_watermark.shape[0], F_watermark.shape[1]))
for i in range(F_watermark.shape[0]):
for j in range(F_watermark.shape[1]):
FF[key1[i]][key2[j]] = F_watermark[i][j]
cv2.imwrite("watermark_decoded.jpg", FF)
运行结果如下:
1. 原图频谱 |
2. 水印编码 |
|
|
3. 加密频谱 |
4. 加密图片 |
|
|
5. 提取水印 |
6. 水印解码 |
|
|
测试1:水印不编码
将水印直接加到原图的频域中,频域之差即水印,观察提取出来的水印
发现一正一反重叠了起来,和上面的图有差距,这是由于傅立叶变换的对称性,在本图中表现为出现了相反的两张水印
但水印的总能量是不变的,两张水印各占有一半的能量,因此水印的灰度相比原水印都发生了变化
因此,随机序列编码的优势在于,只恢复了想要的正确水印,将相反的水印散布在背景中,使得水印看起来清晰可见
该问题很好解决,只要做一张对称的水印即可
对称水印 |
水印提取 |
|
|
在对称水印的情况下,是否编码的结果没有任何差别(可以完全取出水印)
我只是简单把像素加到频域中,是否编码不影响矩阵作差的准确性,在一些使用滤波器的数字水印加密中,编码可以使得图片的频域与原图的频域相对分离,可以做到不需要原图也可以提取水印
测试2:鲁棒性测试
对图片造成一点破坏——添加一块噪声上去:原图 + 噪声系数 * 噪声像素
再次测试结果:
(这里应当有一张全白的图片.jpg)
信息完全丢失了
修改噪声系数小于0.1时能看到被破坏过的结果:
可以看出这种加密对像素攻击有一定的抵御能力(涂改的地方已经挺多了,一般图片不会这样损坏)
优缺点
- 优点:足够隐蔽(完全看不出来-见4. 加密图片),可以抵御一定程度的攻击
- 缺点:信息直接加在图片的频域上,在复频域中,但是在保存加密图片时只能储存0-255的像素值,过大的误差导致计算出来的加密图片和保存的图片频域差距较大,无法正常隐藏信息,暂时没有想到好方法解决该问题
总结
去年实验课的时候有个题目是在音频中隐藏信息,没做成功,后来发现图像领域有类似的鲁棒性水印,在生活中有一定用处,便学习了一下。各位看到水印的文字大概能想到它原来的用途(笑)可惜没能解决保存加密图片的问题,只能提前放出。待我再研究研究使用滤波器的数字水印技术,下次原创发布区见!