QR 碼詳解(上)


關於二維碼,我查了下資料,現在基本都在用日本的 QR 碼,PDF417以及漢信碼日常基本看不到。原因在於各方面來說,的確是 QR 碼最為優秀。所以我准備寫一篇介紹 QR 碼的文章,如果是寫書,可能不方便寫得這么詳盡,但如果是網上的文章,就可以自由發揮了。寫完這篇文章,再抽取部分內容正規化,並整合其它內容形成書的第四章。為方便未來上課講解方便,以及快速畫圖,我還做了一個《QR助手程序》,幫助繪制 QR 碼中的各部分圖形,最后演化成一個二維碼繪制程序。本來並未打算要自己寫二維碼繪制程序的,網上有很多現成的開發包。但一方面為了讓自己更熟悉二維碼,另一方面,用實現一個簡單的二維碼繪制程序讓學生參考,可以有效降低學生的學習門檻。
 
QR 碼是日本發明的一種二維碼,也是目前應用最為廣泛的編碼。在中國,這種編碼幾乎無處不在,微信支付、支付寶支付、共享單車、各種廣告掃描關注等等。
 
1. QR 碼的特點
1. 存儲容量大
傳統的條形碼只能處理 20 位左右的信息,與此相比,QR 碼則可處理幾十倍上百倍的信息。另外,QR 碼還可以支持所有類型的數據(如:數字、英文字母、日文字母、漢字、二進制、控制碼等)。一個 QR 碼最多可處理 7089 個數字的巨大信息量。

2. 占用空間小
QR 碼使用縱向和橫向兩個方向處理數據,相同的信息量,QR 碼所占空間更小。下圖分別是 GS1 編碼 `(02)66901234000049(17)050101(37)10(10)ABC`的 GS1-128 編碼和 QR 碼圖形。我先在屏幕上繪制圖形,然后使用掃碼槍掃描,接下來縮小圖形,直到掃碼槍認不出為止。最后等比例放大,得到下面繪制面積對比。

 
3. 糾錯能力強
QR 碼具備“糾錯功能”,即使部分編碼變臟或破損,也可以恢復數據。數據恢復以碼字為單位(是組成內部數據的單位,在QR碼的情況下,每8比特代表1碼字),最多可以糾錯約30%(根據變臟和破損程度的不同,也存在無法恢復的情況)。

4. 可以從任意方向讀取
QR碼從360°任一方向均可快速讀取。QR碼中在 3 個定位圖案,可以幫助QR碼不受背景樣式的影響,實現快速穩定的讀取。傳統激光掃碼槍在掃描條碼時,只能使用一個方向進行掃描,但如果使用的是 CCD 掃描槍,則可以從任一方向掃描條碼。
 
2. QR 碼的結構
一個 QR 碼可以分為兩個部分:功能圖形和編碼區域。

功能圖形起到定位和校正圖形的作用,數據區記錄了具體的數據信息、糾錯信息和版本信息。

2.1 QR 碼符號版本和規格
QR 碼符號共有 40 種規格,分別為版本1、版本2……版本40。版本 1 的規格為21模塊×21模塊,版本 2 的規格為25模塊×25模塊,以此類推,每一版本符號比前一版本每邊增加4個模塊,直到版本40(規格為177模塊×177模塊)。
 

由上圖可知,版本1圖形被划分為21×21個小方塊,模塊指的就是這些小方塊,是 QR 碼繪制的最小單元,在繪制 QR 碼時,將存在數據的模塊填充為深色,最終組成 QR 碼圖形。下圖是版本40的模塊圖,由177×177個小方塊組成。


 2.2 尋像圖形
尋像圖形包括三個相同的位置探測圖形,分別位於二維碼左上角、右上角、左下角,每個位置探測圖形由7×7個模塊組成如下圖所示:

 
符號中其他地方遇到類似圖形的可能性極小,因此可以在視場中迅速地識別可能的 QR 碼符號。識別組成尋像圖形的三個位置探測圖形,可以明確地確定視場中符號的位置和方向。下圖是版本1和版本6的尋像圖形,由圖可知,版本號越高,尋像圖形在整個圖案中所占比例越小。


2.3 位置探測圖形分隔符
為方便識別位置探測圖形,在每個位置探測圖形和編碼區域之間有寬度為 1 個模塊的分隔符,如下圖黃色區域所示。此區域應全為空白,不能填入數據。


