IM掃碼登入技術專題(四):你真的瞭解二維碼嗎?刨根問底、一文掌握!

JackJiang發表於2021-11-02

本文引用了ELab團隊、騰訊大講堂兩個公眾號分享的文章內容,引用內容見文末參考資料,感謝原作者的無私分享。

1、引言

對於市面上主流的IM來說,跟二維碼有關的功能,比如掃碼加好友、掃碼登陸、掃碼加群等,都是很常見的。

這是微信的掃碼登入功能:

這是微信的掃碼加好友功能:

二維碼技術使用起來很簡單,本系列的前三篇文章也專門針對IM掃碼登入這個功能做了詳細的分享,但本著學習技術不留死角的習慣,我認為有必要單獨學習一下到底什麼是二維碼(說不定哪天被個剛入行的程式設計師輕輕一句“哥,啥是2維碼”,給問的懵逼了,那時就不僅“頭涼”,還會“心梗”...),這也是專門整理本文的目的所在。

說明:準確地說,我們平時見的最多的二維碼其實是QR碼,也就是目前我國應用最為廣泛的一種二維碼。為了表述簡單,如無特指,本文中所述二維碼皆指QR碼。

學習交流:

  • 即時通訊/推送技術開發交流5群:215477170 [推薦]
  • 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
  • 開源IM框架原始碼:https://github.com/JackJiang2...

