iOS扫码组件优化

1,022 阅读19分钟

技术调研

问题背景

一句话:商户打印的条形码和二维码不规范,同时现有组件的技术比较老,导致扫码识别效率低。

  1. 商户打印条形码和二维码编码格式,尺寸和边框距离等影响因素不统一,同时打印机有误差,导致部分残次二维码识别不出来。
  2. 部分场景需要同时识别条形码和二维码,且大小不一样,造成识别效率较低。
  3. 机型方面,考虑到目前优化的是质检小工具app,三大仓机器主要为iphone12及以上,优先兼容高版本机器,之后再考虑低版本机器。

目标

提高现有扫码模块的识别效率,从识别花费时间,识别率,兼容的条码类型提高

技术背景

条形码和二维码对比

特性条形码二维码
样式:123456789
数据存储能力容量小,通常只能存储20个字符左右容量大,可存储数千个字符
数据类型数字和有限的字母字符(如Code 39、Code 128)数字、字母、汉字、URL等多种信息
物理尺寸需要较长的物理空间数据密度高,占用空间小
读取设备专用条码扫描仪,带摄像头设备智能手机、平板电脑等带摄像头的设备
读取方向只能从一个方向(通常是水平)读取可以从多个方向(360度)读取
读取速度读取速度快,适合少量信息读取速度快,即使包含大量数据
准确性对污损和损坏容错性较低对污损和部分损坏容错能力高,能够纠错和恢复数据
常见应用零售商品标识、库存管理、图书馆书籍管理、物流标签等移动支付、电子票务、信息链接、产品追溯、营销广告等
标签成本印刷成本低,标签制作简单印刷成本略高,但相差不大
实施难度系统集成和使用成本较低需要较高的技术支持和设备(如智能手机应用)
优点成本低,实施简单,读取速度快,适合编码少量信息的应用场景数据容量大,灵活性高,读取方向不限,容错能力强
缺点数据容量有限,物理尺寸较大,容错性差,对污损和损坏敏感标签制作成本稍高,实施需要一定的技术支持

条形码生成原理与格式

条形码通常由以下几个部分组成:

  • 起始符:标志条形码的起始部分,帮助扫描器定位和确定读取方向。
  • 数据部分:包含实际的信息,通过黑白条纹的组合表示。
  • 校验码:用于验证条形码数据的正确性。
  • 终止符:标志条形码的结束部分。

下面是多个黑线和白线如何组成一个字符的原理,以code128为例子:

  • 具有A、B、C三种不同的编码类型,可提供标准ASCII中128个字元的编码使用
  • 允许双向扫描(正着反着都能扫)
  • 可自行决定是否加上检验位(但付款码必须要校验位)

为了方便表达,我们用b代表一条黑色像素宽度的线,s代表一条白色像素宽度的线,当bs组合时,中间不留任何缝隙,单个b和s对应的线的宽度是固定的。

ABC三种编码的部分字元的编码表如下:

BandCode是Code 128的核心编码,表示条纹和空白的宽度。每个字符由6个数字组成,每个数字表示对应条或空白的宽度(1、2或3个模块)。例如,ID 0的BandCode为“212222”,表示:

• 条:宽度2

• 空白:宽度1

• 条:宽度2

• 空白:宽度2

• 条:宽度2

• 空白:宽度2

条形码的识别原理

条形码识别是通过检测图像中条形码的黑白条纹,并将这些条纹转换为数字或字母信息。条形码的识别过程可以分为以下几个步骤:

  1. 图像捕获
  2. 图像预处理操作
    灰度化:将彩色图像转换为灰度图像,以简化后续处理。
    二值化:将灰度图像转换为黑白图像,突出条形码的黑白条纹。
    去噪:移除图像中的杂点和噪声,确保条纹清晰。
  3. 特征提取,提取条形码的关键特征,即黑白条纹的宽度和排列顺序。

特征提取步骤包括:

扫描条纹:从左到右(或从上到下)扫描条形码,记录黑条和白条的宽度。例如,记录为一系列宽度值:[2, 1, 3, 1, 2, …]。

识别模式:根据条纹的宽度和组合识别起始符、中间分隔符、数据位和校验位。

  1. 解码

根据提取的特征,将条纹和空白的组合转换为对应的数字或字符。

解码步骤包括:

匹配编码表:每种条形码标准(如UPC、EAN)有其特定的编码表,将扫描到的条纹组合与编码表进行匹配,转换为相应的数字。例如,条纹组合“黑-白-黑-白”对应字符“C”。

校验验证:使用校验位验证解码的数据是否正确,确保识别的准确性。

二维码生成原理和格式

  1. 数据编码

将输入的字符串或数据(如文本、URL)转换为二进制数据。

二维码支持多种编码模式:数字、字母、二进制等。

  1. 纠错编码

使用**纠错码(Reed-Solomon编码)**将数据编码为可以恢复的形式,即使二维码部分损坏也能恢复。

不同的纠错级别(L、M、Q、H)对应不同的容错能力。

  1. 数据结构化

将编码后的数据放入特定的结构中,包括定位符(用于定位二维码位置)、格式信息(包含纠错级别和掩码模式)、版本信息(定义二维码的大小)等。

  1. 掩码应用

为减少图像噪声和提升识别率,二维码图案应用特定的掩码算法(如按特定规则翻转部分黑白块)。

  1. 绘制二维码图像

根据上述步骤生成的二维码矩阵绘制图像,最终形成二维码。

二维码的识别原理

  1. 图像采集
  2. 图片预处理:灰度化,二值化,噪音去除
  3. 二维码定位:定位图案,定位区域确定
  4. 二维码结构分析:数据区域提取
  5. 数据解码:错误检测与纠正,解码数据
  6. 输出结果

识别效果影响因素

  1. 打印的条码是否规范:

下面这个条码就比较不规范,下面图片中的条形码1中的红色线指向的黑线,很明细非常细,这导致条形码根据图纸解析出来的编码,无法找到正确解码。

条形码1

条形码2

  1. 印刷材料和表面处理

材料:不同材料的反光度和吸墨性会影响扫描效果。例如,光滑或反光的材料可能导致反射光干扰扫描。

表面光洁度:粗糙或不均匀的表面可能导致条码/二维码的图案变形,影响识别。

  1. 环境条件

强光,阴影会干扰扫描设备的读取

  1. 位置和角度

安装位置:条码/二维码的位置不当(如弯曲、折叠处)会影响扫描设备的读取。

角度:条码/二维码与扫描设备的角度不合适可能导致部分信息无法被识别。理想的扫描角度通常是垂直于条码/二维码表面。

  1. 扫描设备性能

高分辨率和高灵敏度的扫描设备能够更准确地读取复杂或微小的条码/二维码。

设备的扫描速度影响快速移动物体上的条码/二维码的识别效率。

  1. 软件算法

先进的图像处理和解码算法能够提高在复杂环境下的识别率,减少误读和漏读。例如微信扫码,就针对破损条形码做了特殊优化。

  1. cpu和gpu

不分算法可能会使用到机器学习等算法,将会导致识别效果和处理器能力有关。

现有扫码能力

  1. AVFoundation
  2. ZbarSDK
  3. ZZ 封装的 ZbarSDK
  4. ZXingObjc

方案选择

库名优点缺点语言支持支持条码类型
AVFoundation- 原生框架,性能稳定 - 支持广泛的条码格式 - 不依赖外部库- 需要较多代码处理界面和逻辑 - 扫描界面自定义复杂Swift, Objective-CQR, Code128, EAN, UPC 等多种类型
ZBarSDK- 历史悠久,曾广泛使用 - 轻量级库,支持多种格式- 已停止维护,不再更新 - 在最新 iOS 版本上可能存在兼容性问题Objective-CQR, EAN, UPC 等
ZXingObjC- 支持多种条码格式 - 轻量级开源库 - 支持多平台(iOS 和 Android)- 依赖 Objective-C,可能对纯 Swift 项目不够理想 - 集成配置略复杂Swift, Objective-CQR, EAN, UPC, Code128, PDF417, Data Matrix 等
Vision Framework- 原生库,集成简单 - 利用机器学习,准确性较高- 仅支持 iOS 11 及以上版本 - 仅支持二维码和条形码Swift, Objective-CQR, EAN, UPC
MTBBarcodeScanner- 易于使用,快速集成 - 提供友好的 API,支持自定义扫描区域- 仅限于条形码和二维码 - 社区支持有限,功能较简单Swift, Objective-CQR, EAN, UPC
RSBarcodes_Swift- 支持生成和识别多种条码 - 简单易用 - 支持自定义扫描界面- 较少的社区支持 - 功能更新缓慢SwiftQR, Code128, EAN, PDF417 等
BarcodeScanner- 易于集成,API 友好 - 扫描性能较好- 主要支持二维码和少量条形码类型 - 社区支持较少SwiftQR, EAN, UPC
Scandit SDK- 商业化 SDK,性能极高 - 支持非常多条码格式,企业级解决方案 - 强大的文档支持- 收费库,价格较高 - 集成较复杂Swift, Objective-C支持市面上几乎所有条形码类型
Dynamsoft Barcode Reader- 企业级解决方案,支持多种条码格式 - 高速扫描性能,支持模糊和受损条码 - 文档详细,商业支持强- 收费库 - 集成复杂Swift, Objective-CQR, EAN, UPC, Data Matrix, PDF417 等
Google ML Kit (Barcode Scanning)- Google 提供的机器学习框架,稳定且功能强大 - 跨平台支持- 功能较为基础,条码类型有限 - 依赖 Google 的 SDKSwift, Objective-CQR, EAN, UPC, PDF417

demo验证

转转封装的zbar

教程:dashen.zhuanspirit.com/pages/viewp…

  1. 注意使用特殊的分支,而不是master分支,建议使用下面的分支
pod 'ModuleScanner/Core', :git => 'http://gitlab.zhuanspirit.com/zz-ios-open/ios_module_scanner.git', :branch => 'ios_module_scanner_test', :modular_headers => true
  1. 注意本地的zbarSDk版本
pod 'ZBarSDK' ,                  :git=>'git@gitlab.zhuanspirit.com:zz-zlj-ios/thirdFrameworks/ZBarSDK.git', :tag => '1.3.6', :modular_headers => true

使用系统能力 AvFoundation+ZXing 辅助解析

iOS扫码优化-原生解析+ZXing辅助解析iOS扫码组件的优化,使用原生AV框架解析的同时,用ZXing解码库辅助解析 - 掘金 (juejin.cn)

使用apple 自带的 vision 视觉识别库,底层使用了机器学习

Apple Vision

先说结论:

识别效果不错,且支持大部分的二维码和条形码

但是在低端机器上,识别破损条形码的表现欠佳,例如iphone7,iphone xr max,和其他的几个算法没有拉开差距。

iphone13pro 扫码 效果不错

iphone7 扫码 效果一般 (这个后续了解到,vision支持的最低版本是ios11,而iOS11最早搭载与iphone8)

可能的原因:

  1. 摄像头质量较差,分辨率和低光性能比后续设备较为落后
  2. 算力和性能较差,Vision框架计算依赖于CPU和GPU
  3. 在就设备上,即使使用了最新的算法,受限性能瓶颈,无法重复利用这些优化

下面是潜在的一些坑:

  1. 性能问题,低端机识别性能
不同机型 不同破损情况条形码

各机型分布:

结论:高端机型上的识别效果差不多

设备型号活跃设备数条形码识别数量(总3)二维码识别数量(总3)
苹果iPhone Xs100623
苹果iPhone 8 Plus65723
苹果iPhone 1236923
苹果iPhone 13 Pro19223
支持编码格式实验(条码兼容性)

由于目前没有xs测试机,使用和xs相机模组一样的xs max来进行实验。

结论:目前这四种主流的条码,都是兼容的

image.png

识别率简单测试

多个条码,随机位置扫码(包含一些比较苛刻的角度和距离),识别成功次数/扫码次数

设备型号多个条码,识别率多次实验取平均
苹果iPhone Xs Max45%
苹果iPhone 8 Plus35.2%
苹果iPhone 1272.3%
苹果iPhone 13 Pro78.6%

对比

各种特殊条码测试

结论:微信的扫码模块针对各种条码都可以识别,甚至对于破损的条码都做了特殊优化。ZZ zbar scanner 在 (旧防拆码 有红色痕迹遮挡 )这种类型的识别效果不好。

image.png

条码编码类型兼容测试

结论:zbar scanner在EAN-13和,UPC-A 这两种编码的识别能力欠佳

image.png

查看代码,可能是没有设置这两个条码类型

识别效率

目前还没有做定量分析,下面只是做一个简单的定性分析和简单演示

