

二维码扫描优化
source link: https://techblog.toutiao.com/2018/09/18/untitled-53/?amp%3Butm_medium=referral
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.

用技术提升美好事物发生的概率。 Tech for greater probability to be happy.
前言
zxing是一款跨平台的基于Java实现的处理一维或二维条码的库。支持多种格式,一维条码支持UPC-A,UPC-E,EAN-8,Code 39,Code 93等格式,二维条码支持QR Code,Data Matrix,PDF 417,MaxiCode等格式。
上述的二维条码指的是较宽泛的二维条码,而不是QR Code表示的二维码。
原本Lark直接集成了zxing实现扫一扫功能。由于Lark的特殊业务需求,因此并不需要支持到这么多格式,只需要支持QR Code,因此我们对zxing内部进行定制,使得zxing只支持QR Code。这样既可以减少zxing库的大小,也可以加快zxing处理一帧数据的速度。
优化主要包含两方面:(1)扫描性能(2)交互体验。
扫描性能优化包括:
- 去除zxing额外支持的格式。
- 删除zxing冗余代码。
- 将处理相机帧从串行改为并行。
交互体验优化包括:
- 自动放大。
- 双击放大。
- 重力传感器聚焦。
- 手势调整焦距。
1. 去除zxing额外支持的格式
MultiFormatReader的decodeWithState()是使用方的入口方法,内部调用了decodeInternal(),输入是相机的一帧数据,如果抛了NotFoundException,则表示没找到二维码;如果返回了Result,则表示找到了二维码,并解析完成。代码如下:

其中,readers变量是一个数组,数组的大小表示支持的条码格式个数,zxing原本因为支持很多格式,因此这个数组长度比较长。当拿到相机的一帧数据后,需要去检测是否是所有支持格式的某一个格式,每一种格式的检测都需要花费一些时间,因此这个遍历对于Lark是不必要的。如果将zxing内部定制成只支持QR Code格式,那么就免去了额外的格式检测。
2. 删除zxing冗余代码
我们主要从几方面删除冗余代码:
- 删除zxing除了二维码之外的格式的相关代码,zxing对每种格式的相关代码都放在各自的目录中,因此我们只需要把这些格式对应的目录删除即可,比如aztec、maxicode等。
- 删除二维码的encode相关代码,即"qrcode/encoder"目录。
- 删除decode后文本的解析相关类(比如地址、通讯录、邮件等解析类),只保留URI、URL、Text。
通过以上方式,zxing文件数量从263个缩减到67个,库大小从1.8M缩减到451K,效果非常明显。
3. 将处理相机帧从串行改为并行
原本Lark扫一扫的逻辑是串行的,如下图:
每次从onPreviewFrame()中获取一帧数据,然后调用zxing的decode解析二维码,如果成功,则返回;如果失败,则调用setOneShotPreviewCallback()重新调用一次onPreviewFrame()。
缺点是如果处理一帧数据时间很长,会阻碍下一帧的处理,比如上一帧是没有二维码的,而下一帧是有二维码的,如果上一帧处理时间较长,那么虽然用户对准了二维码,但是实际处理的还是上一帧,因此不太合理。
我们将串行处理改成并行处理,一旦从onPreviewFrame()获取一帧数据,将decode任务丢进线程池,并立即调用setOneShotPreviewCallback()获取下一帧数据。一旦某个任务检测到二维码,立即将isSuccess变量置为true,忽略其他任务。这样能够大大加快二维码检测的速度。
4. 自动放大
当二维码很小很远时,自动放大能大大加快检测二维码的速度。
QRCodeReader的decode()是二维码检测的主方法,分为两步:(1)大致判断是否存在二维码;(2)解码。

