RAC-Hide:把图片藏进图片,为什么压缩后还能找回来?

根据 RAC-Hide 英文 README 节选并用中文整理,省略代码与公式。项目:tuoPzf/rac-hide。

先说它能做什么

RAC-Hide 是一个用 JavaScript 实现的图片隐写项目。简单说,就是把一张“秘密图片”藏进另一张“载体图片”里,让生成的图片看起来仍然接近载体图,再从中提取出隐藏的内容。

它特别关注一个实际问题:图片发到聊天软件或社交平台后,往往会被压缩、缩小。如果隐藏信息太脆弱,转发一次就可能读不出来。RAC-Hide 的目标,是让信息在一定程度的压缩和等比例缩放后仍有机会恢复。

整个过程在本地完成,项目的图片处理库不会把图片上传到服务器,也不依赖第三方 JavaScript 库。

关键不是记住一个数,而是记住两个数的关系

可以把图片想象成由许多小块组成。每一块除了有像素,还可以转换成描述明暗变化、纹理变化的一组数值。JPEG 压缩会简化这些数值,因此直接依靠某一个数值保存信息并不可靠。

RAC-Hide 换了一个思路:挑选变化规律比较接近的两个数,用“谁比谁大”来表示信息。再把它们的差距稍微拉开,给后续压缩留出余地。

这类似于记住两个人谁更高,而不是精确记住他们各自的身高。测量出现一点误差时,具体数值会变,但高矮关系可能仍然成立。当然,误差过大时,关系也可能被破坏。

还有两层帮助:纠错和“尺子”

纠错信息可以理解成额外留下的线索。隐藏的数据里有一部分受损时,提取程序能利用这些线索尝试补回来。项目还会把信息分散存放,并在某些档位重复保存,减少局部损坏的影响。

空间标尺则用来应对缩放。图片被等比例缩小后,原来的小块位置也会变化。项目会在图片的颜色信息中嵌入一把“尺子”,让提取程序尝试判断原始尺寸,再对齐位置进行读取。

如果标尺无法识别,也可以手动提供原始载体的尺寸作为补救。它不意味着任意缩放都能恢复。

一次隐藏和提取,大致经过哪些步骤?

  1. 准备秘密图。先压成 JPEG,再加入识别信息和纠错数据。因此,提取结果并不等于原始秘密图片文件的逐字节备份。
  2. 写入载体。将信息分散写进载体的小块中,加入标尺,最后生成隐写图片,通常保存为 PNG。
  3. 读取隐藏内容。提取时尝试恢复尺寸、读取数值关系、修复错误,再还原秘密图片。

提取不需要提供最初的载体图。隐藏的数据带有识别信息,程序会尝试寻找合适的参数,这也是项目所说的“盲提取”。

更清楚,还是更耐压缩?需要做取舍

同一张载体能容纳的信息是有限的。留给重复和纠错的数据越多,真正能存放秘密图的空间就越少。

  • 容量优先:可以藏入更多内容,秘密图更清晰,但更容易被压缩破坏。
  • 均衡:在容量与恢复能力之间折中,是英文文档推荐的默认选择。
  • 稳健优先:用更多空间保存重复和纠错信息,牺牲容量,换取更强的抗压缩、抗缩放能力。

把隐藏信号做得更强,也可能让载体的变化更容易被看出来。不存在一个同时保证“藏得多、完全看不出、怎么压都不坏”的参数。

哪些情况容易失败?

英文文档列出的主要限制包括:裁剪会破坏位置对齐,加水印或重绘会损坏信号,严重缩小后再强烈压缩也可能超过纠错能力。恢复效果还与载体内容有关,大片平坦区域通常比有纹理的图片更难处理。

所以,“抗压缩”应当理解为提高恢复的可能性,而不是保证经过任何平台处理都能读回。对重要内容,仍应保存原文件,不要只留下隐写后的副本。

隐藏不等于加密

RAC-Hide 解决的是“把信息放在哪里、怎样尽量保存下来”,不是“只有持有密钥的人才能读取”。它本身不是密码学意义上的加密工具。

如果你想继续了解实现,项目将图片变换、纠错、标尺和编解码拆成了独立模块,核心也可以在 Node.js、Web Worker 或浏览器中运行。详细设计与测试代码都在仓库里,采用 GPL-3.0-or-later 许可。

原文与进一步阅读:RAC-Hide 项目仓库。本文是便于理解的节选,不是逐句翻译,也不代表额外的独立性能验证。