二维码原理入门 · 讲义与学习笔记

了解二维码如何把信息编码成黑白方块图案的基本原理。

整理:问学·科技

第 1 关 · 二维码是什么:从扫码日常说起

认识二维码的来历、与一维条形码的区别,以及它在生活中的地位

二维码在日常生活中的普及

二维码在日常生活中的普及

想象一下十年前去菜市场买菜:摊主找零、收钱、记账,全靠现金和脑子里的账本。今天呢?抬头一个二维码,低头一笔支付就完成了。二维码已经像水电气一样,成了我们生活的基础设施。

一、你今天扫了几次码?

试着回忆一下:从早上起床到晚上睡觉,你可能已经在不知不觉中扫过好几次码了。

这些场景有一个共同点:**原本需要「输入」或「口头传递」的信息,现在只需要「扫一下」**。

二、二维码扮演的三种角色

仔细看上面这些场景,二维码其实在扮演三种不同的角色:

  1. **身份凭证**——健康码、乘车码、门禁码:扫码 = 亮明「我是我」
  2. **入口指引**——点餐码、加好友码、公众号关注码:扫码 = 进入一个服务或页面
  3. **资金通道**——收款码、付款码:扫码 = 完成一笔钱的转移
flowchart LR
    A[日常生活中的二维码] --> B[身份凭证]
    A --> C[入口指引]
    A --> D[资金通道]
    B --> B1[健康码/乘车码/门禁码]
    C --> C1[点餐码/加好友码/关注码]
    D --> D1[收款码/付款码]

这三种角色覆盖了「人-机」、「人-人」、「人-钱」三种最基础的连接。所以有人说,**二维码是移动互联网时代最朴素的连接器**——它不漂亮、不复杂,但任何一部手机、任何一个人都能用。

三、一个值得记住的事实

2010 年前后,扫码支付在中国开始普及;到今天,全球绝大多数日常扫码行为都发生在中国。换句话说——**二维码这项日本发明的技术,在中国被用得最广最深**。

这个细节先留个印象,等我们讲到 QR 码的诞生历史时,会再聊这个故事。

**要点:** 二维码早已从「偶尔用一下的工具」变成「生活的基础设施」——它在不同场景里扮演着身份凭证、入口指引、资金通道三种角色,核心作用都是把「信息」和「动作」之间的成本降到最低。

一维条形码与二维码的核心区别

一维条形码与二维码的核心区别

想象你去图书馆找一本书。贴在书脊上的那串「竖线条」就是**一维条形码**——它只能告诉你这本书的「编号」(比如 ISBN 编号),相当于一个身份证号;而贴在封底或海报上的那个「方方正正的黑白格子」就是**二维码**,相当于把整本书的简介、目录、几段文字都印在了这一小块地方。

下面从三个最根本的维度,把这两种码拆开对比。

区别一:方向——条形码认「横竖」,二维码认「方位」

条形码是横向排列的黑白条纹,它储存信息的方式是「这一条粗,那一条细,再来一条细的」。这就要求**扫的时候必须大致水平、顺着条纹方向划过**。你把它竖过来、斜过来、倒过来扫,机器就读不懂了。

二维码是一个**正方形的方阵**,三个角上有三个一模一样的大方块(叫「定位图案」)。无论你正着扫、斜着扫、倒着扫,机器只要认出这三个角,就能立刻判断出方块的朝向和边界,然后正确读取。所以你用手机扫海报、墙上的码,怎么歪都能扫出来——这就是为什么我们从来没见人「摆正了再扫」。

区别二:容量——条形码存「一个编号」,二维码存「一段信息」

条形码最多大概只能存 **几十个数字**(典型如商品 EAN-13 条码就是 13 位数字)。所以你扫一瓶水的条形码,看到的只是「这瓶水的厂商代码 + 商品编号」,背后还要靠后台数据库告诉你这是「农夫山泉 550ml」。

二维码能存的量大得多,**纯数字可以到 7000 多个,汉字也能装 1000 多个**,相当于塞进一整段话、一个小网页、一张电子名片都不在话下。这就是为什么付款、加好友、看菜单这些动作都能靠一个二维码完成——它自己就带得动全部信息,根本不用联网去查。

区别三:容错——条形码「一脏就废」,二维码「缺角也能读」

条形码没有任何保护机制:只要中间一道黑条被污渍盖住、被撕掉、被折皱,那一段对应的数字就丢了,机器直接报错。

二维码则天生**自带「纠错能力」**。它的方阵里专门划分了一部分区域用于存放「纠错码」——你可以把它理解成一份「备份+校验信息」,就像考试答卷时把答案誊抄了一遍,并且还留了空让你事后核对。所以二维码脏了一块、缺了一个角、甚至 logo 盖住中间一小片,通常都能照常扫出来。

flowchart LR
    subgraph A1[一维条形码]
        A1A[方向: 只能横着扫]
        A1B[容量: 几十个数字]
        A1C[容错: 脏了就废]
    end
    subgraph A2[二维码]
        A2A[方向: 360度都能扫]
        A2B[容量: 数千字符]
        A2C[容错: 缺角也能读]
    end
    A1A --- A2A
    A1B --- A2B
    A1C --- A2C
一个直观的数字

同样大小(约 2 厘米见方)的一小块地方:

密度的差距,差了不止一个数量级。

**要点:** 条形码是「横条纹+只能横扫+几十个数字+毫无保护」的线性码;二维码是「方阵+360度可扫+数千字符+自带纠错」的矩阵码——两者不只是形状不同,而是**信息组织方式**完全不同,这正是二维码能撑起今天这么多扫码场景的根本原因。

QR 码的诞生与历史

QR 码的诞生与历史

你可能觉得二维码是「互联网时代的产物」——其实它比你想象的老得多:1994 年,QR 码在日本一家汽车零件工厂的车间里诞生,最初根本没想过要让我们这些普通人扫一扫。

1994 年:丰田工厂里的痛点

QR 码的发明者是日本电装公司(Denso Wave),是丰田汽车集团旗下的零部件供应商。当时的工厂车间里,每个零件都要贴一个一维条形码,用来追踪生产批次、序列号、检验记录等。但一维条形码的问题在工厂里被放大了:

电装的工程师原昌宏(Masahiro Hara)带领团队,被要求设计一种**能在嘈杂工厂环境下快速、稳定读取更多信息**的码。1994 年,这种新的方格码诞生了,名字叫 **QR(Quick Response)码**——「快速响应」,意思就是「一扫就出结果,不用等」。

三个「工厂基因」决定了今天的扫码体验

回头看,QR 码最初为工厂做的几个设计,恰恰成了今天我们随手就能扫一扫的关键:

flowchart LR
    A1[零件反光脏污] --> B1[三个角的定位图案] --> C1[歪着倒着都能扫]
    A2[条形码容量小] --> B2[方阵式二维结构] --> C2[能装下整段网址文字]
    A3[标签破损常发生] --> B3[内置纠错码] --> C3[缺角污渍照常读]

这三个设计不是为今天移动支付准备的,是为 1994 年的丰田工厂准备的——但它们意外地让 QR 码成为了最适合普通人用手机摄像头扫的码。

1997 年:一个改变命运的决策

QR 码刚诞生时,电装主要是为自家和丰田的工厂使用。但 1997 年,电装做了一个在当时看来很不可思议的决定:**把 QR 码的技术规范向全世界公开,不收任何专利费**。

公司当时的判断是:QR 码本身不赚钱,但如果能成为全球通用标准,卖配套的扫描设备、工业读码器就能持续盈利。这个决策为 QR 码后来的爆发扫清了最大的障碍——任何公司、任何国家想用,都不需要付钱。

从工厂车间到街头巷尾

QR 码真正走入日常生活,靠的是两个时间点的叠加:

  1. **2000 年代中后期**,手机摄像头越来越普及,人们开始用手机「拍照」代替专用扫描枪
  2. **2011 年前后**,微信、支付宝先后推出「扫一扫」支付功能,把 QR 码推到了中国几亿人的日常里

到今天,付款、加好友、点餐、查核酸、领优惠券……我们的生活已经被这个 30 岁的小方块承包了大半。

**要点:** QR 码是 1994 年日本电装公司为解决汽车零件工厂的追踪难题而发明的,「QR」意为「快速响应」;1997 年电装免费公开技术规范,使它得以走向全球;它身上那些为工厂环境设计的特点(定位图案、大容量、纠错),恰好也让它成为了最适合手机扫的码。

二维码家族:QR 码的近亲们

二维码家族:QR 码的近亲们

你以为「扫一扫」扫的就都叫二维码?严格说,二维码是一个**家族**,QR 码只是这个家族里最出名的那个成员。它们的共同点是「用黑白方块在两个方向上编码信息」,但形状、容量、适用场景各有不同。

想象一下交通工具家族:轿车、卡车、摩托车、电动车都是车,但有人用来通勤、有人用来拉货、有人用来飙车。QR 码是二维码家族里的「家用轿车」——最常见、最普及,但绝不是唯一一种。

家族主要成员

**QR 码(Quick Response Code)** 方方正正的大方块,**三个角**各有一个「回」字形的大方块作为定位图案。这是最经典、你在街头巷尾扫到的几乎都是它。

**Data Matrix(数据矩阵码)** 也是方形的,但比 QR 码更「迷你」。它的定位图案是一条**沿左下两边的 L 形实线**(不是三个角的大方块),所以整张码可以做得非常小,最小能塞进 2-3 毫米见方的面积。工厂里手机芯片、电子元器件追溯用的就是它——这些元件上的标签本来就小,QR 码塞不进去。

**PDF417** 长得最不一样:**长方形**,看起来像是把好几个一维条形码**堆叠**在一起。容量特别大(可以装下几千个字符),所以美国驾照、登机牌、一些国家的身份证背面贴的都是它。你见过美国大片里机场安检时刷的那张纸片吗?那就是 PDF417,不是 QR 码。

**汉信码** 中国在 2007 年发布的**自主国家标准**。形状和 QR 码很像,但定位图案有**四个**(不只是三个角),理论上抗变形能力更强。在国内一些政务、物流场景偶有应用,但普通消费者几乎不会碰到。

一图看清区别
flowchart LR
    Q[QR 码] --> Q1[方形 三个角定位] --> Q2[支付加好友点餐]
    D[Data Matrix] --> D1[小方形 L 形定位] --> D2[电子元件追溯]
    P[PDF417] --> P1[长方形 条码堆叠] --> P2[美国登机牌驾驶证]
    H[汉信码] --> H1[方形 四个角定位] --> H2[中国国标 政务偶见]
