本文字数:5939字
预计阅读时间:15 分钟
概述
长按图片识别二维码在移动端是很常见的操作,长按后需要对图片进行识别,并且将二维码中所包含的数据解码出来。在我们的业务场景中,是通过点击图片进入大图预览页面。长按大图预览的图片,会识别图片中的二维码,并且显示有跳转按钮,提示用户可以跳转二维码对应的页面。
但是,在现有业务场景中,要求图片中二维码不能在视觉上占据太大的位置,所以只能以很小的尺寸显示在下面。为了更好的配合公司现有业务,保证对图片中二维码的识别率,所以需要对二维码识别进行优化。
优化方案

方案总体分为探测和识别两个核心流程,探测流程主要由图像处理算法,以及OpenCV
来实现,识别流程主要由系统AVFoundation
库的CIDetector
来实现。先将二维码所在区域探测出来,随后对这个区域进行识别增强的处理,以实现模糊、较小的二维码的识别。
探测流程
因为不是每一张图片上都有二维码,探测的意义在于,查找图片中是否有二维码,以及二维码在图片中的位置。从而进行后续的针对性处理。以下,任何一步探测有结果,都将进入识别流程中,并且将探测到的位置传给识别方法。
第一次探测。转灰度图,通过
OpenCV
的cvtColor
函数,将四通道的RGBA
图片,转换为单通道的灰度图。第二次探测。通过算法进行直方图均衡化(非自适应,并且限制对比度),目的是让图片内轮廓清晰。
第三次探测。通过算法进行伽马变换,目的是增强图像对比度。
第四次探测。将原始灰度图的下面
20%
的右半部分,clone
到一个新的Mat
对象中,并且进行3.5
倍的resize
。将得到后的灰度大图调用detect
进行探测。第五次探测。将原始灰度图的下面
20%
的左半部分,clone
到一个新的Mat
对象中,并且进行3.5
倍的resize
。将得到后的灰度大图调用detect
进行探测。探测结束。在二维码的定位图形、码元等核心信息没有受损的前提下,这时候基本断定这张图片上没有二维码。
需要注意的是,在探测方案中,为什么选取下面左右两边的20%
着重进行探测。是因为根据对公司实际业务的调研,绝大多数的二维码都是在图片的右下角位置,其次是左下角。以公司业务为例,目前没见过将二维码放在图片中间的场景。
识别流程
探测到二维码后,会将当时用来探测的图片,以及探测到的二维码区域传递给识别方法。传递过来的图片,可能是
resize
后的大图,对这些大图识别率会更高。从传递过来的图片上,根据二维码所在区域的坐标系,将二维码所在的部分,重绘到一个新的位图对象上。为了保证识别效果,会在重绘时加上一个
15
的外边距,以保证二维码Quiet Zone
的特性。用重绘后的二维码,调用
AVFoundation
的CIDetector
进行识别,并获取识别后的字符串。
为什么不把探测和识别都交给OpenCV
来做。这是因为经过我的测试,我发现OpenCV
的识别率较低,远不如CIDetector
的识别率。所以,只将探测部分交给OpenCV
,但不让OpenCV
去识别二维码。
根据OpenCV
的detect
函数的源码进行查看,发现OpenCV
的探测是基于定位符号进行探测的,分为横向和纵向两个方向进行探测,使用OpenCV
进行探测是不错的选择。但是OpenCV
识别率并不高,所以用OpenCV
进行探测,结合AVFoundation
进行识别的方案,是一个比较不错的策略。
代码实现
方案总体代码量较大,这里列出了一些主要方法的代码实现。探测方法内部实现,会进行不同程度的增强扫描和识别。方案中使用了一些OpenCV
的API
,可以通过OpenCV官方文档了解下API
的定义和调用。
生成灰度图
把传入的图片转为OpenCV
可以识别的Mat
的灰度图,灰度图色彩通道只有一个,在进行后续计算上,会节省很多性能。随后进入后续的探测部分,探测到二维码后,会将二维码拼接业务参数,并在主线程中返回给调用方。