(本文同步釋出於:http://www.52im.net/thread-37...

2、專題目錄

本文是系列文章的第4篇,總目錄如下:

《IM掃碼登入技術專題(一):微信的掃碼登入功能技術原理除錯分析》
《IM掃碼登入技術專題(二):市面主流的掃碼登入技術原理除錯分析》
《IM掃碼登入技術專題(三):通俗易懂,IM掃碼登入功能詳細原理一篇就夠》
《IM掃碼登入技術專題(四):你真的瞭解二維碼嗎?刨根問底、一文掌握!》(* 本文)

推薦:另一篇《難得乾貨,揭祕支付寶的2維碼掃碼技術優化實踐之路》可一併閱讀。

3、二維碼的源起

準確地說,我們平時見的最多的二維碼其實是QR碼,也就是目前我國應用最為廣泛的一種二維碼。為了表述簡單,如無特指,本文中所述二維碼皆指QR碼。

3.1 時代背景
上個世紀60年代之後,日本經濟迎來的高速增長期,種類繁多的商品的超市開始在城市中出現。

當時超市使用的現金出納機要靠手動輸入商品價格,因此負責現金出納的人常常會因手腕的麻木和“腱鞘炎”而苦惱。

“能否減輕超市收款員的負擔呢?”

條形碼的出現解決了這一苦惱。由於POS系統的成功開發,僅通過光感讀取條形碼,價格就會自動顯示在出納機上,同時讀取的商品資訊還能傳送到計算機上。

如此一來,條形碼得以普及,但新的課題又隨之而來。問題在於條形碼的容量有限,英文數字最多隻能容納20個字。

“編碼本身要是能夠含有更多的資訊就好了”、“希望具有漢字和假名的處理功能”。

當時正在從事條形碼讀取機研發的Denso Wave公司(這家公司是日本電裝株式會社(Denso Corporation)旗下的子公司)瞭解到了這類需求。在這一背景下,研發小組懷著“一定要滿足客戶需求”的心願,投入到了新的二維碼的研發之中。

3.2 研發2人組
“當時其他公司研發的二維碼把重點放在了資訊量的納入上”,當時負責二維碼研發的騰弘原(Masahiro Hara)回首當年如是說。


▲ 上圖中的“怪大叔”就是騰弘原(Masahiro Hara)

條形碼只能橫向(一維)儲存資訊,相比之下,二維碼則能縱橫二維儲存資訊。騰弘原的考慮是,除了能夠容納大量的資訊外,“研發的編碼還要便於讀取”,據此投入了新的二維碼的研發之中。研發小組僅僅只有兩人。

3.3 技術難題
騰弘原(Masahiro Hara)的研發小組面臨的最大技術難題,是如何實現高速讀取。

有一天,騰弘原的腦海裡浮現出這樣一個思路:“附上‘此處有編碼’這樣的位置資訊會怎樣?”

於是想到的是回字形的“定點陣圖案”。將這一圖形放入二維碼中,便可實現其他公司無法模仿的高速讀取。


▲ 上圖中三個角的回字型圖案就是“定點陣圖案”


▲ “定點陣圖案”黑白色塊比例是1:1:3:1:1

3.4 “定點陣圖案”為何是回字型?
定點陣圖案為何要使用那種回字型的呢?

騰弘原解釋說:“因為這種圖形在票據等當中出現頻率最小”。

也就是說:如果附近有同樣的圖形,讀取機就會將其誤認為是編碼,為了防止這種誤讀,定點陣圖案必須是唯一的圖形。

經過全面考慮:騰弘原等人決定將印刷在廣告單、雜誌、紙板等處的繪圖和文字全部變成黑白兩色,對其面積比率進行徹底的調查。

研發小組日以繼夜地對無數的印刷品進行調查的結果,終於查明瞭印刷品中“最不常用的比率”,即1︰1︰3︰1︰1。

這樣:便確定了定點陣圖案黑白部分的寬幅比率。所形成的結構是,掃描線可以從360度方向掃描,無論從哪個方向掃描,一旦掃到其獨特的比率,便可計算出編碼的位置。


▲回字型 “定點陣圖案”使得無論從哪個方向掃描都能正常識別

研發專案啟動後經過一年半的時間,在經歷了幾多曲折之後,可容納約7000個數字的二維碼(準確地說是QR碼)終於誕生了。其特點是能進行漢字處理,大容量,而且讀取速度比其他編碼快10倍以上。

3.5 二維碼的專利費
既然二維碼(準確地說是QR碼)這東西是由公司和個人研究完成,那為時至今日它的專利費到底該怎麼收取呢?

坊間傳聞,騰弘原說了這麼一句話:“這種技術其實隨便找個網路工具就能實現,所以這麼簡單的東西,我就不收專利費啦。”。

當然以上只是傳聞,應該不是真的。

實際上Denso Wave公司擁有QR碼的專利權,完全可以向使用QR碼的公司或個人收費專利費,但Denso Wave公司明確表示不會行使這項權利。這是開始研發當初就定下來的方針,也反映了研發者的想法:“希望能有更多的人使用QR碼”。

於是無需成本、可放心使用的QR碼現已作為“公共的編碼”,在全世界得到了廣泛的應用。

4、二維碼的優點

回到技術本身,用現代的視角先來總結一下二維碼到底有什麼有點。

二維碼出現之前,在需要使用類似編碼的場景時,採用的都是一維碼(條形碼)。

但是條形碼承載的資訊太少,只能用於一些很簡單的場景,因此條形碼除了用於商品等資訊,並沒有廣泛流行。

總結一下,二維碼具有以下優點:

1)資訊容量大:可容納多達 1850 個大寫字母或 2710 個數字或 1108 個位元組,或 500 多個漢字,比普通條碼資訊容量約高几十倍;
2)編碼範圍廣:該條碼可以把圖片、聲音、文字、簽字、指紋等可以數字化的資訊進行編碼,用條碼錶示出來;
3)容錯能力強:具有糾錯功能,這使得二維條碼因穿孔、汙損等引起區域性損壞時,照樣可以正確得到識讀,損毀面積達 50% 仍可恢復資訊;
4)譯碼可靠性:它比普通條碼譯碼錯誤率百萬分之二要低得多,誤位元速率不超過千萬分之一;
5)可引入加密:保密性、防偽性好;
6)使用成本低:易製作,持久耐用。

5、初識二維碼

我們可以先試著生成一個二維碼,演示地址是:https://kazuhikoarase.github....