为什么日常说的「扫一扫」特指 QR 码?

不是所有二维码都能被你的微信、支付宝扫出来。这两个 App 里「扫一扫」按钮背后的识别模块,**主要就是 QR 码解码器**。如果你拿着一张 PDF417 的登机牌去扫,弹出来的多半是「无法识别」。

再加上我们生活中 99% 的码(付款码、点餐码、加好友码、防疫码、电影票……)都是 QR 码,**「扫一扫」这个词在中文语境里几乎已经成了 QR 码的代名词**。

**要点:** 二维码是一个家族,QR 码、Data Matrix、PDF417、汉信码是其中常见的几位成员;QR 码因免费公开+设计通用,成为「扫一扫」的实际代名词,但工厂小零件、美国登机牌这些场景里,你看到的会是 QR 码的「近亲」。

学习笔记

二维码学习笔记

一、二维码在日常生活中的角色

二维码已从偶尔使用的工具变成生活的基础设施。

**典型扫码场景**:乘车码、健康码、签到码、点餐码、加好友码、收款码、取件码、门禁码、乘梯码等。共同点是把原本需要「输入」或「口头传递」的信息,变成「扫一下」即可完成。

**二维码扮演的三种角色**:

这三种角色覆盖「人-机」「人-人」「人-钱」三种基础连接,因此二维码被称为移动互联网时代最朴素的连接器。

2010 年前后扫码支付在中国开始普及;二维码是日本发明的技术,却在中国被用得最广最深。

二、一维条形码与二维码的核心区别

| 维度 | 一维条形码 | 二维码 | | --- | --- | --- | | 方向 | 只能横着扫,顺条纹方向划过 | 360 度都能扫,三个角有定位图案 | | 容量 | 最多几十个数字(如 EAN-13 共 13 位) | 纯数字 7000 多个,汉字 1000 多个 | | 容错 | 无保护机制,脏了就废 | 自带纠错码,缺角也能读 |

**直观对比**:同样约 2 厘米见方的面积,条形码能存 13 位数字(一箱可口可乐的全球编号),二维码能存几百个汉字(相当于一篇微博正文)。

两种码的根本差异是**信息组织方式**不同:条形码是「横条纹+线性编码」,二维码是「方阵+二维编码」。这是二维码能撑起今天众多扫码场景的根本原因。

三、QR 码的诞生与历史

**1994 年,丰田工厂的痛点**:日本电装公司(Denso Wave,丰田集团旗下零部件供应商)发明 QR 码,发明者为原昌宏。当时工厂用一维条形码追踪零件,遭遇四大问题:

QR 即 Quick Response(快速响应),意为「一扫就出结果,不用等」。

**三个「工厂基因」决定今天的扫码体验**:

  1. 为应对零件反光脏污→设计三个角的定位图案→歪着倒着都能扫
  2. 为突破条形码容量小→采用方阵式二维结构→可装下整段网址或文字
  3. 为应对标签破损→内置纠错码→缺角、污渍照常读

**1997 年的关键决策**:电装将 QR 码技术规范向全世界免费公开,不收专利费;判断 QR 码本身不赚钱、配套扫描设备才是利润来源。这一决策为 QR 码走向全球扫清了最大障碍。

**走入日常的两个时间点叠加**:

四、二维码家族

「二维码」是一个家族,QR 码只是其中最出名的成员,常见成员包括:

**QR 码(Quick Response Code)**

**Data Matrix(数据矩阵码)**

**PDF417**

**汉信码**

**「扫一扫」特指 QR 码的原因**:微信、支付宝的「扫一扫」模块主要就是 QR 码解码器,扫 PDF417 等其他类型会显示「无法识别」。由于生活中 99% 的码都是 QR 码,「扫一扫」在中文语境中几乎成了 QR 码的代名词。

第 2 关 · 解剖一个二维码:整体结构与各区域作用

看懂一个二维码由哪几部分构成,每个区域分别负责什么

位置探测图形:三个角的『回』字方块

你有没有过这种经历:在陌生商场里找厕所,先抬头找一面特别大的招牌墙,认准了它就知道整个商场的方向了?

二维码三个角上的「回」字方块,就扮演着这个角色——它们是扫码器在画面里最先要找的「地标」。

三个回字方块长什么样

把任何一个二维码放大看,左上角、右上角、左下角各有一个由黑白相间方块组成的图案,看起来像一个汉字「回」——外圈是 7×7 的黑色边框,里面是 5×5 的白色,再里面是 3×3 的黑色实心方块。

这三个图案大小完全一样,黑白比例固定(1:1:3:1:1),无论二维码里的数据怎么变,这三个角的样子永远不变。它们是二维码的「身份证」——只要看到这三个一模一样的方块,扫码器就知道:这是一张二维码。

为什么是三个角,不是四个

你可能会问:四个角都放上回字方块,不是定位更准吗?

其实三个就够了。三个点确定一个平面——只要在画面里找到这三个固定图案的位置和倾斜角度,扫码器就能算出整张二维码的朝向:是歪了 10 度?还是旋转了 90 度?倒着扫的?

而第四个角(与左下角对角的右下角)没有放定位图案,是因为:

这就是为什么你看到的二维码,三个角特别「重」、很显眼,第四个角相对「轻」的原因。

扫码器拿到这三个图案后做什么

整个识别过程可以简化成下面这条流水线:

flowchart LR
    A[摄像头画面] --> B[搜索三个回字方块]
    B --> C{找到三个了吗}
    C -->|否| D[继续扫描等待]
    C -->|是| E[根据三点位置计算朝向]
    E --> F[建立网格坐标系]
    F --> G[按格读取黑白数据]

最关键的一步是「根据三点位置计算朝向」——通过三点确定一个平行四边形,再把这个平行四边形「摆正」成矩形,整个二维码的方向就校正完成了。哪怕你把手机歪着拍、倒着拍、斜着拍,最后落到扫码器眼里的都是一张「正」的二维码。

正因为这三个角图案这么简单又这么显眼,无论你贴出来的海报是斜的、皱的、远到只露出一角,扫码器都能迅速把它从背景里挑出来。

**要点:** 三个角上的「回」字方块是二维码的定位地标——它们帮扫码器在画面里找到二维码、判断朝向、摆正网格,是整个识别流程的起点。

校正图形:用于大尺寸二维码的位置参考

你可能已经注意到:有些大尺寸的二维码,在三个角上的大「回」字方块之外,里面还会冒出一两个甚至更多个更小的「回」字方块。它们是干什么用的?

校正图形长什么样

校正图形(Alignment Pattern)看起来是三个角那种大「回」字方块的缩小版——5×5 的外黑边、3×3 的内白圈、最中间 1×1 的黑点。整个图案只占 5×5 个模块的大小。

它和大「回」字(7×7)不一样,结构更简单,最明显的特征是「中心有一个孤立的黑点」。

什么时候会出现

校正图形不是所有二维码都有。它从**版本 2**开始出现(也就是 25×25 模块以上的二维码),而且随着版本号增大,数量也变多:

每一个校正图形出现的位置都不是随机的,而是按标准严格规定——扫码器只要知道这是哪个版本,就能预判出所有校正图形的坐标。

为什么要多个校正点

三个角上的定位图案只够解决两件事:**找到二维码**、**判断整体朝向**。但当二维码变大、贴的表面不平整时,靠近中心的部分会被拉扯变形。

举个生活中的例子:你把一张 A4 纸平铺在桌上对得很齐,但要是把它贴在一个圆柱面上,纸的中段会被横向拉伸、两端会被压缩。如果你只在纸的四个角画上标记,中间被拉成什么样你根本算不出来——除非中间还有几个参考点。

校正图形就扮演了「中间参考点」的角色。当扫码器算出整张网格的坐标时,它会用每一个校正图形的位置来反推:这一区域的网格间距应该是多少?这一行的模块有没有被挤压或拉伸?发现偏差就立即局部校正。

flowchart LR
    A[三个大回字定位] --> B[确定整体方向]
    A --> C[建立初步网格]
    C --> D[扫描校正图形位置]
    D --> E{实际位置与预期位置一致吗}
    E -->|是| F[网格准确 继续读数据]
    E -->|否| G[局部微调网格间距]
    G --> F

正是因为有校正图形做网格「校准」,哪怕你把二维码印在易拉罐的曲面上、贴在皱巴巴的海报上、远远地斜着拍,扫码器也能把每个模块的边界算得八九不离十。

最后要注意一点:校正图形本身**不存储任何数据**,纯粹是为定位服务的。这也是为什么无论二维码里存什么内容、什么纠错等级,校正图形的位置和样子都不会变。

**要点:** 校正图形是大尺寸二维码里的「中间参考点」——它们不存数据,只负责帮扫码器在大面积、易变形的二维码中精确还原网格坐标。

定时图形:连接模块的隐形坐标线

你已经知道了:三个角的「回」字负责让扫码器找到二维码、判断朝向。但找到整张图之后,还有一个更细致的问题没解决——这张图里有几十上百个模块,每个方块到底应该落在哪个坐标上?光靠三个角的方块是算不出中间的。这一块要讲的「定时图形」就是来解决这个问题的。

定时图形长什么样

定时图形(Timing Pattern)是横纵各一条的黑白交替条纹,从一个角的「回」字延伸到另一个角的「回」字。

这条线的关键特征是**严格的黑白交替**——一个黑模块、一个白模块、一个黑模块、一个白模块……永远不会出现两个相同颜色的模块挨在一起。整条线看起来就像一条虚线,或者像尺子上的刻度。

它在哪里

定时图形的位置是固定的,**永远在第 6 行和第 6 列**(从 0 开始数是第 6 行/列,从 1 开始数是第 7 行/列)。

为什么是这个位置?三个角的「回」字每个都占 7×7 个模块。「回」字和二维码其他内容之间需要留一条边,定时图形就填在第 6 行/列这条边上的位置。

它解决什么问题

回到一开始的问题:扫码器找到二维码、确定朝向之后,怎么知道每个方块的精确边界?

flowchart LR
    A[三个角的定位图案] --> B[确定整体方向]
    B --> C[顺着定时图形追溯]
    C --> D[数黑白交替的次数]
    D --> E[算出每个模块的精确位置]
    E --> F[建立完整网格]

