Grâce à la reconnaissance d'encre numérique de ML Kit, vous pouvez reconnaître du texte manuscrit sur une surface numérique dans des centaines de langues, ainsi que classer des croquis.
Essayer
- Testez l'exemple d'application pour voir un exemple d'utilisation de cette API.
Avant de commencer
- Dans le fichier
build.gradleau niveau du projet, veillez à inclure le dépôt Maven de Google à la fois dans les sectionsbuildscriptetallprojects. - Ajoutez les dépendances des bibliothèques Android ML Kit au fichier Gradle au niveau de l'application de votre module, qui est généralement
app/build.gradle:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:19.0.0'
}
Vous pouvez maintenant commencer à reconnaître du texte dans les objets Ink.
Créer un objet Ink
La principale façon de créer un objet Ink consiste à le dessiner sur un écran tactile. Sur
Android, vous pouvez utiliser un
canevas à
cet effet. Vos
gestionnaires d'événements tactiles
doivent appeler la addNewTouchEvent()
méthode présentée dans l'extrait de code suivant pour stocker les points des traits que l'
utilisateur dessine dans l'objet Ink.
Ce modèle général est illustré dans l'extrait de code suivant. Pour obtenir un exemple plus complet, consultez l' exemple de démarrage rapide de ML Kit.
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();
Obtenir une instance de DigitalInkRecognizer
Pour effectuer la reconnaissance, envoyez l'instance Ink à un
DigitalInkRecognizer objet. Le code ci-dessous montre comment instancier un tel
outil de reconnaissance à partir d'un 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());
Traiter un objet 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));
L'exemple de code ci-dessus suppose que le modèle de reconnaissance a déjà été téléchargé, comme décrit dans la section suivante.
Gérer les téléchargements de modèles
Bien que l'API de reconnaissance d'encre numérique soit compatible avec des centaines de langues, chaque langue nécessite le téléchargement de certaines données avant toute reconnaissance. Environ 20 Mo d'espace de stockage sont nécessaires par langue. Cette opération est gérée par l'objet RemoteModelManager.
Télécharger un nouveau modèle
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));
Vérifier si un modèle a déjà été téléchargé
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
Supprimer un modèle téléchargé
La suppression d'un modèle de l'espace de stockage de l'appareil libère de l'espace.
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));
Conseils pour améliorer la précision de la reconnaissance du texte
La précision de la reconnaissance du texte peut varier d'une langue à l'autre. Elle dépend également du style d'écriture. Bien que la reconnaissance d'encre numérique soit entraînée pour gérer de nombreux types de styles d'écriture, les résultats peuvent varier d'un utilisateur à l'autre.
Voici quelques façons d'améliorer la précision d'un outil de reconnaissance de texte. Notez que ces techniques ne s'appliquent pas aux classificateurs de dessins pour les emojis, le dessin automatique et les formes.
Zone d'écriture
De nombreuses applications disposent d'une zone d'écriture bien définie pour la saisie utilisateur. La signification d'un symbole est partiellement déterminée par sa taille par rapport à celle de la zone d'écriture qui le contient. Par exemple, la différence entre une lettre « o » ou « c » minuscule ou majuscule, et une virgule par rapport à une barre oblique.
Indiquer à l'outil de reconnaissance la largeur et la hauteur de la zone d'écriture peut améliorer la précision. Toutefois, l'outil de reconnaissance suppose que la zone d'écriture ne contient qu'une seule ligne de texte. Si la zone d'écriture physique est suffisamment grande pour permettre à l'utilisateur d'écrire deux lignes ou plus, vous pouvez obtenir de meilleurs résultats en transmettant un WritingArea dont la hauteur correspond à votre meilleure estimation de la hauteur d'une seule ligne de texte. L'objet WritingArea que vous transmettez à l'outil de reconnaissance ne doit pas nécessairement correspondre exactement à la zone d'écriture physique à l'écran. La modification de la hauteur de WritingArea de cette manière fonctionne mieux dans certaines langues que dans d'autres.
Lorsque vous spécifiez la zone d'écriture, indiquez sa largeur et sa hauteur dans les mêmes unités que les coordonnées du trait. Les arguments de coordonnées x,y n'ont pas d'exigence d'unité. L'API normalise toutes les unités. La seule chose qui compte est la taille et la position relatives des traits. Vous êtes libre de transmettre des coordonnées dans l'échelle qui convient à votre système.
Pré-contexte
Le pré-contexte est le texte qui précède immédiatement les traits dans l'Ink que vous essayez de reconnaître. Vous pouvez aider l'outil de reconnaissance en lui indiquant le pré-contexte.
Par exemple, les lettres cursives "n" et "u" sont souvent confondues. Si l'utilisateur a déjà saisi le mot partiel "arg", il peut continuer avec des traits qui peuvent être reconnus comme "ument" ou "nment". La spécification du pré-contexte "arg" résout l'ambiguïté, car le mot "argument" est plus probable que "argnment".
Le pré-contexte peut également aider l'outil de reconnaissance à identifier les sauts de mots, c'est-à-dire les espaces entre les mots. Vous pouvez saisir un espace, mais vous ne pouvez pas en dessiner un. Comment un outil de reconnaissance peut-il déterminer quand un mot se termine et quand le suivant commence ? Si l'utilisateur a déjà écrit "hello" et continue avec le mot écrit "world", sans pré-contexte, l'outil de reconnaissance renvoie la chaîne "world". Toutefois, si vous spécifiez le pré-contexte "hello", le modèle renvoie la chaîne " world", avec un espace au début, car "hello world" est plus logique que "helloword".
Vous devez fournir la chaîne de pré-contexte la plus longue possible, jusqu'à 20 caractères, espaces compris. Si la chaîne est plus longue, l'outil de reconnaissance n'utilise que les 20 derniers caractères.
L'exemple de code ci-dessous montre comment définir une zone d'écriture et utiliser un objet RecognitionContext pour spécifier le pré-contexte.
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);
Ordre des traits
La précision de la reconnaissance est sensible à l'ordre des traits. Les outils de reconnaissance s'attendent à ce que les traits se produisent dans l'ordre dans lequel les personnes écrivent naturellement, par exemple de gauche à droite pour l'anglais. Tout cas qui s'écarte de ce modèle, comme l'écriture d'une phrase anglaise commençant par le dernier mot, donne des résultats moins précis.
Un autre exemple se produit lorsqu'un mot au milieu d'un Ink est supprimé et remplacé par un autre mot. La révision se trouve probablement au milieu d'une phrase, mais les traits de la révision se trouvent à la fin de la séquence de traits.
Dans ce cas, nous vous recommandons d'envoyer le mot nouvellement écrit séparément à l'API et de fusionner le résultat avec les reconnaissances précédentes à l'aide de votre propre logique.
Gérer les formes ambiguës
Dans certains cas, la signification de la forme fournie à l'outil de reconnaissance est ambiguë. Par exemple, un rectangle aux bords très arrondis peut être considéré comme un rectangle ou une ellipse.
Ces cas peu clairs peuvent être gérés à l'aide de scores de reconnaissance lorsqu'ils sont disponibles. Seuls les classificateurs de formes fournissent des scores. Si le modèle est très confiant, le score du meilleur résultat sera bien meilleur que le deuxième meilleur. En cas d'incertitude, les scores des deux meilleurs résultats seront proches. N'oubliez pas non plus que les classificateurs de formes interprètent l'ensemble de l'Ink comme une seule forme. Par exemple, si l'Ink contient un rectangle et une ellipse l'un à côté de l'autre, l'outil de reconnaissance peut renvoyer l'un ou l'autre (ou quelque chose de complètement différent) comme résultat, car un seul candidat de reconnaissance ne peut pas représenter deux formes.