Qraft

那片图案是怎么把信息藏起来的 - 解开 0 与 1 的密码

所有信息都由 0 和 1 组成

在计算机的世界里,文字、数字、图像全都用 0 和 1 的组合来表示,这种表示方法叫做「二进制」。举个例子,字母「A」是 01000001,「B」是 01000010。用 8 位(8 个比特)0 和 1 的组合表示一个字符,一共有 256 种组合,足以覆盖所有英文字母、数字和符号

黑白的方形图案,就是把这些 0 和 1 变成了肉眼可见的形式。白色的格子代表 0,黑色的格子代表 1,也就是说,那片图案本质上是一张巨大的二进制表格。手机的相机读取黑白明暗,把它们还原成 0 和 1,再组装成文字或者链接,这就是扫描的全过程。

日文是怎么存进图案里的

英文字母用 8 个比特就能表示一个字符,日文却做不到。平假名、片假名加上汉字合起来有数万字,8 个比特远远不够。日文采用「UTF-8」这种编码方式,一个字符要用 3 个字节(24 个比特)来表示。

换句话说,即使字数相同,日文的数据量也是英文的 3 倍。「こんにちは」(5 个字)是 15 个字节,「Hello」(5 个字符)只有 5 个字节。把日文存进黑白图案,图案会比存英文时更大、更密,原因就在这里。链接只由英文字母和数字组成,因此能把图案控制得比较小。这也是为什么在很多场合,人们不把文本直接存进二维码,而是存一个链接。

图案里的「地图」,哪里写着什么

黑白图案看上去像是随机排列的黑白格子,实际上每一格的位置都遵循严格的规则。图案内部存在类似「地图」的结构,规定了哪个区域写着哪一种信息。

三个角上的大方块(定位图案)是「图案的边界在这里」的标记。紧挨着它们的小点列(定时图案)起到尺子的作用,告诉扫描端「格子的间隔是这么宽」。剩下的区域用来存放数据,而数据区最开头的几个模块里记录着两件事:这张图案装的是哪一类数据(数字、英文数字、汉字、二进制),以及数据的长度。

掩码图案,让扫描更顺利的最后一道工序

只是把数据换成黑白格子,有时会碰巧出现「黑色聚成一大块」「黑白形成条纹」这样的偏斜。这类偏斜会降低摄像头的识别精度,所以最后还要施加一道叫做「掩码图案」的处理。

掩码图案是按特定规则把部分格子的黑白翻转过来的处理。规范里准备了 8 种掩码图案,制作时会自动选出偏斜最小的那一种。比如其中一条规则是「把行号与列号之和为偶数的格子翻转」。经过这道处理,黑与白分布得更加均匀,最终得到的图案在任何摄像头下都能稳定扫描。

同样的信息,图案却不一样

即使制作内容相同的二维码,图案看起来也可能不同。这是因为二维码除了信息本体,还一并带着解读它所需要的线索,以及为应对污损而准备的备用数据。改变纠错强度,备用数据的量就会变化,图案自然跟着改变。另外,在整理扫描难易度的收尾工序里,外观也会被重新挑选一次。也就是说,图案并不是内容信息的直接映像,而是为了确保能被准确读出来、把各种巧思反复叠加之后呈现出的模样。

对人复杂,对机器简单

密密麻麻排列的格子,在人眼里是复杂到极点的图案。可是对负责扫描的机器来说,它不过是「亮还是暗」这种简单判断的累积。按既定顺序一格一格追踪,把明暗换成 0 和 1,再把这串数字组装成信息,这套工作机器一瞬间就能准确完成。它和人看着理解的文字,表示信息的方式完全不同。看似复杂的图案背后,藏着一套被彻底简化、让机器不会迷路的规则。

动手试试,把自己的名字变成二进制

读到这里,不妨亲自试一次。把自己的名字用英文字母写出来,再把每个字母换成二进制。A=01000001,B=01000010,C=01000011,照着 ASCII 码表就能一个个转换。

比如「TARO」,T=01010100,A=01000001,R=01010010,O=01001111。把它们连起来,就是 01010100010000010101001001001111 这样一串 32 位的 0 和 1。把这 32 个 0 和 1 换成白色和黑色的格子,你的名字就变成了图案。真正的二维码还会在此之上追加纠错数据和结构信息,因此会比这更大,但基本的原理就只有这些。