使用机器学习套件扫描条形码 (iOS)

您可以使用机器学习套件识别和解码条形码。

试试看

  • 您可以试用示例应用, 查看此 API 的用法示例。

准备工作

  1. 在 Podfile 中添加以下机器学习套件 Pod:
    pod 'GoogleMLKit/BarcodeScanning', '15.5.0'
    
  2. 安装或更新项目的 Pod 之后,使用 Xcode 项目的 .xcworkspace。Xcode 12.4 或更高版本支持机器学习套件。

输入图片准则

  • 为了使机器学习套件准确读取条形码,输入图片必须包含 用足够像素数据表示的条形码。

    具体的像素数据要求取决于 因为很多条形码中的内容都与条形码之间 支持可变大小的载荷一般来说,最小的 条形码的单元宽度应至少为 2 像素,并且 二维代码,高 2 像素。

    例如,EAN-13 条形码由条形和空格组成,分别为 1、 宽度为 2、3 或 4 个单位,因此理想情况下,EAN-13 条形码图片应具有 宽度至少为 2、4、6 和 8 像素的空间。因为有一个 EAN-13 条形码总共为 95 个单位,条形码应至少为 190 像素宽。

    更密集的格式(如 PDF417)需要更大的像素尺寸才能 机器学习套件可靠地读取这些数据。例如,一个 PDF417 代码最多可包含 34 个 17 个单位的宽“单词”最好是至少 宽度为 1156 像素。

  • 图片聚焦不良会影响扫描准确性。如果您的应用没有 可以要求用户重新拍摄图片。

  • 对于典型应用,建议为 例如 1280x720 或 1920x1080, 距离摄像头较远的屏幕可扫描。

    不过,在对延迟至关重要的应用中,您可以 以较低分辨率捕获图片,但要求 条形码构成了输入图片的大部分。另请参阅 提高实时性能的相关提示

1. 配置条形码扫描器

如果您知道要读取哪些格式的条形码,就可以提高 方法是将条形码扫描器配置为仅扫描这些格式。

例如,要仅扫描 Aztec 码和 QR 码,请构建 BarcodeScannerOptions 对象,如 示例:

let format = .all
let barcodeOptions = BarcodeScannerOptions(formats: format)
 

支持以下格式:

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ITF
  • qrCode
  • UPCA
  • UPCE
  • PDF417
  • Aztec
MLKBarcodeScannerOptions *options =
 
[[MLKBarcodeScannerOptions alloc]
   initWithFormats
: MLKBarcodeFormatQRCode | MLKBarcodeFormatAztec];

