Skanuj kody kreskowe za pomocą ML Kit na iOS

Za pomocą ML Kit możesz rozpoznawać i dekodować kody kreskowe.

Zanim zaczniesz

  1. Jeśli nie dodałeś jeszcze Firebase do swojej aplikacji, zrób to, wykonując czynności opisane w przewodniku wprowadzającym .
  2. Dołącz biblioteki ML Kit do swojego Podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Po zainstalowaniu lub zaktualizowaniu Podów swojego projektu pamiętaj o otwarciu projektu Xcode przy użyciu jego .xcworkspace .
  3. W swojej aplikacji zaimportuj Firebase:

    Szybki

    import Firebase

    Cel C

    @import Firebase;

Wytyczne dotyczące obrazu wejściowego

  • Aby zestaw ML Kit mógł dokładnie odczytywać kody kreskowe, obrazy wejściowe muszą zawierać kody kreskowe reprezentowane przez wystarczającą ilość danych w pikselach.

    Konkretne wymagania dotyczące danych pikselowych zależą zarówno od typu kodu kreskowego, jak i ilości danych w nim zakodowanych (ponieważ większość kodów kreskowych obsługuje ładunek o zmiennej długości). Ogólnie rzecz biorąc, najmniejsza znacząca jednostka kodu kreskowego powinna mieć co najmniej 2 piksele szerokości (a w przypadku kodów dwuwymiarowych – 2 piksele wysokości).

    Na przykład kody kreskowe EAN-13 składają się z kresek i spacji o szerokości 1, 2, 3 lub 4 jednostek, więc obraz kodu kreskowego EAN-13 w idealnym przypadku zawiera kreski i spacje o długości co najmniej 2, 4, 6 i Szerokość 8 pikseli. Ponieważ kod kreskowy EAN-13 ma łącznie 95 jednostek szerokości, kod kreskowy powinien mieć szerokość co najmniej 190 pikseli.

    Gęstsze formaty, takie jak PDF417, wymagają większych wymiarów w pikselach, aby ML Kit mógł je niezawodnie odczytać. Na przykład kod PDF417 może zawierać do 34 „słów” o szerokości 17 jednostek w jednym wierszu, co w idealnym przypadku powinno mieć co najmniej 1156 pikseli szerokości.

  • Słaba ostrość obrazu może obniżyć dokładność skanowania. Jeśli wyniki nie są akceptowalne, spróbuj poprosić użytkownika o ponowne wykonanie zdjęcia.

  • Do typowych zastosowań zaleca się podanie obrazu o wyższej rozdzielczości (np. 1280x720 lub 1920x1080), dzięki której kody kreskowe będą wykrywalne z większej odległości od kamery.

    Jednakże w zastosowaniach, w których opóźnienie jest krytyczne, można poprawić wydajność, przechwytując obrazy w niższej rozdzielczości, ale wymagając, aby kod kreskowy stanowił większość obrazu wejściowego. Zobacz także Wskazówki dotyczące poprawy wydajności w czasie rzeczywistym .

1. Skonfiguruj detektor kodów kreskowych

Jeśli wiesz, jakich formatów kodów kreskowych chcesz czytać, możesz zwiększyć szybkość detektora kodów kreskowych, konfigurując go tak, aby wykrywał tylko te formaty.

Na przykład, aby wykryć tylko kod Azteków i kody QR, zbuduj obiekt VisionBarcodeDetectorOptions jak w poniższym przykładzie:

Szybki

let format = VisionBarcodeFormat.all
let barcodeOptions = VisionBarcodeDetectorOptions(formats: format)

Obsługiwane są następujące formaty:

  • Kod128
  • Kod39
  • Kod93
  • KodaBar
  • EAN13
  • EAN8
  • ITF
  • UPCA
  • UPCE
  • Kod QR
  • PDF417
  • Aztek
  • DataMatrix

Cel C

FIRVisionBarcodeDetectorOptions *options =
    [[FIRVisionBarcodeDetectorOptions alloc]
     initWithFormats: FIRVisionBarcodeFormatQRCode | FIRVisionBarcodeFormatAztec];

Obsługiwane są następujące formaty:

  • Kod 128 ( FIRVisionBarcodeFormatCode128 )
  • Kod 39 ( FIRVisionBarcodeFormatCode39 )
  • Kod 93 ( FIRVisionBarcodeFormatCode93 )
  • Codabar ( FIRVisionBarcodeFormatCodaBar )
  • EAN-13 ( FIRVisionBarcodeFormatEAN13 )
  • EAN-8 ( FIRVisionBarcodeFormatEAN8 )
  • ITF ( FIRVisionBarcodeFormatITF )
  • UPC-A ( FIRVisionBarcodeFormatUPCA )
  • UPC-E ( FIRVisionBarcodeFormatUPCE )
  • Kod QR ( FIRVisionBarcodeFormatQRCode )
  • PDF417 ( FIRVisionBarcodeFormatPDF417 )
  • Aztek ( FIRVisionBarcodeFormatAztec )
  • Macierz danych ( FIRVisionBarcodeFormatDataMatrix )

2. Uruchom detektor kodów kreskowych

