Use o Kit de ML para reconhecer texto em imagens ou vídeos, como o texto de uma placa de rua. As principais características desse recurso são:
| Recurso | Não agrupada | Agrupada |
|---|---|---|
| Nome da biblioteca | com.google.android.gms:play-services-mlkit-text-recognition
com.google.android.gms:play-services-mlkit-text-recognition-chinese com.google.android.gms:play-services-mlkit-text-recognition-devanagari com.google.android.gms:play-services-mlkit-text-recognition-japanese com.google.android.gms:play-services-mlkit-text-recognition-korean |
com.google.mlkit:text-recognition
com.google.mlkit:text-recognition-chinese com.google.mlkit:text-recognition-devanagari com.google.mlkit:text-recognition-japanese com.google.mlkit:text-recognition-korean |
| Implementação | O modelo é baixado dinamicamente pelo Google Play Services. | O modelo é vinculado estaticamente ao app no tempo de build. |
| Tamanho do app | Aumento de cerca de 260 KB por arquitetura de script. | Aumento de cerca de 4 MB por script por arquitetura. |
| Tempo de inicialização | Talvez seja necessário aguardar o download do modelo antes do primeiro uso. | O modelo está disponível imediatamente. |
| Desempenho | Em tempo real na maioria dos dispositivos para a biblioteca de scripts latinos, mais lento para outros. | Em tempo real na maioria dos dispositivos para a biblioteca de scripts latinos, mais lento para outros. |
Faça um teste
- Teste o app de exemplo para conferir um exemplo de uso dessa API.
- Teste o código com o codelab.
Antes de começar
- No arquivo
build.gradlepara envolvidos no projeto, inclua o repositório Maven do Google nas seçõesbuildscripteallprojects. Adicione as dependências das bibliotecas do Android do Kit de ML ao arquivo Gradle do módulo no nível do app, que geralmente é
app/build.gradle:Para agrupar o modelo e o aplicativo, siga estas etapas:
dependencies { // To recognize Latin script implementation 'com.google.mlkit:text-recognition:16.0.1' // To recognize Chinese script implementation 'com.google.mlkit:text-recognition-chinese:16.0.1' // To recognize Devanagari script implementation 'com.google.mlkit:text-recognition-devanagari:16.0.1' // To recognize Japanese script implementation 'com.google.mlkit:text-recognition-japanese:16.0.1' // To recognize Korean script implementation 'com.google.mlkit:text-recognition-korean:16.0.1' }Para usar o modelo no Google Play Services:
dependencies { // To recognize Latin script implementation 'com.google.android.gms:play-services-mlkit-text-recognition:19.0.1' // To recognize Chinese script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-chinese:16.0.1' // To recognize Devanagari script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-devanagari:16.0.1' // To recognize Japanese script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-japanese:16.0.1' // To recognize Korean script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-korean:16.0.1' }Se você optar por usar o modelo no Google Play Services, você pode configurar seu app para fazer o download automático do modelo para o dispositivo depois que seu app for instalado na Play Store. Para fazer isso, adicione a seguinte declaração ao arquivo
AndroidManifest.xmldo app:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="ocr" > <!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." --> </application>Você também pode 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á baixado na primeira vez em que você executar o scanner. As solicitações feitas antes da conclusão do download não produzem resultados.
1. Criar uma instância de TextRecognizer
Crie uma instância de TextRecognizer, transmitindo as opções
relacionadas à biblioteca em que você declarou uma dependência acima:
Kotlin
// When using Latin script library val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) // When using Chinese script library val recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build()) // When using Devanagari script library val recognizer = TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build()) // When using Japanese script library val recognizer = TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build()) // When using Korean script library val recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())
Java
// When using Latin script library TextRecognizer recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS); // When using Chinese script library TextRecognizer recognizer = TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build()); // When using Devanagari script library TextRecognizer recognizer = TextRecognition.getClient(new DevanagariTextRecognizerOptions.Builder().build()); // When using Japanese script library TextRecognizer recognizer = TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build()); // When using Korean script library TextRecognizer recognizer = TextRecognition.getClient(new KoreanTextRecognizerOptions.Builder().build());
2. Preparar a imagem de entrada
Para reconhecer texto em uma imagem, crie um objeto InputImage com base em
um Bitmap, media.Image, ByteBuffer, matriz de bytes ou um arquivo no
dispositivo. Em seguida, transmita o objeto InputImage para o método processImage do
TextRecognizer.
Você pode criar um InputImage
objeto a partir de diferentes origens, cada uma explicada abaixo.
Como usar um media.Image
Para criar um objeto InputImage
usando um objeto media.Image, como quando você captura uma imagem da câmera de um dispositivo, transmita o objeto media.Image e a rotação da imagem para InputImage.fromMediaImage().
Se você usar a
CameraX biblioteca, as OnImageCapturedListener e
ImageAnalysis.Analyzer classes vão calcular o valor de rotação
automaticamente.
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, você pode calcular usando o grau de rotação do dispositivo e a orientação do sensor da câmera no dispositivo:
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 InputImage
objeto 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 InputImage
objeto com base em um ByteBuffer ou uma ByteArray, primeiro calcule o grau de rotação da imagem
conforme descrito anteriormente para a entrada 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 InputImage
objeto usando um Bitmap objeto, 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. Processar a imagem
Transmita a imagem para o método process:
Kotlin
val result = recognizer.process(image) .addOnSuccessListener { visionText -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
Task<Text> result = recognizer.process(image) .addOnSuccessListener(new OnSuccessListener<Text>() { @Override public void onSuccess(Text visionText) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. Extrair texto de blocos de texto reconhecido
Se a operação de reconhecimento de texto for bem-sucedida, um Text objeto será transmitido para
o listener de êxito. Um objeto Text contém o texto completo reconhecido em
a imagem e zero ou mais TextBlock objetos.
Cada TextBlock representa um bloco de texto retangular,
que contém zero ou mais objetos Line. Cada
Line objeto representa uma linha de texto, que contém zero
ou mais Element objetos. Cada Element
objeto representa uma palavra ou uma entidade semelhante a uma palavra, que contém zero ou mais
Symbol objetos. Cada Symbol
objeto representa um caractere, um dígito ou uma entidade semelhante a uma palavra.
Para cada objeto TextBlock, Line,
Element e Symbol, é possível receber o texto reconhecido na região, as coordenadas delimitadoras da
região e muitos outros atributos, como informações de rotação, pontuação de confiança
etc.
Exemplo:
Kotlin
val resultText = result.text for (block in result.textBlocks) { val blockText = block.text val blockCornerPoints = block.cornerPoints val blockFrame = block.boundingBox for (line in block.lines) { val lineText = line.text val lineCornerPoints = line.cornerPoints val lineFrame = line.boundingBox for (element in line.elements) { val elementText = element.text val elementCornerPoints = element.cornerPoints val elementFrame = element.boundingBox } } }
Java
String resultText = result.getText(); for (Text.TextBlock block : result.getTextBlocks()) { String blockText = block.getText(); Point[] blockCornerPoints = block.getCornerPoints(); Rect blockFrame = block.getBoundingBox(); for (Text.Line line : block.getLines()) { String lineText = line.getText(); Point[] lineCornerPoints = line.getCornerPoints(); Rect lineFrame = line.getBoundingBox(); for (Text.Element element : line.getElements()) { String elementText = element.getText(); Point[] elementCornerPoints = element.getCornerPoints(); Rect elementFrame = element.getBoundingBox(); for (Text.Symbol symbol : element.getSymbols()) { String symbolText = symbol.getText(); Point[] symbolCornerPoints = symbol.getCornerPoints(); Rect symbolFrame = symbol.getBoundingBox(); } } } }
Diretrizes das imagens de entrada
-
Para que o Kit de ML reconheça o texto com precisão, as imagens de entrada precisam conter texto representado por dados de pixel suficientes. O ideal é que cada caractere tenha uma resolução de pelo menos 16 x 16 pixels. Geralmente, não há melhorias de precisão em usar caracteres maiores que 24 x 24 pixels.
Por exemplo, uma imagem de 640 x 480 pixels pode funcionar para digitalizar um cartão de visita que ocupe toda a largura da imagem. Para digitalizar um documento impresso em papel de tamanho carta, talvez seja necessária uma imagem de 720 x 1280 pixels.
-
O foco inadequado da imagem pode afetar a precisão do reconhecimento de texto. Se você não está conseguindo resultados aceitáveis, peça para o usuário recapturar a imagem.
-
Se você estiver fazendo reconhecimento de texto em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Imagens menores podem ser processadas mais rapidamente. Para reduzir a latência, faça o texto ocupar o máximo possível de imagem, e capture imagens em resoluções mais baixas (lembrando os requisitos de precisão mencionados acima). Para mais informações, consulte Dicas para melhorar o desempenho.
Dicas para melhorar o desempenho
- Se você usar a
Cameraoucamera2API, limite as chamadas ao detector. Se um novo frame de vídeo ficar disponível durante a execução do detector, descarte esse frame. Consulte aVisionProcessorBaseclass no app de amostra do guia de início rápido para conferir um exemplo. - Se você usar a
CameraXAPI, verifique se a estratégia de contrapressão está definida como o valor padrãoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. Isso garante que apenas uma imagem seja entregue 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 entrega. Depois que a imagem analisada for fechada chamando ImageProxy.close(), a próxima imagem mais recente será entregue. - Se você estiver usando 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
CameraSourcePrevieweGraphicOverlayclasses no app de exemplo do guia de início rápido para conferir um exemplo. - Se você usar a API Camera2, capture imagens no
ImageFormat.YUV_420_888formato. Se você usar a API Camera mais antiga, capture imagens noImageFormat.NV21formato. - Capture imagens em uma resolução menor. No entanto, lembre-se também dos requisitos de dimensão de imagem da API.