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

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

इसे आज़माएं

वेब कंटेनर इंस्टॉल करने से पहले

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

अब आप Ink ऑब्जेक्ट में टेक्स्ट की पहचान करने के लिए तैयार हैं.

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

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

यह सामान्य पैटर्न नीचे दिए गए कोड स्निपेट में दिखाया गया है. बेहतर उदाहरण के लिए, एमएल किट क्विकस्टार्ट सैंपल देखें.

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();

DigitalInkTagr का इंस्टेंस पाएं

पहचान करने के लिए, 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", और कॉमा और फ़ॉरवर्ड स्लैश के बीच फ़र्क़ होना चाहिए.

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

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

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

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

उदाहरण के लिए, कर्सिव अक्षरों "n" और "u" को अक्सर एक-दूसरे समझ लिया जाता है. अगर उपयोगकर्ता ने पहले से ही आंशिक शब्द "आर्ग" डाल दिया है, तो वह स्ट्रोक के साथ आगे बढ़ सकता है जिसे "ument" या "nment" माना जा सकता है. प्री-कॉन्टेक्स्ट "आर्ग" की जानकारी देने से, सही जानकारी मिल जाती है. ऐसा इसलिए, क्योंकि "तर्क" शब्द के बजाय, "तर्क" शब्द इस्तेमाल करने की संभावना ज़्यादा होती है.

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