É possível usar o Kit de ML para reconhecer e decodificar códigos de barras.
Testar
- Teste o app de exemplo para conferir um exemplo de uso dessa API.
Antes de começar
- Inclua os seguintes pods do Kit de ML no seu Podfile:
pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
- Depois de instalar ou atualizar os pods do projeto, abra o projeto Xcode usando o
.xcworkspace
. O Kit de ML é compatível com a versão 12.4 ou mais recente do Xcode.
Diretrizes de imagens de entrada
-
Para que o Kit de ML leia códigos de barras com precisão, as imagens de entrada precisam conter códigos de barras representados por dados de pixel suficientes.
Os requisitos específicos de dados de pixel dependem do tipo de código de barras e da quantidade de dados codificados nele, já que muitos códigos de barras são compatíveis com um payload de tamanho variável. Em geral, a menor unidade significativa do código de barras precisa ter pelo menos 2 pixels de largura e, para códigos bidimensionais, 2 pixels de altura.
Por exemplo, os códigos de barras EAN-13 são compostos por barras e espaços com 1, 2, 3 ou 4 unidades de largura, portanto, o ideal é que uma imagem de código de barras EAN-13 tenha barras e espaços de pelo menos 2, 4, 6 e 8 pixels de largura. Como um código de barras EAN-13 tem 95 unidades no total, o código de barras deve ter pelo menos 190 pixels de largura.
Formatos mais densos, como PDF417, precisam de dimensões em pixels maiores para que o Kit de ML possa fazer a leitura confiável deles. Por exemplo, um código PDF417 pode ter até 34 "palavras" de 17 unidades em uma única linha, com um ideal de pelo menos 1.156 pixels de largura.
-
O foco inadequado da imagem pode afetar a precisão da leitura. Se o app não estiver recebendo resultados aceitáveis, peça para o usuário recapturar a imagem.
-
Para aplicativos típicos, recomendamos fornecer uma imagem de resolução mais alta, como 1.280 x 720 ou 1.920 x 1.080, o que torna os códigos de barras legíveis a uma distância maior da câmera.
No entanto, em aplicativos em que a latência é crítica, é possível melhorar o desempenho capturando imagens em resolução mais baixa, mas exigindo que o código de barras componha a maior parte da imagem de entrada. Consulte também Dicas para melhorar o desempenho em tempo real.
1. Configurar o leitor de código de barras
Se você souber quais formatos de código de barras espera ler, poderá aumentar a velocidade do leitor de código de barras configurando-o para ler apenas esses formatos.Por exemplo, para ler apenas códigos Aztec e QR, crie um objeto
BarcodeScannerOptions
como no
exemplo a seguir:
Swift
let format = .all let barcodeOptions = BarcodeScannerOptions(formats: format)
Os seguintes formatos são compatíveis:
- code128
- code39
- code93
- codaBar
- dataMatrix
- EAN13
- EAN8
- ITF
- qrCode
- UPCA
- UPCE
- PDF417
- Aztec
Objective-C
MLKBarcodeScannerOptions *options = [[MLKBarcodeScannerOptions alloc] initWithFormats: MLKBarcodeFormatQRCode | MLKBarcodeFormatAztec];
Os seguintes formatos são compatíveis:
- Código 128 (
MLKBarcodeFormatCode128
) - Código 39 (
MLKBarcodeFormatCode39
) - Código 93 (
MLKBarcodeFormatCode93
) - Codabar (
MLKBarcodeFormatCodaBar
) - Matriz de dados (
MLKBarcodeFormatDataMatrix
) - EAN-13 (
MLKBarcodeFormatEAN13
) - EAN-8 (
MLKBarcodeFormatEAN8
) - ITF (
MLKBarcodeFormatITF
) - Código QR (
MLKBarcodeFormatQRCode
) - UPC-A (
MLKBarcodeFormatUPCA
) - UPC-E (
MLKBarcodeFormatUPCE
) - PDF-417 (
MLKBarcodeFormatPDF417
) - Código asteca (
MLKBarcodeFormatAztec
)
2. Preparar a imagem de entrada
Para ler códigos de barras em uma imagem, transmita a imagem comoUIImage
ou
CMSampleBufferRef
para o método process()
ou results(in:)
do BarcodeScanner
:
Crie um objeto VisionImage
usando um UIImage
ou um CMSampleBuffer
.
Se você usa um UIImage
, siga estas etapas:
- Crie um objeto
VisionImage
com oUIImage
. Especifique o.orientation
correto.Swift
let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
Objective-C
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Se você usa um CMSampleBuffer
, siga estas etapas:
-
Especifique a orientação dos dados da imagem contidos em
CMSampleBuffer
.Para ver a orientação da imagem:
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; } }
- Crie um objeto
VisionImage
usando o objetoCMSampleBuffer
e a orientação: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. Acessar uma instância do BarcodeScanner
Receba uma instância deBarcodeScanner
:
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. Processar a imagem
Em seguida, transmita a imagem para o métodoprocess()
:
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. Receber informações de códigos de barras
Se a operação de leitura do código de barras for bem-sucedida, o leitor retornará uma matriz de objetosBarcode
. Cada objeto Barcode
representa um código de barras detectado na imagem. Para cada código de barras, é possível receber as
coordenadas delimitadoras na imagem de entrada, bem como os dados brutos codificados pelo
código de barras. Além disso, se o leitor de código de barras conseguiu determinar o tipo de dados
codificados pelo código de barras, você poderá receber um objeto contendo dados analisados.
Exemplo:
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; } }
Dicas para melhorar o desempenho em tempo real
Caso queira ler códigos de barras em um aplicativo em tempo real, siga estas diretrizes para ter as melhores taxas de frames:
-
Não capture entradas na resolução nativa da câmera. Em alguns dispositivos, a captura de entradas na resolução nativa produz imagens extremamente grandes (mais de 10 megapixels), o que resulta em latência muito baixa, sem nenhum benefício para a precisão. Em vez disso, solicite apenas o tamanho da câmera necessário para a leitura do código de barras, que geralmente não é mais que 2 megapixels.
As predefinições de sessão de captura nomeadas (
AVCaptureSessionPresetDefault
,AVCaptureSessionPresetLow
,AVCaptureSessionPresetMedium
e assim por diante) não são recomendadas porque podem ser mapeadas para resoluções inadequadas em alguns dispositivos. Em vez disso, use as predefinições específicas, comoAVCaptureSessionPreset1280x720
.Se a velocidade de leitura for importante, você poderá diminuir ainda mais a resolução da captura de imagem. No entanto, lembre-se dos requisitos mínimos de tamanho de código de barras descritos acima.
Se você estiver tentando reconhecer códigos de barras de uma sequência de frames de streaming de vídeo, o reconhecedor poderá produzir resultados diferentes de frame para frame. Espere até receber uma série consecutiva do mesmo valor para ter certeza de que está retornando um bom resultado.
O dígito do Checksum não é suportado para ITF e CODE-39.
- Para processar frames de vídeo, use a API síncrona
results(in:)
do detector. Chame esse método na funçãocaptureOutput(_, didOutput:from:)
deAVCaptureVideoDataOutputSampleBufferDelegate
para receber resultados de forma síncrona do frame de vídeo especificado. Mantenha oalwaysDiscardsLateVideoFrames
deAVCaptureVideoDataOutput
comotrue
para limitar as chamadas ao detector. Se um novo frame de vídeo ficar disponível enquanto o detector estiver em execução, ele será descartado. - Se você usar a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e a sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada processado. Consulte updatePreviewOverlayViewWithLastFrame no exemplo do guia de início rápido do Kit de ML.