iOS에서 ML Kit를 사용하여 바코드 스캔

ML Kit를 사용하여 바코드를 인식하고 디코딩할 수 있습니다.

사용해 보기

  • 샘플 앱을 살펴보고 이 API의 사용 예를 확인하세요.

시작하기 전에

  1. Podfile에 다음 ML Kit 포드를 추가합니다.
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. 프로젝트의 포드를 설치하거나 업데이트한 후 .xcworkspace를 사용하여 Xcode 프로젝트를 엽니다. ML Kit는 Xcode 버전 12.4 이상에서 지원됩니다.

입력 이미지 가이드라인

  • ML Kit가 바코드를 정확하게 읽으려면 입력 이미지에 충분한 픽셀 데이터로 표시된 바코드가 있어야 합니다.

    많은 바코드가 가변 크기의 페이로드를 지원하기 때문에 구체적인 픽셀 데이터 요구사항은 바코드 유형과 바코드에 인코딩된 데이터 양에 따라 달라집니다. 일반적으로 바코드의 의미 있는 최소 단위는 너비가 2픽셀 이상이어야 하며 2차원 코드의 경우 높이가 2픽셀 이상이어야 합니다.

    예를 들어 EAN-13 바코드는 너비가 1, 2, 3 또는 4단위인 막대와 공백으로 구성되므로 EAN-13 바코드 이미지에는 너비가 2, 4, 6, 8픽셀 이상인 바와 공백이 있는 것이 좋습니다. EAN-13 바코드의 너비가 총 95단위이므로 바코드의 너비는 190픽셀 이상이어야 합니다.

    PDF417과 같은 밀도 형식을 사용하려면 ML Kit에서 안정적으로 읽을 수 있도록 더 큰 픽셀 크기가 필요합니다. 예를 들어 PDF417 코드는 한 행에 최대 34개의 17단위 너비 '단어'를 포함할 수 있으며 너비는 1156픽셀 이상이어야 합니다.

  • 이미지 초점이 잘 맞지 않으면 스캔 정확성이 저하될 수 있습니다. 앱이 허용 가능한 결과를 얻지 못하면 사용자에게 이미지를 다시 캡처하도록 요청합니다.

  • 일반적인 애플리케이션의 경우 카메라에서 더 먼 거리에서도 바코드를 스캔할 수 있도록 1280x720 또는 1920x1080과 같은 고해상도 이미지를 제공하는 것이 좋습니다.

    하지만 지연 시간이 중요한 애플리케이션의 경우 더 낮은 해상도로 이미지를 캡처하고 바코드가 입력 이미지의 대부분을 차지하도록 하면 성능을 개선할 수 있습니다. 실시간 성능 향상을 위한 팁도 참고하세요.

1. 바코드 스캐너 구성

예상되는 바코드 형식을 알고 있는 경우 바코드 스캐너가 해당 형식만 스캔하도록 구성하여 바코드 스캐너의 속도를 높일 수 있습니다.

예를 들어 Aztec 코드와 QR 코드만 스캔하려면 다음 예시와 같이 BarcodeScannerOptions 객체를 빌드합니다.

Swift

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

지원되는 형식은 다음과 같습니다.

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ITF : ITF : ITF : ITF
  • qrCode
  • UCA
  • UPCE
  • PDF417
  • Aztec

Objective-C

MLKBarcodeScannerOptions *options =
  [[MLKBarcodeScannerOptions alloc]
   initWithFormats: MLKBarcodeFormatQRCode | MLKBarcodeFormatAztec];

