Skanuj kody kreskowe za pomocą ML Kit na iOS

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

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

Wypróbuj

Zanim zaczniesz

  1. Uwzględnij w podfile te pody ML Kit:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. Gdy zainstalujesz lub zaktualizujesz pody projektu, otwórz projekt Xcode, używając jego .xcworkspace. ML Kit jest obsługiwany w Xcode w wersji 12.4 lub nowszej.

Wskazówki dotyczące obrazu wejściowego

  • Aby ML Kit 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żą od typu kodu kreskowego i ilości danych zakodowanych w nim, ponieważ wiele takich kodów obsługuje ładunki o zmiennej wielkości. Zasadniczo najmniejsza istotna jednostka kodu kreskowego powinna mieć co najmniej 2 piksele szerokości, a w przypadku kodów 2-wymiarowych – 2 piksele wysokości.

    Na przykład kody kreskowe EAN-13 składają się z pasków i pokoi o szerokości 1, 2, 3 lub 4 jednostek, dzięki czemu obraz z kodem EAN-13 powinien mieć linie o długości co najmniej 2, 4, 6 i 8 pikseli. Kod kreskowy EAN-13 ma łącznie 95 jednostek, dlatego powinien mieć szerokość co najmniej 190 pikseli.

    Formaty dengi, takie jak PDF417, potrzebują większych wymiarów pikseli, aby ML Kit mógł je prawidłowo odczytywać. Na przykład kod PDF może zawierać do 34 jednostek o szerokości 17 jednostek w jednym wierszu. Najlepiej, aby szerokość wynosiła co najmniej 1156 pikseli.

  • Słaba ostrość obrazu może mieć wpływ na dokładność skanowania. Jeśli aplikacja nie przynosi oczekiwanych wyników, poproś użytkownika o ponowne przechwycenie obrazu.

  • W przypadku typowych aplikacji zalecamy korzystanie z obrazów w wyższej rozdzielczości, takich jak 1280 x 720 lub 1920 x 1080, które pozwalają na skanowanie kodów kreskowych z większej odległości od aparatu.

    Jednak w aplikacjach, w których opóźnienie jest kluczowe, możesz poprawić wydajność, przechwytując obrazy w niższej rozdzielczości, ale wymagając, aby kod kreskowy stanowił większość obrazu wejściowego. Zapoznaj się też ze wskazówkami, jak poprawić skuteczność w czasie rzeczywistym.

1. Skonfiguruj skaner kodów kreskowych

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

Aby na przykład skanować tylko kody Aztec i kody QR, utwórz obiekt BarcodeScannerOptions w następujący sposób:

Swift

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

Obsługiwane są te formaty:

  • kod128
  • kod39
  • kod93
  • codabar
  • macierz danych
  • EAN13
  • EAN8
  • ITF
  • kod QR
  • Kod UPC
  • UPC
  • PDF417
  • Aztec

Objective-C

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

Obsługiwane są te formaty:

  • Code-128 (MLKBarcodeFormatCode128)
  • Kod-39 (MLKBarcodeFormatCode39)
  • Code-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ć kod kreskowy na obrazie, przekaż go jako UIImage lub CMSampleBufferRef do metody process() lub results(in:) BarcodeScanner:

Utwórz obiekt VisionImage za pomocą właściwości UIImage lub CMSampleBuffer.

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

  • Utwórz obiekt VisionImage z elementem UIImage. Pamiętaj o prawidłowej wartości .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 CMSampleBuffer, wykonaj te czynności:

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

    Aby uzyskać orientację obrazu:

    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 za pomocą obiektu CMSampleBuffer i orientacji:

    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 instancję 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. Przetwórz obraz

Następnie przekaż obraz za pomocą 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. Uzyskiwanie informacji z kodów kreskowych

Jeśli skanowanie kodów kreskowych zakończy się powodzeniem, 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 jego granicy na obrazie wejściowym oraz nieprzetworzone dane zakodowane w kodzie kreskowym. Jeśli skaner kodów kreskowych potrafi określić typ danych zakodowanych w kodzie kreskowym, możesz uzyskać 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 (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 zwiększania skuteczności w czasie rzeczywistym

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

  • Nie przechwytuj danych wejściowych w natywnej rozdzielczości kamery. Na niektórych urządzeniach rejestrowanie danych w rozdzielczości natywnej generuje bardzo duże (ponad 10 megapikseli) zdjęcia, co przekłada się na bardzo małe opóźnienie i niekorzystny wpływ na dokładność. Wystarczy, że poprosisz o rozmiar z kamery, który jest wymagany do skanowania kodów kreskowych (zwykle nie więcej niż 2 megapiksele).

    Nie zalecamy jednak stosowania gotowych ustawień sesji przechwytywania (AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium itd.), ponieważ mogą one mapować na nieodpowiednie rozdzielczości na niektórych urządzeniach. Zamiast tego użyj gotowych ustawień, np. 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 kodu kreskowego opisanych powyżej.

    Jeżeli próbujesz rozpoznać kody kreskowe z sekwencji strumieniowej transmisji wideo, moduł rozpoznawania może zwracać różne wyniki z każdej klatki. Zaczekaj, aż otrzymasz kolejną serię o tej samej wartości, by mieć pewność, że zwracasz dobry wynik.

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

  • Aby przetworzyć ramki wideo, użyj synchronicznego interfejsu API detektora results(in:). Wywołaj tę metodę za pomocą funkcji captureOutput(_, didOutput:from:) AVCaptureVideoDataOutputSampleBufferDelegate, aby synchronicznie uzyskać wyniki z danej klatki filmu. Aby ograniczyć liczbę wywołań wykrywania, zachowaj alwaysDiscardsLateVideoFrames AVCaptureVideoDataOutput. Jeśli nowa reguła wideo stanie się dostępna podczas działania wzorca do wykrywania treści, zostanie usunięta.
  • Jeśli używasz danych wyjściowych wzorca do nakładania grafiki na obrazie wejściowym, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. Dzięki temu renderowanie każdej klatki będzie renderowane tylko raz dla każdej przetworzonej klatki wejściowej. Przykład znajdziesz w przykładzie updatepreviewOverlayViewWithLastFrame w przykładzie ML Kit.