二維碼生成原理及解析代碼
添加時間:2019-11-11 21:15:28
來源:
二維碼生成原理及解析代碼
自從大街小巷的小商小販都開始布滿了騰訊爸爸和阿里爸爸的二維碼之后,我才感覺到我大天朝共享支付的優(yōu)越性。最近畢業(yè)論文寫的差不多了,在入職之前多學(xué)一些東西也是好的。這里秉著好奇心,研究一下二維碼的生成,并嘗試性寫一個二維碼解析源碼。
注:暫時只有二維碼原理,筆者這段時間會持續(xù)研究解析代碼,并隨進(jìn)度持續(xù)更新。
參考網(wǎng)址:
《二維碼的生成細(xì)節(jié)和原理》
《QR Code Tutorial》
《Hello World!》—— 知乎專欄文章
《為程序員寫的Reed-Solomon碼解釋》
一. 二維碼基本知識
二維碼另一個名稱是QR Code(Quick Response Code),近年來在移動設(shè)備上經(jīng)常使用,與傳統(tǒng)條形碼相比,可以存儲更多的信息。二維碼本質(zhì)上是個密碼算法,基本知識總結(jié)如下。
首先,二維碼存在 40 種尺寸,在官方文檔中,尺寸又被命名為 Version。尺寸與 Version 存在線性關(guān)系:Version 1 是 21×21 的矩陣,Version 2 是 25×25 的矩陣,每增加一個 Version,尺寸都會增加 4,故尺寸 Size 與 Version 的線性關(guān)系為:
Size=(Version?1)×4
Size=(Version?1)×4
Version 的最大值是 40,故尺寸最大值是(40-1)*4+21 = 177,即 177 x 177 的矩陣。
二維碼結(jié)構(gòu)如下圖 1.1 所示:
圖1.1 二維碼結(jié)構(gòu)
二維碼的各部分都有自己的作用,基本上可被分為定位、功能數(shù)據(jù)、數(shù)據(jù)內(nèi)容三部分。
定位圖案:
Position Detection Pattern, 定位圖案:用于標(biāo)記二維碼矩形的大小;用三個定位圖案即可標(biāo)識并確定一個二維碼矩形的位置和方向了;
Separators for Position Detection Patterns, 定位圖案分割器:用白邊框?qū)⒍ㄎ粓D案與其他區(qū)域區(qū)分;
Timing Patterns, 時序圖案:用于定位,二維碼如果尺寸過大,掃描時容易畸變,時序圖案的作用就是防止掃描時畸變的產(chǎn)生;
Alignment Patterns, 對齊圖案:只有在 Version 2 及其以上才會需要;
功能數(shù)據(jù):
Format Information, 格式信息:存在于所有尺寸中,存放格式化數(shù)據(jù);
Version Information, 版本信息:用于 Version 7 以上,需要預(yù)留兩塊 3×6 的區(qū)域存放部分版本信息;
數(shù)據(jù)內(nèi)容:剩余部分存儲數(shù)據(jù)內(nèi)容
Data Code, 數(shù)據(jù)碼;
Error Correction Code, 糾錯碼;
二. 數(shù)據(jù)編碼
2.1 數(shù)據(jù)編碼信息
二維碼的數(shù)據(jù)編碼信息如下圖 2.1, 2.2 中的列表所示:
圖2.1 模式編號指示器
圖2.2 字符計數(shù)指示器中的位數(shù)
上圖 2.1 中,展示的是二維碼支持的數(shù)據(jù)編碼模式。
注:其中中文編碼模式為 1101;
上圖 2.2 中展示了不同版本(即不同尺寸)的二維碼,單個編碼對應(yīng)二進(jìn)制的位數(shù)。
注:二維碼規(guī)格說明書中,存在各式各樣的編碼規(guī)范表;
圖2.1, 2.2 表格具體含義,在后面的例程中會具體講解。
2.2 數(shù)據(jù)編碼形式
2.2.1 數(shù)字編碼(Numeric Mode)
數(shù)字編碼的范圍為 0~9。
對于數(shù)字編碼,統(tǒng)計需要編碼數(shù)字的個數(shù)是否為 3 的倍數(shù):如果不是 3 的倍數(shù),則剩下的 1 位或 2 位會被轉(zhuǎn)為 4bits 或 8bits(十進(jìn)制轉(zhuǎn)二進(jìn)制),每三位數(shù)字都會被編成 10bits, 12bits, 14bits,具體編碼長度仍然需要二維碼尺寸決定。
2.2.2 字符編碼(Alphanumeric Mode)
字符編碼的范圍有:
數(shù)字 0~9;
大寫 A~Z(無小寫);
幾個符號$ % * + - . / 和空格。
上述字符映射為一個索引表,如下圖 2.3 所示:
圖2.3 字符映射索引表
圖中 Char 表示字符,Value 表示字符對應(yīng)的索引值。
索引表中共 45 種對應(yīng)關(guān)系,字符編碼的過程,就是將每兩個字符分為一組,然后轉(zhuǎn)成上圖 2.3 的 45 進(jìn)制,再轉(zhuǎn)為 11bits 的二進(jìn)制結(jié)果。對于落單的一個字符,則轉(zhuǎn)為 6bits 的二進(jìn)制結(jié)果。
此外,根據(jù)上圖 2.2 的設(shè)定,對不同 Version 的二維碼使用 9/11/13 個二進(jìn)制表示。
注:
上圖 2.3 中的 SP 代表空格。
2.2.3 字節(jié)編碼(Byte Mode)
可以是 0-255 的 ISO-8859-1 字符。有些二維碼的掃描器可以自動檢測是否是 UTF-8 的編碼。
2.2.4 日文編碼(Kanji Mode)
日文編碼同時也是雙字節(jié)編碼,同樣也可以用于中文編碼。
日文與中文編碼流程基本相似:
首先減去一個值;
挑出差值結(jié)果的前兩個 16 進(jìn)制,乘以 0xC0;
加上后兩個 16 進(jìn)制位;
轉(zhuǎn)為 13bits 編碼;
按照日文編碼集 SHIFT_JIS為參照,可查詢?nèi)瘴淖址膶?yīng)編碼。以“雅”與“芒”為例,轉(zhuǎn)換過程如下圖 2.4 所示:
圖2.4 日文編碼流程展示
2.2.5 其他編碼
其他類型的編碼本文中不詳細(xì)說明。其中包括:
特殊字符集(Extended Channel Interpretation Mode):主要用于特殊的字符集,并不是所有的掃描器都支持這種編碼;
混合編碼(Structured Append Mode):說明該二維碼中包含了多種編碼格式;
特殊行業(yè)編碼(FNC1 Mode):主要是給一些特殊的工業(yè)或行業(yè)用的,如GS1條形碼等;
2.3 數(shù)據(jù)編碼示例說明
分別用一個數(shù)字編碼與字符編碼的示例,說明數(shù)據(jù)編碼的過程:
2.3.1 例程1:數(shù)字編碼
問題:對于 Version 1 尺寸的二維碼,糾錯級別為 H,編碼為:01234567
解析步驟:
將上述數(shù)字分為三組:012, 345, 67;
查詢圖 2.2 表格內(nèi)容,Version 1 二維碼的數(shù)字編碼應(yīng)轉(zhuǎn)換為 10bits 的二進(jìn)制數(shù)字,故將上面三組數(shù)字轉(zhuǎn)為二進(jìn)制分別為:012→0000001100, 345→0101011001, 67→1000011;
將三個二進(jìn)制串連接起來:0000001100 0101011001 1000011;
將數(shù)字的個數(shù)轉(zhuǎn)成二進(jìn)制:對于數(shù)字編碼,數(shù)字長度依舊用圖 2.2 表格中查到的 10bits 二進(jìn)制數(shù)字來表示,數(shù)字共有 8 個,故數(shù)字個數(shù)的二進(jìn)制形式為:8→0000001000;
查詢圖 2.1 表格內(nèi)容,數(shù)字編碼的標(biāo)志為 0001,將編碼標(biāo)志與步驟 4 編碼結(jié)果加到步驟 3 結(jié)果之前,故最終結(jié)果為:0001 0000001000 0000001100 0101011001 1000011
2.3.2 例程2:字符編碼
問題:對于 Version 1 尺寸的二維碼,糾錯級別為 H,編碼為:AE-86
解析步驟:
在圖 2.3 的字符索引表中分別找到 AE-86 五個字符的索引分別為:(10, 14, 41, 8, 6);
將五個字符兩兩分組:(10, 14) (41, 8) (6);
字符編碼應(yīng)將字符組轉(zhuǎn)換為 11bits 的二進(jìn)制,故上述三組字符首先轉(zhuǎn)為 45 進(jìn)制后再轉(zhuǎn)為二進(jìn)制:
(10, 14):轉(zhuǎn)為 45 進(jìn)制:10×45+14=464;再轉(zhuǎn)為 11bits 的二進(jìn)制:00111010000;
(41, 8):轉(zhuǎn)為 45 進(jìn)制:41×45+8=1853;再轉(zhuǎn)為 11bits 的二進(jìn)制:11100111101;
(6):轉(zhuǎn)為 45 進(jìn)制:6;再轉(zhuǎn)為 6bits 的二進(jìn)制:000110;
將步驟 3 中得到的三個二進(jìn)制結(jié)果連接起來:00111010000 11100111101 000110;
查詢圖 2.2 表格內(nèi)容,Version 1 二維碼的字符個數(shù)應(yīng)轉(zhuǎn)換為 9bits 的二進(jìn)制數(shù)字,對于 5 個字符,二維碼字符個數(shù)轉(zhuǎn)為 9bits 二進(jìn)制為:000000101;
查詢圖 2.1 表格內(nèi)容,字符編碼的標(biāo)志為 0010,將編碼標(biāo)志與步驟 5 編碼結(jié)果加到步驟 4 結(jié)果之前,故最終編碼結(jié)果為:0010 000000101 00111010000 11100111101 000110;
三. 結(jié)束符與補(bǔ)齊符
對于結(jié)束符和補(bǔ)齊符,我們直接舉例進(jìn)行說明。
問題:對于 Version 1 尺寸的二維碼,糾錯級別為 H,以筆者的英文名作為編碼:CHANDLERGENG
按照 2.3.2 字符編碼例程進(jìn)行分析,得到編碼如下:
編碼 字符數(shù) CHANDLERGENG 的編碼
0010 000001101 01000101101 00111011001 01001011110 01010010001 01011011110 10000011011
3.1 結(jié)束符
在需要在對于上述字符的編碼,需要在最后加上結(jié)束符。結(jié)束符為連續(xù) 4 個 0 值。加上結(jié)束符后,得到的編碼如下:
編碼 字符數(shù) CHANDLERGENG 的編碼 結(jié)束
0010 000001101 01000101101 00111011001 01001011110 01010010001 01011011110 10000011011 0000
如果所有的編碼加起來不是 8 的倍數(shù),則還需要在后面加上足夠的 0。如上面一共有 83bits,所以與 8 的倍數(shù)還相差兩位,故在最后加上 5 個 0,上表最終的數(shù)據(jù)變?yōu)椋?nbsp;
00100000 01101010 00101101 00111011 00101001 01111001 01001000 10101101 11101000 00110110 00000000
3.2 補(bǔ)齊符
如果最后還沒有達(dá)到我們最大的 Bits 數(shù)限制,則需要在編碼最后加上補(bǔ)齊符(Padding Bytes)。
補(bǔ)齊符內(nèi)容是不停重復(fù)兩個字節(jié):11101100 和 00010001。這兩個二進(jìn)制轉(zhuǎn)成十進(jìn)制,分別為 236 與17,具體不知道為什么選這兩個值……關(guān)于每一個Version的每一種糾錯級別的最大Bits限制,可以參看 QR Code Spec 的第35頁到44頁的 Table-7 一表(筆者參考的是《ISO/IEC 18004》2000版),大致如下圖 3.1 所示:
圖3.1 二維碼糾錯級別的最大Bits限制(部分)
上圖 3.1 中提到的 codewords,可譯為碼字,一個碼字是一個字節(jié)。對于 Version 1 的 H 糾錯級別,共需要 26 個碼字,即 104bits。現(xiàn)在加上用 0 補(bǔ)全的結(jié)束符,已經(jīng)有了 88bits,故還需要補(bǔ)上 16 bits。補(bǔ)齊后的編碼為:
00100000 01101010 00101101 00111011 00101001 01111001 01001000 10101101 11101000 00110110 00000000 11101100 00010001
以上數(shù)據(jù)即為數(shù)據(jù)碼(Data Codewords)。
四. 糾錯碼
前文提到了不同的糾錯級別(Error Correction Code Level)。有了糾錯機(jī)制,才可以使得有些二維碼有了殘缺也可以掃碼解析出來,才可以使得二維碼中心位置可以供某些商家加上對解析不必要的圖標(biāo)。
二維碼一共有四種糾錯級別:
糾錯水平 可被修正容量
L 7% 碼字
M 15% 碼字
Q 25% 碼字
H 30% 碼字
二維碼對數(shù)據(jù)碼加上糾錯碼的過程,首先要對數(shù)據(jù)碼進(jìn)行分組,即分成不同的塊(Block)。參看如上圖 3.1 所示 QR Code Spec 的第35頁到44頁的 Table-7 中的最下方說明了分組的定義表:
圖4.1 二維碼糾錯級別說明(部分)
對于表中的最后兩列的內(nèi)容:
糾錯塊個數(shù)(Number of error correction blocks):需要劃分糾錯快的個數(shù);
糾錯塊碼字?jǐn)?shù)(Error Correction Code Per Blocks):每個塊中的碼字個數(shù),即有多少個字節(jié)Bytes;
表中最下面關(guān)于 (c,k,r) 的解釋:
c:碼字總個數(shù);
k:數(shù)據(jù)碼個數(shù);
r:糾錯碼容量
注:
c,k,r的關(guān)系公式:c=k+2×rc=k+2×r。
糾錯碼容量小于糾錯碼個數(shù)的一般
以上圖 4.1 中的 Version 5 + H 糾錯機(jī)為例:圖中紅色方框說明共需要 4 個塊(上下行各一組,每組 2 個塊)。
第一組的屬性:
糾錯塊個數(shù) = 2:該組中有兩個塊;
(c, k, r) = (33, 11, 11):該組中每個塊共有 33 個碼字,其中 11 個數(shù)據(jù)碼, 11×2=22 個糾錯碼;
第二組的屬性:
糾錯塊個數(shù) = 2:該組中有兩個塊;
(c, k, r) = (34, 12, 11):該組中每個塊共有 34 個碼字,其中 12 個數(shù)據(jù)碼, 11×2=22 個糾錯碼;
具體示例如下表所示,且由于使用二進(jìn)制會使得表格過大,故轉(zhuǎn)為范圍在 0~255 的十進(jìn)制。其中組 1 的每個塊,都有 11 個數(shù)據(jù)碼, 22 個糾錯碼;組 2 的每個塊,都有 12 個數(shù)據(jù)碼,22 個糾錯碼。
組 塊 數(shù)據(jù) 每個塊的糾錯碼
1 1
2 67 85 70 134 87 38 85 194 119 50 6
66 7 118 134 242 7 38 86 22 198 199 199 11 45 115 247 241 223 229 248 154 117 236 38 6 50 17 7 236 213 87 148 235
177 212 76 133 75 242 238 76 195 230 189 106 248 134 76 40 154 27 195 255 117 129
2 1
2 247 119 50 7 118 134 87 38 82 6 134 151
194 6 151 50 16 236 17 236 17 236 17 236 96 60 202 182 124 157 200 134 27 129 209 182 70 85 246 230 247 70 66 247 118 134
173 24 147 59 33 106 40 255 172 82 2 157 242 33 229 200 238 106 248 134 76 40
二維碼的糾錯碼主要是通過里德-所羅門糾錯算法(Reed-Solomon Error Correction)實(shí)現(xiàn)的。
(關(guān)于 Reed-Solomon 算法,現(xiàn)在此處占坑,回頭研究了再寫上去)
五. 最終編碼
此時得到了數(shù)據(jù),但還不能開始畫圖,因?yàn)槎S碼還需要將數(shù)據(jù)碼與糾錯碼的各個字節(jié)交替放置。
5.1 穿插放置
繼續(xù)以第四章中給出的示例為例,給出其穿插放置的過程。
5.1.1 數(shù)據(jù)碼穿插放置
第四章示例中的數(shù)據(jù)碼如下表所示:
塊數(shù)
塊1 67 85 70 134 87 38 85 194 119 50 6
塊2 66 7 118 134 242 7 38 86 22 198 199
塊3 247 119 50 7 118 134 87 38 82 6 134
塊4 194 6 151 50 16 236 17 236 17 236 17
提取每一列數(shù)據(jù):
第一列:67, 66, 247, 194;
第二列:85, 7, 119, 6;
……
第十一列:6, 199, 134, 17;
第十二列:151, 236;
將上述十二列的數(shù)據(jù)拼在一起:67, 66, 247, 194, 85, 7, 119, 6,…, 6, 199, 134, 17, 151, 236。
糾錯碼如下表所示:
塊數(shù)
塊1 199 11 45 115 247 241 223 229 248 154 117
塊2 177 212 76 133 75 242 238 76 195 230 189
塊3 96 60 202 182 124 157 200 134 27 129 209
塊4 173 24 147 59 33 106 40 255 172 82 2
同樣的方法,將 22 列數(shù)據(jù)放在一起:199, 177, 96, 173, 11, 212, 60, 24, …, 148, 117, 118, 76, 235, 129, 134, 40。
上述部分即為二維碼的數(shù)據(jù)區(qū)。
5.2 剩余位 (Remainder Bits)
對于某些 Version 的二維碼,得到上面的數(shù)據(jù)區(qū)結(jié)果長度依舊不足,需要加上最后的剩余位。比如對于 Version 5 + H 糾錯等級的二維碼,剩余位需要加 7bits,即加 7 個 0。參看 QR Code Spec 的 Table-1 一表即可查詢不同 Version 的剩余位信息,如下圖 5.1 所示:
圖5.1 不同 Version 的剩余位
六. 二維碼的繪制
終于講到二維碼繪制過程了,繪制的過程按照順序?qū)D 1.1 中各個重要部分依次講解。
6.1 定位圖案 (Position Detection Pattern)
首先在二維碼的三個角上繪制定位圖案。定位圖案與尺寸大小無關(guān),一定是一個 7×7 的矩陣。如下圖 6.1 所示:
圖6.1 定位圖案 (Position Detection Pattern)
6.2 對齊圖案 (Alignment Pattern)
然后繪制對齊圖案。對齊圖案與尺寸大小無關(guān),一定是一個 5×5 的矩陣。如下圖 6.2 所示:
圖6.2 對齊圖案 (Alignment Pattern)
對齊圖案繪制的位置,可參看 QR Code Spec 的 Table-E.1 一表查詢,部分內(nèi)容如下圖 6.3 所示:
圖6.3 對齊圖案位置索引表(部分)
下圖 6.4 是上述表格中 Version 8 的一個例子,對于 Version 8 的二維碼,行列值在 6, 24, 42 的幾個點(diǎn)都會有對齊圖案。
圖6.4 對齊圖案例程 1
下圖 6.5 是最近我老媽慫恿我用支付寶搶紅包時給我發(fā)來的二維碼,該二維碼中只有一個對齊圖案, 故 Version 應(yīng)在 V2——V6 之間。
圖6.5 對齊圖案例程 2
6.3 時序圖案 (Timing Pattern)
時序圖案是兩條連接三個定位圖案的線,如下圖 6.6 所示:
圖6.6 時序圖案例程 1
依舊拿支付寶紅包的二維碼為例,其時序圖案如圖 6.7 所示:
圖6.7 時序圖案例程 2
6.4 格式信息
格式信息如下圖 6.8 所示:
圖6.8 格式信息
格式信息在定位圖案周圍分布,由于定位圖案個數(shù)固定為 3 個,且大小固定,故格式信息也是一個固定 15bits 的信息。每個 bit 的位置如下圖 6.9 所示:(注:圖中的 Dark Module 是固定永遠(yuǎn)出現(xiàn)的)
圖6.9 格式信息位置
15bits 中數(shù)據(jù),按照 5bits 的數(shù)據(jù)位 + 10bits 糾錯位的順序排列:
數(shù)據(jù)位占 5bits:其中 2bits 用于表示使用的糾錯等級 (Error Correction Level),3bits 用于表示使用的蒙版 (Mask) 類別;
糾錯位占 10bits:主要通過 BCH Code 計算;
為了減少掃描后圖像識別的困難,最后還需要將 15bits 與 101010000010010 做異或 XOR 操作。因?yàn)槲覀冊谠袷叫畔⒅锌赡艽嬖谔嗟?0 值(如糾錯級別為 00,蒙版 Mask 為 000),使得格式信息全部為白色,這將增加分析圖像的困難。
糾錯等級的編碼如下圖 6.10 的表格所示:
圖6.10 糾錯等級編碼
關(guān)于蒙版圖案的生成,在后文 6.7 中具體說明。格式信息的示例如下:
假設(shè)存在糾錯等級為 M(對應(yīng) 00),蒙版圖案對應(yīng) 000,5bits 的數(shù)據(jù)位為 00101,10bits 的糾錯位為 0011011100:
則生成了在異或操作之前的 bits 序列為:001010011011100
與 101010000010010 做異或 XOR 操作,即得到最終格式信息:100000011001110
6.5 版本信息 (Version Information)
對于 Version 7 及其以上的二維碼,需要加入版本信息。如下圖 6.11 藍(lán)色部分所示:
圖6.11 版本信息
版本信息依附在定位圖案周圍,故大小固定為 18bits。水平豎直方向的填充方式如下圖 6.12 所示:
圖6.12 版本信息填充方式
18bits 的版本信息中,前 6bits 為版本號 (Version Number),后 12bits 為糾錯碼 (BCH Bits)。示例如下:
假設(shè)存在一個 Version 為 7 的二維碼(對應(yīng) 6bits 版本號為 000111),其糾錯碼為 110010010100;
則版本信息圖案中的應(yīng)填充的數(shù)據(jù)為:000111110010010100
6.6 數(shù)據(jù)碼與糾錯碼
此后即可填充第五章得到的數(shù)據(jù)內(nèi)容了。填充的思想如下圖 6.13 的 Version 3 二維碼所示,從二維碼的右下角開始,沿著紅線進(jìn)行填充,遇到非數(shù)據(jù)區(qū)域,則繞開或跳過。
圖6.13 二維碼數(shù)據(jù)填充(原始版)
然而這樣難以理解,我們可以將其分為許多小模塊,然后將許多小模塊串連在一起,如下圖 6.14 所示(截取自 QR Code Spec 的圖 15):
圖6.14 二維碼數(shù)據(jù)填充
小模塊可以分為常規(guī)模塊和非常規(guī)模塊,每個模塊的容量都為 8。常規(guī)情況下,小模塊都為寬度為 2 的豎直小矩陣,按照方向?qū)?8bits 的碼字填充在內(nèi)。非常規(guī)情況下,模塊會產(chǎn)生變形。
填充方式上圖 6.14,圖中深色區(qū)域(如 D1 區(qū)域)填充數(shù)據(jù)碼,白色區(qū)域(如 E15 區(qū)域)填充糾錯碼。遍歷順序依舊從最右下角的 D1 區(qū)域開始,按照蛇形方向(D1→D2→…→D28→E1→E2→…→E16→剩余碼)進(jìn)行小模塊的填充,并從右向左交替著上下移動。下面給出若干填充原則:
原則 1:無論數(shù)據(jù)的填充方向是向上還是向下,常規(guī)模塊(即 8bits 數(shù)據(jù)全在兩列內(nèi))的排列順序應(yīng)是從右向左,如下圖 6.15所示;
圖6.15 常規(guī)模塊內(nèi)的填充方向
原則 2:每個碼字的最高有效位(即第7個bit)應(yīng)置于第一個可用位。對于向上填充的方向,最高有效位應(yīng)該占據(jù)模塊的右下角;向下填充的方向,最高有效位占據(jù)模塊的右上方。
注:對于某些模塊(以下圖 6.17 為例),如果前一個模塊在右邊模塊的列內(nèi)部結(jié)束,則該模塊成為不規(guī)則模塊,且與常規(guī)模塊相比,原本填充方向向上時,最高位應(yīng)該在右上角,此時則變?yōu)樽笙陆牵?nbsp;
原則 3:當(dāng)一個模塊的兩列同時遇到對齊圖案或時序圖案的水平邊界時,它將繼續(xù)在圖案的上方或下方延續(xù);
原則 4:當(dāng)模塊到達(dá)區(qū)域的上下邊界(包括二維碼的上下邊界、格式信息、版本信息或分隔符)時,碼字中任何剩余 bits 將填充在左邊的下一列中,且填充方向反轉(zhuǎn);如下圖 6.16 中的兩個模塊遇到了二維碼的上邊界,則方向發(fā)生變化;
圖6.16 非常規(guī)模塊填充方向的改變(舉例于 QR Code Spec 圖 13)
原則 5:當(dāng)模塊的右一列遇到對齊圖案,或遇到被版本信息占據(jù)的區(qū)域時,數(shù)據(jù)位會沿著對齊圖案或版本信息旁邊的一列繼續(xù)填充,并形成一個不規(guī)則模塊。如果當(dāng)前模塊填充結(jié)束之前,下一個的兩列都可用,則下一個碼字的最高有效位應(yīng)該放在單列中,如下圖 6.17 所示:
圖6.17 模塊單列填充
6.7 蒙版圖案
按照上述思路即可將二維碼填充完畢。但是那些點(diǎn)并不均衡,如果出現(xiàn)了大面積的空白或黑塊,掃描識別會十分困難,所以按照在前文 6.4 中格式信息的處理思路,對整個圖像與蒙版進(jìn)行蒙版操作(Masking),蒙版操作即為異或 XOR 操作。
二維碼又 8 種蒙版可以使用,如下圖 6.18 所示,公式也在圖中說明。蒙版只會和數(shù)據(jù)區(qū)進(jìn)行異或操作,不會影響與格式信息相關(guān)的功能區(qū)。
注:選擇一個合適的蒙版也是有一定算法的。
蒙版圖案如下圖 6.18 所示,對應(yīng)的產(chǎn)生公式與蒙版 ID 如下圖 6.19 的表格所示:
圖6.18 蒙版圖案
圖6.19 蒙版圖案產(chǎn)生公式
蒙版操作的過程與對比圖如下圖 6.20 所示,圖中最上層是沒有經(jīng)過蒙版操作的原始二維碼,其中存在大量黑色區(qū)域,難以后續(xù)的分析識別。經(jīng)過兩種不同蒙版的處理,可以看到最后生成的二維碼變的更加混亂,容易識別。
圖6.20 蒙版操作示例
蒙版操作之后,得到的二維碼即為最終我們平常看到的結(jié)果。
七. 源碼
筆者原本準(zhǔn)備用 C++ 與 OpenCV 寫一個二維碼解析程序,現(xiàn)在學(xué)了二維碼的原理后,發(fā)現(xiàn)好難。另外網(wǎng)上關(guān)于二維碼解析與生成的程序基本都是用 Python 寫的,筆者又想找個合適機(jī)會學(xué)習(xí)一下 Python,所以這段時間就準(zhǔn)備從二維碼入手,學(xué)習(xí)一下 Python 的基礎(chǔ)~
源碼及解析筆者會隨學(xué)習(xí)的進(jìn)度持續(xù)更新~
八. 后記
筆者學(xué)習(xí)完畢二維碼內(nèi)容后不禁感嘆,二維碼規(guī)則的制定當(dāng)真是凝聚了多少研究者的心血。學(xué)無止境,在知識的海洋中,當(dāng)真是需要抱著敬畏之心和謙卑的態(tài)度,才能體會到這片海洋的浩瀚。
————————————————
2019-11
二維碼生成原理及解析代碼自從大街小巷的小商小販都開始布滿了騰訊爸爸和阿里爸爸的二維碼之后,我才感覺到我大天朝共享支付的優(yōu)越性。最近畢業(yè)論文寫的差不多了,在入職之前多學(xué)一些東西也是好的。這里秉著好奇心,研究一下二維碼的生成,并嘗試性寫一個二維碼解析源碼。注:暫時只有二維碼原理,筆者… [了解更多]
地 址:中國(上海)自由貿(mào)易試驗(yàn)區(qū)臨港新片區(qū)平港路883-885號1幢
郵政編碼:201411
電 話:13761963296
郵 箱:847098433@qq.com
投訴郵 箱:info@xtvu.com