ML Kit की डिजिटल इंक की पहचान करने की सुविधा की मदद से, डिजिटल प्लैटफ़ॉर्म पर हाथ से लिखे गए टेक्स्ट को सैकड़ों भाषाओं में पहचाना जा सकता है. साथ ही, स्केच को अलग-अलग कैटगरी में बांटा जा सकता है.
इसे आज़माएं
- इस एपीआई के इस्तेमाल का उदाहरण देखने के लिए, सैंपल के तौर पर मिले ऐप्लिकेशन को आज़माएं.
शुरू करने से पहले
- प्रोजेक्ट-लेवल की
build.gradleफ़ाइल में,buildscriptऔरallprojects, दोनों सेक्शन में Google की Maven रिपॉज़िटरी को शामिल करना न भूलें. - अपने मॉड्यूल की ऐप्लिकेशन-लेवल Gradle फ़ाइल में, ML Kit की Android लाइब्रेरी के लिए डिपेंडेंसी जोड़ें. आम तौर पर, यह फ़ाइल
app/build.gradleहोती है:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:19.0.0'
}
अब Ink ऑब्जेक्ट में मौजूद टेक्स्ट को पहचाना जा सकता है.
Ink ऑब्जेक्ट बनाना
Ink ऑब्जेक्ट बनाने का मुख्य तरीका, उसे टच स्क्रीन पर ड्रॉ करना है. Android पर, इस काम के लिए Canvas का इस्तेमाल किया जा सकता है. आपके टच इवेंट हैंडलर को यहां दिए गए कोड स्निपेट में दिखाए गए addNewTouchEvent() तरीके को कॉल करना चाहिए. इससे, उपयोगकर्ता के बनाए गए स्ट्रोक के पॉइंट को Ink ऑब्जेक्ट में सेव किया जा सकेगा.
इस सामान्य पैटर्न को यहां दिए गए कोड स्निपेट में दिखाया गया है. ज़्यादा जानकारी के लिए, 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();
DigitalInkRecognizer का इंस्टेंस पाना
पहचानने के लिए, Ink इंस्टेंस को DigitalInkRecognizer ऑब्जेक्ट पर भेजें. यहां दिए गए कोड से पता चलता है कि 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());
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));
ऊपर दिए गए सैंपल कोड में यह माना गया है कि पहचान करने वाला मॉडल पहले ही डाउनलोड किया जा चुका है. इसके बारे में अगले सेक्शन में बताया गया है.
मॉडल डाउनलोड मैनेज करना
डिजिटल इंक रिकग्निशन एपीआई, सैकड़ों भाषाओं के साथ काम करता है. हालांकि, हर भाषा के लिए कुछ डेटा डाउनलोड करना ज़रूरी है, ताकि उसे पहचाना जा सके. हर भाषा के लिए, करीब 20 एमबी स्टोरेज की ज़रूरत होती है. इसे RemoteModelManager ऑब्जेक्ट मैनेज करता है.
नया मॉडल डाउनलोड करना
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));
यह देखना कि कोई मॉडल पहले से डाउनलोड किया गया है या नहीं
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
डाउनलोड किया गया कोई मॉडल मिटाना
डिवाइस के स्टोरेज से किसी मॉडल को हटाने पर, स्टोरेज में जगह खाली हो जाती है.
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));
टेक्स्ट पहचानने की सुविधा को ज़्यादा सटीक बनाने के लिए सलाह
टेक्स्ट की पहचान करने की सुविधा की सटीकता, अलग-अलग भाषाओं के हिसाब से अलग-अलग हो सकती है. नतीजे कितने सटीक होंगे, यह इस बात पर भी निर्भर करता है कि लिखने का तरीका क्या है. डिजिटल इंक रिकग्निशन को कई तरह की लिखावट शैलियों को समझने के लिए ट्रेन किया गया है. हालांकि, इसके नतीजे हर उपयोगकर्ता के लिए अलग-अलग हो सकते हैं.
टेक्स्ट पहचानने की सुविधा को ज़्यादा सटीक बनाने के लिए, यहां कुछ तरीके दिए गए हैं. ध्यान दें कि ये तकनीकें, इमोजी, ऑटोड्रॉ, और शेप के लिए ड्रॉइंग क्लासिफ़ायर पर लागू नहीं होती हैं.
लिखने की जगह
कई ऐप्लिकेशन में, उपयोगकर्ता के इनपुट के लिए लिखने की जगह को अच्छी तरह से तय किया गया है. किसी सिंबल का मतलब, कुछ हद तक उसके साइज़ से तय होता है. यह साइज़, उस राइटिंग एरिया के साइज़ के हिसाब से तय होता है जिसमें वह सिंबल मौजूद होता है. उदाहरण के लिए, लोअर या अपर केस लेटर "o" या "c" के बीच का अंतर और कॉमा बनाम फ़ॉरवर्ड स्लैश.
लिखने की जगह की चौड़ाई और ऊंचाई की जानकारी देने से, लिखावट पहचानने की सुविधा को ज़्यादा सटीक बनाया जा सकता है. हालांकि, लिखावट पहचानने वाला टूल यह मानकर चलता है कि लिखने की जगह पर सिर्फ़ एक लाइन का टेक्स्ट है. अगर लिखने की जगह इतनी बड़ी है कि उपयोगकर्ता दो या उससे ज़्यादा लाइनें लिख सकता है, तो आपको बेहतर नतीजे मिल सकते हैं. इसके लिए, WritingArea में ऐसी ऊंचाई पास करें जो आपके हिसाब से, टेक्स्ट की एक लाइन की ऊंचाई का सबसे सही अनुमान हो. यह ज़रूरी नहीं है कि आपने पहचान करने वाले टूल को जो WritingArea ऑब्जेक्ट पास किया है वह स्क्रीन पर मौजूद फ़िज़िकल राइटिंग एरिया से पूरी तरह मेल खाता हो. इस तरह से WritingArea की ऊंचाई बदलने पर, यह कुछ भाषाओं में दूसरों की तुलना में बेहतर काम करता है.
लिखने की जगह तय करते समय, उसकी चौड़ाई और ऊंचाई को स्ट्रोक कोऑर्डिनेट वाली यूनिट में ही तय करें. x,y कोऑर्डिनेट आर्ग्युमेंट के लिए, किसी यूनिट की ज़रूरत नहीं होती. एपीआई सभी यूनिट को सामान्य बनाता है. इसलिए, स्ट्रोक का साइज़ और पोज़िशन ही मायने रखती है. आपके पास अपने सिस्टम के हिसाब से, किसी भी स्केल में निर्देशांक पास करने की सुविधा होती है.
पहले से मौजूद कॉन्टेक्स्ट
प्री-कॉन्टेक्स्ट वह टेक्स्ट होता है जो Ink में मौजूद उन स्ट्रोक से ठीक पहले आता है जिन्हें पहचानने की कोशिश की जा रही है. आवाज़ पहचानने वाले सिस्टम को पहले के कॉन्टेक्स्ट के बारे में बताकर, उसकी मदद की जा सकती है.
उदाहरण के लिए, कर्सिव लेटर "n" और "u" को अक्सर एक-दूसरे के तौर पर समझ लिया जाता है. अगर उपयोगकर्ता ने पहले ही "arg" शब्द का कुछ हिस्सा डाल दिया है, तो वह ऐसे स्ट्रोक जारी रख सकता है जिन्हें "ument" या "nment" के तौर पर पहचाना जा सकता है. "arg" के पहले के कॉन्टेक्स्ट की जानकारी देने से, भ्रम की स्थिति दूर हो जाती है. ऐसा इसलिए, क्योंकि "argnment" के बजाय "argument" शब्द होने की संभावना ज़्यादा है.
प्री-कॉन्टेक्स्ट की मदद से, पहचान करने वाले सिस्टम को शब्दों के बीच के ब्रेक और स्पेस की पहचान करने में भी मदद मिल सकती है. स्पेस कैरेक्टर को टाइप किया जा सकता है, लेकिन उसे ड्रॉ नहीं किया जा सकता. ऐसे में, लिखावट पहचानने वाला सिस्टम यह कैसे तय कर सकता है कि कोई शब्द कब खत्म हो रहा है और अगला शब्द कब शुरू हो रहा है? अगर उपयोगकर्ता ने पहले ही "hello" लिख दिया है और वह "world" शब्द लिखना जारी रखता है, तो पहले से मौजूद कॉन्टेक्स्ट के बिना, पहचान करने वाला टूल "world" स्ट्रिंग दिखाता है. हालांकि, अगर आपने पहले से मौजूद कॉन्टेक्स्ट "hello" दिया है, तो मॉडल " world" स्ट्रिंग दिखाएगा. इसमें शुरुआत में स्पेस होगा, क्योंकि "helloword" के बजाय "hello world" ज़्यादा सही है.
आपको प्री-कॉन्टेक्स्ट स्ट्रिंग को ज़्यादा से ज़्यादा लंबा रखना चाहिए. इसमें स्पेस के साथ-साथ ज़्यादा से ज़्यादा 20 वर्ण हो सकते हैं. अगर स्ट्रिंग लंबी है, तो पहचान करने वाला सिस्टम सिर्फ़ आखिरी 20 वर्णों का इस्तेमाल करता है.
यहां दिए गए कोड सैंपल से, लिखने की जगह तय करने और पहले से मौजूद कॉन्टेक्स्ट की जानकारी देने के लिए, RecognitionContext ऑब्जेक्ट का इस्तेमाल करने का तरीका पता चलता है.
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);
स्ट्रोक का क्रम
स्ट्रोक के क्रम से, लिखावट पहचानने की सुविधा के सटीक होने पर असर पड़ता है. लिखावट पहचानने वाले सिस्टम को उम्मीद होती है कि स्ट्रोक उसी क्रम में होंगे जिस क्रम में लोग आम तौर पर लिखते हैं. उदाहरण के लिए, अंग्रेज़ी के लिए बाएं से दाएं. इस पैटर्न से अलग कोई भी केस, जैसे कि अंग्रेज़ी के वाक्य को आखिरी शब्द से शुरू करना, कम सटीक नतीजे देता है.
इसका एक और उदाहरण है, जब Ink के बीच में मौजूद किसी शब्द को हटाकर उसकी जगह कोई दूसरा शब्द इस्तेमाल किया जाता है. ऐसा हो सकता है कि बदलाव किसी वाक्य के बीच में हो, लेकिन बदलाव के लिए स्ट्रोक, स्ट्रोक के क्रम के आखिर में हों.
इस मामले में, हमारा सुझाव है कि नए लिखे गए शब्द को एपीआई पर अलग से भेजें. साथ ही, अपने लॉजिक का इस्तेमाल करके, नतीजे को पहले से पहचाने गए शब्दों के साथ मर्ज करें.
अस्पष्ट आकारों से निपटना
कुछ मामलों में, पहचान करने वाले सिस्टम को दिए गए आकार का मतलब साफ़ नहीं होता. उदाहरण के लिए, बहुत ज़्यादा गोल किनारों वाले आयत को आयत या एलिप्स के तौर पर देखा जा सकता है.
ऐसे मामलों को हैंडल करने के लिए, पहचान के स्कोर का इस्तेमाल किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि ये स्कोर उपलब्ध हों. सिर्फ़ शेप क्लासिफ़ायर स्कोर देते हैं. अगर मॉडल को पूरा भरोसा है, तो सबसे अच्छे नतीजे का स्कोर, दूसरे सबसे अच्छे नतीजे के स्कोर से काफ़ी बेहतर होगा. अगर कोई अनिश्चितता है, तो टॉप दो नतीजों के स्कोर करीब होंगे. यह भी ध्यान रखें कि शेप क्लासिफ़ायर, पूरे Ink को एक ही शेप के तौर पर दिखाते हैं. उदाहरण के लिए, अगर Ink में एक-दूसरे के बगल में एक रेक्टैंगल और एक एलिप्स है, तो पहचान करने वाला टूल नतीजे के तौर पर एक या दूसरा (या पूरी तरह से अलग) दिखा सकता है. ऐसा इसलिए, क्योंकि पहचान करने वाला एक कैंडिडेट दो आकृतियों को नहीं दिखा सकता.