2.4 定位圖形
水平和垂直定位圖形分別為一個模塊寬的一行和一列,由深色與淺色模塊交替組成,其開始和結尾都是深色模塊。水平和垂直定位圖形分別位於第6行和第6列(行、列由0開始計數),並且避開位置探測圖形。它們的作用是確定符號的密度和版本,提供決定模塊坐標的基准位置。下圖是繪制了定位圖形后的版本1和版本6圖案。


2.5 校正圖形
校正圖形作為一個固定的參照圖形,在圖像有一定程度損壞的情況下,譯碼軟件可以通過它同步圖像模塊的坐標映像。每個校正圖形由5×5模塊組成。如下圖所示:


校正圖形的數量視符號和版本號而定,版本1沒有校正圖形,版本2及以上均含有校正圖形。校正圖形的數量視 QR 碼的版本號而定。下表列出了前14個版本的數據,完整數據請查看國家標准 GB/T 18284-2000 中的附錄 E。



下面講講如何看懂中心模塊的行/列坐標值。例如在版本 7 中,表中給出值 6、22 和 38。則校正圖形的中心點位置行、列坐標為(6,22)(22,6)(22,22)(22.38)(38,22)(38,38)。由於坐標(6,6)(6,38)(38,6)坐標位置被位置探測圖形占據,因此,這些坐標位置沒有放置校正圖形。說白了就是將中心模塊的行/列坐標值中的每個值分別與自己及其他值組成坐標,然后刪除左上、右上、左下坐標,即得出結果。

下圖是版本 1 及版本 7 的校正圖形:


下圖是版本 15 及版本 40 的校正圖形:


沒有固定公式計算中心模塊的行/列坐標值,但觀察以上 4 張校正圖形,可以發現,QR 碼設計者在選擇坐標值時,使得第一行和第一列校正圖形正好融合進定位圖形,不會使定位圖形遭到破壞。

3. 數據編碼
各種用於圖像識別的功能圖形已經安放完畢,下一步就是對數據進行編碼了。標准 QR 碼可編碼數據類型有四種:數字、字母數字、8位字節和 Kanji,其中 Kanji 為日文日文字符。我們國家標准 GB/T 18284-2000 增加了 QR 碼的表示范圍。下表是 QR 碼可表示的數據類型及其相應的模式指示符。
 
模式 指示符    模式 指示符
ECI 0111   日文  1000 
數字 0001    中文 1101
字母數字 0010   FNC1 0101(第一位置)
8位字節 0100    FNC1 1001(第二位置)
表 1:模式指示符

ECI 是什么東西,我很想搞懂,但很遺憾網上幾乎沒有資料,AIM ECI 規范的外文資料,需收費下載。沒辦法,等以后再說吧。現在只知道 AIM 是一家公司,ECI 是規范,有了這個規范,有了這個東西,可以使用二維碼裝載各類不同的字符集,也可使接收系統在數據被使用之前知道做哪些特定的擴展或解密處理。

FNC1 相信讀過前面寫的《條碼技術》這篇文章中的 GS1-128 條碼的都應當很熟悉,這類編碼是專為 GS1 標准准備的。本文僅針對數字、字母數字、8 位字節以及 FNC1 模式進行講解,示例程序也只實現這四種編碼方式。

3.1 數字模式
數字模式只能對十進制數字 0~9 進行編碼,通常的密度為 10 位表示 3 個字符。

3.1.1 將數字轉換為位流
將要表示的數字從左邊開始每三位分為一組,然后將每組數據轉換為 10 位二進制(10 位二進制可表示的最大數字為 1023)。如果最后一組數字只剩兩位,則使用 7 位二進制表示(7 位二進制可表示的最大數字為 127)這兩個數字。如果最后一組數字只剩一位,則使用4位二進制表示最后一個數字(4 位二進制可表示的最大數字為 15)。將二進制數據連接起來並在前面加上模式指示符和字符計數指示符。

**【例1】:** 數字`01234567`:
1. 分為 3 位一組:012  345  67
2. 將每組轉換為二進制:
    012 -> 0000001100
    345 -> 0101011001
     67 -> 1000011
3. 將二進制連接為一個序列:0000001100 0101011001 1000011

3.1.2  將字符計數指示符轉換為二進制
字符計數指示符表示裝載字符的個數,長度隨不同的 QR 碼版本以及模式而不同。表 2 列出了各版本及模式所對應的字符計數指示符的長度。
 
版本 數字模式 字母數字模式  8 位字節模式  中國漢字模式
1~9 10 9 8 8
10~26 12 11 16 10
27~40  14 13 16 12
表 2:字符計數指示符的位數