定时图形就是「尺子」。它横纵各一条,从角到角穿过去,黑白严格交替——这条线上的每个模块位置都是可以**直接数出来**的:起点是 0,第二个模块是 1,第三个是 2……因为颜色严格地一黑一白,扫码器不可能数错(不会出现两个黑挤在一起,导致判断不出「这一格是从哪开始的」)。

数完这条线,扫码器就知道了:

有了这个基准,再配合上一节讲的校正图形,扫码器就能把整张二维码的网格画出来,然后才准备好去读中间的数据。

它存数据吗

不存。定时图形是固定图案,不管二维码里放的是网址、文字还是名片,定时图形长得都一样。它纯粹是为「定位」服务的。

**要点:** 定时图形是位于第 6 行和第 6 列、从一个「回」字连到另一个「回」字的黑白交替条纹——它像一把尺子,让扫码器能精确数出每个模块的位置,从而建立完整的网格坐标。

数据区与纠错区:方块阵里装了什么

到目前为止,三个角的「回」字帮扫码器找到二维码并确认方向,校正图形和定时图形帮它精确画出网格。但这些都还是「准备工作」——把准备工作做完之后,扫码器其实还没看到二维码里真正装着什么。这一块要讲的「数据区」和「纠错区」,才是二维码的本体。

数据区:装的就是内容本身

中间密密麻麻的黑白方块,绝大多数是「数据区」。它们存的就是你扫码时真正要拿到的内容——一个网址、一段文字、一张名片。

内容是怎么变成方块的呢?大致分三步:

  1. 把内容转成数字(文本有编码、网址直接是字符)
  2. 数字再转成二进制——也就是一串 0 和 1
  3. 二进制按 8 位一组切成小块,每一字节对应 8 个模块:黑代表 1,白代表 0(标准固定)

所以中间那些方块,**每个方块就是一个比特**。一个 V5(37×37)的二维码,扣掉三个角的「回」字、校正图形、定时图形等保留区域,剩下的几乎全是数据区,大约能存 100 多字节,相当于几十个汉字。

纠错区:存的是「数学备份」

数据区装的是真正内容,但中间区域里**不是所有方块都是内容**——还有相当一部分是纠错码。

纠错码的思路是:内容在写入二维码之前,先用一种叫 Reed-Solomon 的算法,给原始数据算出一批「备份数字」。这些备份数字本身不是内容,但它们和原始数据之间有严格的数学关系。

怎么理解「数学关系」?可以用一个类比:

纠错码也是按 0/1 转成黑白模块,**和数据区一起填进中间区域**。

它们怎么混在一起

注意:不是「先填一整片数据区,再填一整片纠错区」。真实情况是数据和纠错码按一定规则**交错排列**。扫码器按规则去读,知道哪些字节是数据、哪些是纠错,遇到某些字节因污损读不出来时,就把剩下的纠错字节代进去反推——只要丢得不太多,就能精确算回原始内容。

flowchart LR
    A[原始内容] --> B[转成二进制]
    B --> C[Reed-Solomon 算法]
    C --> D[生成纠错码]
    B --> D
    D --> E[数据 + 纠错码<br/>按规则交错]
    E --> F[填进中间区域的模块]
    F --> G[扫码时]
    G --> H{模块是否可读}
    H -->|可读| I[直接拼接内容]
    H -->|部分不可读| J[用纠错码反推]
    I --> K[还原出原始信息]
    J --> K

为什么脏了或缺角也能扫

二维码有 4 个纠错等级,生成时由制作者选择:

纠错等级越高,纠错码占的空间越大,能存的内容就越少。朋友圈里带 logo 的彩色艺术二维码,常用 H 级——因为中间要加图,必须留出大量冗余。

这就是为什么:

**要点:** 中间密密麻麻的方块里,既有真正装内容的数据区,也有用 Reed-Solomon 算法算出的「数学备份」纠错区。两者按规则交错排列——只要丢失的模块不超过纠错等级允许的比例(最高 30%),扫码器就能用纠错码把丢失的内容精确反推回来。

格式信息与版本信息:方块阵周边的元数据

上一块讲了中间装内容的数据区和作备份的纠错区。但细想会发现:扫码器在读内容之前,还有几件**必须先知道**的事——这个二维码用了几级纠错、用了哪种掩码图案、是多大尺寸的版本。这些「二维码的身份证信息」,就存在两个专门的区域里。

格式信息:二维码的「小身份证」

把二维码看成一本书,数据区和纠错区是书的**正文**。但书有封面、有目录,告诉读者「这是什么语言写的、几月几号印的、哪个版本」。二维码也有类似的东西——**格式信息**(format information),可以理解为二维码的「小身份证」。

格式信息主要告诉扫码器两件事:

  1. **纠错等级**:L、M、Q、H 中的哪一个(上一块讲过,决定丢了多少能恢复)
  2. **掩码图案编号**:0 到 7 中的哪一种(让黑白分布更均匀的处理,下一块会讲到)

格式信息一共 15 个比特,存放在两个对称位置——**左上角「回」字的两条边**,以及**右上角、左下角「回」字之间的镜像位置**。每 15 比特存两遍,相当于「双保险」。

flowchart LR
    A[左上角回字<br/>右侧一列] --> R[扫码器]
    B[左上角回字<br/>下方一行] --> R
    C[右上角与左下角回字间<br/>的镜像位置] --> R
    R --> O[两份合并<br/>多数表决]

版本信息:尺寸的「名片」

格式信息每个二维码都有,但**版本信息**只在大尺寸二维码里出现——V7(45×45)及以上才有。

为什么?因为小二维码「长得都差不多」,一眼能数清行列;版本一多,V10(57×57)、V20(97×97)、V40(177×177),扫码器要是一开始不知道是哪个版本,就没法提前安排好「要扫多少模块、跳过哪些保留区域」。

版本信息一共 18 比特:6 比特表示版本号(V1–V40,正好 6 比特够用),剩下 12 比特是纠错码。它同样存两遍,放在两个对称位置——**左下角「回」字的上方**和**右上角「回」字的左侧**。

元数据自己也有纠错保护

这里有个值得停一下的细节:格式信息和版本信息**自己也是模块**,也会被污损、遮挡。它们凭什么保证自己被正确读出来?

答案是:它们内部也带了纠错码,用的是 BCH 算法——一种和 Reed-Solomon 同源的纠错思路:

再加上本身存两遍做多数表决,**即使有一份被部分涂掉,扫码器也能从另一份或通过纠错码反推出正确内容**。这就好比书里最重要的「扉页」单独又用硬壳精装了一遍——因为太关键了,必须多穿几层防护。

为什么这是「先决条件」

用生活场景打个比方:你在陌生车站看站牌,第一反应是看**语言标识**——中文、英文、还是日文?只有先确认语言,才能读懂。格式信息和版本信息就扮演这个角色:

任何一个缺失,整个二维码都解不开。**所以这两个小区域,是解码能开始的入场券。**

**要点:** 二维码方块阵周边有专门的「元数据区」——格式信息记录纠错等级和掩码编号(15 比特、所有版本都有、存两遍),版本信息记录尺寸版本(18 比特、V7 及以上才有、存两遍)。这些元数据本身也带了 BCH 纠错保护,因为一旦它们出错,整个二维码都解不开。

静默区:二维码四周的空白边距

静默区:二维码四周的空白边距

到这一节为止,我们已经讲完二维码方块阵内部的全部内容——三个角的「回」字定位图案、散布的「小回字」校正图案、横纵交替的定时坐标线、中间装内容和纠错码的数据区、再加上记录纠错等级与版本号的元数据小区域。

但你把目光拉远一点看整张图,会发现**二维码方块阵并不是直接贴着画布边缘的**。它的四周还留着一圈明显的白色空白——这圈空白就是**静默区**(quiet zone),也叫「空白边距」或「隔离区」。

静默区到底是什么

静默区不是二维码的一部分,而是二维码**外面**的留白。

按照官方标准,这圈空白至少要 **4 个模块宽**(1 个模块就是 1 个最小黑白方块的边长)。也就是说:如果你看到一个版本 1 的二维码(21×21),那它四周还要再各留 4 个方块那么宽的白边,整个白边宽度差不多是方块阵边长的 20%。

flowchart LR
    A[画布边缘] --> B[静默区<br/>4 个模块宽白边]
    B --> C[二维码方块阵<br/>包含定位 校正 定时 数据 元数据]
    C --> D[静默区<br/>4 个模块宽白边]
    D --> E[画布边缘]

静默区为什么必须存在

扫码器第一步要做的事叫**图像定位**——它在摄像头拍到的画面里,先要找到「二维码大致在哪里、占多大、是什么方向」。这一步靠的是识别那三个角的「回」字图案。

但识别算法有个前提:它需要从一片**明显单一**的背景里,把「回」字图案的边缘「拎」出来。如果二维码紧贴着文字、贴着其他图案、贴着边框,算法就会困惑——哪个是二维码、哪个是背景、哪里是边缘?

静默区的作用就是**告诉扫码器:「二维码从这里开始」**。

打个比方:你在剧场的观众席上找舞台,并不需要舞台上的灯光告诉你——**舞台和观众席之间那片空地本身就在告诉你「表演的边界在这里」**。静默区就是二维码和周围环境之间的那片「空地」。

实际场景里常见的踩坑

理解了原理之后,很多日常踩坑就有了清晰解释:

反过来,**只要静默区留够,再脏、缺角、甚至部分被涂黑,只要不破坏三个角的「回」字和整体内容,扫码器依然能正确识别**——这一点我们前面讲过的纠错码帮了大忙,但静默区是这一切能发生的前提。

一个反直觉的小细节

你可能会想:那我把静默区留得**特别宽**呢?比如包了 10 个模块的白边,会不会扫不出来?

不会。**静默区比标准宽,是完全没有问题的**,扫码器仍然能正确识别。它害怕的是「窄」或「没有」,而不是「宽」。

这也是为什么很多印刷品里的二维码看起来白边异常宽——设计师往往是出于美观考虑留了更多空白,结果反而让扫码更稳。

**要点:** 静默区是二维码方块阵四周的纯白边距(标准至少 4 个模块宽),它本身不存任何信息,只负责「告诉扫码器二维码从哪里开始」。没有静默区或静默区被周围图案侵占,再好的二维码也扫不出来;但留得比标准更宽完全没问题。

