二维码生成与识别原理修订版.docx
- 文档编号:10816227
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:22
- 大小:670.87KB
二维码生成与识别原理修订版.docx
《二维码生成与识别原理修订版.docx》由会员分享,可在线阅读,更多相关《二维码生成与识别原理修订版.docx(22页珍藏版)》请在冰豆网上搜索。
二维码生成与识别原理修订版
QR二维码的生成与识别原理
一、简介
二维码(2-dimensionalbarcode),是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的。
二维码的种类包括:
QRCode,DataMatrix,MaxiCode,Aztec,Vericode,PDF417,Ultracode,Code49,Code16K等。
其中QRCode是被广泛使用的二维码,QR全称QuickResponse,与其他编码方式相比,QR二维码具有存储容量大、编码速度快的特点,并且它也能表示更多的数据类型:
比如:
字符,数字,日文,中文等等。
随着近几年智能手机的迅猛发展,QR二维码得到了广泛的应用。
关于QR二维码的标准,可参见标准文档(QRCodeSpec):
二、应用现状
随着智能机的普及和手机摄像头成像能力的提升,为了提高向机器内输入信息的速度,QR二维码得到迅猛发展,在许多行业中得到应用。
在一维码时代,“扫码”主要应用在超市或图书馆等场所,以获取商品价格或图书分类等有限的特定信息。
二维码可以存储大容量数据,给人们的生活带来巨大方便。
从开始的扫描二维码提取文字或网址,到后来“扫一扫”添加好友、关注个人或公司微信或微博,再到扫码支付,二维码的应用已经非常普遍。
三、基础知识
QR码可分为不同的尺寸,或者叫版本Version。
Version1是21x21的矩阵,Version2是25x25的矩阵,Version3是29的尺寸,每增加一个version,就会增加4的尺寸,公式是:
(V-1)*4+21(V是版本号)最高Version40,(40-1)*4+21=177,所以最高是177x177的正方形。
样例如下:
定位图案
PositionDetectionPattern是定位图案,用于标记二维码的矩形大小。
这三个定位图案有白边叫SeparatorsforPostionDetectionPatterns。
之所以三个而不是四个意思就是三个就可以标识一个矩形了。
TimingPatterns也是用于定位的。
原因是二维码有40种尺寸,尺寸过大了后需要有根标准线,不然扫描的时候可能会扫歪了。
AlignmentPatterns只有Version2以上(包括Version2)的二维码需要这个东东,同样是为了定位用的。
功能性数据
FormatInformation存在于所有的尺寸中,用于存放一些格式化数据的。
VersionInformation在Version7以上,需要预留两块3x6的区域存放一些版本信息。
数据码和纠错码
除了上述的那些地方,剩下的地方存放DataCode数据码和ErrorCorrectionCode纠错码。
四、数据编码
QR码支持如下的编码:
Numericmode:
数字编码,从0到9。
如果需要编码的数字的个数不是3的倍数,那么,最后剩下的1或2位数会被转成4或7bits,则其它的每3位数字会被编成10,12,14bits,编成多长还要看二维码的尺寸。
Alphanumericmode:
字符编码,包括0-9,大写的A到Z(没有小写),以及符号$%*+–./:
包括空格。
这些字符会映射成一个字符索引表。
如下所示:
(其中的SP是空格,Char是字符,Value是其索引值)编码的过程是把字符两两分组,然后转成下表的45进制,然后转成11bits的二进制,如果最后有一个落单的,那就转成6bits的二进制。
而编码模式和字符的个数需要根据不同的Version尺寸编成9,11或13个二进制(如下表中Table3)
Bytemode:
字节编码,可以是0-255的ISO-8859-1字符。
有些二维码的扫描器可以自动检测是否是UTF-8的编码。
Kanjimode:
日文编码,也是双字节编码。
同样,也可以用于中文编码。
ExtendedChannelInterpretation(ECI)mode:
主要用于特殊的字符集。
并不是所有的扫描器都支持这种编码。
StructuredAppendmode:
用于混合编码,也就是说,这个二维码中包含了多种编码格式。
FNC1mode:
这种编码方式主要是给一些特殊的工业或行业用的。
比如GS1条形码之类的。
这里我们主要介绍最常用的数字编码和字符编码。
下面两张表中,Table2是各个编码格式的“编号”(注:
中文是1101),编号要写在FormatInformation中。
Table3表示了不同版本(尺寸)的二维码,对于数字、字符、字节和Kanji模式下,对于单个编码的2进制的位数。
(编码规范表可参见二维码规格说明书)
举例说明:
示例一:
数字编码
例如,在Version1的尺寸下,纠错级别为H的情况下,编码:
01234567的编码方式如下:
1.把上述数字分成三组:
01234567
2.把他们转成二进制:
012转成0000001100;345转成010*******;67转成1000011。
3.把这三个二进制串起来:
000000110001010110011000011
4.把数字的个数转成二进制(version1-H是10bits):
8个数字的二进制是0000001000
5.把数字编码的标志0001和第4步的编码加到前面:
00010000001000000000110001010110011000011
示例二:
字符编码
同样,在Version1的尺寸下,纠错级别为H的情况下,编码:
AC-42的编码方式如下:
1.从字符索引表中找到AC-42这五个字条的索引(10,12,41,4,2)
2.两两分组:
(10,12)(41,4)
(2)
3.把每一组转成11bits的二进制:
(10,12)10*45+12等于462转成00111001110
(41,4)41*45+4等于1849转成11100111001
(2)等于2转成000010
4.把这些二进制连接起来:
0011100111011100111001000010;
5.把字符的个数转成二进制(Version1-H为9bits):
5个字符,5转成000000101;
6.在头上加上编码标识0010和第5步的个数编码:
00100000001010011100111011100111001000010;
五、结束符和补齐符
假如我们有个HELLOWORLD的字符串要编码,根据上面的示例二,我们可以得到下面的编码
编码
字符数
HELLOWORLD的编码
0010
000001011
0110000101101111000110100010111001011011100010011010100001101
还要加上结束符:
编码
字符数
HELLOWORLD的编码
结束
0010
000001011
0110000101101111000110100010111001011011100010011010100001101
0000
按8bits重排
如果所有的编码加起来不是8个倍数我们还要在后面加上足够的0,比如上面一共有78个bits,所以还要加上2个0,然后按8个bits分好组:
00100000010110110000101101111000110100010111001011011100010011010100001101000000
补齐码(PaddingBytes)
最后,如果还没有达到最大的bits数的限制,还要加一些补齐码(PaddingBytes),PaddingBytes就是重复下面的两个bytes:
1110110000010001(这两个二进制转成十进制是236和17,关于每一个Version的每一种纠错级别的最大Bits限制,可以参看QRCodeSpec)
假设我们需要编码的是Version1的Q纠错级,那么,其最大需要104个bits,而我们上面只有80个bits,所以,还需要补24个bits,也就是需要3个PaddingBytes,我们就添加三个,于是得到下面的编码:
00100000010110110000101101111000110100010111001011011100010011010100001101000000111011000001000111101100
上面的编码就是数据码了,叫DataCodewords,每一个8bits叫一个codeword,我们还要对这些数据码加上纠错信息。
六、纠错码
上面我们说到了一些纠错级别,ErrorCorrectionCodeLevel,二维码中有四种级别的纠错,这就是为什么二维码有残缺还能扫出来,也就是为什么有人在二维码的中心位置加入图标。
错误修正容量
L水平
7%的字码可被修正
M水平
15%的字码可被修正
Q水平
25%的字码可被修正
H水平
30%的字码可被修正
那么,QR是怎么对数据码加上纠错码的?
首先需要对数据码进行分组,也就是分成不同的Block,然后对各个Block进行纠错编码,对于如何分组,可以查看QRCodeSpec的定义表。
这里注意最后两列:
NumberofErrorCodeCorrectionBlocks:
需要分多少个块。
ErrorCorrectionCodePerBlocks:
每一个块中的code个数,所谓的code的个数,也就是有多少个8bits的字节。
举例说明:
上述的Version5+Q纠错级:
需要4个Blocks(2个Blocks为一组,共两组),第一组的两个Blocks中各15个bits数据+各9个bits的纠错码(注:
表中的codewords就是一个8bits的byte)(再注:
最后一例中的(c,k,r)的公式为:
c=k+2*r,因为后脚注解释了:
纠错码的容量小于纠错码的一半)
下图给一个5-Q的示例(因为二进制写起来会让表格太大,这里使用十进制表示,可以看到每一块的纠错码有18个codewords,也就是18个8bits的二进制数)
组
块
数据
对每个块的纠错码
1
1
67857013487388519411950618610338
21319911451152472412232292481541171541118616111139
2
24624666711813424273886221981991466
8720496602021821241572001342712920917163163120133
2
1
1822302471195071181348738826134151507
14811617721276133752422387619523018910108240192141
2
702471188619461515016236172361723617236
23515951732414759331064025517282213132178236
(注:
二维码的纠错码主要是通过Reed-Solomonerrorcorrection(里德-所罗门纠错算法)来实现的)。
七、最终编码
上述步骤完成之后,还要把数据码和纠错码的各个codewords交替放在一起。
交替规则如下:
对于数据码:
把每个块的第一个codewords先拿出来按顺度排列好,然后再取第一块的第二个,如此类推。
如上述示例中的DataCodewords如下:
块1
67
85
70
134
87
38
85
194
119
50
6
18
6
103
38
块2
246
246
66
7
118
134
242
7
38
86
22
198
199
146
6
块3
182
230
247
119
50
7
118
134
87
38
82
6
134
151
50
7
块4
70
247
118
86
194
6
151
50
16
236
17
236
17
236
17
236
我们先取第一列的:
67,246,182,70
然后再取第二列的:
67,246,182,70,85,246,230,247
如此类推:
67,246,182,70,85,246,230,247………………,38,6,50,17,7,236
对于纠错码,也是一样:
块1
213
199
11
45
115
247
241
223
229
248
154
117
154
111
86
161
111
39
块2
87
204
96
60
202
182
124
157
200
134
27
129
209
17
163
163
120
133
块3
148
116
177
212
76
133
75
242
238
76
195
230
189
10
108
240
192
141
块4
235
159
5
173
24
147
59
33
106
40
255
172
82
2
131
32
178
236
和数据码取的一样,得到:
213,87,148,235,199,204,116,159,…………39,133,141,236
然后,再把这两组放在一起(纠错码放在数据码之后)得到:
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
这就是数据区。
RemainderBits
最后再加上ReminderBits,对于某些Version的QR,上面的还不够长度,还要加上RemainderBits,比如:
上述的5Q版的二维码,还要加上7个bits,RemainderBits加零就好了。
关于哪些Version需要多少个Remainderbit,可以参看QRCodeSpec的第15页的Table-1的定义表。
八、画二维码图
PositionDetectionPattern
首先,先把PositionDetection图案画在三个角上。
(无论Version如何,这个图案的尺寸不变)
AlignmentPattern
然后,再把Alignment图案画上(无论Version如何,这个图案的尺寸就是这么大)
关于Alignment的位置,可以查看QRCodeSpec的第81页的Table-E.1的定义表(下表是不完全表格)
下图是根据上述表格中的Version8的一个例子(6,24,42)
TimingPattern
接下来是TimingPattern的线
ormatInformation
再接下来是FormationInformation,下图中的蓝色部分。
FormatInformation是一个15个bits的信息,每一个bit的位置如下图所示:
(注意图中的DarkModule,是永远出现的)
这15个bits中包括:
5个数据bits:
其中,2个bits用于表示使用什么样的ErrorCorrectionLevel,3个bits表示使用什么样的Mask
10个纠错bits。
主要通过BCHCode来计算
然后15个bits还要与101010000010010做XOR操作。
这样就保证不会因为我们选用了00的纠错级别和000的Mask,从而造成全部为白色,这会增加我们的扫描器的图像识别的困难。
下面是一个示例:
关于ErrorCorrectionLevel如下表所示:
关于Mask图案如后面的Table23所示。
VersionInformation
再接下来是VersionInformation(版本7以后需要这个编码),下图中的蓝色部分。
VersionInformation一共是18个bits,其中包括6个bits的版本号以及12个bits的纠错码,下面是一个示例:
而其填充位置如下:
数据和数据纠错码
然后是填接我们的最终编码,最终编码的填充方式如下:
从左下角开始沿着红线填我们的各个bits,1是黑色,0是白色。
如果遇到了上面的非数据区,则绕开或跳过。
掩码图案
这样下来,图基本填好了,但是,也许那些点并不均衡,如果出现大面积的空白或黑块,扫描识别会变得困难。
所以,最后还要做Masking操作。
QR的Spec中描述到,QR有8个Mask你可以使用,其中,各个mask的公式在各个图下面。
所谓mask,就是和上面生成的图做XOR操作。
Mask只会和数据区进行XOR,不会影响功能区。
(注:
选择一个合适的Mask也是有算法的)
其Mask的标识码如下所示:
(其中的i,j分别对应于上图的x,y)
下面是Mask后的一些样子,可以看到被某些MaskXOR了的数据变得比较零散了。
Mask过后的二维码就成最终的图了。
九、识别:
既然二维码的生成搞懂了,那么识别就会变得简单,基本上就是生成的逆过程。
对于手机端扫描QR二维码的识别而言,重点在于摄像头获取数据后,对数据的最初处理,这里以Android手机识别QR二维码为例、以源码为主要依据进行简要说明。
1、获取摄像头原始数据。
首先Android提供了PreviewCallback接口,只要在Activity里实现PreviewCallback接口后,就会自动重载这个函数:
publicvoidonPreviewFrame(byte[]data,Cameracamera)
这个函数里的data就是实时预览帧视频,也就是摄像头返回的最原始的数据。
这样,就解决了如何获取摄像头数据的问题。
一旦程序调用PreviewCallback接口,就会自动调用onPreviewFrame这个函数。
调用PreviewCallback的方法有三种。
分别是:
setPreviewCallback,setOneShotPreviewCallback,setPreviewCallbackWithBuffer,程序中使用第二种方式,示例如下:
至于何时触发onPreviewFrame()这个函数来获得摄像头数据,一般选择按一个按键触发一次或者每隔一段时间触发一次,无论如何,只要在该触发的地方写上Camera.setOneShotPreviewCallback(RectPhoto.this);便会自动触发一次。
这样我们就可以得到手机摄像头实时预览帧视频数据data。
2、解码数据
获取该数据后,要对二维码进行解码,我们在DecodeHandler类中定义了解码方法:
privatevoiddecode(byte[]data,intwidth,intheight){}
我们重点对该方法进行分析:
1第78-82行代码,最初的数据应该被看成为一个矩阵数据,只是把它存放在一维数组byte中,这里是将其转换成转置矩阵,即a[i][j]与a[j][i]交换,因此在83-85行代码中,对宽高也进行了调换;
2第88-95行代码,这是解析数据最为关键的代码,88行我们定义了PlanarYUVLuminanceSource类的对象,关于YUV数据格式以及该类的介绍参见后面的附录一。
3最后,需要将PlanarYUVLuminanceSource类处理的数据转化成Bitmap对象。
代码的第91行即为此操作。
4至此,我们得到了二维码图片的Bitmap对象,就可以按照二维码生成的逆过程运算对数据进行解析。
5解码示例:
例如,程序在进行解码分析时,首先要根据版本信息和纠错级别进行判断,即读取VersionInformation和DataandErrorCorrectionCodewords处数据,若取得版本为1,接错级别为H,按照QR码的规范,查下表:
可知应读取数据区连续的72个bits作为一个整体,比如我们读到如下数据:
001000000010100111001110111001110010000100000000111011000001000111101100
对于该数据,按照QR码的规范,前四位为编码格式编号,即0010对应的是字符编号,说明后面的内容为字符;
根据QR码编码规范,版本为1、纠错级别为H的编码,数字个数占九位,即取编码格式编号的后九位:
000000101,转换成十进制为5,即得知该数据包含5个字符。
再次按照QR码编码规范,字符编码过程中,每一组数据为11位,并且如果最后有一个落单的,那就转成6bits的二进制。
那么根据有5个字符,我们取随后的28位数据,即为真正的数据编码:
0011100111011100111001000010
将上述三组数据转换成十进制即为(462,1849,2),根据字符编码的编码规范,编码的过程是把字符两两分组,然后转成下表的45进制。
那么,分别将462、1849、2转化为45进
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 二维码 生成 识别 原理 修订版
![提示](https://static.bdocx.com/images/bang_tan.gif)