Skanuj kody kreskowe za pomocą ML Kit na iOS

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

Zanim zaczniesz

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

    Szybki

    import Firebase

    Cel C

    @import Firebase;

Wskazówki dotyczące obrazu wejściowego

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

    Konkretne wymagania dotyczące danych pikseli 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 istotna 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 idealnie zawiera kreski i spacje, które mają 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 zestaw 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ć szerokość co najmniej 1156 pikseli.

  • Słaba ostrość obrazu może zaszkodzić dokładności skanowania. Jeśli nie uzyskujesz zadowalających wyników, poproś użytkownika o ponowne przechwycenie obrazu.

  • W typowych zastosowaniach zaleca się zapewnienie obrazu o wyższej rozdzielczości (np. 1280x720 lub 1920x1080), dzięki czemu kody kreskowe są wykrywalne z większej odległości od kamery.

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

1. Skonfiguruj detektor kodów kreskowych

Jeśli wiesz, jakie formaty kodów kreskowych spodziewasz się odczytać, możesz zwiększyć szybkość działania detektora kodów kreskowych, konfigurując go tak, aby wykrywał tylko te formaty.

Na przykład, aby wykryć tylko kod Aztec 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
  • CodaBar
  • 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 )
  • Aztec ( FIRVisionBarcodeFormatAztec )
  • Macierz danych ( FIRVisionBarcodeFormatDataMatrix )

2. Uruchom wykrywacz kodów kreskowych

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

  1. Uzyskaj 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 wartość .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 obrotu:

      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 się powiedzie, 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 w obrazie wejściowym, a także nieprzetworzone dane zakodowane przez kod kreskowy. Ponadto, jeśli detektor kodów kreskowych był w stanie określić rodzaj danych zakodowanych przez kod kreskowy, 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 dotyczące poprawy wydajności w czasie rzeczywistym

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

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

    Nazwane ustawienia wstępne sesji przechwytywania — AVCaptureSessionPresetDefault , AVCaptureSessionPresetLow , AVCaptureSessionPresetMedium itd.) — nie są jednak zalecane, ponieważ mogą być mapowane na nieodpowiednie rozdzielczości na niektórych urządzeniach. 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 minimalnych wymaganiach dotyczących rozmiaru kodu kreskowego, o których mowa powyżej.

  • Przepustnica wzywa do detektora. Jeśli nowa ramka wideo stanie się dostępna podczas działania detektora, upuść ramkę.
  • Jeśli używasz wyjścia detektora 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. W ten sposób renderujesz na powierzchnię wyświetlania tylko raz dla każdej klatki wejściowej. Zobacz na przykład klasy previewOverlayView i FIRDetectionOverlayView w przykładowej aplikacji prezentacji.