学习笔记

位置探测图形

二维码左上、右上、左下三个角各有一个「回」字方块:外圈 7×7 黑色边框、内圈 5×5 白色、中心 3×3 黑色实心方块。黑白比例为 1:1:3:1:1,三个图案大小完全相同,无论二维码里的数据怎么变,这三个角的样子永远不变。

三个角而不是四个的原因:

扫码流程:摄像头画面 → 搜索三个回字方块 → 找到则根据三点位置计算朝向 → 建立网格坐标系 → 按格读取黑白数据。最关键一步是「根据三点位置计算朝向」——通过三点确定平行四边形再「摆正」成矩形。

校正图形

形似大「回」方块的缩小版:5×5 外黑边、3×3 内白圈、最中间 1×1 黑点。**中心有一个孤立黑点**是与定位图案最显著的区别。

仅在版本 2(25×25)及以上出现,数量随版本增大而增多:

每一个校正图形的位置按标准严格规定,扫码器能预判所有坐标。校正图形**不存任何数据**,只负责在大面积、容易变形的二维码中提供中间参考点,让扫码器反推每个区域的网格间距,局部校正被挤压或拉伸的模块。

定时图形

横纵各一条、严格黑白交替的条纹:

位置固定在**第 6 行和第 6 列**(从 0 开始数)。黑白严格一比一交替,不会出现两个同色模块相邻。

功能相当于一把「尺子」:扫码器沿这条线数黑白交替次数,即可确定每行/列的模块总数(也就是版本号)与每个模块的物理像素宽度。**不存数据**,无论二维码里放什么内容,定时图形长得都一样。

数据区与纠错区

数据区装的是扫码真正要拿到的内容(网址、文字、名片)。内容→二进制→按 8 位分组→每字节对应 8 个模块(黑=1,白=0),**每个方块就是一个比特**。

纠错区用 Reed-Solomon 算法在写入前为原始数据算一批「备份数字」:把信息重新组装成一组数字,任一部分丢失,剩余部分仍可反推全部。纠错码也按黑白模块填入。

数据与纠错码按规则**交错排列**,而非分区独立存放。扫码时按规则区分各字节用途,遇到污损无法读出的字节用纠错码反推。

四个纠错等级(生成时选择):

等级越高,纠错码占比越大,能存内容越少。

格式信息与版本信息

格式信息(共 15 比特)告诉扫码器两件事:

  1. 纠错等级(L/M/Q/H)
  2. 掩码图案编号(0–7)

存放在左上角「回」字的两条边及右上、左下角「回」字之间的镜像位置,**存两遍**做多数表决。

版本信息(18 比特)只在版本 7(45×45)及以上出现:

存放在左下角「回」字上方与右上角「回」字左侧的对称位置,同样存两遍。

两者自身也带 BCH 纠错保护:格式信息 5 比特内容+10 比特纠错;版本信息 6 比特+12 比特纠错。**这些元数据是解码的入场券**——少了它们,纠错等级、掩码、版本、保留区位置全无法判断,整个二维码都解不开。

二维码方块阵外部四周的白色空白边距

二维码方块阵**外部**四周的白色空白边距,按标准至少 4 个模块宽(1 模块=1 个最小黑白方块的边长)。它不是二维码的一部分,而是为扫码器提供的隔离带。版本 1(21×21)的二维码四周各留 4 个模块宽的白边,整个白边宽度约为方块阵边长的 20%。

第 3 关 · 信息如何变成黑白方块:编码的大致流程

理解一段文字或网址是如何一步步『翻译』成二维码里的黑白方块的

起点:要被编码的原始内容

想象一下:你面前有一个固定大小的盒子,最多能装 7000 多个纯数字(或者更少的汉字)。这就是二维码这个「容器」的容量上限。在讨论「怎么把这些东西变成黑白方块」之前,我们先得搞清楚一件事——**到底什么东西能往里装、什么东西装不进去**。

二维码只能装「数字化的文字」

二维码本质上只能装一种东西:**可以用字符表达的内容**。具体来说包括:

这些内容的共同点是——它们都可以被写成「一串字符」。不管原本是中文、英文还是数字,最终在计算机里都是数字和字母的组合。

图片、视频、音频装不进去

但有些东西,**二维码装不下**:

为什么装不下?两个原因:

  1. **太大**:一张普通手机照片就有 2-5 MB,一段手机视频动辄几十上百 MB,而整个二维码最多只能装几千个字符
  2. **不是字符**:照片、视频的原始数据是像素点、音频是采样波形,不是「文字」

那为什么扫码能看到视频?

你可能会问:商场里那些「扫码看视频」「扫码听歌」「扫码看菜单」的海报,二维码里不也装着视频吗?

这里有个**关键的小心机**:海报上的二维码里装的不是视频本身,而是一个**网址链接**——比如 `https://shop.example.com/video/123`。手机扫码识别出这个网址后,自动用浏览器打开它,再从服务器上把视频下载下来播放。

flowchart LR
    A[要装进二维码的内容] --> B{能不能写成字符}
    B -->|能| C[可以直接装]
    C --> D[纯数字]
    C --> E[英文字母]
    C --> F[中文汉字]
    C --> G[网址]
    B -->|不能| H[不能直接装]
    H --> I[照片]
    H --> J[视频]
    H --> K[音频]
    H --> L[只能装一个指向它们的地址]

这就像你家小区门口的信箱格:信箱本身只能装「纸条」——纸条上写着「快递在楼下菜鸟驿站」。你要取的东西不在信箱里,信箱只负责告诉你「去哪取」。

二维码装的是「地址」,不是「东西」

再举几个你可能没注意过的例子:

所以准确地说,二维码是一个**「文字容器」**——它把原本要输入或口述的字符,变成一秒钟就能扫出来的图形。要装的「内容」必须是「可以写成字符」的东西;装不下的,就用一个**指向它的地址**代替。

**要点:** 二维码只能装文字、数字、网址这类「字符内容」;图片、视频、音频装不下,只能装一个指向它们的网址。

编码模式:选择合适的翻译规则

编码模式:选择合适的翻译规则

想象一下你要把一批东西装进旅行箱:装书和装衣服的叠法不一样;装易碎品和装不怕压的堆法也不一样。**二维码也是这样——不同类型的内容,用不同的「翻译规则」能装得更紧凑**。

这种「针对内容类型挑选规则」的思想,就叫做**编码模式**。

为什么需要分模式?

二维码的空间是有限的(最常见的版本最多也就装几千个字符),所以要尽量省着点用。**字符越少,能装的二进制位就越短**。如果不管三七二十一,所有内容都用同一种最「通用」的规则翻译,那英语字母、中文汉字、纯数字全都得占用相同长度——太浪费了。

于是二维码设计了**几种模式**,让你根据内容挑最划算的那种。

三种最常见的模式

**1. 数字模式(Numeric Mode)**

**2. 字母数字模式(Alphanumeric Mode)**

**3. 字节模式(Byte Mode)**

flowchart LR
    A[原始内容] --> B{看内容类型}
    B -->|纯数字| C[数字模式]
    B -->|大写英文加数字加符号| D[字母数字模式]
    B -->|中文等其他字符| E[字节模式]
一个具体例子:不同模式占的「位」差多少

假设要编码「12345」这 5 个字符:

可见选对模式,能省下将近一半的空间。

再比如「您好」这两个汉字:字母数字模式根本装不下(字符集里没有中文),只能走字节模式,2 × 8 = 16 位;而同样的 16 位用字节模式装纯英文「hi」都能装 2 个字符了。

模式是怎么「声明」的

你可能会问:扫码端怎么知道这段 0/1 该按哪种规则解?答案是在所有数据前面,会有 4 位的「模式指示符」——像给这批内容贴个标签:「接下来这串请用数字模式翻译」「接下来这串请用字节模式翻译」。扫码端看到标签,就知道用什么规则反向还原。

**要点:** 二维码有数字、字母数字、字节等多种编码模式,针对内容挑最合适的模式能让二进制序列更短、更省空间;模式本身会在数据开头用一个 4 位的「标签」告诉扫码端按哪种规则还原。

位流到模块:把 0/1 摆成黑白方块

把脸凑近手机屏幕上的二维码看——你会发现它并不是一张整张的图,而是由**几百个、几千个小方块**拼成的:每个小方块要么全黑、要么全白,没有第三种颜色。

这些小方块在扫码端的术语里叫做**模块(module)**——可以理解成二维码的「最小积木」。整个二维码就是由这些积木拼成的一张方阵图。

一长串 0/1 等着落座

上一节讲到,编码模式会把文字或网址翻译成一长串 0 和 1。这串 0/1 现在就像一长串珠子,**接下来要做的事,就是把每颗珠子(一个 bit)落进一个小方块**:1 落进去是黑块,0 落进去是白块。

(约定俗成:1 对应黑、0 对应白,但反一反也不影响理解。)

方阵有多大?

二维码有一个**版本**的概念:

装的内容越多、版本越高,方阵就越大。

落座顺序:蛇形走位

0/1 是按什么顺序填进方阵的?不是横着扫、也不是竖着扫,而是**蛇形(zigzag)走位**:

flowchart LR
    A[右下角起步] --> B[向上两列两列填]
    B --> C[到顶向左移两列]
    C --> D[向下两列两列填]
    D --> E[到底再向左移两列]
    E --> B

蛇形的好处是:不管二维码怎么旋转,扫码端只要找到定位用的固定标志(比如三个角的大方框),就能从右下角开始反着解出整条 0/1 序列。

不是所有方块都给内容用

你看到的二维码里,**三个角的大方框、中间一条条横竖的虚线、零散的小方块**——这些位置并不是给 0/1 序列用的。它们是**功能性图案**,是给扫码端用来定位和校准的「保留席」。

所以 0/1 序列真正能落座的,是**扣掉这些保留席之后**剩下的区域。打个比方:电影票上的「保留席」不卖观众,观众只能坐剩下的位置。

一个具体例子

假设一段 16 位的 0/1:`10011010 | 01101001`,要摆到 4×4 方阵里(现实比这大得多,只是把原理缩小):

最终这 16 个方块按某种深浅交错排出来——每个方块对应原文里的一个 bit,组合起来就是原始内容的视觉投影。

提一句

你扫二维码扫多了可能会发现:偶尔会出现大块连片同色,或者奇怪的「棋盘格」「横条纹」——这些花纹容易让扫码端看花眼。怎么解决?下一节「数据掩码」会讲。

