利維坦按:一個開關(guān)的開合狀態(tài)可以對應“0、1”兩個信號,萊布尼茨三百多年前留下的這個制式影響深遠。日常語言依賴于思維邏輯,與之類似,數(shù)字語言的流通則依賴于數(shù)學邏輯,但本質(zhì)上仍然只是無數(shù)個與非門的邏輯計算。因此相比于計算機,我們的計算能力雖然在效率方面有點難以啟齒,但這并不代表有些事情我們注定辦不到。
比如靠肉眼識別二維碼?雖然人類大腦的計算效率早已落后家用計算機好幾個量級,但這并不妨礙我們光靠人腦去完成一件“看似只有電子設(shè)備才能完成的事情”的嘗試。
畢竟生活已經(jīng)夠無聊了。
本文由利維坦與每天掃碼支付都有獎勵的支付寶聯(lián)合出品
支付就用支付寶
掃碼還得靠手機
我們在生活中消費、轉(zhuǎn)賬時用手機掃的二維碼,屬于QR code(Quick Response)。但除了QR code之外,二維碼還有其他多種制式標準,比如Data Matrix、PDF417、Ultra-code——本文只討論QR碼這個生活場景中最常見的二維碼制,因為……其他的我也不會。
條碼技術(shù)最早需要追溯到上世紀20年代,當時美國威斯汀豪斯(Westinghouse)實驗室的發(fā)明家約翰·科芒德(John Kermode)為了實現(xiàn)對郵政單據(jù)的自動分檢,發(fā)明了一種用條碼對單據(jù)做標記的機制(一個條表示數(shù)字“1”,二個條表示數(shù)字“2”,稱為模塊比較法),以及相應的譯碼器。
但是這種始祖鳥級別的制式所蘊含的信息量極低,不久后科芒德的合作者道格拉斯·楊(Douglas Young)對此進行了改進,利用黑條之間空隙的尺寸變化來編碼數(shù)據(jù)。兩人在1949年獲得了世界上第一個條碼專利,這種最早期的條碼由黑白相間的同心圓組成,被稱為牛眼式條碼。
另一說法稱,牛眼式條紋是由工程師諾曼·伍德蘭(Norman J. Woodland)發(fā)明的
現(xiàn)如今,我們已經(jīng)對掃碼購物的行為非常習慣了,但在條碼技術(shù)普及之前,超市的出納員只能靠敲鍵盤來輸入商品價格,操作成本高不說,患有腱鞘炎的人也不在少數(shù),簡直成了職業(yè)病。條碼技術(shù)的出現(xiàn)無疑節(jié)省了很多工作成本,更何況條碼本身也伴隨著硬件技術(shù)實現(xiàn)一次次的更迭和普及。
欲求不滿是人類的美好品德。條形碼雖然帶來了極大的便利,但是容量依舊有限,只容納20個英文和數(shù)字,且無法編碼漢字和假名。當時的制碼人員幾乎分成了兩類,一類研究怎么往條碼里塞更多的信息,一類研究怎么更快解碼。大多數(shù)人屬于第一類,而當時正在日本豐田子公司DensoWave從事條形碼讀取機研發(fā)的原昌宏開始嘗試研發(fā)更快速的編碼制式,進過一年半的研發(fā)過程,才有了1994年QR code碼制的發(fā)明。
碼如其名,這一碼制的最大特點便是“Quick Response”——快速響應,讀取速度比其他編碼制式快10倍以上,最先被用在汽車零部件生產(chǎn)行業(yè)的電子看板管理(負責傳遞信息和生產(chǎn)管理的系統(tǒng)之一)中,大大提高了生產(chǎn)、出貨乃至單據(jù)的管理效率。
作為一個男性程序員,原昌宏對于“快”有著獨特的理解。他認為要想在應用條碼的過程中實現(xiàn)快速,核心是解決識別的問題,剩下的內(nèi)容只是計算。
——如何才能讓機器快速找到目標碼區(qū)?
用特殊形狀的符號標記區(qū)域?!?/span>
——如何確定碼區(qū)所在平面?
用三個特殊形狀的標記(三點確定一個平面)。——
——如何確定機器讀取碼區(qū)的方向?
設(shè)置好從某一標記出發(fā),以及出發(fā)的方向就可以了。——
邏輯沒有問題,剩下要解決的就是標記的形狀。如果編碼周圍有相同的圖案,讀取時就會被誤認為是編碼信息,從而導致誤讀,因此定位圖案一定要保證特殊性。原昌宏為此收羅了大量廣告單、雜志等印刷品,從各個方向進行掃描,并做了黑白色的二值處理,分析黑白占比,從而得出結(jié)論:黑白間隔比為1:1:3:1:1的比例定位的時,在印刷品中出現(xiàn)的概率最低。
以下方二維碼為例,三個分別位于二維碼的左上角、右上角、左下角的“回”字形符號,便是規(guī)定了尺寸、讀取方向的位置探測圖形。有這三個識別符號放在二維碼的三個角上后,解碼的時間響應就可以很快,比同時代的技術(shù)要快20倍。 這便是肉眼識別二維碼所要了解的第一個知識點:
用位置探測圖形確定碼區(qū)。
此外,該制式還規(guī)定在與位置探測圖形接壤的碼區(qū)部分均為空白,以防止對位置探測圖形產(chǎn)生識別干擾,這便是位置探測圖形分隔符(下圖中藍色區(qū)域)。
再考慮到印有二維碼的載體在實際應用場景中可能會發(fā)生的扭曲、移位和破損,制式還規(guī)定了一種與位置探測圖形相類似的“回”字形圖案(1:1:1:1:1),用來輔助定位減少誤差,這便是校正圖形(僅在版本2以上存在)。
但如果連校正圖形也被污染了呢?還有定位圖形(下圖黃色區(qū)域),這是平行于二維碼邊緣的兩條黑白交替出現(xiàn)的直線,與位置探測圖形分隔符相接,用于確定二維碼的角度,糾正扭曲。
上面介紹的四個圖形均屬于功能區(qū)域,都是為了保證QR碼能被讀碼設(shè)備正確獲取,不存儲具體數(shù)據(jù)信息。在學習肉眼識別二維碼的過程中,我們只要找得到它們就夠了。
那么在剩下來的編碼區(qū)里,究竟可以存儲多少信息?答案是試規(guī)格而定。為了節(jié)省空間,QR碼符號共有40種規(guī)格的矩陣,從最小的21x21(版本1)到最大177x177(版本40),每一版本符號比前一版本每邊增加4個模塊。版本1的二維碼最多可以儲存25個字符或41個數(shù)字,而版本40的二維碼最多可以儲存4296個字符或7089個數(shù)字。
在下圖的版本1二維碼中,只有未被紅黃藍三色填充的幾塊碼區(qū)為我們所需要的數(shù)據(jù)存儲區(qū)。信息將通過特定算法轉(zhuǎn)換成黑色和白色的小方格填充其中,但即便我們知道了該看哪些區(qū)域,離最終解碼還有些距離。
再往下細分,數(shù)據(jù)存儲區(qū)又可以分成三類:數(shù)據(jù)編碼、糾錯編碼,以及代表格式字符串的碼區(qū)。第一種自然不必說,是我們最終需要使用的區(qū)域。至于糾錯編碼,這里不得不提到QR碼的另外一大特點——高容錯率。
日常生活里,沿街餐館里貼在桌角用于收款的二維碼大概是最容易受損的了。印有二維碼的小紙片這邊磨破一個角,那邊沾了點醬油,碼區(qū)可能早已不再完整。糾錯編碼的作用便體現(xiàn)在這里,通過對數(shù)據(jù)碼進行RS糾錯計算(里德-所羅門碼算法)得到與之對應的糾錯碼,以此來保證二維碼的可讀性。當數(shù)據(jù)碼區(qū)受到污染的時候,讀碼器依舊可以憑借糾錯碼得到正確信息。
根據(jù)糾錯能力的不同,糾錯碼共分成L、M、Q、H 4個級別,分別可修正7%、15%、25%、30%的字碼。關(guān)于糾錯碼的級別信息便記錄在數(shù)據(jù)區(qū)的第三塊內(nèi)容——格式字符串中(即下圖紅色部分),這是我們需要處理的第一組數(shù)據(jù)。
格式字符串所蘊含的信息除了糾錯等級以外,還包括表示掩碼類型的信息。所謂的掩碼,粗暴理解是為了讓二維碼更加容易被機器識別而定義的一次計算過程,降低了二維碼本身被誤讀的可能性,使整個二維碼碼區(qū)排列更均勻,更容易被識別。
QR碼制式中一共定義了7種掩碼類型,每一種又可以根據(jù)4個糾錯碼級別產(chǎn)生4個變種,對于同一個信息便有了4*7=28種加工可能。因此只有確定了糾錯等級和掩碼類型,才能正式進入對數(shù)據(jù)的處理階段,而我們首先需要得到的便是掩碼的類型。
黑塊為“1”,白塊為“0”。格式字符串數(shù)據(jù)的翻譯方向為從上往下(或從右到左),需注意當從右往左進行翻譯的時候,右邊半段紅色區(qū)域的左端與左邊半段紅色區(qū)域的右端是重復信息,只取其中一位。基于上述算法法則,我們很快可以得到格式字符串信息(紅色區(qū)域,如上圖所示)——110011000101111,再查閱格式字符串表可得出結(jié)論:該二維碼所采用的糾錯級別為L,掩碼模式4(下圖正中所示掩碼模式)。
接下來便需要對掩碼進行逆向計算。所謂掩碼其實很好理解——這是一個把數(shù)據(jù)區(qū)里的黑白小方塊拆散的過程,每種掩碼類型所規(guī)定的圖像模型中,黑色區(qū)域表示原始碼區(qū)的該位置需要顛倒一次黑白(數(shù)值反轉(zhuǎn)),白色區(qū)域保持不變,以此來打散碼區(qū)。因此我們可以根據(jù)掩碼模式4的圖形來推算這個二維碼在掩碼處理之前的狀態(tài)。
終于等到從圖形碼變成數(shù)字的一刻。對于上圖左側(cè)我們得出的結(jié)論圖,我們定義白塊為0,黑塊為1,從最右下角的模塊開始進行記錄,可以很輕易得到一串數(shù)列。下圖是一個附帶校正圖形(紫色部分)與版本信息(藍色部分)的二維碼示意圖,紅線所示為閱讀方向。
01000000 00110110
01010110 11100110
01000000 ……
接下來的工作便是對這串數(shù)字進行換算。無論是編碼還是解碼,我們都希望二維碼能夠以最短的比特串記錄數(shù)據(jù),因此編碼開始之前便會對四種數(shù)據(jù)形式(數(shù)字、字母數(shù)字、漢字,8位字節(jié))進行編碼方式的選擇,在后期得到的二進制數(shù)列中也能找到每一個編碼方式獨有的二進制標識,這些標識會記錄在數(shù)據(jù)區(qū)的前端(即圖中右下角四個小方塊的位置),使得解碼器可以根據(jù)二維碼使用的編碼方式對數(shù)據(jù)進行解碼。
編碼方式通常用4位數(shù)字表示,因此我們單拎出開頭的4個數(shù)字“0100”,對照下表可查到對應的模式為字節(jié)模式。
8位字節(jié)模式所使用的是ASCII字符集,每個字符都需要用8位二進制碼進行表示。而在代表編碼模式的4個數(shù)字后面,我們?nèi)孕枰倭喑鰜?個數(shù)字“00000011”,這組數(shù)字所表示的是原數(shù)據(jù)的字符數(shù),十進制為3,即原數(shù)據(jù)共有三個字節(jié)。
因此這組數(shù)據(jù)應該這樣排列:
0100 00000011
01100101 01101110
01100100 0000……
在二維碼的解碼過程中,“0000”代表的是原始數(shù)據(jù)的結(jié)束。但QR碼對每種版本都規(guī)定了具體位數(shù),因此后面的數(shù)據(jù)碼往往由補齊碼和大量糾錯碼組成。
補齊碼的設(shè)置其實特別有意思。在終止符出現(xiàn)后,需要先將位數(shù)用“0”補到8的倍數(shù)。如果還不夠,則需要往后頭重復兩組字節(jié):11101100 、00010001。這些字節(jié)分別等于236和17,在二維碼里的地位有如開普勒常數(shù)之于太陽系,自然常數(shù)之于自然。具體為何如此規(guī)定,若悉知,望告知。
至于糾錯碼,雖然本文不多做介紹,但是由于在大容量二維碼中(版本高于4),最后呈現(xiàn)出來的碼是通過將數(shù)據(jù)碼和糾錯碼進行重新排布,再套用掩碼模型制作出來的,因此無法用前文的方法進行計算。