QrCode二维码的实现原理
什么是二维码
什么是QR码
创建QR码的步骤
数据分析
编码
(1)选择纠错码级别
(2)确定数据的最小版本
(3)添加模式标识
(4)添加字符数标识
(5)进行编码
(6)拆分比特串成8位一组,不足补0;
生成纠错编码
生成最终的数据结构
将数据块放置二维码矩阵中
(1)放置固定图案
(2)放置数据位
添加掩码
添加格式和版本信息
(1)格式信息
(2)版本信息
输出最终的二维码
参考
前言
得益于微信的推广,QR二维码可以说已经融入了生活的方方面面,通过二维码极大的缩短了业务流程路径,目前日常最常用的场景莫过于支付、登录认证、信息识别等。希望通过对二维码的学习,了解其背后的原理,在以后的工作中能加以运用。
什么是二维码
二维码也称为二维条码,是指在一维条码的基础上扩展出另一维具有可读性的条码,使用黑白矩形图案表示二进制数据,被设备扫描后可获取其中所包含的信息。一维条码的宽度记载着数据,而其长度没有记载数据。二维码的长度、宽度均记载着数据。二维码有一维条码没有的“定位点”和“容错机制”。容错机制在即使没有识别到全部的条码、或是说条码有污损时,也可以正确地还原条码上的信息。
二维码的种类很多,不同的机构开发出的二维码具有不同的结构以及编写、读取方法。常见的二维码有:
PDF417码、QR码、汉信码、颜色条码、EZ码、Aztec码、QuickMark、Data Matrix。
注意:由于QR码目前普及度最高,所以一般人们所说的二维码=QR码。
什么是QR码
QR码(全称为快速响应矩阵图码;英语:Quick Response Code)是二维码的一种,于1994年由日本DENSO WAVE公司(丰田的子公司,生产汽车零部件)发明,该标准在ISO/IEC 18004:2015中定义,是免费使用的。QR来自英文Quick Response的缩写,即快速反应,因为发明者希望QR码可以快速解码其内容。QR码使用四种标准化编码模式(数字、字母数字、字节(二进制)和日文(Shift_JIS))来存储数据。QR码常见于日本,为目前日本最通用的二维空间条码,在世界各国广泛运用于手机读码操作。QR码比普通一维条码具有快速读取和更大的存储数据容量,也无需要像一维条码般在扫描时需要直线对准扫描仪。
特点:
1.存储大容量信息
2.在小空间内打印
3.有效表现各种字母
4.对变脏和破损的适应能力强
5.可以从任意方向读取
6.支持数据合并功能
创建QR码的步骤
数据分析
QR标准具有四种用于编码文本的模式:数字,字母数字,字节和汉字。每种模式都将文本编码为一位(1或0),但是每种模式都使用不同的方法将文本转换为位,并且每种编码方法都经过了优化,可以使用尽可能短的位串对数据进行编码。因此,第一步应该是执行数据分析,以确定文本是以数字,字母数字,字节还是汉字模式进行编码,选择最佳编码模式。
(1)如果输入字符串仅包含十进制数字(0到9),用数字模式。
(2)如果数字模式不适用,并且可以在字母数字表的左列中找到输入字符串中的所有字符,用字母数字模式。注:小写字母不能以字母数字模式编码;仅大写。
(3)如果某个字符不在字母数字表的左列中,但可以在ISO 8859-1中进行编码,用字节模式。
(4)QR码阅读器可能能够以字节模式识别UTF-8。 如果所有字符都在Shift JIS字符集中,用汉字模式。 Shift JIS字符可以改用UTF-8编码,因此可以将字节模式用于汉字,但通常更有效的方式是使用Shift JIS并将汉字模式用于汉字字符(省空间)。
编码
不同的编码模式是为了使得我们转码的比特串长度最短。
(1)选择纠错码级别
在编码之前,需要先选择纠错码级别
错误修正容量 | |
---|---|
L等级 | 7%的字码可被修正 |
M等级 | 15%的字码可被修正 |
Q等级 | 25%的字码可被修正 |
H等级 | 30%的字码可被修正 |
(2)确定数据的最小版本
不同版本的QR码尺寸不同,目前有40个版本,从1到40,每个版本比之前的大4像素。由于纠错码也占用空间,所以不同版本不同纠错级别的容量也不一样,具体可以参考这里
(3)添加模式标识
每个编码模式都有一个标识它的四位模式标识。编码的数据必须以适当的模式标识开头,下表列出了每种模式的模式标识。
模式名称 | 标识 |
---|---|
Numeric Mode | 0001 |
Alphanumeric Mode | 0010 |
Byte Mode | 0100 |
Kanji Mode | 1000 |
ECI Mode | 0111 |
(4)添加字符数标识
字符计数指示器是一串字符串,代表要编码的字符数。需要注意的是,字符数标识基于不同的QR版本有不同的固定长度,如果实际字符长度不满,则需要在左侧补0。
- 版本1 到 9
Numeric mode: 10 bits
Alphanumeric mode: 9 bits
Byte mode: 8 bits
Japanese mode: 8 bits - 版本10到 26
Numeric mode: 12 bits
Alphanumeric mode: 11 bits
Byte mode: 16
Japanese mode: 10 bits - 版本27到 40
Numeric mode: 14 bits
Alphanumeric mode: 13 bits
Byte mode: 16 bits
Japanese mode: 12 bits
例如,我们选择Alphanumeric模式和version1来编码HELLO WORLD
,那么查表可以知道,Alphanumeric模式的字符数标识固定长度为9bits,HELLO WORLD
的长度为11,转换成二进制为1011,不足9位左侧补0,即000001011,然后再把第三步中的模式标识放在前面。
最终:0010 000001011
(5)进行编码
不同的编码模式,有不同的算法,具体可以参考下面几个页面。
以上述的HELLO WORLD
为例,采用Alphanumeric Mode Encoding
需要注意的是:alphanumeric模式只能编码大写字母。
1、拆成字符对:HE, LL, O (空格), WO, RL, D
2、参考字母数字表,转换成对应的数字,然后用第一个字符数字*45+第二个字符数字
,再转换成二进制,一个字符补满6位,两个字符补满11位。例如:HE=(45*17)+14=779,再转换成二进制
779 → 01100001011
那么最终我们得到的编码数据为
Mode Indicator | Character Count Indicator | Encoded Data |
---|---|---|
0010 | 000001011 | 01100001011 01111000110 10001011100 10110111000 10011010100 001101 |
(6)拆分比特串成8位一组,不足补0;
1、首先要确定当前QR码所需长度,不同的纠错码长度不一致,可以查询参考此表,可以查出所需块组数,然后记得要乘以8。
例如1-Q的码,查表可知需要13组,那么最终的QR码长度为13*8=104
bits。
2、添加终止标识
如果位字符串短于所需位的总数,则必须在字符串的右侧添加最多四个0的终止标识,如果位串短于少于四位,则仅添加达到所需位数所需的0。
从上面可知HELLO WORLD
的编码长度为74位,不满104,终止标识必须补4位。
Mode Indicator | Character Count Indicator | Encoded Data | Terminator |
---|---|---|---|
0010 | 000001011 | 01100001011 01111000110 10001011100 10110111000 10011010100 001101 | 0000 |
3、末尾补0,确保每组长度为8位。
在添加终止标示符之后,总长度变为78位,不能被8整除,还需要补2位。
即00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000
4、如果长度不够,继续补位
经过上面步骤之后,长度目前变成了80位,还差104-80=24位,那么我们需要重复添加11101100 00010001
直到字符串达到最大长度为止,这两个字节转换成数字即236和17,至于为什么是这两个数,协议规定的,添加就对了(也许有彩蛋?)。
那么最终编码结果数据码为:
00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 11101100 00010001 11101100
生成纠错编码
QR码具有纠错能力,当完成编码之后,会用这些数据通过一种叫做Reed-Solomon算法纠错的过程生成纠错码。
QR扫描时同时读取数据代码字和纠错代码字。通过比较两者,扫描仪可以确定它是否正确读取了数据,如果没有正确读取数据,则可以纠正错误。
相对而言,容错率愈高,QR码图形面积愈大。所以一般折衷使用15%容错能力。
错误修正容量 | |
---|---|
L等级 | 7%的字码可被修正 |
M等级 | 15%的字码可被修正 |
Q等级 | 25%的字码可被修正 |
H等级 | 30%的字码可被修正 |
(1)将数据码按顺序分组分块
生产纠错码之前,我们需要查表将数据码(1字节=8位)分组,不同组里可能还存在不同的块,例如5-Q的QR码,查表可知,分为两组,每组包含2个块,组1的块大小为15字节,组2块大小为16字节,那么15+15+16+16 = 62字节,又可知每组块所需纠错码大小为18,那么纠错码总大小为4*18=72字节。
具体算法过程见这里,不详细说明
生成最终的数据结构
在数据和纠错码都生成好了之后,需要将他们以正确的顺序排列。对于小型QR码,将纠错码添加到数据码后面即可。
对于大型QR码,数据和纠错码字是在块中生成的,并且必须根据QR码规范对这些块进行交织。
例如对于5-Q版本2组4块16个字节的数据码:
. | Col 1 | Col 2 | Col 3 | Col 4 | Col 5 | Col 6 | Col 7 | Col 8 | Col 9 | Col 10 | Col 11 | Col 12 | Col 13 | Col 14 | Col 15 | Col 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Block 1 | 67 | 85 | 70 | 134 | 87 | 38 | 85 | 194 | 119 | 50 | 6 | 18 | 6 | 103 | 38 | |
Block 2 | 246 | 246 | 66 | 7 | 118 | 134 | 242 | 7 | 38 | 86 | 22 | 198 | 199 | 146 | 6 | |
Block 3 | 182 | 230 | 247 | 119 | 50 | 7 | 118 | 134 | 87 | 38 | 82 | 6 | 134 | 151 | 50 | 7 |
Block 4 | 70 | 247 | 118 | 86 | 194 | 6 | 151 | 50 | 16 | 236 | 17 | 236 | 17 | 236 | 17 | 236 |
那么把每一列数据拼接即可:
(67,246,182,70),(85,246,230,247)……以此类推。
纠错码同理。
最终将交织的纠错码添加到交织的数据码后面,如:
67, 246, 182, 70, 85, 246, 230, 247, 70, 66, 247, 118, 134, 7, 119, 86, 87, 118, 50, 194, 38, 134, 7, 6, 85, 242, 118, 151, 194, 7, 134, 50, 119, 38, 87, 16, 50, 86, 38, 236, 6, 22, 82, 17, 18, 198, 6, 236, 6, 199, 134, 17, 103, 146, 151, 236, 38, 6, 50, 17, 7, 236, 213, 87, 148, 235, 199, 204, 116, 159, 11, 96, 177, 5, 45, 60, 212, 173, 115, 202, 76, 24, 247, 182, 133, 147, 241, 124, 75, 59, 223, 157, 242, 33, 229, 200, 238, 106, 248, 134, 76, 40, 154, 27, 195, 255, 117, 129, 230, 172, 154, 209, 189, 82, 111, 17, 10, 2, 86, 163, 108, 131, 161, 163, 240, 32, 111, 120, 192, 178, 39, 133, 141, 236
然后转换成二进制
0100001111110110101101100100011001010101111101101110011011110111010001100100001011110111011101101000011000000111011101110101011001010111011101100011001011000010001001101000011000000111000001100101010111110010011101101001011111000010000001111000011000110010011101110010011001010111000100000011001001010110001001101110110000000110000101100101001000010001000100101100011000000110111011000000011011000111100001100001000101100111100100101001011111101100001001100000011000110010000100010000011111101100110101010101011110010100111010111100011111001100011101001001111100001011011000001011000100000101001011010011110011010100101011010111001111001010010011000001100011110111101101101000010110010011111100010111110001001011001110111101111110011101111100100010000111100101110010001110111001101010111110001000011001001100001010001001101000011011110000111111111101110101100000011110011010101100100110101101000110111101010100100110111100010001000010100000001001010110101000110110110010000011101000011010001111110000001000000110111101111000110000001011001000100111100001011000110111101100
不同版本需要补零,可查表
所以对于最后的11101100,我们需要补7个0,既111011000000000
将数据块放置二维码矩阵中
(1)放置固定图案
定位图案(finder patterns):位于图案左上、左下、右上的黑块,用于定位二维码
分隔符(separators):位于定位器周围的白边
校准图案(alignment patterns):和定位图案类似,可能会贯穿于于整个二维码,通常存在于版本2以上的二维码,具体位置取决于二维码版本。
时序图案(timing patterns) :类似虚线,用于连接定位图案。
黑块(dark module):独立的黑块,始终放在左下的定位图案旁,用于保留格式、版本信息。
具体如何放置见这里
(2)放置数据位
白色表示0,黑色表示1,顺序如图所示,一次占两列
向上
向下
当遇到固定图案时,跳过,直到遇到未使用的二维码块。
添加掩码
QR码中的某些模式可能会使QR码扫描器难以正确读取代码。为了解决这个问题,QR码规范定义了八个掩码图案,每个掩码图案都会根据特定图案更改QR码。我们可以通过基于四个惩罚规则的得分来确定这些掩码图案中的哪一个会导致QR码具有最少的不良特征,最终的QR码必须使用最低分的掩码图案。
掩码有八种款式如下图,可以使掩码的黑色的区域反转:
评价标准如下:
- 行(列)中有连续 a 个模块(a>5)同色,每处计 3+(a-5)=a-2 分;
- 模块同色构成的 m 行 n 列矩形,每处计 3(m-1)(n-1) 分;
- 行(列)中出现 1:1:3:1:1(黑白黑白黑)的图形(上图所谓的后者因素),每处计 40 分;
- 黑色模块比率为 x%,则计 分。
添加格式和版本信息
最后一步是通过在代码的特定区域添加在先前步骤中留为空白的像素,向QR码添加格式和版本信息(如果有必要)。格式像素标识此QR码中使用的纠错级别和掩码模式。版本像素对QR矩阵的大小进行编码,并且仅在较大的QR码中使用。
QR码一共提供40种不同版本存储密度的结构,对应指示图的“版本信息”,版本1为21×21模块(模块为QR码中的最小单元),每增加一个版本,长宽各增加4个模块,最大的版本40为177×177模块。
QR码最大数据容量 | (对于版本40) |
---|---|
数字 | 最多7,089字符 |
字母 | 最多4,296字符 |
二进制数(8 bit) | 最多2,953 字节 |
日文汉字/片假名 | 最多1,817字符(采用Shift JIS) |
中文汉字 | 最多984字符(采用UTF-8) |
中文汉字 | 最多1,800字符(采用BIG5/GB2312) |
(1)格式信息
Error Correction Level | Bits | Integer Equivalent |
---|---|---|
L | 01 | 1 |
M | 00 | 0 |
Q | 11 | 3 |
H | 10 | 2 |
例如纠错级别L,掩码模式4的二维码,格式信息二进制为01100
其中100=2^2=4
通过算法再生成纠错码,最终为110011000101111
为了减少计算,可以直接查表
最后,将二进制位横竖按标识贴到如图所示的位置。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
1 1 0 0 1 1 0 0 0 1 0 1 1 1 1
无论二维码版本大小,都可以进行标识。
(2)版本信息
如果是大于版本7的二维码,必须添加18位的版本信息到如图所示的位置:
算法同上,查表即可。
例如版本7,000111110010010100
17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0 0 0 1 1 1 1 1 0 0 1 0 0 1 0 1 0 0
左下角的填充顺序:
00 03 06 09 12 15
01 04 07 10 13 16
02 05 08 11 14 17
右上角的填充顺序:
00 01 02
03 04 05
06 07 08
09 10 11
12 13 14
15 16 17
输出最终的二维码
需要注意,为了提高识别率,一般会加上4个图块宽的空白区域
下图是通过alphanumeric mode编码、1-Q版本的“HELLO WORLD”