测试机器为iphone xs max

  1. 标准且干净的条码

结论:目测,看起来都差不多

Vision scanner

ZZ zbar scanner

zbar scanner

AV Foundation

  1. 有破损的条码

结论:zabr和vision半斤八两,都差强人意

iphone xr max 识别破损条码

  1. 电脑屏幕前的二维码 (开发和测试经常会使用到的场景)

结论:vision库比zbar的好一些,zbar在一些角度和距离下回扫描不到

屏幕前二维码识别

针对不同机型

iphone xr max 和 iphone 12 ,二者扫描破损的二维码

在iphone xr max 上,vision库的效果和zbar 差不多

在iphone 12上,vision库的效果比zbar要好很多

iphone xr max 识别破损条码

iphone 12 识别破损条码

可能的原因:

  1. 摄像头模组不同,vsion 算法对图片的清晰度和分辨率要求更高
  2. gpu处理器不同,vsion算法使用的是机器学习,对处理器要求较高
  3. 系统版本不同,xr max的系统为15.6, iphone 12的系统为17.5.1,这之间的版本,针对vison算法有做优化。

下面是核心代码块

    // 处理视频帧
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        if isScanningPaused { return } // 如果扫描暂停,不处理帧

        // 每隔10帧处理一次
        frameCounter += 1
        if frameCounter < frameThreshold {
            return
        }
        frameCounter = 0

        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

        if isProcessingFrame { return }
        isProcessingFrame = true

        // 创建条形码检测请求
        let request = VNDetectBarcodesRequest { (request, error) in
            DispatchQueue.main.async {
                self.isProcessingFrame = false

                guard let results = request.results, !results.isEmpty else {
                    // 清除检测框
                    self.clearDetectionOverlay()
                    return
                }

                AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))

                self.detectionOverlay.sublayers?.removeAll()  // 移除之前的检测框
                
                // 暂停扫描
                self.isScanningPaused = true
                
                if self.captureSession.isRunning {
                    self.captureSession.stopRunning()
                }
                
                // 遍历检测结果
                for result in results {
                    if let barcodeObservation = result as? VNBarcodeObservation {
                        self.drawBoundingBox(for: barcodeObservation)
                        self.resultLabel.text = "扫描结果: (barcodeObservation.payloadStringValue ?? "---")"
                    }
                }
            }
        }

        // 创建 Vision 请求处理器
        let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
        DispatchQueue.global(qos: .userInitiated).async {
            do {
                try handler.perform([request])
            } catch {
                print("请求失败: (error)")
            }
        }
    }

深入原理

Vision 库

Vison 与 Core ML 的关系

Vision 是 Apple 在 WWDC 2017 推出的图像识别框架。

Core ML 是 Apple 在 WWDC 2017 推出的机器学习框架。

Vison的功能:

  • 对象检测与识别:能够定位图像中的物体边界框并进行分类识别。例如,可以检测图像或实时摄像头画面中的人脸、二维码、条形码,或者利用深度学习模型识别特定对象(如花卉、车辆、动物)。
  • 文本检测与识别:Vision可以在图像中检测文本区域,并与文本识别(通常需要和Core ML或苹果内置OCR能力结合)进行整合,实现从图像或拍摄画面中提取文字。
  • 人脸特征与表情分析:除了检测人脸位置,Vision还能识别人脸特征点(如眼睛、鼻子和嘴巴的位置),甚至通过表情分析来判断笑容、闭眼等状态。
  • 图像注册与配准:Vision提供了图像配准(Image Alignment)和光流分析(Optical Flow)的能力,可用于图像稳定、AR场景中的场景对齐等应用。
  • 基于Core ML的自定义模型集成:Vision的VNCoreMLRequest类允许开发者将自定义的深度学习模型(通过Core ML转换)整合进Vision工作流,从而利用Vision进行更专业的检测和分类任务。

Core ML的功能:

  • 多模型类型支持:Core ML支持多种类型的机器学习模型,包括但不限于深度神经网络(CNN、RNN、Transformer等)、传统的树模型(如XGBoost)、线性回归模型以及特定领域的模型,如推荐系统模型。
  • 高性能与硬件加速:Core ML会自动在设备上利用GPU、ANE(Apple Neural Engine)或CPU进行加速推理,以获得更快的执行速度和更高的能效。
  • 易于集成:开发者可通过Core ML Tools将来自TensorFlow、PyTorch、Caffe等框架的训练模型转换成Core ML模型文件(.mlmodel)。
  • 隐私与安全:在设备端进行模型推断意味着数据不需要上传至云端处理,保护了用户隐私并能在无网络环境下正常工作。

下面是框架图片。

Vision与Core ML的协同工作方式
在典型的视觉智能应用中,Vision框架负责图像的基础预处理和关键特征提取步骤,例如人脸检测、特征点定位、图像裁剪和图像旋转等。随后,利用VNCoreMLRequest将图像处理结果传递给已经转换为Core ML格式的自定义模型进行分类、检测或预测,从而实现更高级的计算机视觉功能。

简而言之:

  • Vision侧重于图像与视频的分析、特征提取和高层次的视觉任务,为开发者提供更为直观的API进行对象、人脸、文本等识别。
  • Core ML则是通用的机器学习推理框架,负责在本地高效运行各种机器学习模型,包括那些可被Vision调用的自定义视觉模型。

技术方案

目标

初步目标:实现一个扫码组件:简单易用,扫描花费的时间少,兼容的条码类型多

进阶目标:可移植性强,可扩展性强

技术实现

要点:

组件要点:

  1. 底层接口封装 支持替换算法,即功能协议声明。
  2. ui 可以自定义,即ui协议声明。
  3. view和viewController 嵌入和跳转两种模式
  4. 多个扫描目标,作为附加能力可配置
  5. 一些可配置的小功能:支持双击和拖拽,放大和缩小 镜头,手电筒,相册
  6. 可以结合使用多种算法,谁识别快,用谁的结果

项目要点:

  1. 阿波罗 ab灰度方案
  2. 埋点分析收益,旧版本埋点分析,新版本新模块分析,

后续优化点:统计多个条码的识别率,以及条码定位框的准确度。

!目前zz scanner组件:

本身是一个objc库,依赖zbar和ZZViewController这两个objc库

现有功能,基于zbar的基础扫码能力,不支持ui自定义和底层算法替换。无统一协议

  1. 可以ui可以复用的不多,现有组件实现的比较简单,不支持替换底层算法等功能
  2. 该模块为oc组件,swift混编有点麻烦

demo ui

参考:

微信 转转

质检小工具 扫描控制器 质检小工具 嵌入式扫码页面

初步效果

演示效果

代码框架设计:

思考🤔:

  1. 一些通用的代码,例如开始视频流传输,多个二维码框选和处理等重复逻辑,如何抽离和公用?

使用基类的方法,对于视频里的开启,多个二维码框选,作为积累的函数实现,子类可以调用这种能力

  1. 对于扫多个码这种能力,如何做成可配置的呢?

一般情况识别到数据直接打印,扫多个码并标注本质上是在识别后,加了一个数据边框的处理。

  1. ui的自定义范围要到什么程度,如何进行ui的配置?
  2. 结合多个扫码算法的结果,看下是否可行

目前支持的自定义有:

(1):中央识别框的大小

(2):标题栏文字

(3):各种按钮和输入框是否隐藏

交互方案:

  1. 默认支持红点标记功能,当只有目标,自动跳转;当出现多个,暂停扫描,等待用户选中其中一个进行跳转
  2. 默认返回多个结果,支持红点标记可配置,开启配置后,红点和暂停扫码能力捆绑

实用主义方案:

1.默认:支持多个结果数据,无需暂停,直接跳转

2.可配置

(1)标点功能 ,单个自动跳转,多个暂停

框架图

注意事项:

  1. 对于输入框,可以作为vc的视图添加,
  2. 对于底下的按钮,可以从下往上布局

上线计划

代码实现

bundle id: org.cocoapods.demo.ios-module-base-scanner-Example

数据分析

添加扫码埋点,记录平均扫码时间

就扫描模块:

// 扫码花费时间

static NSString *const kZLJCheckTools_scan_time_data = @"qualityToolAppScanTime";

// 正常质检模块扫扫码花费时间

static NSString *const kZLJCheckTools_normalCheck_scan_time = @"normalCheckScanTime";

复盘

历史数据

10.15 正式环境,正常质检模块扫描时间 去除无效数据后,平均扫描时间为1.025s,主要机型为 iphone12