如何製作 QR Code #7:功能圖案與版本資訊
現在我們進入 QR Code 中 Yeecy 覺得最有趣的部分囉!
在這篇文章中,我們會了解如何填入定位圖案、分隔圖案、對齊圖案等等的功能圖案,以及加入版本資訊,如果讀者不清楚上面的名詞,請移步《如何製作 QR Code #1:基本介紹》。
進入正題前,我們令 v 表 QR Code 的版本,r 表 row,c 表 column,m 表 module(碼元),且規定 r、c 和 m 形成有序對 (r, c, m),其中 r 與 c 的取值範圍為 0 到 20 + 4×(v−1) 中的整數,而 m 的取值為 0 或 1,若 m 為 1,表示黑色,反之為白色。
由上可知:
- 有序對 (0, 0, 1) 代表圖片最左上的碼元是黑色的。
- 一個圖案,可以視為包含數個有序對 (r, c, m) 的集合。
另外再提一句,有 (r, c, m) 就可以用上一篇文章提過的 Canvas 著色了。
填入定位圖案
現在需要知道定位圖案該放在那些地方,顯然,根據 QR Code 版本的不同,定位圖案要放置的絕對位置就會不同。
我們知道定位圖案有三個,分別位於左上、右上和左下,參考上圖,我們可以先創造出下面的陣列:
[[1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1]]
不難看出這個陣列所對應的,是定位圖案中碼元的顏色,接著我們可以用這個陣列配上雙重迴圈,得到左上定位圖案的 (r, c, m) 集合:
(0, 0, 1), (0, 1, 1), (0, 2, 1), (0, 3, 1), (0, 4, 1),
(0, 5, 1), (0, 6, 1), (1, 0, 1), (1, 1, 0), (1, 2, 0),
(1, 3, 0), (1, 4, 0), (1, 5, 0), (1, 6, 1), (2, 0, 1),
(2, 1, 0), (2, 2, 1), (2, 3, 1), (2, 4, 1), (2, 5, 0),
(2, 6, 1), (3, 0, 1), (3, 1, 0), (3, 2, 1), (3, 3, 1),
(3, 4, 1), (3, 5, 0), (3, 6, 1), (4, 0, 1), (4, 1, 0),
(4, 2, 1), (4, 3, 1), (4, 4, 1), (4, 5, 0), (4, 6, 1),
(5, 0, 1), (5, 1, 0), (5, 2, 0), (5, 3, 0), (5, 4, 0),
(5, 5, 0), (5, 6, 1), (6, 0, 1), (6, 1, 1), (6, 2, 1),
(6, 3, 1), (6, 4, 1), (6, 5, 1), (6, 6, 1)
我們之後還會用到這種技巧。
現在有了左上定位圖案的 (r, c, m) 集合,我們就可以透過「位移」的方式,得到剩餘定位圖案的 (r, c, m) 集合。
舉例來說,如果對左上定位圖案中的所有 c 都加上 1,就代表我們將該定位圖案向右移了一個碼元的距離。
至於要移動多少才能得到剩餘的兩個定位圖案,就留給讀者自己嘗試了。
填入分隔圖案
分隔圖案的 m 值永遠是 0,至於剩下的 r 和 c,只能透過分別找出三個分隔圖案的位置通式來得到,雖然麻煩,不過不是件難事。
左上分隔圖案的 (r, c, m) 集合如下:
(0, 7, 0), (1, 7, 0), (2, 7, 0),
(3, 7, 0), (4, 7, 0), (5, 7, 0),
(6, 7, 0), (7, 7, 0), (7, 0, 0),
(7, 1, 0), (7, 2, 0), (7, 3, 0),
(7, 4, 0), (7, 5, 0), (7, 6, 0)
填入黑色碼元
黑色碼元的 (r, c , m) 為 (4v+9, 8, 1),直接填入就行。
填入定時圖案
定時圖案夾在兩個分隔圖案之間,注意到水平定時圖案的 r 皆為 6,垂直定時圖案的 c 皆為 6。
填入對齊圖案
還記得我們在《如何製作 QR Code #1》中提到,版本 1 的 QR Code 是不需要加入對齊圖案的,至於其他版本的 QR Code 要怎麼填對齊圖案呢?答案又是查表,表列如下:
version | row/column coordinates of center module
--------+-----------------------------------------
2 | 6, 18
3 | 6, 22
4 | 6, 26
5 | 6, 30
--------+-----------------------------------------
6 | 6, 34
7 | 6, 22, 38
8 | 6, 24, 42
9 | 6, 26, 46
10 | 6, 28, 50
--------+-----------------------------------------
11 | 6, 30, 54
12 | 6, 32, 58
13 | 6, 34, 62
14 | 6, 26, 46, 66
15 | 6, 26, 48, 70
--------+-----------------------------------------
16 | 6, 26, 50, 74
17 | 6, 30, 54, 78
18 | 6, 30, 56, 82
19 | 6, 30, 58, 86
20 | 6, 34, 62, 90
--------+-----------------------------------------
21 | 6, 28, 50, 72, 94
22 | 6, 26, 50, 74, 98
23 | 6, 30, 54, 78, 102
24 | 6, 28, 54, 80, 106
25 | 6, 32, 58, 84, 110
--------+-----------------------------------------
26 | 6, 30, 58, 86, 114
27 | 6, 34, 62, 90, 118
28 | 6, 26, 50, 74, 98, 122
29 | 6, 30, 54, 78, 102, 126
30 | 6, 26, 52, 78, 104, 130
--------+-----------------------------------------
31 | 6, 30, 56, 82, 108, 134
32 | 6, 34, 60, 86, 112, 138
33 | 6, 30, 58, 86, 114, 142
34 | 6, 34, 62, 90, 118, 146
35 | 6, 30, 54, 78, 102, 126, 150
--------+-----------------------------------------
36 | 6, 24, 50, 76, 102, 128, 154
37 | 6, 28, 54, 80, 106, 132, 158
38 | 6, 32, 58, 84, 110, 136, 162
39 | 6, 26, 54, 82, 110, 138, 166
40 | 6, 30, 58, 86, 114, 142, 170
那這個表要怎麼看呢?舉版本 7 為例,7 後面的數字有 6、22 和 38,我們先把這些數字用集合符號記作 {6, 22, 38},接著讓自己和自己做笛卡爾積,可以得到:
(6, 6), (6, 22), (6, 38),
(22, 6), (22, 22), (22, 38),
(38, 6), (38, 22), (38, 38)
這九個有序對所代表的是 (r, c),為對齊圖案的可能中心點,為什麼說是可能呢?那是因為有些位置與已經放置好的圖案重疊了,例如 (6, 6) 位於左上定位圖案裡,不能放置對齊圖案,所以不是合格的中心點。
因為我們已經有了其他圖案的 (r, c, m),所以可以輕鬆的過濾掉不合格的中心點,不過要記得,定時圖案是不需要加入判斷的,參考配圖就會知道為什麼囉。
過濾完後,剩下的六個合格中心點為:
(6, 22), (22, 6), (22, 22),
(22, 38), (38, 22), (38, 38)
接著用之前定位圖案使用的方法,建立陣列為:
[[1, 1, 1, 1, 1],
[1, 0, 0, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]]
配合雙重迴圈與中心點,就可以產生代表所有對齊圖形的 (r, c, m) 集合了。
填入版本資訊
以前說過,版本資訊需要填入 18 個碼元,但是我們不僅僅只是把版本轉成長度為 18 的二進位數,而是要使用格雷碼(Golay code)編碼。
雖然看起來好像又遇到大麻煩了,不過因為版本 7 以上才需要加上版本資訊,而 7 到 40 總共也才 34,可以直接把編碼後的值直接做成表,表列如下:
version | version information bit stream
--------+--------------------------------
7 | 000111110010010100
8 | 001000010110111100
9 | 001001101010011001
10 | 001010010011010011
--------+--------------------------------
11 | 001011101111110110
12 | 001100011101100010
13 | 001101100001000111
14 | 001110011000001101
15 | 001111100100101000
--------+--------------------------------
16 | 010000101101111000
17 | 010001010001011101
18 | 010010101000010111
19 | 010011010100110010
20 | 010100100110100110
--------+--------------------------------
21 | 010101011010000011
22 | 010110100011001001
23 | 010111011111101100
24 | 011000111011000100
25 | 011001000111100001
--------+--------------------------------
26 | 011010111110101011
27 | 011011000010001110
28 | 011100110000011010
29 | 011101001100111111
30 | 011110110101110101
--------+--------------------------------
31 | 011111001001010000
32 | 100000100111010101
33 | 100001011011110000
34 | 100010100010111010
35 | 100011011110011111
--------+--------------------------------
36 | 100100101100001011
37 | 100101010000101110
38 | 100110101001100100
39 | 100111010101000001
40 | 101000110001101001
接著來說怎麼放置版本資訊,放置在上方的版本資訊,需要按照以下順序填入,數字代表填入位置,由數字最小的位置開始:
17, 16, 15
14, 13, 12
11, 10, 9
8, 7, 6
5, 4, 3
2, 1, 0
放在下方的版本資訊,順序為:
17, 14, 11, 8, 5, 2
16, 13, 10, 7, 4, 1
15, 12, 9, 6, 3, 0
附圖為版本 7 QR Code 填入相應版本資訊 000111110010010100 的演示,對照上面的說明應該能理解該如何放置。
結語
可以親眼看到自己真的在一步一步實現 QR Code 產生器,真的是一件讓人開心的事,比起看著那些 0 和 1 構成的序列,圖片看起來舒服多了。
下一篇文章我們將會介紹怎麼放置在《如何製作 QR Code #5》中得到的訊息字串,並填入格式資訊。
感謝你的閱讀,我是 Yeecy,我們下一篇文章見。