解混淆 小番茄图片混淆 · 在线还原 打开工具

PRINCIPLE / 原理

算法原理

这台工具没有使用密码,也没有训练模型。它只做了一件事:按照一条确定的路线,把图片里的像素整体搬动一个固定距离。理解了这条路线和这个距离,也就理解了它为什么可逆、为什么看起来像噪声,以及为什么不能当加密使用。

Gilbert 曲线黄金分割位移

混淆与解混淆共用同一套坐标和同一个步长。理解“曲线”“位移”“双射”三个词, 就足以解释你在这个工具里看到的所有现象。

一句话版本

把图片的像素按一条固定曲线排成一队,再把整队数据往前平移约 0.618 倍总长的距离。 平移是循环的,每个位置都有唯一来源,所以反向平移同样距离就能恢复。

没有密码,没有随机种子,也没有隐藏参数。所有规则都可以写成几行公式,这也是它“可逆但不可保密”的原因。

像素是一张按行列排好的表

浏览器通过 Canvas 读取图片时,得到的是一段连续的内存:每四个字节描述一个像素, 依次是红、绿、蓝和透明度。图片的宽度决定了一行有多少个像素, 于是这段一维数据可以被重新折成一张二维的表。

人眼之所以能看到画面,是因为这些数值按照原来的行列关系排列。 只要把表格里任意两个格子的内容互换,画面就会开始失真;互换得越多、越远,越难辨认。 混淆做的事情,就是大规模、确定性地互换位置,而不修改任何一个颜色值。

为什么不直接随机打乱

随机打乱听起来更安全,但会带来两个工程问题。第一,真正随机的排列需要保存一张映射表, 800 万像素就是 800 万个索引,内存开销很大;第二,解混淆必须知道同一张随机表, 而这张表没有天然的分发方式,除非额外引入密钥或种子。

空间填充曲线提供了另一种思路:它只依赖宽高,不需要额外保存任何表。 给定宽高,每个人都能算出完全相同的遍历顺序;给定顺序,每个人也都能算出反向映射。 这正好满足“可逆、无需密钥、可复现”的目标。

顺便说一句,曲线还有一个副作用:它让相邻的格子在顺序上也尽量相邻。 这样搬运之后的画面会呈现细碎的噪点,而不是大块的色带,视觉遮挡效果更均匀。

Gilbert 曲线怎么走

常见的希尔伯特曲线定义在 2 的幂次方形网格上。本站使用的是它的推广版本, 可以处理任意宽高的矩形,因此 1200 × 800、1080 × 1920 这类图片不需要补边或裁切。

递归过程可以这样理解:把当前矩形沿长边切成两块或三块,分别递归; 需要反向遍历的块,把方向取反。切到只剩一行或一列时,直接顺序填满。 由于每次切分都完整覆盖原矩形,最终得到的坐标序列长度恰好等于 W × H,每个格子出现且只出现一次。

这不是“近似覆盖”,而是严格的划分。它保证了后面所有步骤都建立在一个完整的双射上, 不会有像素被漏掉,也不会有像素被写两次。

0.618 倍的步长

如果只按曲线顺序把数据往前挪一位,相邻像素仍然挨在一起,画面几乎不会变。 步长必须足够大,才能把原本相邻的像素甩到队列的另一端。本站使用的步长是:

shift = round((√5 − 1) / 2 × W × H)

(√5 − 1) / 2 约等于 0.618,也就是黄金分割比例的倒数。 它乘以像素总数,再四舍五入到整数,得到一个与总长度同一量级的位移。 对任意尺寸的图片,这个比例都能让搬运后的像素落在队列中段附近, 而不是只在局部小范围里打转。

为什么不用 0.5?因为 0.5 倍总长会让前后两半整体交换,容易形成肉眼可辨的对称结构; 也不容易产生“挪了几次就回到原位”的短周期。黄金分割比例是一个无理数的近似, 在离散化之后仍然能让位移与总长度保持较为“错开”的关系,实际效果更均匀。

一次搬运的完整流程

把上面的内容合起来,一次混淆可以用下面几行伪代码描述:

N     = W × H
shift = round(0.618 × N)
curve = gilbertPath(W, H)      // 长度 N 的坐标序列

for i in 0 .. N-1:
    from = curve[i]
    to   = curve[(i + shift) % N]
    out[to] = in[from]

解混淆把赋值方向反过来,其余完全相同:

for i in 0 .. N-1:
    from = curve[i]
    to   = curve[(i + shift) % N]
    out[from] = in[to]

注意 out 和 in 是两块独立的像素缓冲区。原地交换会互相覆盖, 因此实现时总是先读源数据,再写目标数据。最终结果写回同一张画布,导出为图片。

可逆性从哪来

可逆性由两步复合保证:

  1. 曲线是一个双射。每个网格坐标对应唯一的序号,每个序号也对应唯一的坐标。
  2. 平移是一个置换。在长度为 N 的环上,i → (i + shift) mod N 是一一对应的,反向就是 i → (i − shift) mod N。

两个双射复合仍然是双射,所以每个输出位置都能找到唯一来源,反向执行就能完整恢复。 这也解释了为什么少一个像素都不行:宽高变化会改变 N、曲线形状和步长, 整个映射关系随之改变,旧图片的坐标就对不上了。

动手画一遍

下面的图把 12 × 8 的网格和它的 Gilbert 曲线画了出来。拖动滑杆改变序号 i, 深色格表示曲线上的第 i 个点,橙色格表示它被搬到的第 i + shift 个点。 切换网格尺寸,可以看到步长随总像素数变化。

第 i 个点 j = i + shift

它能做什么,不能做什么

从数学上看,这是一个没有密钥的置换。任何人都可以构造出同样的曲线和步长, 因此任何人都能还原。它真正提供的是观看门槛:缩略图不再直接暴露内容, 随手转发的人需要多走一步才能看到画面。

如果需要“没有授权就打不开”,请使用带密钥的加密;如果需要“这段信息根本不该出现”, 请直接裁剪或彻底打码。混淆可以和其他手段组合,但不能替代它们。 更完整的讨论见隐私与安全边界与方案对比。

继续

想确认自己的图片能否与参考实现互通,看兼容与参数;想直接动手,回到首页工具即可。

看懂了原理,不妨亲手验证一次

用示例图片混淆再解混淆,观察画面是否回到起点;也可以打开对比,看看像素被搬动前后的差别。