**要点:** 二维码每一个 0 或 1 都有自己对应的「小方块座位」(叫模块);扫码端从右下角开始按蛇形路径一个一个填进方阵,1 涂黑、0 涂白,填完就得到一张我们看到的黑白方块图。

数据掩码:让花纹更易识别

上一节我们把 0/1 序列按蛇形摆进了方阵里——理论上这就够了。但实际操作时会遇到一个头疼的问题。

问题:摆完之后可能「不好看」

假设内容碰巧就是 `0000000000` 这种全 0,或者 `1010101010` 这种规律横纹。直接按位摆进方阵会出现什么?

可以理解成:你画了一幅画,但不小心把整片天空涂成了一模一样的蓝色——别人想看清你画的是什么,反而被这大片同色「晃」到了眼。

flowchart LR
    A[原始 0/1 序列] --> B[按位摆到方阵]
    B --> C{出现大块同色?}
    C -->|是| D[扫码端难以识别]
    C -->|否| E[正常可读]
解决方案:套一层「掩码模板」

为了让最终图样「花得更均匀」,二维码规范定义了 **8 种掩码模板**,每一种都是一张和二维码同样大小的固定 0/1 图案。

具体做法是**异或(XOR)**——一种「相同就变 0、不同就变 1」的翻转规则:

简单说:**掩码是黑的格子把原图翻色,掩码是白的格子原样保留**。一层「打散花纹的筛子」就套上去了。

flowchart LR
    A[原始方块图] --> C[XOR 异或运算]
    B[选中的掩码模板] --> C
    C --> D[最终方块图:花纹更均匀]
八种模板,哪种最好?

8 种掩码模板的图案是规范里固定写死的(每种用一行数学公式就能描述出来),生成二维码时会**挨个试一遍**:

  1. 把原方块图和模板 0 异或 → 算出评分
  2. 把原方块图和模板 1 异或 → 算出评分
  3. …… 一直试到模板 7
  4. 选**评分最低**的那一个

评分依据是几条「惩罚规则」,大意是:

可以理解成:扫码端有一份「最讨厌看到的图案」清单,每种模板都拿去对照一遍打分,**最让人不讨厌**的那一个胜出。

选完之后,还要告诉扫码端

既然从 8 种里挑了 1 种用,最终图里**必须告诉扫码端用的是哪一个**——否则扫码端拿到图也没法反推出原始 0/1。

所以二维码的固定功能区里,**有 3 个小块专门用来记录「我用的是几号掩码」**(用 3 bit 表示 0-7)。扫码端扫到图后先读这 3 个小块,知道了掩码编号,再把图和对应掩码再异或一次,就能还原出原始 0/1 序列。

一个直观的对比

想象一段纯 0 序列(比如 16 个 0),直接摆进 4×4 方阵会得到一整片白块。套上掩码后——白块里有些位置被翻成了黑,**整张图变成了均匀分布的散点花纹**,扫码端一扫就能找到方块边界、读出 0/1。

**要点:** 摆好方块之后,二维码会用 8 种固定掩码模板之一(用 XOR 翻转)去「打散」大块同色区域,让最终图样更易识别;生成时会按惩罚规则挑评分最低的那一种,并把「用了几号掩码」记在二维码里,扫码端据此反推原始 0/1。

纠错码:编码流程中的备份步骤

你有没有过这种经历:传纸条给同桌,关键字被墨水糊掉一半,但靠上下文能猜出来?纠错码的思路跟这个很像——主信息被弄脏了一部分,凭借「额外的备份信息」还能把原文还原回来。

为什么需要纠错码?

二维码和纸条一样,会被磨损:

如果方块图只是「原原本本」的内容,缺一块就读不出来。生成二维码时必须**提前预留**应对这些情况的能力——这就是纠错码的作用。

纠错码是什么?

可以把它想成「**原文的备份线索**」:

扫码端读出图后,先用纠错码反推哪些 0/1 出了错,**修正或重建**那些丢失的部分,再还原出原始网址。

打个比方

想象你给朋友寄一封手写信,但信封可能被雨淋湿:

纠错码就是这种「提示词」的角色——**它本身不携带新信息,但能让丢失的部分被反推出来**。

四种纠错等级

二维码规范规定了**四个纠错等级**,从弱到强:

**等级越高,纠错码占的方块越多,留给主信息的位置就越少**——所以同一尺寸的二维码:

可以这样理解:你准备背一段课文。

纠错等级越高,分配给「提示词」的位置就越多。

编码流程里的位置

到这里,整个编码流程就完整了:

flowchart LR
    A[原始内容] --> B[选编码模式]
    B --> C[变成 0/1 序列]
    C --> D[追加纠错码]
    D --> E[摆成方阵]
    E --> F[套掩码]
    F --> G[最终二维码]

纠错码是**主信息编码完成、摆进方阵之前**追加的——和主信息一起被摆进方块,又一起被掩码处理。

**要点:** 纠错码是编码流程里的「备份步骤」——在主信息之后追加一批根据主信息算出的额外 0/1,扫码端据此反推并修复丢失的部分;规范规定了 L/M/Q/H 四种等级,等级越高越耐脏,但能装的主信息也越少。

学习笔记

二维码编码流程笔记

二维码的容量与能装的内容

三种编码模式:让 0/1 序列更短

位流落座:把 0/1 摆成黑白方块

数据掩码:用 8 种模板打散花纹

纠错码:给主信息加备份线索

完整编码流程

原始内容 → 选编码模式 → 变成 0/1 序列 → 追加纠错码 → 按蛇形走位摆成方阵 → 套掩码(XOR)→ 最终二维码。

第 4 关 · 为什么脏了缺角也能扫:纠错码的基本概念

明白二维码的容错能力来自哪里,能区分四个纠错级别

冗余思想:纠错码的直觉来源

冗余思想:纠错码的直觉来源

想象一个场景:朋友让你在小区的公告栏上抄一条通知——比如周六下午三点在 B 栋一楼开业主大会。公告栏露天下雨会被雨淋、被人撕角、被涂鸦都可能发生。

**最朴素的做法:把同一句话多抄几遍。**

比如你在公告栏上写三遍「周六下午三点 B 栋一楼」。就算下大雨冲糊了一遍,或者有人撕掉一个角,只要另外两遍还看得清,消息就保住了。这个「多说几遍」的做法,核心思想就是**冗余**——冗余的意思是「超出必需的那一份」。本来抄一遍就够的信息,你多放了同样的几份作为备份。

二维码的容错能力,最底层就来自这个思想。

**为什么「多说几遍」还不够聪明?**

把所有信息都重复多份太浪费空间了。公告栏就这么大点地方,你抄三遍同样的话,本来可以再放三条别的通知的位置就被挤掉了。

更聪明的做法是:不是把原话原封不动多抄几遍,而是**用原话「算」出一些「陪练」信息**——这些陪练信息跟原话有数学上的关联,但占的空间更小。

举个例子,老师批改作业时不只看你答案对不对,还会看你列的算式。如果你只写「答案=42」,老师会怀疑你是不是抄的;但你把「算式过程」也写出来,老师就更容易判断你是不是真懂。这「算式过程」不重复答案本身,但能从它反推答案是否正确——这就是一种更聪明的「陪练信息」。

二维码用的纠错码就是这种更聪明的「陪练信息」:根据原始数据,用数学方法「算」出一些附加方块。这些方块本身不重复原始内容,但跟原始内容绑在一起。哪怕原始数据被雨淋、缺角了一部分,只要这些陪练方块还在一部分,扫码器就能反推出原始内容应该是什么。

整个流程可以这样看:

flowchart LR
    A[原始数据] --> B[算出纠错码]
    A --> C[组合成二维码方块]
    B --> C
    C --> D[部分方块丢失]
    D --> E[用纠错码反推原始数据]

**核心结论:**

二维码的容错不是靠「把方块多放几份」这种笨办法,而是靠「用数学方法算出来的关联备份」——业内叫「纠错码」。具体怎么算的我们后面再展开,但这一节你要先建立一个心智模型:

> 原始数据 + 纠错码 一起塞进二维码 → 局部丢失 → 扫码器用剩下的纠错码反推原始数据。

**要点:** 纠错码的底层思想是冗余——多放一些跟原始信息相关联的「陪练」内容,让丢失的部分可以被反推回来;不是简单重复,而是数学关联。

里德-所罗门码:纠错码的工作原理(概念版)

里德-所罗门码:纠错码的工作原理(概念版)

上一节我们说,二维码的「陪练信息」是「用原话算出来的数学关联」。但具体是怎么算的?算出来之后,怎么就能「反推」回被弄丢的部分?这就是里德-所罗门码(Reed-Solomon Code)要解决的问题——它是二维码实际使用的纠错码方案。

从一个更简单的例子入手:校验位

你有没有注意过,身份证最后一位、银行账号的某几位,是「算出来的」而不是随便填的?比如,把身份证前面 17 位数字按一定规则加权求和,再除以 11,得到的余数对应的数字就是第 18 位。这最后一位本身不是身份信息,但和前面 17 位绑得死死的——你前面填错一位,这一位就一定对不上。

这就是「聪明的冗余」的最简单形态:多加一位,它不是原信息的重复,但能帮你**发现**前面有没有错。

但校验位只能告诉你「哪里出错了」,还不能直接告诉你「正确的是什么」。而且它一般只能扛一两个错误,遇到大面积缺角就抓瞎了。

里德-所罗门码的升级思路

里德-所罗门码干的是同一件事,但做得更狠:

整个过程可以这样看:

flowchart LR
    A[原始数据] --> B[里德-所罗门编码<br/>算出多份陪练数据]
    B --> C[原始加陪练<br/>一起塞进二维码]
    C --> D[部分方块丢失]
    D --> E[用剩余方块反推]
    E --> F[定位丢失位置]
    F --> G[算出丢失内容]
    G --> H[恢复完整原始数据]
一个生活化的类比

想象 5 个朋友约好去某个地方会合,出发前大家约定:

  1. 每个人记一下自己的名字;
  2. 大家再约定一个「暗号计算规则」:比如「我们 5 人名字首字母拼起来」是个特定词。

到了现场,如果其中 2 个人被大风吹走了(缺角),剩下 3 个人只要还记得这个「暗号」,就能反推那 2 个人的名字应该是什么——因为「暗号」是 5 个人的名字共同决定的,反过来也能定出每个位置应该是谁。

