大家都熟知二維碼,那麼字符串是怎麼轉換成二維碼的呢?原理是啥?

閒雲俠鶴


二維碼的生成細節和原理

二維碼又稱QR Code,QR全稱Quick Response,是一個近幾年來移動設備上超流行的一種編碼方式,它比傳統的Bar Code條形碼能存更多的信息,也能表示更多的數據類型:比如:字符,數字,日文,中文等等。這兩天學習了一下二維碼圖片生成的相關細節,覺得這個玩意就是一個密碼算法,在此寫一這篇文章 ,揭露一下。供好學的人一同學習之。

關於QR Code Specification,可參看這個PDF:http://raidenii.net/files/datasheets/misc/qr_code.pdf

基礎知識

首先,我們先說一下二維碼一共有40個尺寸。官方叫版本Version。Version 1是21 x 21的矩陣,Version 2是 25 x 25的矩陣,Version 3是29的尺寸,每增加一個version,就會增加4的尺寸,公式是:(V-1)*4 + 21(V是版本號) 最高Version 40,(40-1)*4+21 = 177,所以最高是177 x 177 的正方形。

下面我們看看一個二維碼的樣例:

Byte mode, 字節編碼,可以是0-255的ISO-8859-1字符。有些二維碼的掃描器可以自動檢測是否是UTF-8的編碼。

Kanji mode 這是日文編碼,也是雙字節編碼。同樣,也可以用於中文編碼。日文和漢字的編碼會減去一個值。如:在0X8140 to 0X9FFC中的字符會減去8140,在0XE040到0XEBBF中的字符要減去0XC140,然後把結果前兩個16進制位拿出來乘以0XC0,然後再加上後兩個16進制位,最後轉成13bit的編碼。如下圖示例:

下面我們看幾個示例,

示例一:數字編碼

在Version 1的尺寸下,糾錯級別為H的情況下,編碼: 01234567

1. 把上述數字分成三組: 012 345 67

2. 把他們轉成二進制: 012 轉成 0000001100; 345 轉成 0101011001; 67 轉成 1000011。

3. 把這三個二進制串起來: 0000001100 0101011001 1000011

4. 把數字的個數轉成二進制 (version 1-H是10 bits ): 8個數字的二進制是 0000001000

5. 把數字編碼的標誌0001和第4步的編碼加到前面: 0001 0000001000 0000001100 0101011001 1000011

示例二:字符編碼

在Version 1的尺寸下,糾錯級別為H的情況下,編碼: AC-42

1. 從字符索引表中找到 AC-42 這五個字條的索引 (10,12,41,4,2)

2. 兩兩分組: (10,12) (41,4) (2)

3.把每一組轉成11bits的二進制:

(10,12) 10*45+12 等於 462 轉成 00111001110(41,4) 41*45+4 等於 1849 轉成 11100111001(2) 等於 2 轉成 000010

4. 把這些二進制連接起來:00111001110 11100111001 000010

5. 把字符的個數轉成二進制 (Version 1-H為9 bits ): 5個字符,5轉成 000000101

6. 在頭上加上編碼標識 0010 和第5步的個數編碼: 0010 000000101 00111001110 11100111001 000010

結束符和補齊符

假如我們有個HELLO WORLD的字符串要編碼,根據上面的示例二,我們可以得到下面的編碼,

編碼字符數HELLO WORLD的編碼001000000101101100001011 01111000110 10001011100 10110111000 10011010100 001101

我們還要加上結束符:

編碼字符數HELLO WORLD的編碼結束001000000101101100001011 01111000110 10001011100 10110111000 10011010100 0011010000按8bits重排

如果所有的編碼加起來不是8個倍數我們還要在後面加上足夠的0,比如上面一共有78個bits,所以,我們還要加上2個0,然後按8個bits分好組:

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000

補齊碼(Padding Bytes)

最後,如果如果還沒有達到我們最大的bits數的限制,我們還要加一些補齊碼(Padding Bytes),Padding Bytes就是重複下面的兩個bytes:11101100 00010001 (這兩個二進制轉成十進制是236和17,我也不知道為什麼,只知道Spec上是這麼寫的)關於每一個Version的每一種糾錯級別的最大Bits限制,可以參看QR Code Spec的第28頁到32頁的Table-7一表。

假設我們需要編碼的是Version 1的Q糾錯級,那麼,其最大需要104個bits,而我們上面只有80個bits,所以,還需要補24個bits,也就是需要3個Padding Bytes,我們就添加三個,於是得到下面的編碼:

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 11101100 00010001 11101100

上面的編碼就是數據碼了,叫Data Codewords,每一個8bits叫一個codeword,我們還要對這些數據碼加上糾錯信息。

糾錯碼

上面我們說到了一些糾錯級別,Error Correction Code Level,二維碼中有四種級別的糾錯,這就是為什麼二維碼有殘缺還能掃出來,也就是為什麼有人在二維碼的中心位置加入圖標。

錯誤修正容量L水平7%的字碼可被修正M水平15%的字碼可被修正Q水平25%的字碼可被修正H水平30%的字碼可被修正

那麼,QR是怎麼對數據碼加上糾錯碼的?首先,我們需要對數據碼進行分組,也就是分成不同的Block,然後對各個Block進行糾錯編碼,對於如何分組,我們可以查看QR Code Spec的第33頁到44頁的Table-13到Table-22的定義表。注意最後兩列:

  • Number of Error Code Correction Blocks :需要分多少個塊。
  • Error Correction Code Per Blocks:每一個塊中的code個數,所謂的code的個數,也就是有多少個8bits的字節。

下圖是根據上述表格中的Version8的一個例子(6,24,42)

Format Information是一個15個bits的信息,每一個bit的位置如下圖所示:(注意圖中的Dark Module,那是永遠出現的)

關於Error Correction Level如下表所示:

Version Information一共是18個bits,其中包括6個bits的版本號以及12個bits的糾錯碼,下面是一個示例:

數據和數據糾錯碼

然後是填接我們的最終編碼,最終編碼的填充方式如下:從左下角開始沿著紅線填我們的各個bits,1是黑色,0是白色。如果遇到了上面的非數據區,則繞開或跳過。

掩碼圖案

這樣下來,我們的圖就填好了,但是,也許那些點並不均衡,如果出現大面積的空白或黑塊,會告訴我們掃描識別的困難。所以,我們還要做Masking操作(靠,還嫌不復雜)QR的Spec中說了,QR有8個Mask你可以使用,如下所示:其中,各個mask的公式在各個圖下面。所謂mask,說白了,就是和上面生成的圖做XOR操作。Mask只會和數據區進行XOR,不會影響功能區。(

注:選擇一個合適的Mask也是有算法的

下面是Mask後的一些樣子,我們可以看到被某些Mask XOR了的數據變得比較零散了。

Mask過後的二維碼就成最終的圖了。

參考:https://www.cnblogs.com/alantu2018/p/8504373.html


嘉靖不上朝


移動互聯網時代,二維碼在我們身邊可以隨處可見,手機支付有付款碼,公交地鐵有乘車碼,還有最近出現的健康碼。

其實不管是付款碼還是乘車碼,本質上它們都是二維碼,那麼二維碼到底是什麼呢?

二維碼是什麼?

回想一下我們在超市買東西時結賬的流程:首先我們打開付款碼,接著收營員掃我們的付款碼,然後錢就自動從我們的賬戶中扣除了。

可以看出,掃碼付款的過程就是從二維碼到人的一個過程,從二維碼所代表的信息中找到我們的賬戶信息並從中扣款。

那麼二維碼所代表的信息是什麼時候確定的?當然是生成二維碼的時候啦!

我們要使用手機支付功能,首先必須註冊帳號,此時帳號就代表了我們。打開付款碼的時候,系統會將我們的身份信息經過一些計算,最終生成一個二維碼並展示給我們。

生成二維碼的過程就是將信息轉換成二維碼的過程,掃碼的過程則是將二維碼再轉換成信息的過程,所以二維碼本質上只是信息的一種展示形式而已。

二維碼結構分析

雖然二維碼看起來是毫無規律的,但其實二維碼的生成有著固定的格式。

大小

二維碼的大小存在 40 種版本(Version),從Version1(21x21的正方形)到Version40(177x177的正方形),每增加一個 Version,大小都會增加 4。

版本越大二維碼就會越大,隨之所能表示的數據量也就越大。

結構

二維碼呈正方形,通常由黑白小方塊組成,二維碼的整體結構可以分為定位標記、功能區以及數據區三大部分:

  • 定位標記——在二維碼的左上角、右上角、左下角有3個類似“回”字的方形圖案,這3個圖案是用於二維碼的定位標記。正因為有定位標記的存在,所以不管我們以什麼角度掃描,二維碼總是可以被準確的識別出來。二維碼中還存在一些小一點“回”字,它們是用於校正定位的標記,並且只會在Version 2及以上版本的二維碼中才會出現。

  • 功能區——功能區分為兩部分,即圖中紅色部分和紫色部分。紅色部分存放格式信息,用於表示該二維碼的糾錯級別(至於糾錯級別是什麼後面在解釋),紫色部分存放版本信息,即該二維碼的尺寸大小。

  • 數據區——數據區指的是除去定位標記和功能區的其他部分(圖中灰色部分),用於保存二維碼實際所表示的信息的數據碼以及用於糾錯的糾錯碼。

糾錯級別

二維碼一般有一定的容錯率,所以即使二維碼被遮擋住一部分,仍然可以被掃描出來,原理就是在生成二維碼的過程中對數據做了冗餘處理,冗餘程度越高容錯率就越高(糾錯級別越高),二維碼能被遮擋的部分自然也就越多。

二維碼的糾錯級別從低到高分為L(7%)、M(15%)、Q(25%)、H(30%)4個等級,如果需要在二維碼中間加logo,那麼我們就應該生成H級別的二維碼,但也並不是糾錯級別越高越好,級別越高需要冗餘的數據就越多,那麼生成的二維碼的複雜程度也就越高,實際使用過程中我們應該根據需求選擇合適的糾錯級別。

編碼模式

我們都知道計算機只認二進制信息,所以我們需要把字符串中的數字、字母、漢字以及各種字符統一轉換成由0和1組成的二進制數字串計算機才能識別,所以生成二維碼的過程就是將字符串轉換成二進制數字串的過程。

最後生成的二進制數字串對應到二維碼上,0對應白色的小方塊、1對應黑色小方塊,按照8個一組依次填充到數據區就是一個完整的可以被識別的二維碼了。

根據源字符串類型,二維碼主要支持4種標準化的編碼模式,分別為數字編碼、字符編碼、字節編碼以及日文編碼,當然也支持其他一些特殊的字符集編碼以及混合編碼。

每種編碼模式對應一個4位的二進制的編碼模式指示符:

下面以字符編碼舉個例子,現在有字符串“HELLO WORLD”

1、模式指示符——根據查表可知HELLO WORLD需要使用字符編碼模式,所以它的模式指示符為0010。

2、字符計數指示符——表示源字符串的長度,字符計數指示符必須放在模式指示符之後。

字符計數指示符的計算依賴於二維碼的版本以及編碼模式:

以版本1為例,字符編碼需要9bits,HELLO WORLD長度為11,轉為二進制為1011,不足9位,左邊補5個0得到最終結果000001011,加上模式指示符目前我們得到的二進制數字串為0010 000001011。

3、對原字符串編碼,

將HELLO WORLD兩兩分組得到(H,E)、 (L,L)、(O, )、(W,O)、(R,L)、(D);

根據索引表找到每個字符對應的值,得到(17,14)、 (21,21)、(24,36)、(32,24)、(27,21)、(13);

將每組數字先轉成45進制、再將結果轉成長度11的二進制串,長度不足的補0。例如(17,14) 轉成45進製為17*45+14=779,再將779轉成二進制 1100001011,左邊補1個0得到01100001011,如果最後一組是單獨一個,則轉成6位的二進制串,所以字符串HELLO WORLD經過字符編碼之後,再加上前面計算好的模式指示符和字符計數指示符之後最終得到

0010 000001011 01100001011 01111000110 10001011100 10110111000 10011010100 001101。

4、 在確定二維碼的版本和糾錯級別以後,字符串的編碼結果應該是一個固定長度的二進制串,如果我們計算的結果長度不夠就需要進行不起操作。

例如確定二維碼的版本1和糾錯級別Q,則最終二進制串的長度需要達到104位。

在第3步中我們得到的最二進制串的長度為74,所以需要進行補齊操作。首先末尾可以至多追加4個0,接著末尾補齊更多的0直到長度是8的倍數,如追加4個0後長度達到78,要達到8的倍數則還需要補2個0;如果這時候長度還沒有達到目標,那就在末尾循環追加固定的二進制碼11101100 00010001直到達到目標長度。

最後來看一下HELLO WORLD經過字符編碼之後結果

掩碼圖

源字符串在經過編碼之後被轉換成二進制的數字串,根據這個二進制數字串畫出來的二維碼通常會存在黑白色塊分佈不均的情況,這會導致二維碼容易掃描失敗。

為了解決這個問題,官方提供了8種掩碼方案:

我們需要選擇一個掩碼圖與我們生成的二維碼再做一次異或操作,這樣黑白色塊分佈就會更加的均勻。

那應該選擇哪個掩碼圖呢?這裡面涉及到的原理就比較複雜了,大概過程就是先將8個掩碼圖與二維碼都分別進行異或操作,再按照某個規則計算出8個結果的均勻程度,最後取其中效果最好的一個。


萌新程序員成長日記


您好,很高興回答您的問題。

二維碼就目前而言已經被廣泛應用於手機支付、帳號登錄、防偽溯源、信息獲取、網站跳轉、手機電商等領域。

其實現思路跟一維碼,如條形碼一致,即將字符串通過特殊編碼,使用若干個與二進制相對應的幾何形體來表示數字和文字,並通過圖像輸入設備或光電掃描設備自動識別,以實現信息自動處理還原。

將字符串轉化為二維碼核心部分是編碼,通常市面上的二維碼分為兩類,一類為堆疊式二維條碼,一類是矩陣式二維條碼,下面主要講述一下矩陣式二維條碼實現原理:

矩陣式二維碼通過在一個矩形空間內,通過黑白像素在矩陣中不同分佈進行編碼,在矩陣相應元素位置上,用點表示二級制“1”,空白表示二進制“0”,點的排列組合所代表的二維碼的意義。



分享到:


相關文章: