Con il riconoscimento dell'inchiostro digitale di ML Kit, puoi riconoscere il testo scritto a mano su una superficie digitale in centinaia di lingue, nonché classificare gli schizzi.
Prova
- Prova l'app di esempio per vedere un esempio di utilizzo di questa API.
Prima di iniziare
- Nel file
build.gradlea livello di progetto, assicurati di includere il repository Maven di Google nelle sezionibuildscripteallprojects. - Aggiungi le dipendenze per le librerie Android di ML Kit al file Gradle a livello di app del modulo, che in genere è
app/build.gradle:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:19.0.0'
}
Ora puoi iniziare a riconoscere il testo negli oggetti Ink.
Creare un oggetto Ink
Il modo principale per creare un oggetto Ink è disegnarlo su un touch screen. Su
Android, puoi utilizzare un
Canvas a
questo scopo. I gestori di eventi
touch devono chiamare il addNewTouchEvent()
metodo mostrato nel seguente snippet di codice per memorizzare i punti nei tratti disegnati dall'
utente nell'oggetto Ink.
Questo pattern generale è illustrato nel seguente snippet di codice. Per un esempio più completo, consulta l'esempio di guida rapida di 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();
Ottenere un'istanza di DigitalInkRecognizer
Per eseguire il riconoscimento, invia l'istanza Ink a un
DigitalInkRecognizer oggetto. Il codice riportato di seguito mostra come creare un'istanza di un
riconoscitore da 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());
Elaborare un oggetto 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));
Il codice campione riportato sopra presuppone che il modello di riconoscimento sia già stato scaricato, come descritto nella sezione successiva.
Gestire i download dei modelli
Sebbene l'API di riconoscimento dell'inchiostro digitale supporti centinaia di lingue, per ogni lingua è necessario scaricare alcuni dati prima di qualsiasi riconoscimento. Sono necessari circa 20 MB di spazio di archiviazione per lingua. Questa operazione viene gestita dall'oggetto RemoteModelManager.
Scaricare un nuovo modello
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));
Controllare se un modello è già stato scaricato
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
Eliminare un modello scaricato
La rimozione di un modello dallo spazio di archiviazione del dispositivo libera spazio.
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));
Suggerimenti per migliorare la precisione del riconoscimento del testo
La precisione del riconoscimento del testo può variare a seconda della lingua. La precisione dipende anche dallo stile di scrittura. Sebbene il riconoscimento dell'inchiostro digitale sia addestrato a gestire molti tipi di stili di scrittura, i risultati possono variare da utente a utente.
Ecco alcuni modi per migliorare l'accuratezza di un riconoscitore di testo. Tieni presente che queste tecniche non si applicano ai classificatori di disegni per emoji, disegno automatico e forme.
Area di scrittura
Molte applicazioni hanno un'area di scrittura ben definita per l'input dell'utente. Il significato di un simbolo è determinato in parte dalle sue dimensioni rispetto alle dimensioni dell'area di scrittura che lo contiene. Ad esempio, la differenza tra una lettera "o" o "c" minuscola o maiuscola e una virgola rispetto a una barra.
Comunicare al riconoscitore la larghezza e l'altezza dell'area di scrittura può migliorare la precisione. Tuttavia, il riconoscitore presuppone che l'area di scrittura contenga una sola riga di testo. Se l'area di scrittura fisica è abbastanza grande da consentire all'utente di scrivere due o più righe, potresti ottenere risultati migliori passando un oggetto WritingArea con un'altezza che sia la tua migliore stima dell'altezza di una singola riga di testo. L'oggetto WritingArea che passi al riconoscitore non deve corrispondere esattamente all'area di scrittura fisica sullo schermo. La modifica dell'altezza di WritingArea in questo modo funziona meglio in alcune lingue rispetto ad altre.
Quando specifichi l'area di scrittura, specifica la larghezza e l'altezza nelle stesse unità delle coordinate del tratto. Gli argomenti delle coordinate x,y non hanno requisiti di unità: l'API normalizza tutte le unità, quindi l'unica cosa che conta è la dimensione e la posizione relativa dei tratti. Puoi passare le coordinate in qualsiasi scala abbia senso per il tuo sistema.
Pre-contesto
Il pre-contesto è il testo che precede immediatamente i tratti nell'oggetto Ink che stai tentando di riconoscere. Puoi aiutare il riconoscitore fornendogli informazioni sul pre-contesto.
Ad esempio, le lettere corsive "n" e "u" vengono spesso confuse tra loro. Se l'utente ha già inserito la parola parziale "arg", potrebbe continuare con tratti che possono essere riconosciuti come "ument" o "nment". La specifica del pre-contesto "arg" risolve l'ambiguità, poiché la parola "argument" è più probabile di "argnment".
Il pre-contesto può anche aiutare il riconoscitore a identificare le interruzioni di parole, gli spazi tra le parole. Puoi digitare un carattere spazio, ma non puoi disegnarne uno, quindi come può un riconoscitore determinare quando una parola termina e inizia la successiva? Se l'utente ha già scritto "hello" e continua con la parola scritta "world", senza pre-contesto il riconoscitore restituisce la stringa "world". Tuttavia, se specifichi il pre-contesto "hello", il modello restituirà la stringa " world", con uno spazio iniziale, poiché "hello world" ha più senso di "helloword".
Devi fornire la stringa di pre-contesto più lunga possibile, fino a 20 caratteri, spazi inclusi. Se la stringa è più lunga, il riconoscitore utilizza solo gli ultimi 20 caratteri.
Il codice di esempio riportato di seguito mostra come definire un'area di scrittura e utilizzare un oggetto RecognitionContext per specificare il pre-contesto.
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);
Ordine dei tratti
La precisione del riconoscimento è sensibile all'ordine dei tratti. I riconoscitori si aspettano che i tratti si verifichino nell'ordine in cui le persone scrivono naturalmente, ad esempio da sinistra a destra per l'inglese. Qualsiasi caso che si discosti da questo pattern, ad esempio la scrittura di una frase in inglese che inizia con l'ultima parola, produce risultati meno precisi.
Un altro esempio è quando una parola al centro di un oggetto Ink viene rimossa e sostituita con un'altra parola. La revisione è probabilmente al centro di una frase, ma i tratti per la revisione si trovano alla fine della sequenza di tratti.
In questo caso, ti consigliamo di inviare la parola appena scritta separatamente all'API e di unire il risultato con i riconoscimenti precedenti utilizzando la tua logica.
Gestire le forme ambigue
In alcuni casi, il significato della forma fornita al riconoscitore è ambiguo. Ad esempio, un rettangolo con bordi molto arrotondati potrebbe essere considerato un rettangolo o un'ellisse.
Questi casi poco chiari possono essere gestiti utilizzando i punteggi di riconoscimento, se disponibili. Solo i classificatori di forme forniscono punteggi. Se il modello è molto sicuro, il punteggio del risultato principale sarà molto migliore del secondo migliore. In caso di incertezza, i punteggi dei primi due risultati saranno simili. Inoltre, tieni presente che i classificatori di forme interpretano l'intero oggetto Ink come una singola forma. Ad esempio, se l'oggetto Ink contiene un rettangolo e un'ellisse uno accanto all'altro, il riconoscitore potrebbe restituire uno o l'altro (o qualcosa di completamente diverso) come risultato, poiché un singolo candidato di riconoscimento non può rappresentare due forme.