Aby zeskanować kody kreskowe na obrazie, przekaż obraz jako UIImage lub CMSampleBufferRef do metody detect(in:) klasy VisionBarcodeDetector :

  1. Pobierz instancję VisionBarcodeDetector :

    Szybki

    lazy var vision = Vision.vision()
    
    let barcodeDetector = vision.barcodeDetector(options: barcodeOptions)
    

    Cel C

    FIRVision *vision = [FIRVision vision];
    FIRVisionBarcodeDetector *barcodeDetector = [vision barcodeDetector];
    // Or, to change the default settings:
    // FIRVisionBarcodeDetector *barcodeDetector =
    //     [vision barcodeDetectorWithOptions:options];
    
  2. Utwórz obiekt VisionImage przy użyciu UIImage lub CMSampleBufferRef .

    Aby użyć UIImage :

    1. W razie potrzeby obróć obraz tak, aby jego właściwość imageOrientation miała .up .
    2. Utwórz obiekt VisionImage przy użyciu poprawnie obróconego UIImage . Nie określaj żadnych metadanych rotacji — należy użyć wartości domyślnej .topLeft .

      Szybki

      let image = VisionImage(image: uiImage)

      Cel C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    Aby użyć CMSampleBufferRef :

    1. Utwórz obiekt VisionImageMetadata , który określa orientację danych obrazu zawartych w buforze CMSampleBufferRef .

      Aby uzyskać orientację obrazu:

      Szybki

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Cel C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      Następnie utwórz obiekt metadanych:

      Szybki

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Cel C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. Utwórz obiekt VisionImage przy użyciu obiektu CMSampleBufferRef i metadanych rotacji:

      Szybki

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Cel C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Następnie przekaż obraz do metody detect(in:) :

    Szybki

    barcodeDetector.detect(in: visionImage) { features, error in
      guard error == nil, let features = features, !features.isEmpty else {
        // ...
        return
      }
    
      // ...
    }
    

    Cel C

    [barcodeDetector detectInImage:image
                        completion:^(NSArray<FIRVisionBarcode *> *barcodes,
                                     NSError *error) {
      if (error != nil) {
        return;
      } else if (barcodes != nil) {
        // Recognized barcodes
        // ...
      }
    }];
    

3. Uzyskaj informacje z kodów kreskowych

Jeśli operacja rozpoznawania kodu kreskowego zakończy się pomyślnie, detektor zwróci tablicę obiektów VisionBarcode . Każdy obiekt VisionBarcode reprezentuje kod kreskowy wykryty na obrazie. Dla każdego kodu kreskowego można uzyskać jego współrzędne graniczne na obrazie wejściowym, a także surowe dane zakodowane w kodzie kreskowym. Ponadto, jeśli detektor kodów kreskowych był w stanie określić typ danych zakodowanych w kodzie kreskowym, można uzyskać obiekt zawierający przeanalizowane dane.

Na przykład:

Szybki

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

Cel C

 for (FIRVisionBarcode *barcode in barcodes) {
   NSArray *corners = barcode.cornerPoints;

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

   FIRVisionBarcodeValueType valueType = barcode.valueType;
   switch (valueType) {
     case FIRVisionBarcodeValueTypeWiFi:
       // ssid = barcode.wifi.ssid;
       // password = barcode.wifi.password;
       // encryptionType = barcode.wifi.type;
       break;
     case FIRVisionBarcodeValueTypeURL:
       // url = barcode.URL.url;
       // title = barcode.URL.title;
       break;
     // ...
     default:
       break;
   }
 }

Wskazówki, jak poprawić wydajność w czasie rzeczywistym

Jeśli chcesz skanować kody kreskowe w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z poniższymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:

  • Nie przechwytuj sygnału wejściowego w natywnej rozdzielczości kamery. Na niektórych urządzeniach przechwytywanie danych wejściowych w natywnej rozdzielczości powoduje powstanie niezwykle dużych obrazów (ponad 10 megapikseli), co skutkuje bardzo niskimi opóźnieniami bez korzyści w zakresie dokładności. Zamiast tego żądaj od aparatu tylko rozmiaru wymaganego do wykrywania kodów kreskowych: zwykle nie więcej niż 2 megapiksele.

    Nazwane ustawienia wstępne sesji przechwytywania — AVCaptureSessionPresetDefault , AVCaptureSessionPresetLow , AVCaptureSessionPresetMedium itd.) — nie są jednak zalecane, ponieważ na niektórych urządzeniach mogą być mapowane na nieodpowiednie rozdzielczości. Zamiast tego użyj określonych ustawień wstępnych, takich jak AVCaptureSessionPreset1280x720 .

    Jeśli szybkość skanowania jest ważna, możesz jeszcze bardziej obniżyć rozdzielczość przechwytywania obrazu. Należy jednak pamiętać o wymaganiach dotyczących minimalnego rozmiaru kodu kreskowego opisanych powyżej.

  • Przepustnica wzywa do detektora. Jeżeli w trakcie działania detektora pojawi się nowa klatka wideo, usuń ją.
  • Jeśli używasz wyjścia detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. W ten sposób renderujesz na powierzchnię wyświetlacza tylko raz dla każdej klatki wejściowej. Zobacz przykładowe klasy PreviewOverlayView i FIRDetectionOverlayView w przykładowej aplikacji prezentacyjnej.