8

IM扫码登录技术专题(四):你真的了解二维码吗?刨根问底、一文掌握!

 2 years ago
source link: http://www.blogjava.net/jb2011/archive/2021/11/01/436027.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 253, comments - 13, trackbacks - 0, articles - 0

本文引用了ELab团队、腾讯大讲堂两个公众号分享的文章内容,引用内容见文末参考资料,感谢原作者的无私分享。

对于市面上主流的IM来说,跟二维码有关的功能,比如扫码加好友、扫码登陆、扫码加群等,都是很常见的。

这是微信的扫码登录功能:

这是微信的扫码加好友功能:

 二维码技术使用起来很简单,本系列的前三篇文章也专门针对IM扫码登录这个功能做了详细的分享,但本着学习技术不留死角的习惯,我认为有必要单独学习一下到底什么是二维码(说不定哪天被个刚入行的程序员轻轻一句“哥,啥是2维码”,给问的懵逼了,那时就不仅“头凉”,还会“心梗”...),这也是专门整理本文的目的所在。

说明:准确地说,我们平时见的最多的二维码其实是QR码,也就是目前我国应用最为广泛的一种二维码。为了表述简单,如无特指,本文中所述二维码皆指QR码。

(本文同步发布于:http://www.52im.net/thread-3735-1-1.html

2、专题目录

本文是系列文章的第4篇,总目录如下:

推荐:另一篇《难得干货,揭秘支付宝的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.io/qrcode-generator/js/demo/

 如上图所示:可以发现,二维码有 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/ArtQRCode

更简单一点的,也可以直接降低黑白透明度,背景渲染特定的图片:

 如果只是网页上显示,还可以做成 gif 动图形式(这里就不贴样本了)。

不过,艺术二维码由于颜色更丰富干扰信息更多,因此相比黑白二维码,艺术二维码对扫描软件的要求也更高。

10、微信小程序码

小程序码和二维码虽然看起来完全不一样,但是它们的设计思想及生成流程是基本一样的。

如上图所示,微信小程序码的特征有:

  • 1)具有位置探测图形;
  • 2)支持3种容量:36 射线、54 射线和 72 射线;
  • 3)具有 L、M、Q、H 4种容错级别;
  • 4)掩码操作均匀0、1码点。

题外话:了解二维码的原理后,其实我们自己也可以手画二维码。

11、一个有趣的问题:“二维码会被用完吗?”

这个问题很简单,答案是:

因为二维码的尺寸是有限的,那二维码的数量就是有限的。

但是用完所有的二维码,需要很长很长的时间。。。

现在的二维码有40个官方版本,从Version1-40,最小为21*21、最大为177*177矩阵。

其中,以微信名片为例,就是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-3735-1-1.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK