É possível usar o Kit de ML para reconhecer e decodificar códigos de barras.
Recurso | Não agrupado | Agrupadas |
---|---|---|
Implementação | O modelo é baixado dinamicamente pelo Google Play Services. | O modelo é vinculado de forma estática ao app no momento da criação. |
Tamanho do app | Aumento de cerca de 200 KB. | Aumento de cerca de 2,4 MB. |
Tempo de inicialização | Talvez seja necessário esperar o download do modelo antes do primeiro uso. | O modelo fica disponível imediatamente. |
Faça um teste
- Teste o app de exemplo para conferir um exemplo de uso dessa API.
- Consulte o aplicativo de exibição do Material Design para conferir uma implementação completa dessa API.
Antes de começar
No arquivo
build.gradle
no nível do projeto, inclua o repositório Maven do Google nas seçõesbuildscript
eallprojects
.Adicione as dependências das bibliotecas do Android do Kit de ML ao arquivo Gradle no nível do app do módulo, que geralmente é
app/build.gradle
. Escolha uma das dependências a seguir com base nas suas necessidades:Para agrupar o modelo com o app:
dependencies { // ... // Use this dependency to bundle the model with your app implementation 'com.google.mlkit:barcode-scanning:17.3.0' }
Para usar o modelo no Google Play Services:
dependencies { // ... // Use this dependency to use the dynamically downloaded model in Google Play Services implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1' }
Se você optar por usar o modelo no Google Play Services, poderá configurar seu app para fazer o download automático do modelo no dispositivo depois que ele for instalado na Play Store. Para fazer isso, adicione a declaração abaixo ao arquivo
AndroidManifest.xml
do app:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="barcode" > <!-- To use multiple models: android:value="barcode,model2,model3" --> </application>
Também é possível verificar explicitamente a disponibilidade do modelo e solicitar o download pela API ModuleInstallClient do Google Play Services.
Se você não ativar os downloads do modelo no momento da instalação ou solicitar o download explícito, o modelo será transferido na primeira vez em que você executar o scanner. As solicitações feitas antes da conclusão do download não produzem resultados.
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 nele codificados, já que muitos códigos de barras são compatíveis com payload de tamanho variável. Em geral, a menor unidade significativa do código de barras deve 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 de barras e espaços de 1, 2, 3 ou 4 unidades de largura. Portanto, uma imagem de código de barras EAN-13 apresenta barras e espaços de, no mínimo, 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 o PDF417, precisam de dimensões em pixels maiores para que o Kit de ML possa ler de forma confiável. Por exemplo, um código PDF417 pode ter até 34 "palavras" de 17 unidades em uma única linha, que, em uma situação ideal, teria pelo menos 1.156 pixels de largura.
-
Uma imagem com foco inadequado pode prejudicar a precisão. Se o app não estiver conseguindo resultados aceitáveis, peça para o usuário recapturar a imagem.
-
Para aplicativos típicos, é recomendável fornecer uma imagem de resolução mais alta, como 1280 x 720 ou 1920 x 1080, que permite a leitura de códigos de barras 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 com uma resolução menor, desde 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ê sabe quais formatos de código de barras espera ler, aumente a velocidade do detector de código de barras configurando-o para detectar apenas esses formatos.Por exemplo, para detectar apenas o código Aztec e códigos QR, crie um
objeto BarcodeScannerOptions
como no exemplo a seguir:
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_QR_CODE, Barcode.FORMAT_AZTEC) .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_QR_CODE, Barcode.FORMAT_AZTEC) .build();
Os seguintes formatos são compatíveis:
- Code 128 (
FORMAT_CODE_128
) - Code 39 (
FORMAT_CODE_39
) - Code 93 (
FORMAT_CODE_93
) - Codabar (
FORMAT_CODABAR
) - EAN-13 (
FORMAT_EAN_13
) - EAN-8 (
FORMAT_EAN_8
) - ITF (
FORMAT_ITF
) - UPC-A (
FORMAT_UPC_A
) - UPC-E (
FORMAT_UPC_E
) - QR Code (
FORMAT_QR_CODE
) - PDF417 (
FORMAT_PDF417
) - Aztec (
FORMAT_AZTEC
) - Data Matrix (
FORMAT_DATA_MATRIX
)
A partir do modelo agrupado 17.1.0 e do modelo não agrupado 18.2.0, também é possível chamar
enableAllPotentialBarcodes()
para retornar todos os possíveis códigos de barras, mesmo que eles
não possam ser decodificados. Isso pode ser usado para facilitar a detecção, por exemplo,
aplicando zoom na câmera para ter uma imagem mais clara de qualquer código de barras na caixa delimitadora
retornada.
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .enableAllPotentialBarcodes() // Optional .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .enableAllPotentialBarcodes() // Optional .build();
Further on, starting from bundled library 17.2.0 and unbundled library 18.3.0, a new feature called auto-zoom has been introduced to further enhance the barcode scanning experience. With this feature enabled, the app is notified when all barcodes within the view are too distant for decoding. As a result, the app can effortlessly adjust the camera's zoom ratio to the recommended setting provided by the library, ensuring optimal focus and readability. This feature will significantly enhance the accuracy and success rate of barcode scanning, making it easier for apps to capture information precisely.
To enable auto-zooming and customize the experience, you can utilize the
setZoomSuggestionOptions()
method along with your
own ZoomCallback
handler and desired maximum zoom
ratio, as demonstrated in the code below.
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .setZoomSuggestionOptions( new ZoomSuggestionOptions.Builder(zoomCallback) .setMaxSupportedZoomRatio(maxSupportedZoomRatio) .build()) // Optional .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .setZoomSuggestionOptions( new ZoomSuggestionOptions.Builder(zoomCallback) .setMaxSupportedZoomRatio(maxSupportedZoomRatio) .build()) // Optional .build();
zoomCallback
is required to be provided to handle whenever the library
suggests a zoom should be performed and this callback will always be called on
the main thread.
The following code snippet shows an example of defining a simple callback.
Kotlin
fun setZoom(ZoomRatio: Float): Boolean { if (camera.isClosed()) return false camera.getCameraControl().setZoomRatio(zoomRatio) return true }
Java
boolean setZoom(float zoomRatio) { if (camera.isClosed()) { return false; } camera.getCameraControl().setZoomRatio(zoomRatio); return true; }
maxSupportedZoomRatio
is related to the camera hardware, and different camera
libraries have different ways to fetch it (see the javadoc of the setter
method). In case this is not provided, an
unbounded zoom ratio might be produced by the library which might not be
supported. Refer to the
setMaxSupportedZoomRatio()
method
introduction to see how to get the max supported zoom ratio with different
Camera libraries.
When auto-zooming is enabled and no barcodes are successfully decoded within
the view, BarcodeScanner
triggers your zoomCallback
with the requested
zoomRatio
. If the callback correctly adjusts the camera to this zoomRatio
,
it is highly probable that the most centered potential barcode will be decoded
and returned.
A barcode may remain undecodable even after a successful zoom-in. In such cases,
BarcodeScanner
may either invoke the callback for another round of zoom-in
until the maxSupportedZoomRatio
is reached, or provide an empty list (or a
list containing potential barcodes that were not decoded, if
enableAllPotentialBarcodes()
was called) to the OnSuccessListener
(which
will be defined in step 4. Process the image).
2. Prepare the input image
To recognize barcodes in an image, create anInputImage
object
from either a Bitmap
, media.Image
, ByteBuffer
, byte array, or a file on
the device. Then, pass the InputImage
object to the
BarcodeScanner
's process
method.
You can create an InputImage
object from different sources, each is explained below.
Using a media.Image
To create an InputImage
object from a media.Image
object, such as when you capture an image from a
device's camera, pass the media.Image
object and the image's
rotation to InputImage.fromMediaImage()
.
If you use the
CameraX library, the OnImageCapturedListener
and
ImageAnalysis.Analyzer
classes calculate the rotation value
for you.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
Se você não usar uma biblioteca de câmera que ofereça o grau de rotação da imagem, será possível calcular usando o grau de rotação do dispositivo e a orientação do sensor da câmera:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
Em seguida, transmita o objeto media.Image
e o
valor do grau de rotação para InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Como usar um URI de arquivo
Para criar um objeto InputImage
usando o URI de um arquivo, transmita o contexto do app e o URI do arquivo para
InputImage.fromFilePath()
. Isso é útil ao usar
uma intent ACTION_GET_CONTENT
para solicitar que o usuário selecione
uma imagem no app de galeria dele.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Como usar um ByteBuffer
ou ByteArray
Para criar um objeto InputImage
usando um ByteBuffer
ou um ByteArray
, primeiro calcule o grau de rotação da imagem
conforme descrito anteriormente para a entrada de media.Image
.
Em seguida, crie o objeto InputImage
com o buffer ou a matriz, com a altura,
a largura, o formato de codificação de cores e o grau de rotação da imagem:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
Como usar um Bitmap
Para criar um objeto InputImage
usando um objeto Bitmap
, faça a seguinte declaração:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
A imagem é representada por um objeto Bitmap
com os graus de rotação.
3. Acessar uma instância do BarcodeScanner
Kotlin
val scanner = BarcodeScanning.getClient() // Or, to specify the formats to recognize: // val scanner = BarcodeScanning.getClient(options)
Java
BarcodeScanner scanner = BarcodeScanning.getClient(); // Or, to specify the formats to recognize: // BarcodeScanner scanner = BarcodeScanning.getClient(options);
4. Processar a imagem
Transmita a imagem para o métodoprocess
:
Kotlin
val result = scanner.process(image) .addOnSuccessListener { barcodes -> // Task completed successfully // ... } .addOnFailureListener { // Task failed with an exception // ... }
Java
Task<List<Barcode>> result = scanner.process(image) .addOnSuccessListener(new OnSuccessListener<List<Barcode>>() { @Override public void onSuccess(List<Barcode> barcodes) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
5. Receber informações de códigos de barras
Se a operação de reconhecimento de código de barras for bem-sucedida, uma lista de objetosBarcode
será transmitida para o listener de êxito. 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 no código de barras. Além disso, se o leitor de código de barras tiver sido capaz de determinar o tipo de dados
codificados pelo código de barras, será possível receber um objeto contendo dados analisados.
Exemplo:
Kotlin
for (barcode in barcodes) { val bounds = barcode.boundingBox val corners = barcode.cornerPoints val rawValue = barcode.rawValue val valueType = barcode.valueType // See API reference for complete list of supported types when (valueType) { Barcode.TYPE_WIFI -> { val ssid = barcode.wifi!!.ssid val password = barcode.wifi!!.password val type = barcode.wifi!!.encryptionType } Barcode.TYPE_URL -> { val title = barcode.url!!.title val url = barcode.url!!.url } } }
Java
for (Barcode barcode: barcodes) { Rect bounds = barcode.getBoundingBox(); Point[] corners = barcode.getCornerPoints(); String rawValue = barcode.getRawValue(); int valueType = barcode.getValueType(); // See API reference for complete list of supported types switch (valueType) { case Barcode.TYPE_WIFI: String ssid = barcode.getWifi().getSsid(); String password = barcode.getWifi().getPassword(); int type = barcode.getWifi().getEncryptionType(); break; case Barcode.TYPE_URL: String title = barcode.getUrl().getTitle(); String url = barcode.getUrl().getUrl(); break; } }
Dicas para melhorar o desempenho em tempo real
Se você quiser ler códigos de barras em um aplicativo em tempo real, siga estas diretrizes para conseguir as melhores taxas de frames:
-
Não capture a entrada na resolução nativa da câmera. Em alguns dispositivos, a captura da entrada 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 detecção de códigos de barras, que geralmente não é mais que 2 megapixels.
Se a velocidade de leitura for importante, você poderá diminuir ainda mais a resolução da captura da imagem. No entanto, considere os 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 vídeo de streaming, o reconhecedor poderá produzir resultados diferentes de frame para frame. Aguarde até receber uma série consecutiva do mesmo valor para ter certeza de que está retornando um bom resultado.
O dígito de checksum não é compatível com ITF e CODE-39.
- Se você usar a
API
Camera
oucamera2
, limite as chamadas para o detector. Se um novo frame de vídeo ficar disponível durante a execução do detector, descarte esse frame. Consulte a classeVisionProcessorBase
no app de amostra do guia de início rápido para conferir um exemplo. - Se você usar a API
CameraX
, verifique se a estratégia de backpressure está definida como o valor padrãoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Isso garante que apenas uma imagem seja enviada para análise por vez. Se mais imagens forem produzidas quando o analisador estiver ocupado, elas serão descartadas automaticamente e não serão enfileiradas para envio. Quando a imagem que está sendo analisada é fechada chamando ImageProxy.close(), a próxima imagem mais recente é entregue. - 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. Em seguida, renderize a imagem
e faça a sobreposição de uma só vez. Isso renderiza a superfície de exibição
apenas uma vez para cada frame de entrada. Consulte as classes
CameraSourcePreview
eGraphicOverlay
no app de exemplo do guia de início rápido para conferir um exemplo. - Se você usar a API Camera2, capture imagens no
formato
ImageFormat.YUV_420_888
. Se você usar a API Camera mais antiga, capture imagens no formatoImageFormat.NV21
.
Exceto em caso de indicação contrária, o conteúdo desta página é licenciado de acordo com a Licença de atribuição 4.0 do Creative Commons, e as amostras de código são licenciadas de acordo com a Licença Apache 2.0. Para mais detalhes, consulte as políticas do site do Google Developers. Java é uma marca registrada da Oracle e/ou afiliadas.
Última atualização 2024-12-18 UTC.