第一步只是检测是否存在二维码,比如去寻找是否存在Position Detection Pattern,Timing Pattern,Alignment Pattern。如果检测到了,则返回DetectorResult,内部包含了定位点的位置信息;如果没检测到,则抛出NotFoundException。如果二维码很小,即使第一步检测存在二维码,但是第二步解码也可能会失败。由于我们在第一步已经能够知道二维码的大小,因此根据DetectorResult返回的二维码定位点信息计算出二维码的大致宽度,然后判断二维码大小在扫码框中是否足够小,如果足够小,则放大一定焦距:如果小于十分之一,则放大到最大焦距;如果小于等于六分之一,则放大到最大焦距的一半。
具体二维码的原理参见: 二维码的生成细节和原理 。
我们实现了zoomCamera(),如果判断需要放大,则返回true,如果不需要放大,则返回false。代码如下:
我们在第一步和第二步中间插入该方法,如果需要放大,则不执行第二步;如果二维码已经足够大,则执行第二步。代码如下:
5. 双击放大
原本Lark的二维码扫描中没有调整焦距的功能,这个对于一些特定场景下会不太方便,因此这里加入了双击放大的功能能够对焦距进行粗略的调整。利用GestureDetector的onDoubleTap()回调捕捉用户双击事件,并在CameraPreview中的onTouchEvent()中添加mGestureDetector.onTouchEvent()。实现如下:
6. 重力传感器聚焦
重力传感器能够捕捉用户手机的运动状态,当检测到用户手机停止时,触发对焦逻辑。我们通过实现SensorEventListener接口,并重写onSensorChanged()监听手机的运动状态。
7. 手势调整焦距
为了更精细化的让用户调整焦距,我们提供了手势来缩放焦距。通过在onTouchEvent()中获取用户两个手指的距离是越来越近还是越来越远来调整焦距。代码如下:
优化结果
经过上述优化,不仅增加了用户体验,而且还大幅增加了二维码扫描速度。 测试手机:坚果Pro,4G内存,Android 7.1.1。

上图表示了从打开相机到二维码解码成功的耗时,可以看出,整体时间提升了300%+。
上图表示检测失败时的耗时指的是当相机帧中没有二维码时检测的时间,检测失败耗时的减少有助于更快地处理相机帧数据,当包含二维码的帧出现时更快地处理它;上图中看出,耗时减少300%+。
上图表示检测成功时的耗时指的是当相机帧中有二维码时检测+解码的时间;上图中看出,耗时减少150%+。
Recommend
-
101
查验发票 - 扫描发票二维码即可免费查验发票真伪,还可对发票进行管理统计 - NEXT
-
36
之前都是第三方的二维码,操作简单pod集成,美滋滋。随着公司项目越来越恶心到大,还是自己撸一个比较实在。 demo的主要功能扫描二维码和生成二维码。 扫描二维码 1.首先倒入框架 #import
-
20
前言:最近想在我的 '个人网站' 上面加上一个扫二维码登录后台的一个功能,之前写过websocket在线聊天,知道扫码成功之后,需要用到websocket来实现pc同步登录,百度了一下 '扫码登录思路
-
27
扫二维码登录现在比较常见,比如微信、支付宝等 PC 端登录,并且好像每款 APP 都支持扫码登录,不搞个扫码登录都不好意思。作为技术人员,不知道您对这背后的实现逻辑是否感兴趣,反正我是一直都对这背后实现好奇。最近刚好看到一个关于扫...
-
27
Android调用扫码界面,扫码后获取二维码信息返回
-
8
二维码扫描识别 Jul 20th, 2016 2:31 pm | 评论 最...
-
4
Android终于要推出Google官方的二维码扫描库了?-51CTO.COM Android终于要推出Google官方的二维码扫描库了? 作者:郭霖 2022-03-15 08:10:05 这里我要问大家一个问题,你们都是如何在自己的App...
-
5
设置无线扩展器时,使用手机等移动终端扫描扩展器壳体上的二维码后无法登录管理界面。
-
9
淘宝内测微信支付:扫描二维码付款 2022-3-25 10:35:42 | 作者: 投稿 | 分类: 业界动态 | 评论: 0 | 浏...
-
5
微信扫描带参数的二维码事件推送返回 qrscene_0 技术笔记 / 2022年3月30日 / 0 条评论
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK