
3分钟给图片加上看不见的盲水印:blind_watermark 快速实战指南【免费下载链接】blind_watermarkBlindInvisible Watermark 图片盲水印提取水印无须原图项目地址: https://gitcode.com/GitHub_Trending/bl/blind_watermark你刚把插画发出去三天,发现它挂在了别人主页当头像,而平台只会让你自证原创。截图加时间戳不算证据,最直接的,是在图片里放一个肉眼看不见、事后还能取出来的标记。blind_watermark 就是做这件事的 Python 库:它把文字或图片做成盲水印嵌进照片,之后提取水印无须原图,也不需要当初那张底图。blind_watermark 怎么装,两分钟跑通第一张图安装就一条命令:pip install blind-watermark装完直接用 Python API,嵌入和提取各几行。假设你有一张图pic/ori_img.jpeg,想嵌一句文字:from blind_watermark import WaterMark bwm WaterMark(password_img1, password_wm1) bwm.read_img(pic/ori_img.jpeg) bwm.read_wm(guofei9987 开源万岁!, modestr) bwm.embed(output/embedded.png) print(len(bwm.wm_bit)) # 记下来,提取水印要用到modestr表示嵌文字;换成read_wm(pic/watermark.png)就是嵌一张小图片,modebit则是直接塞 0/1 数据。提取时把之前记下的长度作为wm_shape传回去:bwm2 WaterMark(password_img1, password_wm1) wm_extract bwm2.extract(output/embedded.png, wm_shape111, modestr) print(wm_extract) # guofei9987 开源万岁!不想写代码也行,命令行把嵌入和提取都包好了:blind_watermark --embed --pwd 1234 examples/pic/ori_img.jpeg watermark text examples/output/embedded.png blind_watermark --extract --pwd 1234 --wm_shape 111 examples/output/embedded.png盲水印的嵌入和提取到底在做什么先看效果。左边是原图,右边是嵌入水印后的图,肉眼几乎找不出差别,但第二张图里已经装进了完整的文字水印:嵌入:把 0 和 1 写成半步位移可以把它理解成在乐器的泛音里刻字:人耳听的是主旋律(图像里人眼敏感的亮度、颜色),而水印写在人眼不敏感的频域细节里,听起来还是原曲,懂行的人却能听出来。具体做法在 blind_watermark/bwm_core.py:每个 4×4 小块的最强频率分量,被取整到一个基准值,然后 0 不动、1 抬半步。人眼看的是整体明暗,对这种半步级的细节不敏感,所以图看起来没变。提取:为什么不需要原图关键有两点。第一,信息靠的是半步位移本身,不是某个像素的绝对值——提取时只看分量对基准值取余,判断它落在半步的哪一侧,原图像素根本不参与判断。第二,同一个 bit 被循环写进很多小块、三个颜色通道各写一份,提取时把几百上千份投票求平均,个别块被噪声干扰也会被冲掉。所以提取只需要三样东西:被攻击后的图、两个密码、水印比特长度。底图从来就不在依赖清单里,这就是盲的含义。 打它几下试试:旋转、加噪、缩放扛不扛得住别光听它说,自己动手打。攻击函数都放在 blind_watermark/att.py,完整演示在 examples/example_str.py,下面三招都是照它跑的。旋转:把图转 60°,再转回来,直接提取——结果还是完整的水印文字,一个字节不少。椒盐噪声:按 5% 概率往图里撒白色噪点,模拟传输和劣质压缩。加噪后的图明显脏了,提取依旧正确。原因是前面说的冗余:一个 bit 被写了上千份,噪点打坏几份,平均下来票还是对的。缩放:把图缩到 400×300 再拉回原尺寸,提取正常。注意缩到的小图四周会有空白,拉回去只是尺寸复原,水印跟着块一起缩放,所以还能读出来。README 里还列了遮挡、裁剪、亮度共 8 类攻击,提取结果全部一致。其中裁剪稍微特殊:如果你不知道被裁去了哪里,可以用 blind_watermark/recover.py 的estimate_crop_parameters先推测裁剪位置和缩放比例,再recover_crop补回原尺寸,然后照常提取。⚙️ 底层怎么实现的:水印写在 DWT-DCT-SVD 链路上整条链路在 blind_watermark/bwm_core.py,用一句话说:图像先做 DWT(离散小波变换,把图拆成人眼敏感的低频和细节高频),取低频部分切成 4×4 小块;每块做 DCT(离散余弦变换,把能量集中到少数系数上);再对块做 SVD(奇异值分解,拆出几个强度分量 s[0]、s[1]),把水印位按取整到基准 半步位移写进这两个分量,然后层层逆变换回图像。两个密码其实是两个随机种子:password_img决定各小块内部系数怎么置换,password_wm决定水印比特顺序怎么打乱——没有种子,你连哪个块对应第几位都不知道。提取端用同一组种子还原顺序,逐块读出 0/1,三通道求平均后经过一维 k-means 找到 0 和 1 的自动分界,输出干净的水印。鲁棒性基本就来自多份冗余 平均 自动阈值这套组合拳。适合谁,不适合谁适合:在社交平台、图库发布作品,想事后能自证原创的创作者和摄影师;需要给一批图各打一个编号做溯源的内容平台(用 bit 模式,每张图嵌一段 ID);以及分发格式可控、能拿到 PNG 或高质量 JPG 的场景。不适合:指望被人随意裁剪、二次压缩、换角度上传之后还能无参考提取——裁剪还原需要一张攻击前的图做参照,旋转回正需要你知道转了多少度;也不做视频水印,只处理图片。还有一个常见误解:那两个密码是打乱顺序的随机种子,只防顺手提取,不是加密,别拿它当保密通信的密钥。 用之前先知道的几件事提取必须给长度。extract的wm_shape要传嵌入时len(bwm.wm_bit)的值,嵌入那一刻就把它存进数据库,丢了就提取不了。存 PNG,少折腾 JPG。水印图再被有损压缩一遍会多一层损失,embed()支持compression_ratio控制输出质量,但最稳妥是分发无损格式。密码要对齐,别指望它加密。嵌入和提取的password_img、password_wm必须一致,它们是种子,换了就全乱。裁剪和缩放要留底。用 recover 模块还原裁剪时,参照物是攻击前那张嵌好水印的图,所以发布渠道里留一份原发图。容量够用但不无限。可嵌比特数约为(高/8)×(宽/8),一张 1108×738 的图大约能装 1.2 万 bit,塞一句带 ID 的短文本绰绰有余。blind_watermark 把图片盲水印从论文概念变成了三五行代码就能验证的工具,嵌入、提取、抗攻击全链路都是现成的。建议你先拿自己的一张真实图,把 examples/example_str.py 里的旋转、椒盐、缩放三种攻击跑一遍,确认你的分发链路(尤其是压缩程度)下水印提取无误,再接入正式流程。【免费下载链接】blind_watermarkBlindInvisible Watermark 图片盲水印提取水印无须原图项目地址: https://gitcode.com/GitHub_Trending/bl/blind_watermark创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考