里德-所罗门码做的就是这件事的数学版。「暗号」不止一个,而是好几个互相独立的暗号;「名字」也不是字面名字,而是数据片段。哪怕丢了相当一部分,暗号还在,原始信息就能被算回来。

**要点:** 里德-所罗门码的核心是「算出来的多份关联数据」——它能定位丢失的位置,也能反推出原文内容;和单校验位比,它能扛的错更多、更智能,是二维码能抗脏抗缺角的真正功臣。

四个纠错等级:L、M、Q、H

四个纠错等级:L、M、Q、H

上一节我们讲了里德-所罗门码的工作哲学:多算几份关联数据,既能定位丢失位置,也能反推原文。但——多算几份,到底应该算几份?QR 码规范没有一刀切,而是提供了**四档选择**,让你按场景挑。这四档就是 L、M、Q、H(取自 Low、Medium、Quartile、High 的首字母),对应不同的「陪练数据」数量。

四个等级的具体能力

| 等级 | 名字 | 容错率(约) | 实际意义 | |------|------|--------------|----------| | L | Low 低 | 7% | 丢失不超过约 7% 的方块,仍能扫出来 | | M | Medium 中 | 15% | 丢失不超过约 15% 的方块,仍能扫出来 | | Q | Quartile 四分之一 | 25% | 丢失不超过约 25% 的方块,仍能扫出来 | | H | High 高 | 30% | 丢失不超过约 30% 的方块,仍能扫出来 |

注意:7%、15%、25%、30% 是「约数」,规范里写的更精确一点(7.5%、15%、25%、30% 左右),但业界普遍用这一组整数,方便记忆。

「30%」是什么概念?打个比方:一个 10cm × 10cm 的二维码,中央被贴了一个 3cm × 3cm 的 logo,它依然能扫——这就是 H 等级的实力。

怎么直观理解四个档位的差异?

用你工作中的备份思维来打比方:

备份越完善,能抗的风险就越大——但占用的「资源」(这里指二维码的方块)也越多。这条「容错能力 vs 占地方」的取舍,下一节会专门讲。

实际场景里,谁在用哪个档?

这些地方「能不能扫出来」比「能不能多塞点字」更重要,宁可牺牲容量也要选 H。

flowchart LR
    L[L 级<br/>约 7% 容错]
    M[M 级<br/>约 15% 容错]
    Q[Q 级<br/>约 25% 容错]
    H[H 级<br/>约 30% 容错]
    L --> M --> Q --> H

**要点:** QR 码的纠错强度分 L、M、Q、H 四档,分别能扛约 7%、15%、25%、30% 的损坏;选哪一档,是在「抗损能力」和「能装多少信息」之间做权衡。

纠错等级与容量的取舍

纠错等级与容量的取舍

上一节我们说了 L、M、Q、H 四档:H 档最强,能扛约 30% 损坏。但——等等,那为什么不全用 H 级?答案就是「好东西是要付代价的」。

为什么不直接全选 H 级?

用你最熟悉的场景来打比方:你有一个固定大小的行李箱,要装易碎品去托运。

QR 码的「泡泡纸」就是上一节讲的纠错陪练数据。它们也要占方块——方块总数是固定的(25×25 就是 625 个模块),纠错陪练数据一档一档变多,**留给「真正内容」的方块就一档一档变少**。这就是同一段内容在高纠错等级下「方块需求变多」的真正原因:不是内容变长了,而是陪练数据挤占了内容原本的位置。

具体数字:版本 2(25×25)的例子

拿一个最常见的尺寸 25×25 的二维码举例,里面能装的字母数字字符数大约是:

| 纠错等级 | 大约能装多少 | |----------|--------------| | L | 约 47 个 | | M | 约 38 个 | | Q | 约 29 个 | | H | 约 20 个 |

(数据来源:ISO 18004 标准,版本 2、字母数字模式。)

同一张图,从 L 升到 H,能装的内容**差不多腰斩**——47 个掉到 20 个,剩下不到一半。这就是「容错」的价格。

那到底怎么选?

记住一个原则:**先想「会坏到什么程度」,再想「要装多少东西」,最后才决定选哪档**。

几个常见判断:

还有一个隐藏选项

如果你发现 H 级装不下你想放的内容,**不是只能选 H**。还有一条路:换成更大的二维码(升一个「版本」)。从 25×25 升到 29×29,总方块多了,每个等级装的内容也就都多了一些。

flowchart LR
    A[先评估环境] --> B{会坏吗?}
    B -- 会很坏 --> C[H 级]
    B -- 一般 --> D[Q 或 M]
    B -- 几乎不坏 --> E[L 级]
    C --> F{装得下吗?}
    D --> F
    E --> F
    F -- 装得下 --> G[定稿]
    F -- 装不下 --> H[升一档版本<br/>换更大的图]

**要点:** 纠错等级越高,陪练数据挤占的方块越多,固定大小的二维码能装的内容就越少;选哪档本质是在「抗损能力」和「信息容量」之间做权衡——而不是「越高级越好」。

动手玩一玩:贴 Logo、画花、撕角看扫码

动手玩一玩:贴 Logo、画花、撕角看扫码

前面四节我们聊了「为什么」「是什么」「怎么选」。这一节,我们关上书,去**扫一扫**——光看不练,等于没学。

准备一张「耐造的」二维码

先去网上找一个「二维码在线生成器」(百度搜一下一大堆),输入你公众号的链接、一段话,或者任意网址作为内容。

**关键一步:把纠错等级调到 H**。原因你现在已经知道了:我们要主动破坏它,必须用最高档才扛得住。

生成出来,打印在 A4 纸上——纸越大越好,方便后面动手「糟蹋」它。

五轮「糟蹋」实验

打开手机相机或微信扫一扫,对准这张二维码。**先扫一下确认能扫出来**,这是你的「基线」。

然后开始动手:

**第 1 关:贴 Logo** 在二维码正中央贴一块不透明贴纸,或者用马克笔涂黑一小片。 扫一下——还能扫出来吗?(H 级能扛,但你应该能感觉到识别稍微变慢了一点。)

**第 2 关:画一朵花** 用细马克笔在二维码上随便画几笔,模拟「有人在上面签字、画花」的场景。 ⚠️ 注意:别把**三个角的定位方框**涂掉——那是定位用的,坏了就找不着北了。 扫一下——能扫吗?

**第 3 关:撕一个角** 沿着对角线把二维码的一个角撕掉一小块(大约 10%)。 扫一下——还行?

**第 4 关:撕两个角** 再撕掉一个相对的角。这时候大概有 20% 的方块没了。 扫——还能?恭喜,H 级真的扛得住。

**第 5 关:涂黑定位方框** 把左上角那个大方框用记号笔涂黑。 扫一下——扫不出来了。 为什么?因为定位图案本身不算「内容数据」,不在纠错的保护范围里。它一坏,扫描器根本「找不到这张图在哪」。

这个实验告诉你的三件事

flowchart TD
    A[动手实验] --> B[Logo 能贴]
    A --> C[涂抹能扫]
    A --> D[缺角能扫]
    A --> E[定位方框坏了就废]
    B --> F[纠错码能识别'遮住的是什么']
    C --> F
    D --> F
    E --> G[定位图案是基础设施<br/>不是纠错能救的]
  1. **纠错是真实的**:你亲手贴上去的黑色方块,扫描器真的能「猜出来」原来是什么。这不是玄学,是前面说的里德-所罗门码在算。
  2. **但纠错不是万能的**:三个角的定位方框是「地图」,地图没了,再聪明的纠错也找不到路。
  3. **H 级的 30% 是怎么来的**:你撕两个角还能扫出来,差不多就是这个数。前面那张表的数字不是拍脑袋——是你亲手上撕出来的。

给运营同学的一句话

下次老板让你「把二维码做漂亮点,中间加上 logo」——你不用再问「这能行吗」,直接选 H 级,然后告诉老板:「放心,logo 最多能占 30%,我有数。」

**要点:** 纠错等级不是抽象参数,是你能亲眼「撕出来」的数字;但定位图案是另一个故事——它是寻路的地图,纠错码管不到它。

学习笔记

二维码纠错码核心要点

一、纠错码的底层思想:冗余

二、里德-所罗门码的工作原理

三、四个纠错等级 L、M、Q、H

| 等级 | 名称 | 容错率(约) | |------|------|--------------| | L | Low 低 | 7% | | M | Medium 中 | 15% | | Q | Quartile 四分之一 | 25% | | H | High 高 | 30% |

四、纠错等级与容量的取舍

| 纠错等级 | 大约可装字符数 | |----------|----------------| | L | 约 47 个 | | M | 约 38 个 | | Q | 约 29 个 | | H | 约 20 个 |

五、动手实验验证

  1. 中央贴 Logo 或涂黑一小片 → 仍能扫(识别略慢)。
  2. 细马克笔涂画几笔(避开三个角定位方框)→ 仍能扫。
  3. 沿对角线撕掉一个角(约 10%)→ 仍能扫。
  4. 再撕一个相对的角(约 20%)→ 仍能扫。
  5. 涂黑左上角定位方框 → 扫不出。

第 5 关 · 手机是怎么读懂二维码的:扫描识别的基本流程

知道一次扫码动作背后大致发生了什么

识别定位:扫码如何找到二维码

识别定位:扫码如何找到二维码

想象你在一个拥挤的火车站接人——背景里有上百张脸、行李箱、广播大屏、咖啡杯,但你只用扫一眼就能认出你朋友。为什么?因为你认得他脸上那几个「标志特征」:发际线的轮廓、眼镜的形状、下巴的弧度。扫码 App 从一张复杂的画面里找到二维码的过程,几乎就是这个「在人海中找朋友」的过程。

你打开手机相机,对准一张贴在公告栏上的海报。海报上可能有大段文字、商标、底纹、装饰图案,旁边可能还贴着别的通知、过期的宣传单、别人随手写的电话号码。摄像头的画面里,真正的二维码也许只占四分之一,剩下的全是干扰。扫码 App 面对的**第一个问题**不是「读数据」,而是「先把二维码从这一堆东西里认出来、框出来」。

它怎么找?靠的是上一模块学过的、三个角上永远不变的「回」字方块——位置探测图形。这个图形之所以被设计成三个角而不是四个、而且是「外黑 7×7、中白 5×5、内黑 3×3」这个特定结构,**首要目的**就是让扫码器在混乱的画面里能快速锁定它。

