Puedes usar ML Kit para reconocer texto en imágenes o videos, como el texto de una señal de tránsito. Las características principales de esta función son las siguientes:
Función | Sin agrupar | Red de Búsqueda y Red de Display |
---|---|---|
Nombre de la 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 |
Implementación | El modelo se descarga de forma dinámica a través de los Servicios de Google Play. | El modelo se vincula de forma estática a tu app durante el tiempo de compilación. |
Tamaño de la app | Aproximadamente 260 KB de aumento de tamaño por arquitectura de secuencia de comandos | Aproximadamente 4 MB de aumento de tamaño por secuencia de comandos por arquitectura. |
Hora de inicialización | Es posible que debas esperar a que el modelo se descargue antes de usarlo por primera vez. | El modelo está disponible de inmediato. |
Rendimiento | En tiempo real en la mayoría de los dispositivos para la biblioteca de escritura latina, más lento en otros. | En tiempo real en la mayoría de los dispositivos para la biblioteca de escritura latina, más lento en otros. |
Probar
- Prueba la app de ejemplo para ver un ejemplo de uso de esta API.
- Prueba el código por tu cuenta con el codelab.
Antes de comenzar
- En tu archivo
build.gradle
a nivel de proyecto, asegúrate de incluir el repositorio Maven de Google en las seccionesbuildscript
yallprojects
. Agrega las dependencias para las bibliotecas de Android de ML Kit al archivo Gradle a nivel de la app de tu módulo, que suele ser
app/build.gradle
:Para empaquetar el modelo con tu app, sigue estos pasos:
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 el modelo en los Servicios de Google Play, haz lo siguiente:
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' }
Si eliges usar el modelo en los Servicios de Google Play, puedes configura tu app para que descargue automáticamente el modelo al dispositivo después tu app se instala desde Play Store. Para ello, agrega lo siguiente al archivo
AndroidManifest.xml
de tu 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>
También puedes verificar de forma explícita la disponibilidad del modelo y solicitar la descarga a través de la API de ModuleInstallClient de los Servicios de Google Play. Si no habilitas el modelo al momento de la instalación o solicitar una descarga explícita, el modelo se descarga el primer cada vez que ejecutes el escáner. Las solicitudes que realices antes de que finalice la descarga completado no producen resultados.
1. Crea una instancia de TextRecognizer
.
Crea una instancia de TextRecognizer
y pasa las opciones relacionadas con la biblioteca en la que declaraste una dependencia anteriormente:
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. Prepara la imagen de entrada
Para reconocer texto en una imagen, crea un objeto InputImage
a partir del
un array de bytes de Bitmap
, media.Image
, ByteBuffer
o un archivo en el
dispositivo. Luego, pasa el objeto InputImage
al
método processImage
de TextRecognizer
.
Puedes crear un objeto InputImage
a partir de diferentes fuentes, que se explican a continuación.
Usa un media.Image
Para crear una InputImage
, sigue estos pasos:
objeto de un objeto media.Image
, como cuando capturas una imagen de una
la cámara del dispositivo, pasa el objeto media.Image
y el
rotación a InputImage.fromMediaImage()
.
Si usas la biblioteca
CameraX, las clases OnImageCapturedListener
y
ImageAnalysis.Analyzer
calculan el valor de rotación
por ti.
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 // ... } } }
Si no usas una biblioteca de cámaras que te proporcione el grado de rotación de la imagen, puedes calcularla a partir de la rotación del dispositivo y la orientación del sensor de la cámara en el 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; }
Luego, pasa el objeto media.Image
y el valor de grado de rotación a InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Usa un URI de archivo
Para crear un objeto InputImage
a partir de un URI de archivo, pasa el contexto de la app y el URI del archivo a InputImage.fromFilePath()
. Esto es útil cuando
usa un intent ACTION_GET_CONTENT
para solicitarle al usuario que seleccione
una imagen de su app de galería.
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(); }
Usa un objeto ByteBuffer
o ByteArray
Para crear una InputImage
, sigue estos pasos:
objeto de una ByteBuffer
o una ByteArray
, primero calcula la imagen
grado de rotación como se describió anteriormente para la entrada media.Image
.
Luego, crea el objeto InputImage
con el búfer o array, junto con los atributos
El alto, el ancho, el formato de codificación de color y el grado de rotación:
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 );
Usa un Bitmap
Para crear un objeto InputImage
a partir de un objeto Bitmap
, realiza la siguiente declaración:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
La imagen está representada por un objeto Bitmap
junto con los grados de rotación.
3. Procesa la imagen
Pasa la imagen al 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. Extrae texto de bloques de texto reconocido
Si la operación de reconocimiento de texto se ejecuta correctamente, se pasará un objeto Text
al objeto de escucha que detecta el resultado correcto. Un objeto Text
contiene el texto completo reconocido en la imagen y cero o más objetos TextBlock
.
Cada TextBlock
representa un bloque rectangular de texto que contiene cero o más objetos Line
. Cada objeto Line
representa una línea de texto que contiene cero o más objetos Element
. Cada Element
un objeto representa una palabra o una entidad similar a una palabra, que contiene cero o más
Objetos Symbol
. Cada Symbol
un objeto representa un carácter, un dígito o una entidad con forma de palabra.
Para cada TextBlock
, Line
,
los objetos Element
y Symbol
, puedes
puedes obtener el texto reconocido en la región, las coordenadas que limitan la región
región y muchos otros atributos, como información de rotación, puntuación de confianza
etcétera
Por ejemplo:
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(); } } } }
Lineamientos para imágenes de entrada
-
Para que el Kit de AA reconozca texto con exactitud, las imágenes de entrada deben contener texto representado con datos de píxeles suficientes. Idealmente, cada carácter debe tener al menos 16x16 píxeles. Por lo general, no hay se beneficia de que los caracteres sean mayores que 24 x 24 píxeles.
Por ejemplo, una imagen de 640 x 480 podría funcionar bien para escanear una tarjeta que ocupa todo el ancho de la imagen. Para escanear un documento impreso en tamaño carta, es posible que necesites una imagen de 720 x 1280 píxeles.
-
Un enfoque de imagen deficiente puede afectar la exactitud del reconocimiento de texto. Si no obtienes resultados aceptables, intenta pedirle al usuario que vuelva a capturar la imagen.
-
Si reconoces texto en una aplicación en tiempo real, debes tener en cuenta las dimensiones generales de las imágenes de entrada. Más pequeña las imágenes se pueden procesar más rápido. Para reducir la latencia, asegúrate de que el texto ocupe la mayor cantidad de y capturar imágenes con menores resoluciones (ten en cuenta la precisión los requisitos mencionados anteriormente). Para obtener más información, consulta Sugerencias para mejorar el rendimiento
Sugerencias para mejorar el rendimiento
- Si usas la API de
Camera
ocamera2
, limita las llamadas al detector. Si un video nuevo esté disponible mientras se ejecuta el detector, descarta el fotograma. Consulta la claseVisionProcessorBase
de la app de ejemplo de la guía de inicio rápido para ver un ejemplo. - Si usas la API de
CameraX
, asegúrate de que la estrategia de contrapresión esté configurada en su valor predeterminadoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. De esta forma, se garantiza que solo se entregará una imagen a la vez para su análisis. Si hay más imágenes que se producen cuando el analizador está ocupado, se eliminarán automáticamente y no se agregarán a la cola y la entrega de modelos. Una vez que se cierre la imagen que se está analizando llamando a ImageProxy.close(), se entregará la siguiente imagen más reciente. - Si usas la salida del detector para superponer gráficos
la imagen de entrada, primero obtén el resultado del Kit de AA y, luego, renderiza la imagen
y superponerla en un solo paso. Esto se renderiza en la superficie de visualización solo una vez por cada fotograma de entrada. Consulta las clases
CameraSourcePreview
yGraphicOverlay
en la app de ejemplo de la guía de inicio rápido para ver un ejemplo. - Si usas la API de Camera2, captura imágenes en formato
ImageFormat.YUV_420_888
. Si usas la API de Camera, captura imágenes enImageFormat.NV21
. - Intenta capturar imágenes con una resolución más baja. Sin embargo, también ten en cuenta los requisitos de dimensiones de imágenes de esta API.