条码简介与zxing扫码优化

3,230 阅读10分钟

条码简介

如今,二维码算是真正的家喻户晓了,之前不怎么会使用手机的老年人,由于疫情的原因,无论是坐公交、去医院还是去买菜,也都需要学会扫描健康码。

随着十年前移动互联网的普及,二维码得到了广泛的应用。付款码、收款码、火车票二维码、微信添加好友二维码、网页二维码……

除了小程序码,我们日常所见绝大多数二维码都是QR码,Quick Responding Code,它的发明人是日本的原昌宏。

原昌宏在丰田工作期间,被安排去解决条码使用效率的问题,比如一维码的数据量少、已有一些二维码的识别率低出错率高等。

通过设计回字定位符,引入里徳所罗门编码,大大提升了条码的识别速度与识别率,也使其逐渐成为世界最主流的二维码格式。

推荐阅读:


条形码

二维码和一维码都属于条码(barcode)。

虽然一维码所能承载的信息量不多,但也有非常广泛的使用场景。在超市里,收银员用扫码枪扫描商品条形码,快速获取商品信息和价格;图书、快递,都有自己的条形码和编码规则。

比如图书,背后条形码中的数据就是 ISBN(International Standard Book Number),国际标准书号。

如:978-7-5419-6632-3

  • 978 图书产品代码
  • 7 组号,代表中国
  • 5419 是出版者号,代表出版社
  • 6632 书序号
  • 3 校验码

虽然相比较而言,信息承载量比二维码少很多,但结构一点也不简单。条形码的结构包括:

  • 两侧静空区
  • 起始码
  • 资料码
  • 校验码
  • 终止码

不同区域在一起,保证条形码的读取快速可靠。

推荐阅读:

推荐大家看英文版的wikipedia,因为中文版本中有价值的信息太少了,对比一下就能发现。


QR码

回到QR码。

QR的全称是quick responding,快速响应。为了保证能快速响应,需要能快速定位二维码的位置和区域、快速获取二维码的数据内容,并尽可能避免由于二维码图片破损导致的识别失效。

为了解决这些问题,QR码引入了两个最重要的机制:

  • 回字定位符
  • 里德-所罗门编码 Reed-Solomon codes

QR码是正方形的。

我们在看到一个标准的QR码(没有美化工具修饰)时,会发现在码区的左上角、右上角和左下角都有一个回字。三个回字即可确定一个二维码的有效区域。

与条形码类似的是,条形码识别0/1是依据黑白竖线的区间宽度,二维码则是根据黑白方形区域来进行0/1二进制数字识别。

理想情况下,二维码是干净完整的。但如果在现实环境,甚至是工厂车间的生产环境中,图片就很容易被污染。所谓的污染,反馈到数据上就是0/1的混乱,导致识别错误。

此时,QR码需要具备一条能够发现并纠正错误数据的机制。实现这套纠错机制的编码方式,就叫里德所罗门编码(Reed-Solomon codes)。

更详细的介绍,网上已经有很多非常优质的资料,感兴趣可以查阅。

推荐阅读:


zxing开源项目

zxing是google开源的一套条码解析库。以Android工程为例,使用的目录包括android-core、android和core三个目录。

  • android 工程是主工程,主要代码是相机获取预览帧相关
  • core 目录是二维码解析的核心工程,可以以依赖库的方式导入主工程
  • android-core 中只有一个类 CameraConfigurationUtils,可以直接拷贝到主工程中或添加依赖

源码的阅读,除了 android 目录下相机相关的配置及获取预览帧的逻辑,核心解析逻辑可以从 DecodeHandler decode看起。

解析的流程包括:

  1. 二值化
  2. 寻找定位符
  3. 寻找校正符
  4. 经过去掩码、获取版本号、获取格式信息、数据解析等过程,获取最终结果

zxing的源码分析也有很多博客资料都在介绍,此处不过多阐述。

推荐阅读:


zxing优化思路分析

通过阅读源码的逻辑可以发现,zxing 所解析的图片基本处于一个比较理想的环境,如果出现图片有噪点(尤其是出现在定位符区域)、拍照角度较大、或者二维码图片有折痕或弯曲等情况,都会导致识别失败。

所以,zxing只是一个非常基础的二维码解析库,如果希望扫码有更好的用户体验,需要进行一些针对性的优化。

既然要优化,首先需要确定优化目标。

二维码扫码的关键指标有四个:

  • 扫码识别率:在产品使用路径中,扫码识别率=识别出二维码内容的次数/用户打开相机的次数
  • 有效二维码识别速度:若预览帧中有有效二维码(即结果为可检出),则从打开相机开始到识别出二维码的速度
  • 二维码识别召回率:对于无效二维码,可以在流程尽可能早期被丢弃而不进入后期的解析环节
  • 无效二维码检出速度:如果相机获取的预览帧中无有效二维码,应当尽快丢弃而无需继续消耗资源进行解析

zxing中,主流程是串行的,从相机获取一帧,解析识别,若未解析出则重新获取;所以,获取图像与解析可以考虑设计成并行方案。

此外,更多的优化环节在于从相机到解析中的各个步骤的优化。

抽象一下整个流程,最主要的就是两步:

  1. 获取合适的图片
  2. 解析图片

再将步骤细化并具象化,得到:

  1. 相机拍摄
  2. 图像优化
  3. 图像二维码解析

每个环节,都可以尝试从扫码识别率、有效二维码识别速度、二维码识别召回率和无效二维码检出速度这四个方面思考优化方案。

相机拍摄

  • 自动聚焦
  • 聚焦间隔
  • 聚焦区域
  • 连续拍照
  • 暗环境补光(或开灯,开灯逻辑zxing android工程已包含)
  • 过远放大

在扫码识别率上,通过聚焦的方式提升拍照清晰度;通过环境光传感器获取环境的亮度确定如何补光或开灯;若扫码时距离较远,则需要进行拉近焦距的放大操作。

我们有尝试过前几秒没有识别出二维码的时候直接放大,这导致了更低的识别率。于是我们暂时放弃了放大方案,因为我们的业务场景中很难遇到远距离扫码的场景。

放大的关键点在于放大时机和放大倍数。在网上见过有10次未识别则放大的,放大倍数需要依据预测当前二维码大小和扫描框的大小计算得来。当然也可以通过机器学习预测图像中是否包含二维码再决定是否放大。

在识别效率上,对不同的机型调整不同的聚焦间隔时间来尽可能降低耗时。该环节暂不涉及二维码识别召回率和无效二维码检出速度。

图像优化

  • 噪点滤波处理(如椒盐噪声)
  • 反光处理
  • 异形纠正(大角度拍摄、图片弯曲等)
  • 图像二分,有无二维码,CNN or Logical Regession
  • AI图像加强,超分辨率技术(低分辨率优化成高分辨率)

理想的条件下,图像都是完美无瑕的,但是显示的情况,由于设备的差异、环境的差异等,导致图像出现不同程度的污染或变形。

在识别率上,尽可能提升图片的质量。噪点、反光、纠正和提升分辨率,都是为了优化图片使其更容易识别,避免定位符解析不出等问题。

在识别效率上,通过图像二分,辨别出图像中是否包含二维码,提升二维码识别的召回率,避免后续无效图像的解析,提升整体识别效率。当然,检出无效二维码的速度也是越快越好,否则还不如直接走完整个识别流程。

图像二维码解析

  • 解析格式精简
  • 二值化(不同的二值化的适用场景不同)
  • 定位符识别(兼容更复杂的场景,1:1:3:1:1 过于严格)

在zxing中,二值化的逻辑提供了两个算法,HybridBinarizer 和 GlobalHistogramBinarizer。

这两个二值化的方案有各自的适用场景。GlobalHistogramBinarizer 是理想环境下的二值化,对阴影渐变之类的处理不好,但对于老手机来说,cpu算力弱内存低,能有较好的表现;HybridBinarizer 则相反,对性能要求稍高但能处理一些异常场景。

所以,如果用户的使用环境比较确定,且处于较理想的环境,GlobalHistogramBinarizer 是个理想的选择;否则选 HybridBinarizer。

上述讨论了识别效率的问题,对于识别率,主要聚焦在定位符的识别。1:1:3:1:1的匹配方式是理想的环境,在现实场景下可能显得较为严格。有些厂商采用了1:5:1的方案,或N:1:3,用于解决中间有噪点,或周边为深色背景的情况。

参考资料:


基于Machine Learning的识别SDK介绍:Google Vision & HMS ScanKit

一个偶然的机会,跟一位字节的Android开发沟通了二维码优化方面的经验,他在掘金上也写了很多二维码和zxing相关的总结,包括QR码的原理、zxing源码以及带有具体代码的优化方案,推荐有兴趣的去阅读。

在沟通中他跟我提到现在最新的条码识别方案有Google Vision和华为的HMS Scankit。

这两个SDK没有具体的源码可以分析,从我实际的测试来看,HMS Scankit能力更强一些,识别速度、多码识别上都有较好的表现。

从流程图可以推测,HMS Scankit 主要是使用ML的方式对图片进行识别优化和裁剪,其他的部分,如定位符识别、RS编码(里的所罗门编码)解析等,都与zxing类似。

此外,HMS Scankit 的集成方案更完整,如自带相机逻辑和UI的展示,方便快速接入。

推荐阅读: