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

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

চেষ্টা করে দেখুন

আপনি শুরু করার আগে

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

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

একটি Ink বস্তু তৈরি করুন

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

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

কোটলিন

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 এর একটি উদাহরণ পান

স্বীকৃতি সঞ্চালন করতে, একটি DigitalInkRecognizer অবজেক্টে Ink ইনস্ট্যান্স পাঠান। নীচের কোডটি দেখায় যে কীভাবে একটি 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" প্রায়ই একে অপরের জন্য ভুল হয়। ব্যবহারকারী যদি ইতিমধ্যেই আংশিক শব্দ "আর্গ" প্রবেশ করে থাকে, তাহলে তারা স্ট্রোক চালিয়ে যেতে পারে যা "ument" বা "nment" হিসাবে স্বীকৃত হতে পারে। প্রাক-প্রসঙ্গ "আর্গ" নির্দিষ্ট করা অস্পষ্টতার সমাধান করে, যেহেতু "আর্গমেন্ট" শব্দটি "আর্গনমেন্ট" এর চেয়ে বেশি সম্ভাবনাময়।

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

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

নীচের কোড নমুনা দেখায় কিভাবে একটি লেখার এলাকা সংজ্ঞায়িত করতে হয় এবং প্রাক-প্রসঙ্গ নির্দিষ্ট করতে একটি 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 একটি আয়তক্ষেত্র এবং একে অপরের পাশে একটি উপবৃত্ত থাকে, তাহলে সনাক্তকারী একটি বা অন্যটি (বা সম্পূর্ণ ভিন্ন কিছু) ফলাফল হিসাবে ফিরিয়ে দিতে পারে, যেহেতু একটি একক স্বীকৃতি প্রার্থী দুটি আকার উপস্থাপন করতে পারে না।