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 | Se aumentó aproximadamente 4 MB 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 alfabeto latino (más lento para los demás). | En tiempo real en la mayoría de los dispositivos para la biblioteca de alfabeto latino (más lento para los demás). |
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
de nivel de proyecto, asegúrate de incluir el repositorio Maven de Google en las seccionesbuildscript
yallprojects
. Agrega las dependencias para las bibliotecas de Android del Kit de AA 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, haz lo siguiente:
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:
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 su descarga a través de la API de ModuleInstallClient de 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.
relacionada 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 InputImage
objeto de diferentes fuentes, cada uno se explica a continuación.
Usa un media.Image
Para crear un elemento 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
biblioteca de CameraX, los elementos OnImageCapturedListener
y
Las clases 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 indique el grado de rotación de la imagen, calcularlo a partir del grado de rotación del dispositivo y la orientación de la cámara sensor 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 elemento InputImage
, sigue estos pasos:
objeto 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 un elemento InputImage
, sigue estos pasos:
objeto de una ByteBuffer
o 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 elemento InputImage
, sigue estos pasos:
objeto 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 se representa con 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 pasa un objeto Text
a
la persona que escucha el éxito. Un objeto Text
contiene todo el texto reconocido en
la imagen y cero o más objetos TextBlock
.
Cada objeto TextBlock
representa un bloque rectangular de texto.
que contiene cero o más objetos Line
. Cada
El objeto Line
representa una línea de texto que contiene cero.
o más objetos Element
. Cada Element
El 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 con precisión el texto, las imágenes de entrada deben contener texto representado con suficientes datos de píxeles. 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 puede requerir una imagen de 720 x 1280 píxeles.
-
Un enfoque de imagen deficiente puede afectar la exactitud del reconocimiento de texto. Si no eres obtener resultados aceptables, intenta pedirle al usuario que vuelva a capturar la imagen.
-
Si reconoces texto en una aplicación en tiempo real, deberías considerar 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
Camera
o API decamera2
, limitar las llamadas al detector. Si un video nuevo esté disponible mientras se ejecuta el detector, descarta el fotograma. Consulta laVisionProcessorBase
en la app de muestra de inicio rápido para ver un ejemplo. - Si usas la API de
CameraX
, asegúrate de que la estrategia de contrapresión se haya establecido 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 la entrega de software. Una vez que la imagen que se está analizando se cierra con una llamada a ImageProxy.close(), se publicará 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. Se renderiza en la superficie de visualización.
solo una vez para cada fotograma de entrada. Consulta la
CameraSourcePreview
yGraphicOverlay
en la app de muestra de inicio rápido para ver un ejemplo. - Si usas la API de Camera2, captura imágenes en
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.