Con el reconocimiento de tinta digital de ML Kit, puedes reconocer texto escrito a mano en una en cientos de idiomas, así como clasificar bocetos.
Probar
- Prueba la app de ejemplo para ver un ejemplo de uso de esta API.
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
:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}
Ya puedes comenzar a reconocer texto en objetos Ink
.
Compila un objeto Ink
La forma principal de compilar un objeto Ink
es dibujarlo en una pantalla táctil. Activada
En Android, puedes usar un
Canvas para
con este propósito. Tu
controladores de eventos táctiles
debería llamar a addNewTouchEvent()
.
que se muestra en el siguiente fragmento de código para almacenar los puntos de los trazos que
el usuario dibuja en el objeto Ink
.
Este patrón general se demuestra en el siguiente fragmento de código. Consulta la Muestra de la guía de inicio rápido del Kit de AA para obtener un ejemplo más completo.
Kotlin
var inkBuilder = Ink.builder() lateinit var strokeBuilder: Ink.Stroke.Builder // Call this each time there is a new event. fun addNewTouchEvent(event: MotionEvent) { val action = event.actionMasked val x = event.x val y = event.y var t = System.currentTimeMillis() // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create when (action) { MotionEvent.ACTION_DOWN -> { strokeBuilder = Ink.Stroke.builder() strokeBuilder.addPoint(Ink.Point.create(x, y, t)) } MotionEvent.ACTION_MOVE -> strokeBuilder!!.addPoint(Ink.Point.create(x, y, t)) MotionEvent.ACTION_UP -> { strokeBuilder.addPoint(Ink.Point.create(x, y, t)) inkBuilder.addStroke(strokeBuilder.build()) } else -> { // Action not relevant for ink construction } } } ... // This is what to send to the recognizer. val ink = inkBuilder.build()
Java
Ink.Builder inkBuilder = Ink.builder(); Ink.Stroke.Builder strokeBuilder; // Call this each time there is a new event. public void addNewTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); long t = System.currentTimeMillis(); // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: strokeBuilder = Ink.Stroke.builder(); strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_MOVE: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_UP: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); inkBuilder.addStroke(strokeBuilder.build()); strokeBuilder = null; break; } } ... // This is what to send to the recognizer. Ink ink = inkBuilder.build();
Cómo obtener una instancia de DigitalInkRecognizer
Para realizar el reconocimiento, envía la instancia de Ink
a una
DigitalInkRecognizer
. En el siguiente código, se muestra cómo crear una instancia de este tipo
reconocedor de una etiqueta BCP-47.
Kotlin
// Specify the recognition model for a language var modelIdentifier: DigitalInkRecognitionModelIdentifier try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US") } catch (e: MlKitException) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } var model: DigitalInkRecognitionModel = DigitalInkRecognitionModel.builder(modelIdentifier).build() // Get a recognizer for the language var recognizer: DigitalInkRecognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build())
Java
// Specify the recognition model for a language DigitalInkRecognitionModelIdentifier modelIdentifier; try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US"); } catch (MlKitException e) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } DigitalInkRecognitionModel model = DigitalInkRecognitionModel.builder(modelIdentifier).build(); // Get a recognizer for the language DigitalInkRecognizer recognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build());
Procesa un objeto Ink
Kotlin
recognizer.recognize(ink) .addOnSuccessListener { result: RecognitionResult -> // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. Log.i(TAG, result.candidates[0].text) } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error during recognition: $e") }
Java
recognizer.recognize(ink) .addOnSuccessListener( // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. result -> Log.i(TAG, result.getCandidates().get(0).getText())) .addOnFailureListener( e -> Log.e(TAG, "Error during recognition: " + e));
El código de muestra anterior supone que el modelo de reconocimiento ya se descargarse, como se describe en la siguiente sección.
Administra las descargas de modelos
Si bien la API de reconocimiento de tinta digital admite cientos de idiomas, cada uno
idioma requiere que se descarguen algunos datos antes de cualquier reconocimiento. Cerca
Se requieren 20 MB de almacenamiento por idioma. Esto se controla
RemoteModelManager
.
Descarga un modelo nuevo
Kotlin
import com.google.mlkit.common.model.DownloadConditions import com.google.mlkit.common.model.RemoteModelManager var model: DigitalInkRecognitionModel = ... val remoteModelManager = RemoteModelManager.getInstance() remoteModelManager.download(model, DownloadConditions.Builder().build()) .addOnSuccessListener { Log.i(TAG, "Model downloaded") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while downloading a model: $e") }
Java
import com.google.mlkit.common.model.DownloadConditions; import com.google.mlkit.common.model.RemoteModelManager; DigitalInkRecognitionModel model = ...; RemoteModelManager remoteModelManager = RemoteModelManager.getInstance(); remoteModelManager .download(model, new DownloadConditions.Builder().build()) .addOnSuccessListener(aVoid -> Log.i(TAG, "Model downloaded")) .addOnFailureListener( e -> Log.e(TAG, "Error while downloading a model: " + e));
Verifica si ya se descargó un modelo
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
Borra un modelo descargado
Quitar un modelo del almacenamiento del dispositivo libera espacio.
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener { Log.i(TAG, "Model successfully deleted") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while deleting a model: $e") }
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener( aVoid -> Log.i(TAG, "Model successfully deleted")) .addOnFailureListener( e -> Log.e(TAG, "Error while deleting a model: " + e));
Sugerencias para mejorar la precisión del reconocimiento de texto
La precisión del reconocimiento de texto puede variar según el idioma. La precisión también depende en el estilo de escritura. Si bien el reconocimiento de tinta digital está entrenado para manejar muchos tipos de estilos de escritura, los resultados pueden variar de un usuario a otro.
Estas son algunas formas de mejorar la precisión de un reconocedor de texto. Ten en cuenta que estas técnicas No se aplican a los clasificadores de dibujo para emojis, autodibujo y formas.
Área de escritura
Muchas aplicaciones tienen un área de escritura bien definida para las entradas del usuario. El significado de un símbolo es determinado parcialmente por su tamaño en relación con el tamaño del área de escritura que lo contiene. Por ejemplo, la diferencia entre una "o" minúscula o mayúscula o "c", y una coma versus una tiene una barra diagonal.
Decirle al reconocedor el ancho y la altura del área de escritura puede mejorar la precisión. Sin embargo, el reconocedor supone que el área de escritura solo contiene una línea de texto. Si los componentes físicos El área de escritura es lo suficientemente grande como para permitir que el usuario escriba dos o más líneas, puedes mejorar resultados pasando un WritingArea con una altura que sea tu mejor estimación de la altura de una una sola línea de texto. El objeto WritingArea que pasas al reconocedor no tiene que corresponder exactamente con el área física de escritura en la pantalla. Se cambia la altura de WritingArea de esta manera funciona mejor en algunos lenguajes que en otros.
Cuando especifiques el área de escritura, especifica su ancho y alto en las mismas unidades que el trazo coordenadas. Los argumentos de las coordenadas x,y no tienen requisitos de unidades; la API normaliza todos por lo que lo único que importa es el tamaño relativo y la posición de los trazos. Puedes pasar coordenadas en la escala que tenga sentido para tu sistema.
Contexto previo
El contexto previo es el texto que precede inmediatamente a los trazos en la Ink
que
intentan reconocer. Puedes ayudar al reconocedor si le informas sobre el contexto previo.
Por ejemplo, las letras cursivas "n" y "u" a menudo se confunden unos con otros. Si el usuario tiene ingresaron la palabra parcial "arg", podrían continuar con trazos que se puedan reconocer como "ument" o "nment". Cómo especificar el “argumento” previo al contexto resuelve la ambigüedad, ya que la palabra "argumento" es más probable que “argumento”.
El contexto previo también puede ayudar al reconocedor a identificar saltos de palabras, los espacios entre palabras. Puedes Escribe un carácter de espacio, pero no puedes dibujarlo, ¿cómo puede un reconocedor determinar cuándo termina una palabra? y empieza la siguiente? Si el usuario ya escribió "hello" y continúa con la palabra escrita “world”, sin el contexto previo, el reconocedor muestra la string “world”. Sin embargo, si especificas pre-context “hello”, el modelo mostrará la cadena “ mundo", con un espacio líder, ya que “hola en todo el mundo" tiene más sentido que “helloword”.
Debes proporcionar la cadena previa al contexto más larga posible, con un máximo de 20 caracteres, incluida la y espacios. Si la cadena es más larga, el reconocedor solo usa los últimos 20 caracteres.
En la siguiente muestra de código, se indica cómo definir un área de escritura y usar un
RecognitionContext
para especificar el contexto previo.
Kotlin
var preContext : String = ...; var width : Float = ...; var height : Float = ...; val recognitionContext : RecognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(WritingArea(width, height)) .build() recognizer.recognize(ink, recognitionContext)
Java
String preContext = ...; float width = ...; float height = ...; RecognitionContext recognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(new WritingArea(width, height)) .build(); recognizer.recognize(ink, recognitionContext);
Orden de trazo
La precisión del reconocimiento depende del orden de los trazos. Los reconocedores esperan que los trazos ocurrir en el orden en que la gente naturalmente escribiría; por ejemplo, de izquierda a derecha para inglés. Cualquier caso que se aparta de este patrón, como escribir una oración en inglés empezando con la última palabra, da resultados menos precisos.
Otro ejemplo es cuando se quita una palabra del medio de un Ink
y se reemplaza por
otra palabra. La revisión probablemente esté en la mitad de una oración, pero los trazos para la revisión
están al final de la secuencia de trazo.
En este caso, recomendamos enviar la palabra nueva por separado a la API y combinar la
resultado con los reconocimientos previos usando tu propia lógica.
Cómo abordar las formas ambiguas
Hay casos en los que el significado de la forma proporcionada al reconocedor es ambiguo. Para ejemplo, un rectángulo con bordes muy redondeados podría verse como un rectángulo o una elipse.
Estos casos poco claros se pueden manejar con puntuaciones de reconocimiento cuando están disponibles. Solo
los clasificadores de formas proporcionan puntuaciones. Si el modelo es muy seguro, la puntuación del resultado principal será
mucho mejor que la segunda mejor. Si hay incertidumbre, las puntuaciones de los dos primeros resultados
y estar cerca. Además, ten en cuenta que los clasificadores de formas interpretan el Ink
completo como un elemento
forma única. Por ejemplo, si Ink
contiene un rectángulo y una elipse junto a cada uno
el reconocedor puede mostrar uno u otro (o algo completamente diferente) como
dado que un solo candidato de reconocimiento no puede representar dos formas.