অ্যান্ড্রয়েডে এমএল কিট দিয়ে ডিজিটাল কালি শনাক্ত করা

ML Kit-এর ডিজিটাল কালি স্বীকৃতির সাহায্যে, আপনি শত শত ভাষায় ডিজিটাল পৃষ্ঠায় হাতে লেখা টেক্সট চিনতে পারবেন, পাশাপাশি স্কেচগুলিকে শ্রেণীবদ্ধ করতে পারবেন।

চেষ্টা করে দেখো

শুরু করার আগে

  1. আপনার প্রজেক্ট-লেভেল build.gradle ফাইলে, আপনার buildscript এবং allprojects উভয় বিভাগেই Google এর Maven রিপোজিটরি অন্তর্ভুক্ত করতে ভুলবেন না।
  2. আপনার মডিউলের অ্যাপ-লেভেল গ্র্যাডল ফাইলে ML কিট অ্যান্ড্রয়েড লাইব্রেরির জন্য নির্ভরতা যোগ করুন, যা সাধারণত app/build.gradle হয়:
dependencies {
  // ...
  implementation 'com.google.mlkit:digital-ink-recognition:19.0.0'
}

আপনি এখন Ink অবজেক্টে টেক্সট চিনতে শুরু করতে প্রস্তুত।

একটি Ink অবজেক্ট তৈরি করুন

একটি Ink অবজেক্ট তৈরির প্রধান উপায় হল এটিকে টাচ স্ক্রিনে আঁকা। অ্যান্ড্রয়েডে, আপনি এই উদ্দেশ্যে একটি ক্যানভাস ব্যবহার করতে পারেন। আপনার টাচ ইভেন্ট হ্যান্ডলারদের নিম্নলিখিত কোড স্নিপেট দেখানো addNewTouchEvent() পদ্ধতিটি কল করা উচিত যাতে ব্যবহারকারী Ink অবজেক্টে যে স্ট্রোকগুলি আঁকেন তার পয়েন্টগুলি সংরক্ষণ করা যায়।

এই সাধারণ প্যাটার্নটি নিম্নলিখিত কোড স্নিপেটে দেখানো হয়েছে। আরও সম্পূর্ণ উদাহরণের জন্য ML Kit কুইকস্টার্ট নমুনাটি দেখুন।

কোটলিন

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

জাভা

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 ট্যাগ থেকে এই ধরনের একটি স্বীকৃতি প্রদানকারীকে ইনস্ট্যান্টিয়েট করতে হয়।

কোটলিন

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

জাভা

// 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 অবজেক্ট প্রক্রিয়া করুন

কোটলিন

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")
    }

জাভা

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

উপরের নমুনা কোডটি ধরে নিচ্ছে যে স্বীকৃতি মডেলটি ইতিমধ্যেই ডাউনলোড করা হয়েছে, যেমনটি পরবর্তী বিভাগে বর্ণনা করা হয়েছে।

মডেল ডাউনলোড পরিচালনা করা

ডিজিটাল কালি স্বীকৃতি API শত শত ভাষা সমর্থন করে, তবে প্রতিটি ভাষা স্বীকৃতির আগে কিছু ডেটা ডাউনলোড করতে হয়। প্রতিটি ভাষাতে প্রায় 20MB স্টোরেজ প্রয়োজন। এটি RemoteModelManager অবজেক্ট দ্বারা পরিচালিত হয়।

একটি নতুন মডেল ডাউনলোড করুন

কোটলিন

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")
    }

জাভা

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

একটি মডেল ইতিমধ্যেই ডাউনলোড করা হয়েছে কিনা তা পরীক্ষা করুন।

কোটলিন

var model: DigitalInkRecognitionModel =  ...
remoteModelManager.isModelDownloaded(model)

জাভা

DigitalInkRecognitionModel model = ...;
remoteModelManager.isModelDownloaded(model);

ডাউনলোড করা মডেলটি মুছুন

ডিভাইসের স্টোরেজ থেকে একটি মডেল সরানো হলে জায়গা খালি হয়।

কোটলিন

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 স্থানাঙ্ক আর্গুমেন্টের কোনও ইউনিটের প্রয়োজন নেই - API সমস্ত ইউনিটকে স্বাভাবিক করে তোলে, তাই একমাত্র গুরুত্বপূর্ণ বিষয় হল স্ট্রোকের আপেক্ষিক আকার এবং অবস্থান। আপনার সিস্টেমের জন্য যে স্কেলেই যুক্তিসঙ্গত হোক না কেন, আপনি স্থানাঙ্কগুলি পাস করতে স্বাধীন।

প্রাক-প্রসঙ্গ

প্রি-কন্টেক্সট হলো সেই লেখা যা Ink স্ট্রোকের ঠিক আগে লেখা হয় যা আপনি চিনতে চাইছেন। আপনি প্রি-কন্টেক্সট সম্পর্কে বলে শনাক্তকারীকে সাহায্য করতে পারেন।

উদাহরণস্বরূপ, "n" এবং "u" অক্ষর দুটি প্রায়শই একে অপরের সাথে ভুল করে ব্যবহার করা হয়। যদি ব্যবহারকারী ইতিমধ্যেই আংশিক শব্দ "arg" লিখে থাকেন, তাহলে তারা এমন স্ট্রোক ব্যবহার করতে পারে যা "ument" বা "nment" হিসেবে স্বীকৃত হতে পারে। প্রাক-প্রসঙ্গ "arg" উল্লেখ করলে অস্পষ্টতা দূর হয়, কারণ "argument" শব্দটি "argment" এর চেয়ে বেশি সম্ভাব্য।

প্রি-কন্টেক্সট রিকগনাইজারকে শব্দের বিরতি, শব্দের মধ্যে ফাঁকা স্থান সনাক্ত করতেও সাহায্য করতে পারে। আপনি একটি স্পেস অক্ষর টাইপ করতে পারেন কিন্তু আপনি একটি আঁকতে পারবেন না, তাহলে একজন রিকগনাইজার কীভাবে নির্ধারণ করবেন যে একটি শব্দ কখন শেষ হবে এবং পরবর্তীটি কখন শুরু হবে? যদি ব্যবহারকারী ইতিমধ্যেই "হ্যালো" লিখে থাকেন এবং লিখিত শব্দ "ওয়ার্ল্ড" দিয়ে চালিয়ে যান, তাহলে প্রি-কন্টেক্সট ছাড়াই রিকগনাইজার "ওয়ার্ল্ড" স্ট্রিংটি ফেরত দেয়। তবে, যদি আপনি প্রি-কন্টেক্সট "হ্যালো" নির্দিষ্ট করেন, তাহলে মডেলটি "ওয়ার্ল্ড" স্ট্রিংটি একটি অগ্রণী স্থান সহ ফেরত দেবে, কারণ "হ্যালো ওয়ার্ল্ড" "হ্যালোওয়ার্ড" এর চেয়ে বেশি অর্থবহ।

আপনার সম্ভাব্য দীর্ঘতম প্রাক-প্রসঙ্গ স্ট্রিং প্রদান করা উচিত, স্পেস সহ সর্বোচ্চ ২০টি অক্ষর পর্যন্ত। যদি স্ট্রিংটি দীর্ঘ হয়, তাহলে শনাক্তকারী শুধুমাত্র শেষ ২০টি অক্ষর ব্যবহার করবে।

নিচের কোড নমুনাটি দেখায় কিভাবে একটি লেখার ক্ষেত্র সংজ্ঞায়িত করতে হয় এবং প্রাক-প্রসঙ্গ নির্দিষ্ট করতে একটি RecognitionContext অবজেক্ট ব্যবহার করতে হয়।

কোটলিন

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)

জাভা

String preContext = ...;
float width = ...;
float height = ...;
RecognitionContext recognitionContext =
    RecognitionContext.builder()
                      .setPreContext(preContext)
                      .setWritingArea(new WritingArea(width, height))
                      .build();

recognizer.recognize(ink, recognitionContext);

স্ট্রোক অর্ডারিং

শনাক্তকরণের নির্ভুলতা স্ট্রোকের ক্রম সম্পর্কে সংবেদনশীল। শনাক্তকারীরা আশা করেন যে স্ট্রোকগুলি মানুষ স্বাভাবিকভাবেই যে ক্রমে লিখবে সেই ক্রমেই ঘটবে; উদাহরণস্বরূপ ইংরেজিতে বাম থেকে ডানে। এই প্যাটার্ন থেকে ভিন্ন যেকোনো ক্ষেত্রে, যেমন শেষ শব্দ দিয়ে শুরু হওয়া একটি ইংরেজি বাক্য লেখা, কম সঠিক ফলাফল দেয়।

আরেকটি উদাহরণ হল যখন একটি Ink মাঝখানে থাকা একটি শব্দ সরিয়ে অন্য একটি শব্দ দিয়ে প্রতিস্থাপন করা হয়। সংশোধনটি সম্ভবত একটি বাক্যের মাঝখানে থাকে, তবে সংশোধনের স্ট্রোকগুলি স্ট্রোক ক্রমের শেষে থাকে। এই ক্ষেত্রে আমরা নতুন লেখা শব্দটি আলাদাভাবে API-তে পাঠানোর এবং আপনার নিজস্ব যুক্তি ব্যবহার করে পূর্ববর্তী স্বীকৃতির সাথে ফলাফলটি একত্রিত করার পরামর্শ দিচ্ছি।

অস্পষ্ট আকারের সাথে মোকাবিলা করা

এমন কিছু ক্ষেত্রে আছে যেখানে শনাক্তকারীকে দেওয়া আকৃতির অর্থ অস্পষ্ট। উদাহরণস্বরূপ, খুব গোলাকার প্রান্তবিশিষ্ট একটি আয়তক্ষেত্রকে আয়তক্ষেত্র অথবা উপবৃত্ত হিসেবে দেখা যেতে পারে।

এই অস্পষ্ট কেসগুলি যখন উপলব্ধ থাকে তখন স্বীকৃতি স্কোর ব্যবহার করে পরিচালনা করা যেতে পারে। শুধুমাত্র আকৃতি শ্রেণীবদ্ধকারীরা স্কোর প্রদান করে। যদি মডেলটি খুব আত্মবিশ্বাসী হয়, তাহলে শীর্ষ ফলাফলের স্কোর দ্বিতীয় সেরার চেয়ে অনেক ভালো হবে। যদি অনিশ্চয়তা থাকে, তাহলে শীর্ষ দুটি ফলাফলের স্কোর কাছাকাছি হবে। এছাড়াও, মনে রাখবেন যে আকৃতি শ্রেণীবদ্ধকারীরা পুরো Ink একটি একক আকৃতি হিসাবে ব্যাখ্যা করে। উদাহরণস্বরূপ, যদি Ink একটি আয়তক্ষেত্র এবং একে অপরের পাশে একটি উপবৃত্ত থাকে, তাহলে স্বীকৃতিকারী একটি বা অন্যটি (অথবা সম্পূর্ণ ভিন্ন কিছু) ফেরত দিতে পারে, কারণ একটি একক স্বীকৃতি প্রার্থী দুটি আকার উপস্থাপন করতে পারে না।