RAC-Hide:把图片藏进图片,为什么压缩后还能找回来?
根据 RAC-Hide 英文 README 节选并用中文整理,省略代码与公式。项目:tuoPzf/rac-hide。
先说它能做什么
RAC-Hide 是一个用 JavaScript 实现的图片隐写项目。简单说,就是把一张“秘密图片”藏进另一张“载体图片”里,让生成的图片看起来仍然接近载体图,再从中提取出隐藏的内容。
它特别关注一个实际问题:图片发到聊天软件或社交平台后,往往会被压缩、缩小。如果隐藏信息太脆弱,转发一次就可能读不出来。RAC-Hide 的目标,是让信息在一定程度的压缩和等比例缩放后仍有机会恢复。
整个过程在本地完成,项目的图片处理库不会把图片上传到服务器,也不依赖第三方 JavaScript 库。
关键不是记住一个数,而是记住两个数的关系
可以把图片想象成由许多小块组成。每一块除了有像素,还可以转换成描述明暗变化、纹理变化的一组数值。JPEG 压缩会简化这些数值,因此直接依靠某一个数值保存信息并不可靠。
RAC-Hide 换了一个思路:挑选变化规律比较接近的两个数,用“谁比谁大”来表示信息。再把它们的差距稍微拉开,给后续压缩留出余地。
这类似于记住两个人谁更高,而不是精确记住他们各自的身高。测量出现一点误差时,具体数值会变,但高矮关系可能仍然成立。当然,误差过大时,关系也可能被破坏。
还有两层帮助:纠错和“尺子”
纠错信息可以理解成额外留下的线索。隐藏的数据里有一部分受损时,提取程序能利用这些线索尝试补回来。项目还会把信息分散存放,并在某些档位重复保存,减少局部损坏的影响。
空间标尺则用来应对缩放。图片被等比例缩小后,原来的小块位置也会变化。项目会在图片的颜色信息中嵌入一把“尺子”,让提取程序尝试判断原始尺寸,再对齐位置进行读取。
如果标尺无法识别,也可以手动提供原始载体的尺寸作为补救。它不意味着任意缩放都能恢复。
一次隐藏和提取,大致经过哪些步骤?
- 准备秘密图。先压成 JPEG,再加入识别信息和纠错数据。因此,提取结果并不等于原始秘密图片文件的逐字节备份。
- 写入载体。将信息分散写进载体的小块中,加入标尺,最后生成隐写图片,通常保存为 PNG。
- 读取隐藏内容。提取时尝试恢复尺寸、读取数值关系、修复错误,再还原秘密图片。
提取不需要提供最初的载体图。隐藏的数据带有识别信息,程序会尝试寻找合适的参数,这也是项目所说的“盲提取”。
更清楚,还是更耐压缩?需要做取舍
同一张载体能容纳的信息是有限的。留给重复和纠错的数据越多,真正能存放秘密图的空间就越少。
- 容量优先:可以藏入更多内容,秘密图更清晰,但更容易被压缩破坏。
- 均衡:在容量与恢复能力之间折中,是英文文档推荐的默认选择。
- 稳健优先:用更多空间保存重复和纠错信息,牺牲容量,换取更强的抗压缩、抗缩放能力。
把隐藏信号做得更强,也可能让载体的变化更容易被看出来。不存在一个同时保证“藏得多、完全看不出、怎么压都不坏”的参数。
哪些情况容易失败?
英文文档列出的主要限制包括:裁剪会破坏位置对齐,加水印或重绘会损坏信号,严重缩小后再强烈压缩也可能超过纠错能力。恢复效果还与载体内容有关,大片平坦区域通常比有纹理的图片更难处理。
所以,“抗压缩”应当理解为提高恢复的可能性,而不是保证经过任何平台处理都能读回。对重要内容,仍应保存原文件,不要只留下隐写后的副本。
隐藏不等于加密
RAC-Hide 解决的是“把信息放在哪里、怎样尽量保存下来”,不是“只有持有密钥的人才能读取”。它本身不是密码学意义上的加密工具。
如果你想继续了解实现,项目将图片变换、纠错、标尺和编解码拆成了独立模块,核心也可以在 Node.js、Web Worker 或浏览器中运行。详细设计与测试代码都在仓库里,采用 GPL-3.0-or-later 许可。
原文与进一步阅读:RAC-Hide 项目仓库。本文是便于理解的节选,不是逐句翻译,也不代表额外的独立性能验证。