Skanowanie kodów kreskowych za pomocą ML Kit na iOS

Możesz używać ML Kit do rozpoznawania i dekodowania kodów kreskowych.

Wypróbuj

Zanim zaczniesz

  1. W pliku Podfile uwzględnij te pody ML Kit:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. Po zainstalowaniu lub zaktualizowaniu podów w projekcie otwórz projekt Xcode, korzystając z jego polecenia .xcworkspace. ML Kit obsługuje Xcode w wersji 12.4 lub nowszej.

Zalecenia dotyczące obrazu wejściowego

  • Aby usługa ML Kit mogła dokładnie odczytywać kody kreskowe, obrazy wejściowe muszą zawierać kody kreskowe reprezentowane przez wystarczającą ilość danych pikseli.

    Konkretne wymagania dotyczące danych w pikselach zależą zarówno od typu kodu kreskowego, jak i ilości zakodowanych w nim danych, ponieważ wiele takich kodów obsługuje ładunki o zmiennym rozmiarze. Ogólnie najmniejsza znacząca jednostka kodu kreskowego powinna mieć co najmniej 2 piksele szerokości, a dla kodów dwuwymiarowych – 2 piksele wysokości.

    Na przykład kody kreskowe EAN-13 składają się ze słupków i spacji o szerokości 1, 2, 3 lub 4 jednostek, dlatego obraz kodu kreskowego EAN-13 powinien mieć co najmniej 2, 4, 6 lub 8 pikseli szerokości. Ponieważ kod kreskowy EAN-13 ma łącznie 95 jednostek, powinien mieć co najmniej 190 pikseli szerokości.

    Formaty o większej gęstości, np. PDF417, wymagają większej liczby pikseli, by narzędzie ML Kit mogło je prawidłowo odczytywać. Na przykład kod PDF417 może zawierać w jednym wierszu maksymalnie 34 „słowa” o szerokości 17 jednostek, czyli co najmniej 1156 pikseli szerokości.

  • Słaba ostrość obrazu może obniżyć dokładność skanowania. Jeśli aplikacja nie uzyskuje zadowalających wyników, poproś użytkownika o ponowne wykonanie zdjęcia.

  • W przypadku typowych zastosowań zalecane jest obrazy o wyższej rozdzielczości (np. 1280 x 720 lub 1920 x 1080), dzięki którym kody kreskowe będą skanowane z większej odległości od aparatu.

    Jednak w aplikacjach, w których opóźnienia mają kluczowe znaczenie, można poprawić wydajność, robiąc zdjęcia w niższej rozdzielczości, wymagając, aby kod kreskowy zajmował większość obrazu wejściowego. Zobacz też wskazówki dotyczące zwiększania skuteczności w czasie rzeczywistym.

1. Skonfiguruj skaner kodów kreskowych

Jeśli wiesz, które formaty kodów kreskowych zamierzasz odczytać, możesz zwiększyć szybkość skanera kodów kreskowych, konfigurując go do skanowania tylko tych formatów.

Aby na przykład zeskanować tylko kod Aztec i kody QR, utwórz obiekt BarcodeScannerOptions jak w tym przykładzie:

Swift

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

Obsługiwane są te formaty:

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

Objective-C

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

Obsługiwane są te formaty:

  • Kod-128 (MLKBarcodeFormatCode128)
  • Kod-39 (MLKBarcodeFormatCode39)
  • Kod-93 (MLKBarcodeFormatCode93)
  • Codabar (MLKBarcodeFormatCodaBar)
  • Macierz danych (MLKBarcodeFormatDataMatrix)
  • EAN-13 (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • Kod QR (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Kod aztecki (MLKBarcodeFormatAztec)

2. Przygotuj obraz wejściowy

Aby zeskanować kody kreskowe na obrazie, przekaż go jako UIImage lub CMSampleBufferRef do metody process() lub results(in:) BarcodeScanner:

Utwórz obiekt VisionImage za pomocą UIImage lub CMSampleBuffer.

Jeśli używasz urządzenia UIImage, wykonaj te czynności:

  • Utwórz obiekt VisionImage za pomocą UIImage. Pamiętaj, aby podać prawidłową wartość .orientation.

    Swift

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

    Objective-C

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

Jeśli używasz urządzenia CMSampleBuffer, wykonaj te czynności:

  • Określ orientację danych obrazu w elemencie CMSampleBuffer.

    Aby pobrać orientację:

    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;
      }
    }
          
  • Utwórz obiekt VisionImage, używając obiektu i orientacji CMSampleBuffer:

    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. Pobieranie instancji aplikacji BarcodeScanner

Pobierz wystąpienie 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. Przetwarzanie obrazu

Następnie przekaż obraz do metody 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. Uzyskuj informacje na podstawie kodów kreskowych

Jeśli skanowanie kodu kreskowego się powiedzie, skaner zwróci tablicę obiektów Barcode. Każdy obiekt Barcode reprezentuje kod kreskowy wykryty na obrazie. W przypadku każdego kodu kreskowego możesz uzyskać współrzędne ograniczające na obrazie wejściowym, a także nieprzetworzone dane zakodowane przez ten kod. Ponadto, jeśli skaner kodów paskowych rozpoznał typ danych kodowanych za pomocą kodu kreskowego, możesz uzyskać obiekt zawierający przeanalizowane dane.

Na przykład:

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

Wskazówki dotyczące poprawy skuteczności w czasie rzeczywistym

Jeśli chcesz skanować kody kreskowe w aplikacji w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:

  • Nie rejestruj danych wejściowych w natywnej rozdzielczości aparatu. Na niektórych urządzeniach nagrywanie danych wejściowych w rozdzielczości natywnej powoduje utworzenie bardzo dużych obrazów (ponad 10 megapikseli), co prowadzi do bardzo małego opóźnienia i braku dokładności. Zamiast tego żądaj z aparatu tylko rozmiaru wymaganego do skanowania kodów kreskowych, czyli zwykle nie więcej niż 2 megapiksele.

    Nazwane gotowe ustawienia sesji przechwytywania – AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium itd.) nie są zalecane, ponieważ na niektórych urządzeniach mogą zostać zmapowane na nieodpowiednie rozdzielczości. Zamiast tego użyj określonych gotowych ustawień, takich jak AVCaptureSessionPreset1280x720.

    Jeśli szybkość skanowania jest ważna, możesz jeszcze bardziej obniżyć rozdzielczość przechwytywania obrazu. Pamiętaj jednak o minimalnych wymaganiach dotyczących rozmiaru kodu kreskowego wymienionych powyżej.

    Jeśli próbujesz rozpoznać kody kreskowe z sekwencji klatek przesyłanych strumieniowo, moduł rozpoznawania może podawać różne wyniki w poszczególnych klatkach. Aby mieć pewność, że uzyskujesz dobry wynik, poczekaj, aż uzyskasz kolejną serię o tej samej wartości.

    Cyfra sumy kontrolnej nie jest obsługiwana w przypadku ITF i CODE-39.

  • Do przetwarzania klatek wideo użyj synchronicznego interfejsu API results(in:) wzorca. Wywołaj tę metodę z funkcji captureOutput(_, didOutput:from:) obiektu AVCaptureVideoDataOutputSampleBufferDelegate, aby synchronicznie pobierać wyniki z danej klatki wideo. Pozostaw alwaysDiscardsLateVideoFrames obiektu AVCaptureVideoDataOutput jako true, aby ograniczać wywołania do wzorca. Jeśli podczas działania wzorca pojawi się nowa ramka wideo, zostanie ona usunięta.
  • Jeśli używasz danych wyjściowych wzorca do nakładania grafiki na obraz wejściowy, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. Dzięki temu na każdą przetworzoną klatkę wejściową renderujesz się tylko raz. Przykład znajdziesz w sekcji updatePreviewOverlayViewWithLastFrame w przykładowym krótkim wprowadzeniu do ML Kit.