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

संग्रह की मदद से व्यवस्थित रहें अपनी प्राथमिकताओं के आधार पर, कॉन्टेंट को सेव करें और कैटगरी में बांटें.

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

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

  1. अपने प्रोजेक्ट-लेवल की build.gradle फ़ाइल में, पक्का करें कि आपके buildscript और allprojects, दोनों सेक्शन में Google's Maven का डेटा स्टोर करने की जगह शामिल की गई हो.
  2. अपने मॉड्यूल की App-लेवल Gradle फ़ाइल में ML किट Android लाइब्रेरी के लिए डिपेंडेंसी जोड़ें, जो आम तौर पर app/build.gradle होती है:
dependencies {
  // ...
  implementation 'com.google.mlkit:digital-ink-recognition:18.0.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();

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

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

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

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

कॉन्टेक्स्ट

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

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

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

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