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

1、引言

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

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

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

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

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

学习交流:

- 即时通讯/推送技术开发交流5群:215477170 [推荐]

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK

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

2、专题目录

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

  1. IM扫码登录技术专题(一):微信的扫码登录功能技术原理调试分析
  2. IM扫码登录技术专题(二):市面主流的扫码登录技术原理调试分析
  3. IM扫码登录技术专题(三):通俗易懂,IM扫码登录功能详细原理一篇就够
  4. 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.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

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

  1. Android项目实战(四十四):Zxing二维码切换横屏扫描

    原文:Android项目实战(四十四):Zxing二维码切换横屏扫描 Demo链接 默认是竖屏扫描,但是当我们在清单文件中配置横屏显示的时候: <activity android:name=&q ...

  2. java二维码生成-谷歌(Google.zxing)开源二维码生成学习及实例

    java二维码生成-谷歌(Google.zxing)开源二维码生成的实例及介绍   我们使用比特矩阵(位矩阵)的QR码编码在缓冲图片上画出二维码 实例有以下一个传入参数 OutputStream ou ...

  3. QRCode二维码生成方案及其在带LOGO型二维码中的应用(1)

    原文:QRCode二维码生成方案及其在带LOGO型二维码中的应用(1) 提要:很多公司为商业宣传之需,常将企业LOGO加入二维码中,但如果LOGO遮挡区域足够地大,二维码就变得无法识别.那么,有没有一 ...

  4. QRCode二维码生成方案及其在带LOGO型二维码中的应用(2)

    原文:QRCode二维码生成方案及其在带LOGO型二维码中的应用(2) 续前:QRCode二维码生成方案及其在带LOGO型二维码中的应用(1)  http://blog.csdn.net/johnsu ...

  5. Cordova各个插件使用介绍系列(四)—canvas2ImagePlugin保存二维码到手机本地

    详情链接地址:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/cordova-4-canvas2imageplugin/ 在前面几篇 ...

  6. Canvas与Image互相转换示例以及利用该技术实现微信长按自动识别二维码功能

    现在扫描二维码已经很普遍,微信扫一扫即可,但是如果二维码是在自己的手机上呢?那就要用到微信里的一个功能了,手指长按二维码,会弹出自动识别的选项,点确定就可以看到二维码的内容了.那么怎么通过前端实现这个 ...

  7. android炫酷动画源码,QQ菜单、瀑布流、二维码源码

    Android精选源码 自定义弹框封装,ProgressDialog,StatusDialog和Toast,支持自定义颜色 有深度感的fragment代码 在屏幕顶部或者底部显示提示 短信转发工具,自 ...

  8. 二维码框架ZBarSDK的使用和自己定义二维码扫描界面方法

    假设你不知道ZBarSDK怎么用,请下载demo http://download.csdn.net/detail/u013686641/7858917 假设你已经配置好ZBarSDK .那么以下这个类 ...

  9. 二维码扫码登录原理及简单demo

    扫码登录原理转载自: https://www.cnblogs.com/liyasong/p/saoma.html 需求介绍 首先,介绍下什么是扫码登录.现在,大部分同学手机上都装有qq和淘宝,天猫等这 ...

  10. 使用java解析和制作二维码

    项目结构 文件源码 QR.zip 第一步:导入zxing的两个架包 core.jar和javase.jar 第二步:使用工具类 MatrixToImageWriter.java package uti ...

随机推荐

  1. 在 KubeSphere 中使用 Rook 构建云原生存储环境

    Rook 介绍 Rook 是一个开源的云原生存储编排器,为各种存储解决方案提供平台.框架和支持,以便与云原生环境进行原生集成. Rook 将分布式存储系统转变为自管理.自扩展.自修复的存储服务.它使存 ...

  2. DHorse v1.6.0 发布,基于 k8s 的发布平台

    版本说明 新增特性 支持Codeup(阿里云云效)代码仓库: 支持环境的自动部署: 优化特性 管理员角色部署环境部需要审批: 优化页面展示: 升级指南 升级指南 DHorse介绍 DHorse是一个轻 ...

  3. 谷歌跨域错误解决:request client is not a secure context and the resource is in more-private address

    chrome://flags/  

  4. SDUT 2021 Autumn Team Contest 36th G - Alice’s Stamps(补)

    队友做出的这个题,赛后补的=.= G - Alice's Stamps (一般DP) 题目大意 :给你m个区间,选k个,求k个区间并集 1≤T≤100 1≤K≤M 1≤N,M≤2000 1≤Li≤ R ...

  5. 斜率优化初探:以 [HNOI2008]玩具装箱 为例

    题目传送门 记 \(f[i]\) 表示装好前 \(i\) 个的最小花费.容易写出转移: \[f[i] = \min_{j \lt i} \ [f[j]+(s[i] - s[j] - 1 - L) ^ ...

  6. ABC365(D,E)

    ABC365(D,E) D - AtCoder Janken 3 石头剪刀布,给出对手的出招,问在保证不败的情况下最多能赢多少回 记 \(f_i,{0/1/2}\) 表示第 \(i\) 局出石头/剪刀 ...

  7. C++ stl锁的使用

    我们在日常开发中经常要用到锁,这里记录一下实际开发过程中stl提供的锁的使用. 1.读写锁 读写锁算是用到的比较多的一种类型,主要实现对于同一个共享数据区,一个时间点只能有一个线程进行写(增删改),但 ...

  8. Qt Creator快捷键记录

    Ctrl + K:代码格式化(先选中要格式化的代码) F4:在头文件和源文件之间切换 Ctrl+/:注释和取消注释 F2:在声明和实现之间切换 Ctrl+K:打开定位器(locator),搜索文件名 ...

  9. 【Playwright + Python】系列(九)Playwright 调用 Chrome 插件,小白也能事半功倍

    哈喽,大家好,我是六哥!今天我来给大家分享一下如何使用playwight调用chrome插件,面向对象为功能测试及零基础小白,我尽量用大白话的方式举例讲解,力求所有人都能看懂,建议大家先收藏,以免后面 ...

  10. Springboot集成WebSocket实现智能聊天【Demo】

    背景 openai 目前越来越流行,其他 ai 产业也随之而来,偶然翻到 openai接口文档,就想着可以调用接口实现智能聊天,接下来就写写我怎么接入 websocket 的过程,文笔不佳,谅解. 接 ...