如上圖所示:可以發現,二維碼有 4 個可變項,其中主要的 2 個為版本和容錯率。

1)版本(TypeNumber):

一共有 40 個尺寸,對應 40 個版本;Version1是 21*21 的矩形,之後每增加一個版本,就增加4的尺寸,即(v-1)4+21,最高是Version40,177177 的正方形。

2)容錯率(ErrorCorrectionLevel):

二維碼容錯率即是指二維碼圖示被遮擋多少後,仍可以被掃描出來的能力。容錯率越高,則二維碼圖片能被遮擋的部分越多。

二維碼有 4 個容錯等級:

根據以上配置不同,相同的內容,在不同的配置下產生的二維碼不一樣,但掃出的內容是一樣的。

6、二難碼的設計原理

日常我們看到的二維碼就是一張由黑白色塊混合在一起的一張圖片,我們肯定也知道這些黑白色塊就是內容的某種編碼,但其實除了內容外,二維碼還有許多其他輔助掃碼識別的資訊。

如上圖所示,按右側標識的從上到下的順序,分別是功能區和資料區。

6.1 功能區
功能區的主要內容是:

1)位置探測圖形:位於左上、左下、右上的三個矩形,可以說是二維碼最重要的組成部分;
2)位置探測圖形分隔符:位置探測圖形周圍的白邊,用於分割位置探測圖形和資料。
3)定點陣圖形:三個位置探測圖形之間的兩根“線”,用於確定二維碼符號中模組的座標(相當於座標軸);
4)校正圖形:用於校正定位(只有版本2以上有),版本越高個數越多,以校正可能發生的定位偏移。
位置探測圖形的作用主要是:

1)確定二維碼的放置方向:不管順著掃倒著掃,都可以準確找到第一個編碼字元的位置(左上矩形的右邊);如果任一矩形被遮擋,掃描裝置將無法定位;
2)確定編碼字元的邊界:確定編碼字元的上下左右邊界,不被周圍其他資訊干擾。
從網上搜二維碼圖片,可以觀察下,這些二維碼都具有位置探測圖形、位置探測圖形分隔符及定點陣圖形。

其中第三幅,定點陣圖形不太對,也確實就掃不出來:

6.2 資料區
資料區的主要內容是:

1)格式資訊:存放包括糾錯碼型別、掩碼型別等資訊;
2)資料和糾錯碼字:最主要的部分,用於存放資料和糾錯資訊。
除掉定位區以及格式資訊,資料和糾錯碼字的排布如下:

7、二維碼的生成流程

在所有資料都完成放置之後,還有一步操作:新增掩碼。

掩碼主要是為了避免,如果出現大面積的空白或黑塊,導致我們掃描識別的困難。

常用的掩碼如下:

資料經過掩碼後,基本不會再出現大面積的黑塊和白塊,利於掃描。

注:掩碼只會與資料區進行 XOR,不會影響功能區。

8、Show me the code

只要遵循以上二維碼的規範,生成對應的二維碼圖形即可,具體實現邏輯不影響。

這裡我們參考jquery-qrcode,簡單看下實現。

主要流程如下:

makeImpl : function(test, maskPattern) {

this.moduleCount = this.typeNumber * 4 + 17;

this.modules = newArray(this.moduleCount);

for(varrow = 0; row < this.moduleCount; row++) {
  this.modules[row] = newArray(this.moduleCount);

  for(varcol = 0; col < this.moduleCount; col++) {
    this.modules[row][col] = null;//(col + row) % 3;

  }

}

this.setupPositionProbePattern(0, 0);// 位置探測圖形

this.setupPositionProbePattern(this.moduleCount - 7, 0);// 位置探測圖形

this.setupPositionProbePattern(0, this.moduleCount - 7);// 位置探測圖形

this.setupPositionAdjustPattern(); // 校正圖形

this.setupTimingPattern(); // 定點陣圖形(座標軸)

this.setupTypeInfo(test, maskPattern); // 版本資訊

if(this.typeNumber >= 7) {
  this.setupTypeNumber(test);

}

if(this.dataCache == null) {
  this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList); // 生成資料

}

