随着二维码的流行,几乎所有手持设备都支持二维码的扫描识别。对于大部分普通的情景下,使用zxing和zbar就可以很好的完成二维码的识别,但是如果碰到一些特殊的情景,zxing和zbar的识别率并不高。这些特殊的情景包括:
- 二维码是灰色的
- 大角度斜扫二维码
- 由光源引发的摄像头干扰,比如手机扫描屏幕上的二维码会出现条纹或者噪点
说明
在介绍二维码的优化前,可以参考二维码基础原理了解二维码识别的相关知识。本博客所讨论的二维码相关优化代码已提交到github 感兴趣的读者可以下载使用
普通优化
在介绍难点优化之前,先介绍网上常见的优化点。这些优化点同样被使用在了作者的项目中,具体包括:
解码优化
- 减少解码格式
- 解码算法优化
- 减少解码数据
- 解码库Zbar与Zxing融合
优化相机设置
- 选择最佳预览尺寸/图片尺寸
- 设置适合的相机放大倍数
- 调整聚焦时间
- 设置自动对焦区域
- 调整合理扫描区域
以上普通优化均参考自智能设备上的二维码解码优化,
不过作者在实现过程中,对该链接所对应的项目实现做了一些修改,修改点包括:
- 旋转android后置摄像头preview数据时,根据yuv NV21数据的特点进行旋转
- 摄像头预览数据中,对应预览框的数据的截取,并非通过PlanarYUVLuminanceSource创建时提供的rect进行截取。而是通过opencv,将yuv数据转换成rgb之后,在进行截取
- 摄像头最佳预览尺寸选取时,按照设备屏宽,屏高最接近的方式选取,并没有通过屏幕宽高比进行筛选。因为部分android手机,屏幕的宽高比和摄像头所支持最接近的size的宽高比并不一致(ps:比如nexus 4,屏幕宽高为720/1280, 但是摄像头支持的最接近屏幕宽高比的preview size,都要少几个像素,印象中,好像是 716/1276, 具体多少记不清了)
- 支持用户双指对摄像头进行缩放,并未采用链接引用项目的zoom策略
- 添加zbar结合zxing一起检测的代码
难点
整个二维码扫描识别过程中,个人认为最关键的一步在于图片的二值化处理。所谓二值化,可以简单理解为给定一个阈值,低于阈值的像素标记为白色,高于阈值的像素标记为黑色。一旦进行二值化后,图片形成黑白两色图,再利用二维码原理进行定位查询并识别解码并不困难。因此能否对一个图片进行正确的二值化极大影响二维码识别率。而以下特殊情况很大程度上会干扰扫描过程中二值化过程:
二维码是灰色的:
如上图,某些二维码是灰色的,或者由于光照的情况下色调比较淡。灰色二维码识别难度在于背景往往比二维码的灰点色度更黑,当用手机对这类灰色二维码进行扫描时,总是或多或少会有一些非二维码的背景显示在扫描框中,而这部分背景由于色调比灰点深,这会造成zxing,或者zbar二值化过程中选择阈值受到干扰,从而无法识别二维码
当大角度对二维码进行扫描识别
在这种情况下如果是纯黑色的二维码影响不会太大,但如果是灰色的二维码,就非常容易受到光照的印象。 由于是斜着扫二维码,那么对于摄像头来说,二维码的两边所带来的亮度是不同的。
如上图例子,如果是从左侧进行扫描二维码,那么左边由于距离摄像头更近,因此更亮,而右边由于距离摄像头更远,会比较暗,此时整个预览框进行二值化时,非常容易造成右边暗的部分,全部超过阈值而被判断为黑色,进而极大影响二维码的识别
由光源引发的摄像头干扰,比如手机扫描屏幕上的二维码会出现条纹或者噪点
如果二维码位于电脑屏幕中,或者位于其他投影设备里,或者当时处于白炽灯等灯光照耀的情况下,进行扫描,摄像头有时候会受到光源频率的干扰,从而形成条纹状。而某些情况下,当摄像头zoom到一定程度是,形成的早点也会对二维码识别造成极大干扰,如下图所示作者遇到的情况:
这类情况,如果二维码是黑色的还好,如果二维码是灰色的,则条纹会对二维码的识别起到很大的干扰,如上边右侧图像所示
优化
针对上述难点,作者尝试通过opencv对相机的preview数据进行预处理,从而提升二维码的扫描识别,作者进行过的尝试如下:
尝试1:通过opencv方法进行预览数据的降噪
作者本想通过opencv的各种降噪滤波算法(例如mediaBlur等),去光照算法(例如RGB归一化等)等对camera预览数据进行处理。可能是作者对于opencv算法理解还不足,也可能是其他原因,测试下来效果都不太好。最好作者选择mediaBlur作为降噪算法进行保留,对于一些椒盐噪点的去除还挺有帮助
尝试2:缩小二值化阈值计算范围
考虑到灰色二维码之所以难以识别是因为背景的色调过暗,拉低了二值化阈值的计算,导致整个二维码的难以识别。因此作者将二值化阈值的计算区域缩小到预览框的2/5,然后再通过计算的阈值对整个预览区域的数据进行二值化。这个效果对于灰色二维码的识别提升很大。可惜的是,只对正面扫描的情况下有用,一旦斜着超过一定角度扫描灰色二维码,往往识别不出。
尝试3:分块对预览区进行二值化
考虑到斜扫二维码不好识别因为矩形预览区域各个部分的亮度不统一,因此作者下一步尝试是,将矩形预览框再次分成4个块,每个块取其靠近中心部分的1/5区域进行阈值计算,最后分块进行二值化后,拼接而成。具体如下示意图所示
之所以分块进行计算阈值,是考虑到不同区域的亮度是不同的,如果整体进行计算的话,会丢失部分有效信息。而之所以选取靠近中心的小部分进行阈值化计算,是因为用户行为通常会将二维码对准预览框的中心,因此中心部分,包含有效亮度信息更为精确,减少背景亮度对整体二值化的影响。这一步尝试之后,大大提升了特殊情景二维码的识别概率。至此,opencv预处理部分到此结束了。
总结
经过上述opencv预处理尝试之后,大大提升了特殊情景的二维码识别,但是作者认为二维码识别还有更多优化的地方,特别是二维码的识别算法作者还没有时间研究,只好留待后续了。