具体怎么扫?大致是这样一条流水线:

flowchart LR
  A[摄像头采集画面] --> B[转灰度图] --> C[二值化黑白] --> D[逐行扫描找 1:1:3:1:1 比例] --> E[定位到三个角的回字方块] --> F[算出位置大小和朝向] --> G[把二维码区域框出来]

每一步展开看:

**第一步到第三步:把画面简化成黑白图。** 摄像头拍下的是一张几百万像素的彩色照片,但颜色对二维码完全没用——二维码只有黑和白。所以 App 先把彩色图转成灰度图(每个像素的 R、G、B 按公式合成一个 0–255 的亮度值),再二值化:比某个亮度阈值暗的标黑,比阈值亮的标白。这一步把「彩色明暗图」砍成「纯黑白图」,每个像素就一个 0 或 1。这一步砍掉了颜色和灰度两个维度,画面里就只剩下「黑和白」两种信息。

**第四步:逐行扫描找 1:1:3:1:1 比例。** 这是整套识别里最关键的一步。App 从画面第一行像素开始,一行一行地扫,每扫到一段连续的黑白交替,就量一下每段黑色、白色区域的宽度。如果黑-白-黑-白-黑这五段的宽度比正好是 1:1:3:1:1(中间那段最宽,恰好是两侧的两倍多),那就几乎可以确认:扫到了定位图案的某一行。

为什么这种比例这么好用?因为它太精确了——背景里的文字、装饰、底纹、自然纹理,几乎不可能恰好呈现 1:1:3:1:1 这种严格的五段比例。所以扫到这种比例,就**几乎可以排除**背景干扰;反过来,如果连续多行都扫到这种比例、而且这些行的水平位置也一致,就能确定一个完整的「回」字方块被找到了。

**第五步到第七步:三个一起找,定位二维码。** 三个角各跑一遍同样的扫描,三个「回」字方块都被找到。三个点齐了之后,App 就拿到了三个关键信息:

到这一步为止,App 已经从「一张海报的照片」变成了「一个被框出来的、方向已知的二维码区域」。读取 0/1 数据、纠错、还原内容,是后面几步的事。

为什么这种设计这么有效?回到开头的类比:你能在火车站一眼认出朋友,是因为他脸上有**几个同时满足**的特征——独特、稳定、不容易变。定位图案也一样:

**要点:** 扫码 App 在一张复杂的画面里,第一步不是读数据,而是靠三个角的「回」字方块(1:1:3:1:1 比例)从背景中**快速锁定**二维码——三个点确定一个平面,让 App 把二维码的位置、大小、朝向一次性算清楚。

方向校正:处理倾斜与倒置

方向校正:处理倾斜与倒置

想象你拍了一张挂在歪墙上的画作——你能看到画框的四个边,但画本身是斜的。要把照片「正过来」,你会去找画面里那些**原本应该是直线的地标**——地板的缝、墙角的线、画框本身的边缘——然后把整张图旋转到这些地标对齐。扫码器把倾斜的二维码「拉正」,用的就是同一招,但它的「地标」是三种刻在二维码内部的小图案。

上一节讲了扫码 App 怎么用三个角的大「回」字方块把二维码从整个画面里框出来。但是「框出来」只是第一步——框出来的二维码可能还是斜的、倒的、歪的、或者**被透视拉伸变形了**。想象一下贴在矿泉水瓶身上的二维码:瓶身是弯的,斜着拍出来一定是个梯形、四个角不再各占九十度。这一节就讲扫码 App 怎么把这些「不规整」的二维码「拉正」成一张规整的网格。

为什么需要校正?

你可能会想:二维码不是正方形吗?三个角都找到了,转一下不就行了?问题在于实际场景里要复杂得多:

光靠三个角的位置探测图形还不够。还需要更多「地标」。

校正图形:变形二维码的「校准锚点」

二维码除了三个大角上的位置探测图形,还有**几个小一号的「回」字方块**散落在内部几个固定位置——这就是**校正图形**。其中最关键的一个在右下角附近,其他几个在某些版本上还会按规则出现在内部某些交叉点上。

它们长得很像位置探测图形,但尺寸更小(5×5 而不是 7×7),而且和周围白色边框连在一起。它的作用是:**当二维码被透视拉伸或贴在曲面上时,校正图形能告诉扫码器「这里原本应该是一个规整的网格」。**

打个比方:你在一张白纸上画了一个九宫格,每一格的交叉点都画了一个「+」字。然后你把纸揉皱——九个「+」字的位置全乱了。但你仔细看还是能认出每个「+」字原本应该在哪儿,于是可以反推出「纸被揉成了什么形状」,从而把网格「算回来」。校正图形在扫码里做的就是这件事——它是个**内部的坐标锚点**,帮助扫码器反推出整个二维码的网格被扭曲成了什么样。

定时图形:数格子用的「尺子」

另一个关键地标是**定时图形**——一条连接两个位置探测图形的、由黑白相间小方块组成的线。横的、竖的各一条,从左上角出发,沿着第 6 行和第 6 列往里走,整条线都是**黑白黑白黑白…严格 1:1 交替**的。

这条线的作用是**数格子、定大小**。上一节里,扫码器只找到了三个角的「回」方块,但还**不知道每个小方块(模块)到底占多少像素、整个二维码一共有多少个模块**。有了这条定时图形,扫码器沿它数一数「一黑一白一黑一白…一共 28 个交替」,就能反推出整张二维码的边长有多少个模块、每个模块的像素尺寸应该是多少,从而**精确定位**每个格子的位置。

打个比方:你拿到一张模糊的街景照,看不清里面的小细节,但你能数到一排路灯正好 14 根、每根之间间隔一样——这样你就能反推出「哦,每根路灯大概占照片这么宽」,进而算出整个街道的尺度。定时图形就是扫码 App 的「路灯」——用已知等距的参照物去测出整个图的比例尺。

怎么知道哪个方向是「上」?

最后还有一个关键问题:识别出来的二维码,到底哪边是「上」?倒过来也得能扫,所以扫码器必须自己判断「原本应该是哪边朝上」。

诀窍藏在两处细节里:

三个大回字方块自己看是看不出方向(三个长得一模一样)。但加上暗模块和校正图形的位置,整张二维码就有了**方向线索**——三个大角中,靠近暗模块的那个一定是左下、靠近校正图形的那个一定是右下,剩下两个一上一下。这样扫码器就能确定「这张二维码的『上』是这一边」,然后把它旋转到正确朝向。

把这一节的流程串起来

flowchart LR
  A[三个大回字方块已框出] --> B[扫描定时图形 数出模块总数和单模块大小]
  B --> C[定位校正图形 反推网格被扭曲的形状]
  C --> D[通过暗模块和校正图形位置判断上下方向]
  D --> E[把二维码旋转 拉伸 反变形为正方形]
  E --> F[每个模块的位置精准对齐 待读]

到这里,扫码器手里就不再是「画面里一个斜的、变形的二维码区域」,而是**一个规规整整的、方向正确的、每个模块位置都精确对齐的网格**。下一节要做的「把方块读成 0/1」才有意义——因为只有网格对齐了,才能准确判断「这个方块到底是黑的还是白的」。

**要点:** 扫码器在找到三个角之后,还需要借助**校正图形**(反推网格变形)、**定时图形**(数清模块数和大小)、**暗模块位置**(判断上下方向)这三种辅助地标,把斜的、倒的、变形的二维码「拉正」成一个规整、方向正确的网格,才能进入下一步读数据。

模块译码与纠错还原

经过定位和校正两步,扫码器手里已经是一张规规整整、方向正确、每个格子都精确对齐的网格。现在它要做的,就是「读出」里面的内容——把一张黑白格子图,翻译回一段有意义的信息。这正是模块 2 里「信息 → 二维码」那条路的**反向**:从二维码回到信息。

认黑白:每个方块对应 0 或 1

最基本的规则其实你已经猜到了——**黑块代表 1,白块代表 0**。但这里有一个细节:编码方在画二维码之前,会先给整张图加一层「花纹」,叫**掩码**。

为什么要加花纹?想象如果原始数据恰好有连续一大片「白」——扫起来一长条都没反差,扫码器在判断每个方块颜色时就容易出错。所以编码时会按一套规则(共有 8 种花纹),给整张图叠加一种棋盘式变化,把大块同色打散。

扫的时候,扫码器必须先把「花纹」**减掉**,才能看到真实的 0/1。它怎么知道用的是哪种花纹?——读出**格式信息**就明白了。

读元信息:先看「封面」再读「正文」

和很多文件一样,二维码也有一小块**「封面」**专门存放元信息。但要分清楚两类东西:

**格式信息**(所有二维码都有,靠近左上和右上两个角)只包含两件事:

**版本信息**(**仅在 Version 7 及以上**——也就是 45×45 以上的较大二维码中才有)单独放在二维码另一处位置,存的是版本号本身。

那小尺寸的二维码(Version 1~6,也就是 21×21 到 41×41)怎么办?它们没有版本信息块——不过没关系,扫码器已经从图像整体尺寸直接推算出了它是第几版。

扫码器在「翻译」之前必须先拿到这两层「封面」,否则它不知道该减哪种花纹、也不知道这张图一共有多少个格子。

数格子:按特定顺序读

元信息确认之后,就按**从右下角开始的 Z 字形蛇形走位**一行行往里读——每次读两列、换方向、再读两列。功能区(定位、校正、定时、格式信息、版本信息)自动跳过。

这块细节不用记太深,只需要知道:**读格子是有顺序的,不是乱扫的**——所以即使看上去位置是「打散」的,扫码器依然有办法把信息准确拼回去。

纠错还原:「救援队」上场

现在扫码器手里有了「数据 + 纠错码」。模块 2 讲过,编码时除了原始数据,还额外准备了一支「救援队」——纠错码。它本身不是数据,但和数据有**数学上的关联**。

扫的时候,扫码器会做这件事:

  1. **核对**:用纠错码去「验算」读出来的数据,看是否对得上;
  2. **对得上**:数据完整,直接进入下一步;
  3. **对不上**:调用纠错算法,根据「数据 + 纠错码」的整体结构,**反推出哪几个格子读错了,或者直接补回缺失的格子**——前提是损坏的格子数没超过该纠错级别能承受的上限。