數據的數字位數為 8,查上表數字模式列,如果使用版本 1 來表示,則位數為 10。最終:
8 -> 0000001000

#### 加入模式指示符及字符計數指示符
查表 1 ,數字模式的指示符為 0001。以【模式指示符 + 字符計數指示符 + 數字位流】的方式連接數據最終位流為:

0001 0000001000 0000001100 0101011001 1000011

接下來再做一個例子加深理解:

**【例2】:** 數字`0123456789012345`:
1. 分為 3 位一組:012  345  678  901  234  5
2. 將每組轉換為二進制:
    012 -> 0000001100
    345 -> 0101011001
    678 -> 1010100110
    901 -> 1110000101
    234 -> 0011101010
      5 -> 0101
3. 將二進制連接為一個序列:
    0000001100 0101011001 1010100110 1110000101 0011101010 0101

4. 數字位數為 16,使用版本 1 表示,位數為 10。最終:
16 -> 0000010000

5. 加上數字模式的指示符 0001 最終位流為:
    0001 0000010000 0000001100 0101011001 1010100110 1110000101 0011101010 0101

3.2 字母數字模式
字母數字模式對 45 個字符的字符集進行編碼,即:10 個數字 0~9,26 個大寫字母 A~Z,以及 9 個符號 SP、$、%、*、+、-、.、/。通常情況下,兩個輸入字符用 11 位表示。下表是 45 個字符及其編碼:
字符   字符   字符   字符   字符
0 0   9 9   I 18   R 27   SP 36
1 1   A 10   J 19   S 28   $ 37
2 2   B 11   K 20   T 29   % 38
3 3   C 12   L 21   U 30   * 39
4 4   D 13   M 22   V 31   + 40
5 5   E 14   N 23   W 32   - 41
6 6   F 15   O 24   X 33   . 42
7 7   G 16   P 25   Y 34   / 43
8 8   H 17   Q 26   Z 35   : 44

要完整表示 45 個字符中的一個,需要至 6 個位的二進制數字($2^6=64$),那么每兩個字符需要占用 12 個位的空間。QR 碼使用了另一種編碼方法,將表示兩個字符壓縮為使用 11 個位的空間。

將輸入的數字分為兩個字符一組,將左邊的字符的值乘以 45 與第二個字符的值相加,將所得的結果轉換為 11 位二進制數。最大字符組為"::"(44×45+44=2024,而2 11=2048)。如果輸入的數據的字符不是 2 的倍數,將最后一個字符編碼為 6 位二進制數。將所得的二進制數連接起來並在前面加上模式指示符和字符計數指示符,得到最終編碼。

**【例 3】:** 數據`AC-42`,QR 碼版本號 1:
1. 根據表 5 查出字符的值:AC-42 -> (10,12,41,4,2)
2. 將結果分為 2 個一組:(10,12)(41,4)(2)
3. 將每組數據轉換為 11 位二進制數:
    (10,12) -> 10×45+12=462 -> 00111001110
    (41,4) -> 41×45+4=1849 -> 11100111001
    (2) -> 2 -> 000010
4. 二進制數據順次連接得到數據位流: 00111001110 11100111001 000010
5. 字符數為 5,查表2,使用 9 位二進制數表示。將字符計數指示符轉換為二進制,
    5 -> 000000101
6. 查表 1,模式指示符為 0010。以【模式指示符 + 字符計數指示符 + 數字位流】的方式連接數據最終位流為:
    0010 000000101 00111001110 11100111001 000010