支持以下格式:

  • Code-128 (MLKBarcodeFormatCode128)
  • Code-39 (MLKBarcodeFormatCode39)
  • Code-93 (MLKBarcodeFormatCode93)
  • Codabar(MLKBarcodeFormatCodaBar
  • 数据矩阵 (MLKBarcodeFormatDataMatrix)
  • EAN-13(MLKBarcodeFormatEAN13
  • EAN-8(MLKBarcodeFormatEAN8
  • ITF (MLKBarcodeFormatITF)
  • 二维码 (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417(MLKBarcodeFormatPDF417
  • 阿兹特克代码 (MLKBarcodeFormatAztec)

2. 准备输入图片

如需扫描图片中的条形码,请将图片作为 UIImageCMSampleBufferRefBarcodeScannerprocess()results(in:) 方法:

使用 UIImageVisionImage CMSampleBuffer

如果您使用 UIImage,请按以下步骤操作:

  • 使用 UIImage 创建一个 VisionImage 对象。请务必指定正确的 .orientation
    let image = VisionImage(image: UIImage)
    visionImage
    .orientation = image.imageOrientation
    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage
    .orientation = image.imageOrientation;

如果您使用 CMSampleBuffer,请按以下步骤操作:

  • 指定 CMSampleBuffer

    如需获取图片方向,请执行以下操作:

    func imageOrientation(
      deviceOrientation
    : UIDeviceOrientation,
      cameraPosition
    : AVCaptureDevice.Position
    ) -> UIImage.Orientation {
     
    switch deviceOrientation {
     
    case .portrait:
       
    return cameraPosition == .front ? .leftMirrored : .right
     
    case .landscapeLeft:
       
    return cameraPosition == .front ? .downMirrored : .up
     
    case .portraitUpsideDown:
       
    return cameraPosition == .front ? .rightMirrored : .left
     
    case .landscapeRight:
       
    return cameraPosition == .front ? .upMirrored : .down
     
    case .faceDown, .faceUp, .unknown:
       
    return .up
     
    }
    }
         
    - (UIImageOrientation)
      imageOrientationFromDeviceOrientation
    :(UIDeviceOrientation)deviceOrientation
                             cameraPosition
    :(AVCaptureDevicePosition)cameraPosition {
     
    switch (deviceOrientation) {
       
    case UIDeviceOrientationPortrait:
         
    return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                               
    : UIImageOrientationRight;

       
    case UIDeviceOrientationLandscapeLeft:
         
    return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                               
    : UIImageOrientationUp;
       
    case UIDeviceOrientationPortraitUpsideDown:
         
    return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                               
    : UIImageOrientationLeft;
       
    case UIDeviceOrientationLandscapeRight:
         
    return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                               
    : UIImageOrientationDown;
       
    case UIDeviceOrientationUnknown:
       
    case UIDeviceOrientationFaceUp:
       
    case UIDeviceOrientationFaceDown:
         
    return UIImageOrientationUp;
     
    }
    }
         
  • 使用VisionImage CMSampleBuffer 对象和方向:
    let image = VisionImage(buffer: sampleBuffer)
    image
    .orientation = imageOrientation(
      deviceOrientation
    : UIDevice.current.orientation,
      cameraPosition
    : cameraPosition)
     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image
    .orientation =
       
    [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition
    :cameraPosition];

3. 获取 BarcodeScanner 的实例

获取 BarcodeScanner 的一个实例:
let barcodeScanner = BarcodeScanner.barcodeScanner()
// Or, to change the default settings:
// let barcodeScanner = BarcodeScanner.barcodeScanner(options: barcodeOptions)
MLKBarcodeScanner *barcodeScanner = [MLKBarcodeScanner barcodeScanner];
// Or, to change the default settings:
// MLKBarcodeScanner *barcodeScanner =
//     [MLKBarcodeScanner barcodeScannerWithOptions:options];

4. 处理图片

然后,将图片传递给 process() 方法:
barcodeScanner.process(visionImage) { features, error in
  guard error
== nil, let features = features, !features.isEmpty else {
   
// Error handling
    return
 
}
 
// Recognized barcodes
}
[barcodeScanner processImage:image
                  completion
:^(NSArray<MLKBarcode *> *_Nullable barcodes,
                               
NSError *_Nullable error) {
 
if (error != nil) {
   
// Error handling
   
return;
 
}
 
if (barcodes.count > 0) {
   
// Recognized barcodes
 
}
}];

5. 从条形码中获取信息

如果条形码扫描操作成功,扫描器会返回一个数组, Barcode 对象。每个 Barcode 对象代表一个 图片中检测到的条形码。对于每个条形码,您可以 输入图像中的边界坐标以及由 条形码。此外,如果条形码扫描器能够确定数据的类型, 您可以获取一个包含已解析数据的对象。

例如:

for barcode in barcodes {
 
let corners = barcode.cornerPoints

 
let displayValue = barcode.displayValue
 
let rawValue = barcode.rawValue

 
let valueType = barcode.valueType
 
switch valueType {
 
case .wiFi:
   
let ssid = barcode.wifi?.ssid
   
let password = barcode.wifi?.password
   
let encryptionType = barcode.wifi?.type
 
case .URL:
   
let title = barcode.url!.title
   
let url = barcode.url!.url
 
default:
   
// See API reference for all supported value types
  }
}
for (MLKBarcode *barcode in barcodes) {
   
NSArray *corners = barcode.cornerPoints;

   
NSString *displayValue = barcode.displayValue;
   
NSString *rawValue = barcode.rawValue;

   
MLKBarcodeValueType valueType = barcode.valueType;
   
switch (valueType) {
     
case MLKBarcodeValueTypeWiFi:
       ssid
= barcode.wifi.ssid;
       password
= barcode.wifi.password;
       encryptionType
= barcode.wifi.type;
       
break;
     
case MLKBarcodeValueTypeURL:
       url
= barcode.URL.url;
       title
= barcode.URL.title;
       
break;
     
// ...
     
default:
       
break;
   
}
 
}

提高实时性能的相关提示

如果要在实时应用中扫描条形码,请按以下说明操作 实现最佳帧速率的准则:

  • 请勿以相机的原始分辨率捕获输入内容。在某些设备上 以原始分辨率捕获输入会产生极大的(超过 10 而这导致非常糟糕的延迟时间,对 准确率。正确做法是,仅从相机中请求所需的尺寸 通常不超过 200 万像素。

    已命名的拍摄会话预设:AVCaptureSessionPresetDefaultAVCaptureSessionPresetLowAVCaptureSessionPresetMedium、 等)(因此不推荐使用),因为它们可能会映射到 分辨率不合适。您可以改用特定预设值 例如 AVCaptureSessionPreset1280x720

    如果扫描速度很重要,您可以进一步降低图片拍摄速度 分辨率。不过,请注意最小条形码尺寸要求 。

    如果您要尝试从一系列流式视频中识别条形码 识别器在不同视频帧之间生成不同的结果 帧。您应该等到收到同一系列的 值以确保您会返回良好的结果。

    ITF 和 CODE-39 不支持校验和数字。

  • 如需处理视频帧,请使用检测器的 results(in:) 同步 API。致电 此方法(可从 获取) AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) 函数,用于同步获取指定视频的结果 帧。保留 AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames 作为 true,以限制对检测器的调用。如果新的 视频帧在检测器运行时可用,则会丢失。
  • 如果您使用检测器的输出在图像上叠加显示 输入图片,首先从机器学习套件获取结果, 和叠加层。通过这种方式,您可以在显示屏上呈现 只对每个已处理的输入帧运行一次。请参阅 updatePreviewOverlayViewWithLastFrame