这就是为什么——

这里要避免一个常见误解:**H 级并不是说「这张二维码能放的数据只有正常版本的 30%」**。准确的理解是——H 级意味着**最多能恢复约 30% 的码字**(也就是允许约 30% 的格子被遮盖或损坏),L 级最多只能恢复约 7%。纠错级别越高,**「救援队」越强**(能承受更多损坏),**留给数据的空间相对越少**——这是个典型的**此消彼长**。

还原成原始信息

纠错完成、确认数据干净之后,扫码器再按模块 2 提过的规则,把 0/1 序列翻译回字符——

那个 `https://example.com` URL、那段文字、那个联系人、那张支付订单号,就回到了你手机的屏幕上。

与模块 2 编码流程对照

把整节串起来,对照模块 2 的编码流程:

flowchart LR
  A[对齐好的网格] --> B[读出格式信息和版本信息]
  B --> C[按 Z 字走位读数据位和纠错位]
  C --> D[用掩码规则还原真实 0/1]
  D --> E[用纠错码校验和修复]
  E --> F[按模式指示还原为字符]
  F --> G[屏幕上显示 URL 或文字]

至此,二维码就完成了从「贴在墙上的一张图」到「手机里能跳转的链接」的完整旅程。

**要点:** 扫码的「译码 + 纠错」本质上是模块 2 编码流程的**逆过程**——先把黑白格子按掩码规则还原成 0/1,再用纠错码验算和修复缺失的格子,最后按编码时的模式把 0/1 翻译回字符。**纠错级别越高,纠错码占比越大、能恢复的码字越多,但留给数据的空间相对越少。**

整体流程:扫码的四步流水线

前 3 块你分别学了「定位」「校正」「译码纠错」三步。这一块要把它们串成一条完整的流水线——**这一步的目标,就是让你能在朋友面前,从头到尾讲清楚「一次扫码到底发生了什么」**。

一次扫码的完整旅程

想象你举着手机,对准奶茶店柜台上那张优惠券二维码。整个过程在零点几秒内发生——但拆开看,它严格按**四步流水线**走,少任何一步都不行:

flowchart LR
    A[举起手机打开扫码] --> B[定位:找到三个角并框出二维码]
    B --> C[校正:拉正方向并对齐网格]
    C --> D[译码:把方块读成 0/1 序列]
    D --> E[纠错:验算并修复损坏的格子]
    E --> F[屏幕上跳出链接或文字]

拆开看每一步在做什么

**第 1 步 · 定位**:摄像头拍下整张画面,扫码 App 在画面里**找到那三个角的「回」字方块**,圈出二维码所在的矩形范围。这一步要是没成功,后面三步都没法启动——因为根本不知道该去处理画面里的哪一块。

**第 2 步 · 校正**:把框出来的矩形**摆正**——不管二维码是斜着贴、倒着贴、还是贴在曲面上,都要拉成一个规整的、每个格子大小一致的方形网格。这一步靠的是另外两类辅助图案(校正图形和定时图形),告诉扫码器「网格边界在哪、每个格子多宽」。

**第 3 步 · 译码**:按特定顺序**读出每个格子的颜色**(黑 = 1,白 = 0),先剥掉编码时叠上的「掩码花纹」,再翻译回原始的二进制数据流。

**第 4 步 · 纠错**:用编码时预埋的「救援队」(纠错码)**验算读出来的数据**,对不上的话就反推哪里出错、修复缺失的格子——前提是损坏没超过这一级纠错能承受的上限。

走完这四步,0/1 序列被按字符集还原成文字、URL 或其他内容,推送到手机屏幕上。

一句话能讲清的版本

如果你只想用一句话向朋友解释,可以这么说:

> 扫码就是「**先找位置,再摆正方向,然后翻译内容,最后兜底纠错**」——四步流水线依次跑过,一张贴在墙上的黑白格子图就变成手机里能跳转的链接了。

为什么这四步缺一不可

每一步都对应一种「现实里可能出问题」的情况:

四步组合起来,才让二维码在真实世界里「**怎么贴都能扫、有点损伤也能扫**」。这正是二维码比一维条形码强大的根本原因——条形码只有一维信息,容错能力几乎为零;二维码则在二维平面上预埋了多层保护机制。

讲给别人听的小技巧

向朋友解释时,最有效的路径是「**先说结论,再拆步骤**」:

  1. 先抛结论:「扫码不是『拍一张照就读出文字』那么简单,它是一条四步流水线」;
  2. 再按顺序讲四步的名字(定位 → 校正 → 译码 → 纠错),每步用一句生活化的类比(比如「定位就像在地图上找到店铺」「纠错就像考试时的备用答题卡」);
  3. 结尾强调「所以二维码被弄脏一小块也能扫出来——因为有纠错这一步兜底」。

这样讲下来,朋友不仅记住了四步名字,更记住了「**为什么需要这四步**」——也就是二维码设计的精妙之处。

**要点:** 一次完整的扫码,是「**定位 → 校正 → 译码 → 纠错**」四步流水线——前三步让扫码器**找得到、读得出**格子,第四步让它在格子**不完美**的情况下也能还原出原始信息;四步组合起来,才让二维码在现实里「怎么贴都能扫、有点损伤也能扫」。

学习笔记

手机扫描二维码的基本流程

一、识别定位:从画面中框出二维码

三个角的位置探测图形

二维码三个角落(左上、右上、左下)各有一个「回」字方块,结构固定为外黑 7×7、中白 5×5、内黑 3×3。右下角没有。它的首要目的是让扫码器在混乱画面里快速锁定二维码。

定位流水线
flowchart LR
  A[摄像头采集画面] --> B[转灰度图] --> C[二值化黑白] --> D[逐行扫描找 1:1:3:1:1 比例] --> E[定位到三个角的回字方块] --> F[算出位置大小和朝向] --> G[把二维码区域框出来]
四个特征同时满足

四个特征同时满足:① **独特**——1:1:3:1:1 在自然画面几乎不出现;② **稳定**——三个角永远不变;③ **不脆弱**——部分被遮挡或光线不均时,从完整部分可反推整体;④ **几何够用**——三个点确定一个平面,足够反推位置、尺寸、朝向。

二、方向校正:把不规整的二维码拉正

实际场景里二维码可能倾斜、倒置、被透视拉伸(贴在圆柱瓶身)、或弯曲变形。光靠三个角的位置探测图形不够,需要更多「地标」。

校正图形:内部的网格锚点

二维码内部固定位置有几个比位置探测图形**更小**的「回」字方块(5×5 而不是 7×7),其中最关键的一个在右下角附近。它们和白色边框连在一起。当二维码被透视拉伸或贴在曲面上时,校正图形告诉扫码器「这里原本应该是规整网格的一部分」——扫码器借助它反推出网格被扭曲成了什么样,再算回规整网格。

定时图形:数格子的尺子

定时图形是一条连接两个位置探测图形的黑白相间线,横竖各一条,从左上角出发沿第 6 行和第 6 列往里走,整条线严格 1:1 黑白交替。

扫码器沿它数「一黑一白……一共 28 个交替」,可反推整张二维码的边长有多少个模块、每个模块的像素尺寸多少,从而精确定位每个格子的边界。

判断方向:暗模块

二维码固定位置上永远有一个**深色小方块**(暗模块),周围数据和纠错都不会改变它。它专门起「方向锚点」的作用——让扫码器判断哪边原本应该朝上,保证倒置时也能正确识别。

定位和校正完成后

定位和校正完成后,扫码器手里是一张规整、方向正确、每个格子精确对齐的网格。接下来把黑白格子翻译回有意义的信息——这是「信息 → 二维码」的反向过程。

但编码时给整张图叠了一层掩码(共有 8 种花纹)

但编码时给整张图叠了一层**掩码**(共有 8 种花纹),目的是打散大块同色区域,避免扫描时一长条无反差。扫码器必须先**减掉**这层花纹才能看到真实 0/1;通过读出**格式信息**来知道用的是哪种花纹。

元信息:先看「封面」再读「正文」

读格子之前必须先拿到这两层「封面」,否则不知道该减哪种花纹、也不知道一共有多少格子。

按从右下角开始的 Z 字形蛇形走位一行行往里读

按**从右下角开始的 Z 字形蛇形走位**一行行往里读,每次读两列、换方向、再读两列。功能区(定位、校正、定时、格式信息、版本信息)自动跳过。

纠错还原

扫码器拿到「数据 + 纠错码」后做三步:

  1. **核对**:用纠错码验算读出的数据。
  2. **对得上**:数据完整,进入下一步。
  3. **对不上**:调用纠错算法,根据整体结构反推哪几个格子读错了,或补回缺失的格子——前提是损坏格子数没超过该纠错级别能承受的上限。

各纠错级别的容错能力:

注意:H 级不是说「二维码能放的数据只有正常版本的 30%」,而是说**最多能恢复约 30% 的码字**(允许约 30% 格子被遮盖或损坏)。

还原成原始信息

纠错完成、数据干净后,扫码器按**模式指示**(这一段是数字、字母、汉字还是混合)把 0/1 序列翻译回字符。

四、整体流程:四步流水线

一次完整扫码严格按四步流水线走:

flowchart LR
  A[定位] --> B[校正] --> C[译码] --> D[纠错] --> E[屏幕上跳出链接或文字]

| 步骤 | 做什么 | 解决什么问题 | |------|--------|--------------| | **定位** | 找到三个角的「回」字方块,圈出二维码矩形范围 | 画面里有成百上千个东西,不知道该处理哪一块 | | **校正** | 把框出的矩形摆正,对齐每个格子 | 歪贴、倒贴、贴曲面时格子大小不一、方向不正 | | **译码** | 按顺序读出每个格子的颜色,剥掉掩码,翻译为 0/1 | 不知道怎样把黑白变成 0/1 | | **纠错** | 用纠错码验算,修复损坏的格子 | 弄脏、刮花、被 logo 盖住时数据可能不完整 |

四步缺一不可:组合起来才让二维码在真实世界里「**怎么贴都能扫、有点损伤也能扫**」。这也是二维码比一维条形码强大的根本原因——条形码只有一维、容错能力几乎为零,二维码则在二维平面上预埋了多层保护机制。

一句话总结

扫码就是「**先找位置,再摆正方向,然后翻译内容,最后兜底纠错**」——四步流水线依次跑过,黑白格子图变成手机里的链接。