3.3  8 位字節模式
在 8 位字節模式中,一個 8 位碼字直接表示一個輸入數據字符的 ASCII 字符值(ASCII 譯碼表請查[這里](http://iotxfd.cn/demo/ascii.html))。即密度為每個字符 8 位。將二進制數據連接起來並在前面加上模式指示符和字符計數指示符,得到最終編碼。

**【例 4】:** 數據`Ab>Cd`,QR 碼版本號 1:
1. 在 ASCII表中查出各字符的值,並轉換為 8 位二進制:
    A -> 65 -> 01000001
    b -> 98 -> 01100010
    `>` -> 62 -> 00111110
    C -> 67 -> 01000011
    d -> 100 -> 01100100
2.  二進制數據順次連接得到數據位流:01000001 01100010 00111110 01000011 01100100
3. 字符數為 5,查表2,使用 8 位二進制數表示。將字符計數指示符轉換為二進制,
    5 -> 00000101
4. 查表 1,模式指示符為 0100。以【模式指示符 + 字符計數指示符 + 數字位流】的方式連接數據最終位流為:
    0100 00000101 01000001 01100010 00111110 01000011 01100100

3.4 混合模式
之前在學習 Code 128 編碼時大家應該還記得,為了達到符號長度最小的目的,Code 128 條碼支持混合編碼的方式。QR 碼也支持以混合方式進行編碼。其基本結構為【模式指示符 + 字符計數指示符 + 數據】,其后緊跟下一段的指示符開始另一段。下圖為有 n 段數據的結構。


由於 GS1 標准僅支持 ASCII 碼,這里只針對數字模式、字母數字模式、8 位字節模式的混合編碼進行討論。以下是針對上述模式的編碼規則,完整編碼規則請參考 GB/T 18284-2000 的附錄 H。

3.4.1 位流長度最優化的編碼規則
下列是形成最短位流的算法的基礎。在方括號中的字符數如[5,7,9]分別用於版本 1~9,10~26 和版本 27~40。

1. 選擇初始模式
    * 如果初始輸入數據是在 8 位字節的二進制字符的專有子集中,選擇 8 位字節模式;
    * 如果初始輸入數據是在字母數字字符集的專有子集中,並且如果字符個數少於[6,7,8],其后緊跟 8 位字節專有子集中的數據,那么選擇 8 位字節模式,否則選擇字母數據模式;
    * 如果初始數據是數字,並且如果數字個數少於[4,4,5],其后緊跟 8 位字節字符集專有子集中的數據,那么選擇 8 位字節模式,否則如果少於[6,7,8]后隨字母數字字符集的專有子集中的數據,那么選擇字母數字模式,否則選擇數字模式。
2. 在 8 位字節模式中
    * 如果有不少於[6,8,9]的數字字符序列出現在來自 8 位字節二進制字符集的專有子集的多個數據前,那么轉至數字模式;
    * 如果有字母數字字符集的專有子集的不少於[11,15,16]的字符序列出現在來自 8 位字節二進制字符集的專有子集的數據前,那么轉至字母數字模式。
3. 在字母數字模式中
    * 如果有 8 位字節字符集的專有子集的一個或多個字符出現,轉至 8 位字節模式;
    * 如果有不少於[13,15,17]的數字字符數據序列在來自字母數字字符集的專有子集的數據前出現,轉至數字模式。
4. 在數字模式中
    * 如果有一個或多個來自 8 位字節的專有子集中的字符出現,轉至 8 位字節模式;
    * 如果有一個或多個字母數字的專有子集中的字符出現,轉至字母數字模式。

> 編者注:上述規則摘抄至 GB/T 18284-2000,最后一句是有問題或者不完善的。如果在數字模式中出現字符`Aabcd`,因為`A`屬於字母數字字符集,而`abcd`只屬於 8 位字節字符集。此時按最后一句話,需先轉換為字母數字模式,加入字符`A`,然后馬上又轉換為 8 位字節模式。很顯然,這樣做浪費了空間。由於字母數字字符集是 8 位字節字符集的子集,正確的做法應當是轉換為 8 位字節模式,然后加入字符`Aabcd`。

**【例 5】:** 數據`123456ABC123`,QR 碼版本號 1:
1. 根據編碼規則,首先應選擇數字模式作為初始模式:0001
2. 加入字符計數指示符:6 -> 0000000110
3. 加入 123 編碼:123 -> 0001111011
4. 加入 456 編碼:456 -> 0111001000
5. 根據編碼規則,轉換為字母數字模式:0010
6. 加入字符計數指示符:6 -> 000000110
7. 加入 AB 編碼:(10,11) -> 10×45+11=461 -> 00111001101
8. 加入 C1 編碼:(12,1) -> 12×45+1=541 -> 01000011101
9. 加入 23 編碼:(2,3) -> 2×45+3=93 -> 00001011101

將以上生成的二進制數據從上至下連接,生成最終位流。

3.5 FNC1 模式
FNC1 模式有兩種模式指示符,用於標識按特定的行業或應用格式化信息的符號。FNC1 在第一位置時支持 GS1 標准,FNC1 在第二位置時支持按 AIM 認可的特定行業或者特定應用規范格式化信息。由於 AIM 查不到資料,這里只介紹 FNC1 第一位置模式,即模式指示符為:0101。

在 GS1-128 中,FNC1 用於數據段分隔,而 QR 碼中沒有專用的 FNC1 字符,在字母數字模式中用 % 字符代替,如果在數據中也有 % 符號出現,則 應按 %% 進行編碼。在 8 位字節模式中用字符 GS(ASCII 值 29)代替。

> 編者注:個人感覺,這個設計方式相比 GS1-128 來說,並不太合理。即使數據只是純數字,但只要在其中需要分段,安置 FNC1 ,就必須轉為字母數字模式,如果后面還有足夠多的數字,還得再次轉回來。這樣做的成本太高了。數字模式,還有部分未使用的編碼,應當有自已的 FNC1,即使這個編碼占據 10 個位,還是合算的。

**【例 6】:** 數據`(02)66901234000049(17)050101(37)10(10)ABC`,QR 碼版本號 1:
首先分析數據中的 AI:(02)表示物流單元內貿易項目,長度固定;(17)表示有效期,長度固定;(37)表示物流單元內貿易項目數量,長度不固定,需在此 AI 最后加入 FNC1;(10)表示批號,長度不固定,它為最后一個 AI,無需加入 FNC1 標志。最終編碼變為:

`(02)66901234000049(17)050101(37)10<FNC1>(10)ABC`

編碼過程:

1. 將 FNC1 第一位置作為初始模式:0101
2. 根據位流長度最優化的編碼規則,加入數字模式指示符:0001
3. 加入字符計數指示符:28 -> 0000011100
4. 加入 026 編碼:026 -> 0000011010
5. 加入 690 編碼:690 -> 1010110010
6. 加入 123 編碼:123 -> 0001111011
7. 加入 400 編碼:400 -> 0110010000
8. 加入 004 編碼:004 -> 0000000100
9. 加入 917 編碼:917 -> 1110010101
10. 加入 050 編碼:050 -> 0000110010
11. 加入 101 編碼:101 -> 0001100101
12. 加入 371 編碼:371 -> 0101110011
13. 加入 0 編碼:0 -> 0000
14. 根據編碼規則,轉換為字母數字模式:0010
15. 加入字符計數指示符:6 -> 000000110
16. 加入 %1 編碼:(38,1) -> 38×45+1=1711 -> 11010101111
17. 加入 0A 編碼:(0,10) -> 0×45+10=10 -> 00000001010
18. 加入 BC 編碼:(11,12) -> 11×45+12=507 -> 00111111011

將以上生成的二進制數據從上至下連接,生成最終位流。

3.6  終止符
符號的數據結尾由緊跟在最后一個模式段后面的終止符序列 0000 表示,當數據位流數量正好填滿符號容量時,它可以省略,或者當符號所余的容量不足 4 位時它可以截短。

3.7  位流到碼字的轉換
每個模式段的位流需要按順序連接在一起,最后添加終止符,除非數據位流正好填滿符號容量。所得的數據位流將被分為一個個碼字;所有碼字長度都是 8 位,如果位流長度最后一個碼字不足 8 位,則用二進制值為 0 的填充位填充至 8 位,填充位應加在數據位流最后 1 位(最低位)的后面。說白了就是將位流進行 8 位對齊。

我們以【例 1】最終生成的數據位流為例說明上述過程。

1. 【例 1】最終生成的數據位流:000000110001010110011000011
2. 加入終止符 0000,(現在符號容量足夠,不用考慮容量不足的情況):
    0000001100010101100110000110000
3. 將數據位流划分為碼字,這里在每 8 個位后加一個逗號表示:
    00000011,00010101,10011000,0110000
4. 我們發現最后一個碼字只有 7 位數字,不足 8 位,補 1 個零,最終數據變為:
    00000011,00010101,10011000,01100000

最終【例 1】生成的數據使用了 4 個碼字。

接下來就要查表了,下表列出了各個版本 QR 碼的碼字容量,這里只列出一小部分,完整表格請參考 GB/T 18284-2000。


注意表格中的紅框部分,由於只使用了 4 個碼字,我們完全可以選擇最高糾錯等級 H。所以最終選擇為:版本 1 的 H 糾錯等級 QR 碼。在此等級中可放置 9 個碼字,還剩余 5 個碼字是不能放空的,需要交替填充碼字 11101100 和 00010001,直至填滿整個 9 個碼字。

5. 將空位填滿至 9 個碼字,最終數據變為:
    00000011,00010101,10011000,01100000,11101100,00010001,11101100,00010001,11101100

好!上半場結束,圖多,還是分兩篇寫吧。下半場內容就比較抽象了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM