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() कोड को कॉल करना चाहिए.

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

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

DigitalInkआइडेंटिफ़ायर का उदाहरण पाएं

पहचानने के लिए, 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" वाले कर्सिव अक्षरों को, गलती से एक-दूसरे के तौर पर समझ लिया जाता है. अगर उपयोगकर्ता ने पहले ही कुछ हद तक "आर्ग्युमेंट" शब्द डाल दिया है, तो हो सकता है कि उनके पास स्ट्रोक हो. इसकी पहचान, "न्यूमेंट" या "नेम" के तौर पर हो सकती है. "आर्ग्युमेंट" शब्द का इस्तेमाल करने से, "आर्ग्युमेंट" शब्द का इस्तेमाल होने पर, "तर्क" शब्द की दुविधा खत्म हो जाती है.

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

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