지원되는 형식은 다음과 같습니다.

  • Code-128 (MLKBarcodeFormatCode128)
  • Code-39 (MLKBarcodeFormatCode39)
  • Code-93 (MLKBarcodeFormatCode93)
  • Codabar (MLKBarcodeFormatCodaBar)
  • Data Matrix (MLKBarcodeFormatDataMatrix)
  • EAN-13 (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • QR 코드 (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Aztec 코드 (MLKBarcodeFormatAztec)

2. 입력 이미지 준비

이미지 속 바코드를 스캔하려면 이미지를 UIImage 또는 CMSampleBufferRefBarcodeScannerprocess() 또는 results(in:) 메서드에 전달합니다.

UIImage 또는 CMSampleBuffer를 사용하여 VisionImage 객체를 만듭니다.

UIImage를 사용하는 경우 다음 단계를 따르세요.

  • UIImageVisionImage 객체를 만듭니다. 올바른 .orientation를 지정해야 합니다.

    Swift

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

CMSampleBuffer를 사용하는 경우 다음 단계를 따르세요.

  • CMSampleBuffer에 포함된 이미지 데이터의 방향을 지정합니다.

    이미지 방향을 가져오는 방법은 다음과 같습니다.

    Swift

    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
      }
    }
          

    Objective-C

    - (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;
      }
    }
          
  • CMSampleBuffer 객체와 방향을 사용하여 VisionImage 객체를 만듭니다.

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Objective-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. BarcodeScanner의 인스턴스 가져오기

BarcodeScanner의 인스턴스를 가져옵니다.

Swift

let barcodeScanner = BarcodeScanner.barcodeScanner()
// Or, to change the default settings:
// let barcodeScanner = BarcodeScanner.barcodeScanner(options: barcodeOptions)

Objective-C

MLKBarcodeScanner *barcodeScanner = [MLKBarcodeScanner barcodeScanner];
// Or, to change the default settings:
// MLKBarcodeScanner *barcodeScanner =
//     [MLKBarcodeScanner barcodeScannerWithOptions:options];

4. 이미지 처리

이제 이미지를 process() 메서드에 전달합니다.

Swift

barcodeScanner.process(visionImage) { features, error in
  guard error == nil, let features = features, !features.isEmpty else {
    // Error handling
    return
  }
  // Recognized barcodes
}

Objective-C

[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 객체는 이미지에서 인식된 바코드를 나타냅니다. 바코드별로 입력 이미지의 경계 좌표 및 바코드로 인코딩된 원시 데이터를 가져올 수 있습니다. 또한 바코드 스캐너가 바코드로 인코딩된 데이터 유형을 결정할 수 있는 경우 파싱된 데이터가 포함된 객체를 가져올 수 있습니다.

예를 들면 다음과 같습니다.

Swift

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
  }
}

Objective-C

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메가픽셀 이상) 이미지가 생성되어 정확도 향상 없이 지연 시간이 매우 길어집니다. 대신 카메라에서 바코드 스캔에 필요한 크기(일반적으로 2메가픽셀 이하)만 요청하세요.

    그러나 이름이 지정된 캡처 세션 미리 설정(AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium 등)은 기기에 따라 부적합한 해상도에 매핑될 수 있으므로 사용하지 않는 것이 좋습니다. 대신 AVCaptureSessionPreset1280x720와 같은 특정 사전 설정을 사용하세요.

    스캔 속도가 중요한 경우에는 이미지 캡처 해상도를 더 낮출 수 있습니다. 단, 위에 설명한 바코드 크기 최소 요구사항에 유의해야 합니다.

    스트리밍 동영상 프레임의 시퀀스에서 바코드를 인식하려는 경우 인식기는 프레임마다 다른 결과를 생성할 수 있습니다. 좋은 결과를 반환하고 있는지 확인하려면 동일한 값이 연속으로 나올 때까지 기다려야 합니다.

    ITF 및 CODE-39에는 체크섬 숫자가 지원되지 않습니다.

  • 동영상 프레임을 처리하려면 감지기의 results(in:) 동기 API를 사용합니다. AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) 함수에서 이 메서드를 호출하여 주어진 동영상 프레임에서 결과를 동기식으로 가져옵니다. AVCaptureVideoDataOutput alwaysDiscardsLateVideoFramestrue로 유지하여 감지기 호출을 제한합니다. 감지기가 실행 중일 때 새 동영상 프레임이 사용 가능해지면 삭제됩니다.
  • 인식기 출력을 사용하여 입력 이미지에서 그래픽을 오버레이하는 경우 먼저 ML Kit에서 결과를 가져온 후 이미지를 렌더링하고 단일 단계로 오버레이합니다. 이렇게 하면 처리된 입력 프레임마다 한 번만 디스플레이 표면에 렌더링됩니다. 예시는 ML Kit 빠른 시작 샘플의 updatePreviewOverlayViewWithLastFrame을 참조하세요.