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

Do rozpoznawania i dekodowania kodów kreskowych możesz używać pakietu ML Kit.

Zanim zaczniesz

  1. Jeśli nie masz jeszcze w aplikacji dodanej Firebase, wykonaj czynności podane w przewodniku dla początkujących.
  2. Umieść biblioteki ML Kit w pliku Podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Po zainstalowaniu lub zaktualizowaniu podów projektu otwórz Xcode projektu korzystającego z: .xcworkspace.
  3. W aplikacji zaimportuj Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

Wytyczne dotyczące obrazu wejściowego

  • Aby ML Kit mógł dokładnie odczytywać kody kreskowe, obrazy wejściowe muszą zawierać również kody kreskowe, które są reprezentowane przez wystarczającą ilość danych pikseli.

    Konkretne wymagania dotyczące danych pikseli zależą od typu kodu kreskowego oraz ilości zakodowanych w nim danych (ponieważ większość kodów obsługują ładunek o zmiennej długości). Ogólnie rzecz biorąc, najmniej istotne musi mieć co najmniej 2 piksele szerokości (oraz dwuwymiarowych kodów o wysokości 2 pikseli).

    Na przykład kody kreskowe EAN-13 składają się z kresek i spacji oznaczonych znakiem 1, 2, 3 lub 4 jednostki szerokości, więc na obrazie z kodem kreskowym EAN-13 powinny być słupki i o szerokości co najmniej 2, 4, 6 lub 8 pikseli. Ponieważ kod EAN-13 Kod kreskowy ma łącznie 95 jednostek szerokości, a kod kreskowy powinien mieć co najmniej 190 jednostek. pikseli szerokości ekranu.

    Formaty o większej gęstości, np. PDF417, wymagają większych wymiarów w pikselach ML Kit. Na przykład kod PDF417 może mieć maksymalnie 34 „słowa” o szerokości 17 jednostek w jednym wierszu, czyli przynajmniej 1156 pikseli szerokości.

  • Słaba ostrość obrazu może obniżyć dokładność skanowania. Jeśli nie otrzymujesz akceptowalne wyniki, spróbuj poprosić użytkownika o ponowne przechwycenie obrazu.

  • W typowych zastosowaniach zaleca się podanie wyższego obrazu o rozdzielczości (np. 1280 x 720 lub 1920 x 1080), który tworzy kody kreskowe wykrywalny z większej odległości od kamery.

    Jednak w aplikacjach, w których opóźnienia są kluczowe, można poprawić dzięki możliwości robienia zdjęć w niższej rozdzielczości, ale wymagając kod kreskowy stanowi większość zdjęcia wejściowego. Zobacz też Wskazówki, jak zwiększyć skuteczność w czasie rzeczywistym.

1. Skonfiguruj wykrywacz kodów kreskowych

Wiedząc, jakie formaty kodu kreskowego spodziewasz się odczytać, możesz zwiększyć szybkość detektora kodów kreskowych, konfigurując go tak, aby wykrywał tylko te formaty.

Na przykład, aby wykrywać tylko kody Aztec i kody QR, utwórz VisionBarcodeDetectorOptions, taki jak następujący przykład:

Swift

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

Obsługiwane formaty:

  • Kod128
  • Kod39
  • Code93
  • Kod CodaBar
  • EAN13
  • EAN8
  • ITF
  • UPCA
  • UPCE
  • Kod QR
  • PDF417
  • Aztecki
  • Magazyn danych

Objective-C

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

Obsługiwane 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)
  • aztecki (FIRVisionBarcodeFormatAztec)
  • Macierz danych (FIRVisionBarcodeFormatDataMatrix)

2. Uruchom wykrywacz kodów kreskowych

Aby skanować kody kreskowe na obrazie, przekaż go w formacie UIImage lub CMSampleBufferRef na: detect(in:): VisionBarcodeDetector :

  1. Pobierz instancję VisionBarcodeDetector:

    Swift

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

    Objective-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 za pomocą UIImage lub CMSampleBufferRef.

    Aby użyć karty UIImage:

    1. W razie potrzeby obróć zdjęcie, tak by jego imageOrientation właściwość to .up.
    2. Utwórz obiekt VisionImage przy użyciu prawidłowo wykonanej rotacji UIImage Nie określaj żadnych metadanych rotacji – są to metadane domyślne. .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Aby użyć karty CMSampleBufferRef:

    1. Utwórz obiekt VisionImageMetadata, który określa orientacji danych zdjęć zawartych w Bufor CMSampleBufferRef.

      Aby sprawdzić orientację obrazu:

      Swift

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

      Objective-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:

      Swift

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

      Objective-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 za pomocą Obiekt CMSampleBufferRef i metadane rotacji:

      Swift

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

      Objective-C

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

    Swift

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

    Objective-C

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

3. Uzyskiwanie informacji z kodów kreskowych

Jeśli rozpoznawanie kodu kreskowego się powiedzie, detektor zwróci tablicę VisionBarcode obiektów. Każdy obiekt VisionBarcode reprezentuje kodu kreskowego wykrytego na zdjęciu. Dla każdego kodu kreskowego można sprawdzić jego wartość współrzędnych ograniczających w obrazie wejściowym, jak i nieprzetworzonych danych zakodowanych przez kodu kreskowego. Ponadto, jeśli detektor kodu kreskowego był w stanie określić typ danych zakodowanym kodem kreskowym, otrzymasz obiekt zawierający przeanalizowane dane.

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 (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 dotyczące poprawy skuteczności w czasie rzeczywistym

Jeśli chcesz skanować kody kreskowe za pomocą aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z tymi wytycznych dotyczących uzyskiwania najlepszej liczby klatek na sekundę:

  • Nie rejestruj danych wejściowych w rozdzielczości natywnej kamery. Na niektórych urządzeniach przechwytywanie danych wejściowych w rozdzielczości natywnej zapewnia bardzo duże w megapikselach), co skutkuje bardzo małym opóźnieniem bez dokładności. Zamiast tego żądaj od aparatu tylko wymaganego rozmiaru dla wykrywania kodu kreskowego: zwykle nie więcej niż 2 megapiksele.

    Gotowe ustawienia sesji nagrywania nazwanych: AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium itp.) — nie są jednak zalecane, ponieważ mogą odpowiadać w nieodpowiednich rozdzielczościach na niektórych urządzeniach. Zamiast tego użyj określonych gotowych ustawień, na przykład AVCaptureSessionPreset1280x720.

    Jeśli szybkość skanowania jest ważna, możesz zmniejszyć zakres skanowania i ich rozwiązania. Należy jednak pamiętać o minimalnym rozmiarze kodu kreskowego. opisane powyżej.

  • Ogranicz wywołania do detektora. Jeśli nowa klatka wideo dostępnych, gdy detektor jest uruchomiony, upuść ramkę.
  • Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obrazu wejściowego, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładanie nakładek w jednym kroku. W ten sposób renderowanie na powierzchni tylko raz na każdą ramkę wejściową. Zobacz previewOverlayView. i FIRDetectionOverlayView w aplikacji z funkcją prezentacji.