小番茄图片混淆的算法原理
没有黑盒。这一页把打乱一张图所用的全部规则摊开来讲——一条曲线、一个比例,仅此而已。
先说结论:小番茄混淆是一次集体搬家
把图片看成一张 W×H 的网格,每个格子里住着一个像素。混淆做的事,是给所有像素安排一次集体搬家:每个像素按同一套规则搬进新家,且新家互不重复。只要搬家路线可逆,就能在一次反向搬运后,让所有像素回到原位。
所以整个工具的全部问题,归结为两件事:一是设计一条经过每个格子恰好一次的路线,二是确定一个固定的搬运距离。下面分别说清楚。
一、路线:Gilbert 空间填充曲线
空间填充曲线是一条「不跳格、不重复」的连续路线:从起点出发,沿着格子一格一格地走,最终恰好经过矩形里的每个格子一次。数学上最有名的是希尔伯特曲线,它把平面反复对折,得到一条越折越密、最终「填满」整个区域的路径。但标准希尔伯特曲线对尺寸有要求,得是 2 的整数次幂边长的正方形才好构造。
本工具用的是它的推广版本——Gilbert 曲线。它通过递归切分来适配任意宽高比的矩形:宽明显大于高时,纵向剖成两段;高明显大于宽时,横向切一刀;接近正方形时,切成「上一步、长横段、下一步」三段。子块继续按同样规则再分,直到退化成单行或单列,直接顺序走完。
这种切法有两个好处。其一,任意尺寸的图片无需补边或缩放,就能直接生成路线。其二,曲线上相邻的两个格子,在图片上也总是挨着的——这个「局部性」在后文解释打乱效果时非常关键。
二、距离:为什么偏偏是 0.618
路线排定后,每个格子都有了序号。混淆时,把序号 i 的像素搬进序号 (i + offset) mod N 的格子,N 是像素总数,offset 是搬运距离。本工具取 offset = round(0.618… × N),也就是黄金分割比乘以像素总数。
选黄金分割不是美学趣味,而是数学上的需要。黄金比例是「最难用分数逼近」的无理数——它的最佳有理近似收敛得比其他任何无理数都慢。取 0.618N 作为搬运距离,意味着曲线上相邻的两个像素,搬运后恰好相隔整条路线的 0.618 长度:既不靠近,也不构成简单比例(既不是对折,也不是三等分),因此不会出现对称重影或网格状的花纹。反过来,如果改用 N/2、N/4 这类整数比距离,相邻像素会成对地镜像分布,画面会留下肉眼可辨的对称结构,正是要避开的失败案例。
另一个决定性的因素是前文提到的曲线局部性。原始图片里,相邻像素的颜色高度接近,会连成肉眼可辨的色块——天空连成一片蓝,皮肤连成一片暖色。而在曲线上加一段 0.618N 的全局搬运后,这些连片的色块被整条路线均匀拉散,局部相关性被彻底稀释,剩下的画面只有统计意义上的颜色噪声,看不出任何结构。
三、互逆:小番茄解混淆为什么一定有效
混淆是 i → (i + offset) mod N,解混淆是 (i + offset) mod N → i,同一张路线表正走与反走的关系。由于搬运是一一对应的双射,不存在两个像素挤进同一格的情况,反向搬运必然严格复原。
把这个性质推一步:连续混淆 k 次,相当于序号整体偏移了 k × offset,因此执行 k 次「解混淆」即可归零。这就是教程里「混淆几次就解几次」的数学由来。
四、处理参数一览
| 像素上限 | 800 万像素以内直接处理,超出自动等比压缩 |
|---|---|
| 单边上限 | 宽或高超过 4000 像素时触发压缩 |
| 输出格式 | JPG,质量档最高 |
| 处理位置 | 浏览器本地画布(Canvas),全程无网络请求 |
| 曲线开销 | 处理时需要生成与像素数同长度的路线表,这也是上限设为 800 万像素的原因之一 |
五、小番茄混淆与加密的区别(建议读完)
本工具的映射规则完全公开、无密钥参与,属于「混淆」而非「加密」。它能让画面在第一时间不可读,但挡不住掌握算法的人还原。若你的需求是「除了指定的人谁也看不了」,请使用真正的加密工具。边界想清楚,工具才用得其所——更多说明见使用场景与常见问题。