如何製作 QR Code #9:資料遮罩與完成圖片
本文是生成篇的最後一篇囉,我們會在這裡認識 8 種遮罩的樣式,了解如何挑選最適合的遮罩,並完成完整圖片。
資料遮罩(data mask)
遮罩樣式
這次我們要來認真講解上一篇文章沒講到的 condition 欄位。
mask pattern mask | condition
-------+----------------------------
000 | (r + c) % 2 = 0
001 | r % 2 = 0
010 | c % 3 = 0
011 | (r + c) % 3 = 0
100 | (c//3 + r//2) % 2 = 0
101 | (r * c)%2 + (r * c)%3 = 0
110 | ((r * c)%3 + r*c) % 2 = 0
111 | ((r * c)%3 + r + c) % 2 = 0
上面式子中的 //
表示整除,*
表示乘法,%
表示模除。
這時候可能有讀者會好奇,怎麼這張表對應到的式子,跟在其他網站上看到的不同。
原因很簡單,因為在上一篇文章中,我們提到格式資訊需要套用格式資訊的遮罩,上表的編號是未經過遮罩處理的,而其他網站上的是已經經過遮罩處理的,而容錯等級會不同也是一樣的原因。
我們看 010 對應的 c % 3 = 0
,我們假設 (r, c, m) 的值為 (0, 0, 1),首先,我們看到 c 的值為 0,我們計算 0 % 3,得值為 0,接著再評估 0 = 0,可以知道這個敘述為真(true),所以我們應該把 m 的值翻轉,改成 0,也就是 (0, 0, 1),套用 010 遮罩後,會改變為 (0, 0, 0)。
因為敘述為真才翻轉,我們把真視為 1,假(false)視為 0,那麼經過處理的 (r, c, m) 可以敘述為:
(r, c, XOR(m, mask(r, c))
令 mask = (c % 3 = 0),我們重新描述前面的例子,可以得到:
(0, 0, XOR(1, (c % 3 = 0)(0, 0)))
整理一下得:
(0, 0, XOR(1, 0 % 3 = 0))
因為 0 % 3 為 0,且 0 = 0 為真,得:
(0, 0, XOR(1, 1))
又因為 XOR(1, 1) 為 0,我們得到套用遮罩後的新 (r, c, m) 為:
(0, 0, 0)
再舉一個例子,假設 (r, c, m) = (14, 23, 1),使用遮罩 100,我們知道:
(r, c, XOR(m, mask(r, c)) = (14, 23, XOR(1, ((c//3 + r//2) % 2 = 0)(14, 23)))
先整理可得:
(14, 23, XOR(1, ((23//3 + 14//2) % 2 = 0))
接著計算等號左邊:
(14, 23, XOR(1, (0 = 0))
接著判斷等式真假:
(14, 23, XOR(1, 1))
經 XOR 運算後可以得到:
(14, 23, 0)
雖然看起來很複雜,但是程式寫起來是很輕鬆的,只需要短短幾行而已,接下來讓我們看看各個遮罩本身的樣子吧!
套用遮罩
要套用遮罩非常簡單,如果上面有看懂的話就會知道,只需要用雙重迴圈來執行異或運算就可以辦到了,記得不要把遮罩套用到功能圖案、版本資訊和格式資訊喔!
附圖為上一篇文章文末範例,套用遮罩 000 的情況。
選擇最合適遮罩
基本上,不管套用哪一個遮罩,製作出來的 QR Code 都是可以使用的,讀者可以試著掃描以下八張內容都為Yeecy is the best!
,但是遮罩不同的 QR Code。
雖然八張 QR Code 都是合格的,不過標準規定了一個流程,來決定該使用哪張 QR Code。
要挑選出一張最好的 QR Code 的原因,主要是為了 QR Code 能被辨識的更加清楚。
那要怎麼挑選呢?我們要幫 QR Code 打分數。標準敘明,我們要針對四項特徵進行評分,對八個 QR Code 都評分後,挑選四項評分總和最低的 QR Code 做為最優的 QR Code。
特徵一:連續同色碼元
當水平或垂直方向出現連續五個同顏色的碼元,則分數一加 3,當出現超過五個連續同色碼元時,每多一個,則分數多加 1。
■ ■ ■ ■ ■ ■ ■ □ □ □ ■ ■ □ □ □
-------------
以上面的例子來說,因為出現了 7 個連續的 ■,所以分數為基本分的 3 分,加上超出兩個所得的額外 2 分,共取得 5 分。
特徵二:2×2 同色方塊
當我們在圖片中找到一個 2×2 的同色方塊,分數二就加 3 分,假設存在一個 2×3 的同色方塊,應該將其視為兩個 2×2 的同色方塊計算。
■ ■ □ □ □
■ ■ □ □ □
■ ■ □ □ □
□ ■ □ ■ ■
□ □ □ □ □
我們從上面的例子中,可以找到 2 個 2×2 的 ■ 方塊,和 4 個 2×2 的 □ 方塊,所以分數二為 6×3 = 18 分。
特徵三:■□■■■□■ 圖案
當我們在圖片的水平或垂直方向發現 □□□□■□■■■□■ 或 ■□■■■□■□□□□ 的圖案時,分數三加 40 分。
■ ■ □ □ □ □ □ □ ■ □ ■ ■ ■ □ ■ □ □ □ □ □ ■ ■
---------------------
---------------------
如上所示,因為出現了兩個指定圖案,所以分數三應為 40×2 = 80 分。
特徵四:黑色碼元佔比
首先,我們計算黑色碼元的總數和白色碼元的總數,然後計算黑色碼元佔全體碼元的百分比,接下來以 5% 為級距,計算該百分比與 50% 差了幾個級距,若差距級距數為小數,則無條件捨去,最後將相差級距的數量乘上 10,即為分數四。
■ ■ □ □ □
■ ■ □ □ □
■ ■ □ □ □
□ ■ □ ■ ■
□ □ □ □ □
我們以 ■ 為黑色碼元,□ 為白色碼元,則上圖的黑色碼元數為 9,總碼元數為 25,計算可得黑色碼元佔全體碼元的百分比為 36%,因為 36% 與 50% 相距 2.8 個級距,將 0.8 無條件捨去得到 2 ,再乘上 10,得到分數四為 20。
挑選 QR Code:以上面八張圖為例
為了不重複貼圖占版面,以下列出上面八張 QR Code 的各項分數:
Version: 2-L
Mode: Byte (Latin-1)
Message: Yeecy is the best! Score
Mask 1 2 3 4 tot
000 211 219 80 0 500
001 209 159 120 0 488
010 246 171 160 0 577
011 242 198 40 0 480
100 245 174 0 0 419 ✓
101 241 195 120 0 556
110 248 195 40 0 483
111 247 228 40 0 515
從上表可知,我們應該挑選遮罩為 100 的 QR Code 作為最終圖片。
靜默區域(quiet zone)
選出最後的 QR Code 後,我們必須在其外圍加上寬度為 4 個白色碼元的靜默區域。
下圖即為加上靜默區域的最終成品,對於白色背景的讀者,可以將背景改成黑色,或是透過下載圖片來看到外圍加上的白色靜默區域。
結語
經過了九篇文章,從剛開始認識各個組成圖案,到完成添加靜默區域,現在,我們可以驕傲地跟別人說,我們完成屬於自己的 QR Code 產生器了!
希望讀者能喜歡這一系列文章,至此,跟產生 QR Code 相關的文章都已經完成了,至於解碼,事實上就是把產生 QR Code 的過程反著做而已,相信這個精神讀者已經在先前的基本編碼模式中就領略過了。
解碼篇目前安排有四篇,不過可能永遠都不會完成了,就我目前對 Medium 後臺數據的觀察,發現絕大多數人對 QR Code 的原理不感興趣,寫了大概也是白寫,說不定這整個系列文都是白寫吧,誰知道呢?
如果 Medium 看不到有多少人閱讀過文章,或許我還會充滿動力的把全部文章寫出來,看著花了自己這麼多時間的心血結晶卻乏人問津,這種感覺確實不好受,整個系列文截至目前為止,花了我大約 120 小時,其中不含自己實現 QR Code 產生器、解碼器和查閱資料的時間。耗費了這麼多時間的文章,沒人閱讀更沒人支持,任誰都很難再繼續寫下去吧。
說來也諷刺,一開始明明就是抱著沒人看也沒關係的心態在寫文章,沒想到現在發現其實事實並不是這樣,只要是人,都會希望得到別人肯定的,不過,這幾篇文章都是以 Yeecy 的最高規格呈現給來自四面八方的看客們,並沒有因此而讓文章品質下降,老實說,我還滿喜歡自己這幾篇文章的呢!
下一篇文章將會展示如何從圖片中辨識出 QR Code,並將其提取出來,雖然方法不怎麼好就是了。
感謝你的閱讀,我是 Yeecy,我們下一篇文章見。