this.mapData(this.dataCache, maskPattern); // 加入掩碼

},

生成二維資料後,再把點一一繪製出來即可:

var createCanvas = function(){

  // create the qrcode itself

  var qrcode  = new QRCode(options.typeNumber, options.correctLevel);

  qrcode.addData(options.text);

  qrcode.make();

  // create canvas element

  var canvas  = document.createElement('canvas');

  canvas.width  = options.width;

  canvas.height = options.height;

  var ctx   = canvas.getContext('2d');

  // compute tileW/tileH based on options.width/options.height

  var tileW = options.width  / qrcode.getModuleCount();

  var tileH = options.height / qrcode.getModuleCount();

  // draw in the canvas

  for( varrow = 0; row < qrcode.getModuleCount(); row++ ){
    for( varcol = 0; col < qrcode.getModuleCount(); col++ ){
      ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background;

      var w = (Math.ceil((col+1)*tileW) - Math.floor(col*tileW));

      var h = (Math.ceil((row+1)*tileH) - Math.floor(row*tileH));

      ctx.fillRect(Math.round(col*tileW),Math.round(row*tileH), w, h); 

    }

  }

  // return just built canvas

  return canvas;

}

9、藝術二維碼

日常我們見到的二維碼都是黑白的,但其實二維碼可以多姿多彩。

這裡有很多樣式,可以試一試:https://qrbtf.com/

二維碼之所以可以五顏六色,是因為二維碼的有用資訊只是 0 和 1,至於 0 和 1 用什麼資訊表達,只要掃碼軟體可以識別就行。目前的掃碼軟體一般都是對圖片進行灰度處理,所以二維碼上的點無論如何表達,只要經過灰度處理後 0 和 1 沒有顛倒,則資訊不會出錯,不會影響掃碼結果。

藝術二維碼實現起來也不難,只要區分特定的資料區,並用特定的圖片渲染即可,有興趣可以參考:https://github.com/252860883/...

更簡單一點的,也可以直接降低黑白透明度,背景渲染特定的圖片:

如果只是網頁上顯示,還可以做成 gif 動圖形式(這裡就不貼樣本了)。

不過,藝術二維碼由於顏色更豐富干擾資訊更多,因此相比黑白二維碼,藝術二維碼對掃描軟體的要求也更高。

10、微信小程式碼

小程式碼和二維碼雖然看起來完全不一樣,但是它們的設計思想及生成流程是基本一樣的。

如上圖所示,微信小程式碼的特徵有:

1)具有位置探測圖形;
2)支援3種容量:36 射線、54 射線和 72 射線;
3)具有 L、M、Q、H 4種容錯級別;
4)掩碼操作均勻0、1碼點。

題外話:瞭解二維碼的原理後,其實我們自己也可以手畫二維碼。

11、一個有趣的問題:“二維碼會被用完嗎?”

這個問題很簡單,答案是:會。

因為二維碼的尺寸是有限的,那二維碼的數量就是有限的。

但是用完所有的二維碼,需要很長很長的時間。。。

現在的二維碼有40個官方版本,從Version1-40,最小為2121、最大為177177矩陣。

其中,以微信名片為例,就是37×37 的矩陣規格,微信的付款碼是 25×25 的矩陣規格。為了方便理解,我們用方塊作為矩陣單位。

▲ 上圖就是微信名片(即37×37矩陣的二維碼)

如何計算,各矩陣中生成的二維碼個數?

我們來舉個例子:

如上圖所示的四宮格,每個格子有兩種顏色變化,請問一個四宮格可以組合出多少個圖形?

答案是:一個格子兩種顏色,那就是兩種可能,兩個格子就是四種可能,三個格子就是8種可能,四個格子就是16種可能。所以,四宮格能夠組成2^4,共16個圖形。

以此類推,以微信的 25X25 付款碼為例:

每一排有 25 個方塊,共 25 列,除去定位用的方塊和冗餘糾錯的方塊等,還剩下478 個方塊。按照二進位制,每個方塊只有黑或白兩種選擇,所以 478 個小方塊理論上一共可以組合 2^478 個二維碼。

也就是一個25X25規格尺寸的二維碼可以生成:

780437137578998057845399307448291576437149535666242787714789239906342934704941405030076525765872992789956732780351655723861993919822071326572544個二維碼。

大家可以嘗試念出來大概多少個?

根據疫情期間1400億個二維碼的數量來計算,假設微信一年會用掉6000億個二維碼。那微信用掉25X25這一個尺寸產生的二維碼需要多少年呢?

我們來算一下:2^478/6000億=1.301×10^132 年(超多億億億億年)。

所以:雖然理論上二維碼確實有用完的那一天,但實際上這個時間幫長了,以至於我們現在完全可以不用考慮!

12、參考資料

[1] 難得乾貨,揭祕支付寶的2維碼掃碼技術優化實踐之路
[2] 你每天都掃的二維碼,知道它是怎麼來的麼?
[3] 二維碼,你真的瞭解嗎?
[4] 二維碼會被人類掃完嗎?
[5] QR碼的成功之路
[6] 有趣的二維碼

附錄:更多IM相關入門級文章

《新手入門一篇就夠:從零開發移動端IM》
《技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)》
《通俗易懂-深入理解TCP協議(上):理論基礎》
《網路程式設計懶人入門(一):快速理解網路通訊協議(上篇)》
《網路程式設計懶人入門(二):快速理解網路通訊協議(下篇)》
《腦殘式網路程式設計入門(一):跟著動畫來學TCP三次握手和四次揮手》
《腦殘式網路程式設計入門(二):我們在讀寫Socket時,究竟在讀寫什麼?》
《網路程式設計入門從未如此簡單(一):假如你來設計網路,會怎麼做?》
《網路程式設計入門從未如此簡單(二):假如你來設計TCP協議,會怎麼做?》
《IM開發者的零基礎通訊技術入門(十一):為什麼WiFi訊號差?一文即懂!》
《IM開發者的零基礎通訊技術入門(十二):上網路卡頓?網路掉線?一文即懂!》
《IM開發者的零基礎通訊技術入門(十三):為什麼手機訊號差?一文即懂!》
《IM開發者的零基礎通訊技術入門(十四):高鐵上無線上網有多難?一文即懂!》
《從根上理解高效能、高併發(一):深入計算機底層,理解執行緒與執行緒池》
《從根上理解高效能、高併發(二):深入作業系統,理解I/O與零拷貝技術》
《史上最強Java NIO入門:擔心從入門到放棄的,請讀這篇!》
《網頁端IM通訊技術快速入門:短輪詢、長輪詢、SSE、WebSocket》
《搞懂現代Web端即時通訊技術一文就夠:WebSocket、socket.io、SSE》
《一套海量線上使用者的移動端IM架構設計實踐分享(含詳細圖文)》
《一套原創分散式即時通訊(IM)系統理論架構方案》
《一套億級使用者的IM架構技術乾貨(上篇):整體架構、服務拆分等》
《一套億級使用者的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等》
《即時通訊音視訊開發(十八):詳解音訊編解碼的原理、演進和應用選型》
《即時通訊音視訊開發(十九):零基礎,史上最通俗視訊編碼技術入門》
《零基礎入門:實時音視訊技術基礎知識全面盤點》
《零基礎IM開發入門(一):什麼是IM系統?》
《零基礎IM開發入門(二):什麼是IM系統的實時性?》
《零基礎IM開發入門(三):什麼是IM系統的可靠性?》
《零基礎IM開發入門(四):什麼是IM系統的訊息時序一致性?》
《開源移動端IM技術框架MobileIMSDK:快速入門》

本文已同步釋出於“即時通訊技術圈”公眾號。
同步釋出連結是:http://www.52im.net/thread-37...

相關文章