盲水印笔记
简述
水印(Water-Mark)是一种保护数字产品版权、完整性、防复制或去向追踪的技术,通常作为署名的一种。但水印的缺点显而易见,不止 PhotoShop 有去除水印的功能,同时可以使用截图等手段将水印抹除。
于是盲水印(Blind-Water-Mask)被发明出来,即字面意思——一种没办法用肉眼直接观察的水印。
盲水印有着隐蔽性(Imperceptible)、不易移除性(Non-removable)、强健性(Robustness)和明确性(Unambiguous),可以很好的满足当前数字版权保护的所需。
频域隐写技术
原理
盲水印流程图如下:
傅里叶变换
盲水印用到的傅里叶变换是将信号在时域或空域的函数转变为用频域表示。
图像是二维离散的,对该二维信号在各个方向上进行一次一维的傅里叶变换即可得到二维频域图(使用的是离散傅里叶变换 DFT 算法),其中二维信号为时域中图像的灰度 \(f(u,v)\)。
1 | import cv2 |
我们可以发现灰度信号被转化为了频域信号。
如果我们将图片的低频部分移动到中间,将高频部分移动到外围:
1 | # 将图像中的低频部分移动到图像的中心 |
同样的逆变换就是做相反的操作
1 | # 逆变换 |
我们可以发现完美地还原了图片。
那么根据这个原理,我们在变换和逆变换的中间,加入水印图像即可。
水印对称性
由于傅里叶变换后,频域图 \(f(u, v)\) 与 \(f(-u,-v)\) 是共轭的,所以其实部应该相等(如果不相等的话,逆变换后结果将在虚部上损失一部分信息,因为我们只写入实部数据),故我们应使得水印编码后的数据是对称的。
工具
BlindWaterMark
在盲水印的加密和解密中,我们通常使用 GitHub 上的开源脚本 BlindWaterMark
需要注意的是,该项目需要原图才能提取盲水印。
bwm.py
程序文件python2版本bwmforpy3.py
程序文件python3版本hui.png
无水印的原图wm.png
水印图hui_with_wm.png
有盲水印的图wm_from_hui.png
解出来的水印图
加密语法:
1 | python bwm.py encode hui.png wm.png hui_with_wm.png |
解密语法:
1 | python bwm.py decode hui.png hui_with_wm.png wm_from_hui.png |
缺陷是该工具需要一张原图,且注意python2和python3版本程序的 encode 和
decode 结果会有所不同。主要原因是 python2 和 python3
的随机算法不同。如果想让 python3 兼容 python2 的随机算法,请添加
--oldseed
参数。
同时由于其水印编码方式是使用默认随机数的,如果需要指定随机数,需要使用
--seed
参数。
blind_watermark
该项目的优点是无需原图即可还原盲水印,同时支持文字水印等。
该项目在 GitHub 上开源,主页链接:https://github.com/guofei9987/blind_watermark
安装方式:
1 | pip install blind-watermark |
在 bash 中使用
1 | # embed watermark into image: |
在 Python 中使用
嵌入文字
嵌入水印:
1 | from blind_watermark import WaterMark |
提取水印:
1 | bwm1 = WaterMark(password_img=1, password_wm=1) |
输出:
@guofei9987 开源万岁!
嵌入图像
嵌入水印:
1 | from blind_watermark import WaterMark |
提取水印:
1 | bwm1 = WaterMark(password_wm=1, password_img=1) |
嵌入位数组
作为演示,我们嵌入了 6 个字节的数据:
1 | wm = [True, False, True, True, True, False] |
嵌入:
1 | from blind_watermark import WaterMark |
提炼:
1 | bwm1 = WaterMark(password_img=1, password_wm=1, wm_shape=6) |
注意wm_shape
(水印的形状)是必要的
输出wm_extract
是一个浮点数组。设置一个阈值,例如
0.5。