Android पर एमएल किट की मदद से डिजिटल इंक की पहचान करना

ML Kit की डिजिटल इंक की पहचान करने की सुविधा की मदद से, डिजिटल प्लैटफ़ॉर्म पर लिखे गए टेक्स्ट को सैकड़ों भाषाओं में पहचाना जा सकता है. साथ ही, स्केच को अलग-अलग कैटगरी में बांटा जा सकता है.

इसे आज़माएं

शुरू करने से पहले

  1. प्रोजेक्ट-लेवल की build.gradle फ़ाइल में, buildscript और allprojects, दोनों सेक्शन में Google की मेवन रिपॉज़िटरी शामिल करना न भूलें.
  2. अपने मॉड्यूल की ऐप्लिकेशन-लेवल की Gradle फ़ाइल में, ML Kit की Android लाइब्रेरी की डिपेंडेंसी जोड़ें. आम तौर पर, यह फ़ाइल app/build.gradle होती है:
dependencies {
  // ...
  implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}

अब आपके पास Ink ऑब्जेक्ट में टेक्स्ट की पहचान करने की सुविधा है.

Ink ऑब्जेक्ट बनाना

Ink ऑब्जेक्ट बनाने का मुख्य तरीका, इसे टच स्क्रीन पर ड्रॉ करना है. Android पर, इस काम के लिए कैनवस का इस्तेमाल किया जा सकता है. आपके टच इवेंट हैंडलर को, Ink ऑब्जेक्ट में उपयोगकर्ता के खींचे गए स्ट्रोक में पॉइंट सेव करने के लिए, यहां दिए गए कोड स्निपेट में दिखाए गए addNewTouchEvent() तरीके को कॉल करना चाहिए.

इस सामान्य पैटर्न को यहां दिए गए कोड स्निपेट में दिखाया गया है. ज़्यादा जानकारी के लिए, 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 की ऊंचाई बदलने पर, कुछ भाषाओं में बेहतर परफ़ॉर्मेंस मिलती है.

लिखने के लिए जगह तय करते समय, उसकी चौड़ाई और ऊंचाई को स्ट्रोक के निर्देशांक की ही इकाइयों में बताएं. x,y कोऑर्डिनेट आर्ग्युमेंट के लिए, यूनिट की ज़रूरत नहीं होती - एपीआई सभी यूनिट को सामान्य बना देता है. इसलिए, सिर्फ़ स्ट्रोक का साइज़ और पोज़िशन मायने रखती है. आपके पास अपने सिस्टम के हिसाब से, कोऑर्डिनेट को किसी भी स्केल में पास करने का विकल्प होता है.

प्री-कॉन्टेक्स्ट

प्री-कॉन्टेक्स्ट, उस टेक्स्ट को कहते हैं जो Ink में मौजूद उन स्ट्रोक से ठीक पहले होता है जिनकी पहचान की जा रही है. बोली पहचानने की सुविधा को बेहतर बनाने के लिए, बोली से पहले की बातचीत के बारे में बताएं.

उदाहरण के लिए, कर्सिव लिखावट वाले अक्षर "n" और "u" को अक्सर एक-दूसरे के लिए गलत समझा जाता है. अगर उपयोगकर्ता ने पहले से ही "arg" शब्द का कुछ हिस्सा डाला है, तो वह "ument" या "nment" के तौर पर पहचाने जा सकने वाले स्ट्रोक के साथ जारी रख सकता है. पहले के संदर्भ में "arg" बताने से, संदेह की स्थिति हल हो जाती है, क्योंकि "argnment" के बजाय, "argument" शब्द का इस्तेमाल ज़्यादा होता है.

प्री-कॉन्टेक्स्ट की मदद से, शब्दों के बीच के ब्रेक और शब्दों के बीच के स्पेस की पहचान करने में भी मदद मिलती है. स्पेस वर्ण को टाइप किया जा सकता है, लेकिन उसे ड्रॉ नहीं किया जा सकता. इसलिए, यह कैसे तय किया जा सकता है कि एक शब्द कब खत्म होता है और अगला शब्द कब शुरू होता है? अगर उपयोगकर्ता ने पहले ही "नमस्ते" लिखा है और लिखे गए शब्द "दुनिया" पर आगे बढ़ता है, तो पहले से मौजूद संदर्भ के बिना, पहचानने वाला टूल "दुनिया" स्ट्रिंग दिखाता है. हालांकि, अगर आपने पहले से "नमस्ते" के तौर पर कोई कॉन्टेक्स्ट दिया है, तो मॉडल " दुनिया" स्ट्रिंग को स्पेस के साथ दिखाएगा. ऐसा इसलिए, क्योंकि "नमस्ते" के मुकाबले "नमस्ते दुनिया" ज़्यादा सही है.

आपको ज़्यादा से ज़्यादा लंबी प्री-कॉन्टेक्स्ट स्ट्रिंग देनी चाहिए. इसमें स्पेस के साथ 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 में एक-दूसरे के बगल में एक रेक्टैंगल और एक दीर्घवृत्त है, तो पहचान करने वाला टूल नतीजे के तौर पर इनमें से किसी एक या दोनों को दिखा सकता है. इसके अलावा, वह पूरी तरह से अलग नतीजा भी दिखा सकता है, क्योंकि पहचान करने के लिए इस्तेमाल होने वाली किसी एक आकृति से दो आकृतियों की पहचान नहीं की जा सकती.