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

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

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

শুরু করার আগে

  1. আপনার পডফাইলে নিম্নলিখিত ML কিট লাইব্রেরিগুলি অন্তর্ভুক্ত করুন:

    pod 'GoogleMLKit/DigitalInkRecognition', '8.0.0'
    
    
  2. আপনার প্রোজেক্টের পড ইনস্টল বা আপডেট করার পর, .xcworkspace ব্যবহার করে আপনার Xcode প্রোজেক্টটি খুলুন। ML Kit Xcode সংস্করণ 13.2.1 বা তার পরবর্তী সংস্করণে সমর্থিত।

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

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

একটি Ink অবজেক্ট তৈরির প্রধান উপায় হল এটিকে টাচ স্ক্রিনে আঁকা। iOS-এ, আপনি একটি UIImageView ব্যবহার করতে পারেন টাচ ইভেন্ট হ্যান্ডলারের সাথে যা স্ক্রিনে স্ট্রোক আঁকে এবং স্ট্রোকের পয়েন্টগুলিও সংরক্ষণ করে Ink অবজেক্ট তৈরি করে। এই সাধারণ প্যাটার্নটি নিম্নলিখিত কোড স্নিপেটে প্রদর্শিত হয়েছে। আরও সম্পূর্ণ উদাহরণের জন্য কুইকস্টার্ট অ্যাপটি দেখুন, যা টাচ ইভেন্ট হ্যান্ডলিং, স্ক্রিন অঙ্কন এবং স্ট্রোক ডেটা ম্যানেজমেন্টকে পৃথক করে।

সুইফট

@IBOutlet weak var mainImageView: UIImageView!
var kMillisecondsPerTimeInterval = 1000.0
var lastPoint = CGPoint.zero
private var strokes: [Stroke] = []
private var points: [StrokePoint] = []

func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) {
  UIGraphicsBeginImageContext(view.frame.size)
  guard let context = UIGraphicsGetCurrentContext() else {
    return
  }
  mainImageView.image?.draw(in: view.bounds)
  context.move(to: fromPoint)
  context.addLine(to: toPoint)
  context.setLineCap(.round)
  context.setBlendMode(.normal)
  context.setLineWidth(10.0)
  context.setStrokeColor(UIColor.white.cgColor)
  context.strokePath()
  mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  mainImageView.alpha = 1.0
  UIGraphicsEndImageContext()
}

override func touchesBegan(_ touches: Set, with event: UIEvent?) {
  guard let touch = touches.first else {
    return
  }
  lastPoint = touch.location(in: mainImageView)
  let t = touch.timestamp
  points = [StrokePoint.init(x: Float(lastPoint.x),
                             y: Float(lastPoint.y),
                             t: Int(t * kMillisecondsPerTimeInterval))]
  drawLine(from:lastPoint, to:lastPoint)
}

override func touchesMoved(_ touches: Set, with event: UIEvent?) {
  guard let touch = touches.first else {
    return
  }
  let currentPoint = touch.location(in: mainImageView)
  let t = touch.timestamp
  points.append(StrokePoint.init(x: Float(currentPoint.x),
                                 y: Float(currentPoint.y),
                                 t: Int(t * kMillisecondsPerTimeInterval)))
  drawLine(from: lastPoint, to: currentPoint)
  lastPoint = currentPoint
}

override func touchesEnded(_ touches: Set, with event: UIEvent?) {
  guard let touch = touches.first else {
    return
  }
  let currentPoint = touch.location(in: mainImageView)
  let t = touch.timestamp
  points.append(StrokePoint.init(x: Float(currentPoint.x),
                                 y: Float(currentPoint.y),
                                 t: Int(t * kMillisecondsPerTimeInterval)))
  drawLine(from: lastPoint, to: currentPoint)
  lastPoint = currentPoint
  strokes.append(Stroke.init(points: points))
  self.points = []
  doRecognition()
}

অবজেক্টিভ-সি

// Interface
@property (weak, nonatomic) IBOutlet UIImageView *mainImageView;
@property(nonatomic) CGPoint lastPoint;
@property(nonatomic) NSMutableArray *strokes;
@property(nonatomic) NSMutableArray *points;

// Implementations
static const double kMillisecondsPerTimeInterval = 1000.0;

- (void)drawLineFrom:(CGPoint)fromPoint to:(CGPoint)toPoint {
  UIGraphicsBeginImageContext(self.mainImageView.frame.size);
  [self.mainImageView.image drawInRect:CGRectMake(0, 0, self.mainImageView.frame.size.width,
                                                  self.mainImageView.frame.size.height)];
  CGContextMoveToPoint(UIGraphicsGetCurrentContext(), fromPoint.x, fromPoint.y);
  CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), toPoint.x, toPoint.y);
  CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
  CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 10.0);
  CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1, 1, 1, 1);
  CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
  CGContextStrokePath(UIGraphicsGetCurrentContext());
  CGContextFlush(UIGraphicsGetCurrentContext());
  self.mainImageView.image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
}

- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event {
  UITouch *touch = [touches anyObject];
  self.lastPoint = [touch locationInView:self.mainImageView];
  NSTimeInterval time = [touch timestamp];
  self.points = [NSMutableArray array];
  [self.points addObject:[[MLKStrokePoint alloc] initWithX:self.lastPoint.x
                                                         y:self.lastPoint.y
                                                         t:time * kMillisecondsPerTimeInterval]];
  [self drawLineFrom:self.lastPoint to:self.lastPoint];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event {
  UITouch *touch = [touches anyObject];
  CGPoint currentPoint = [touch locationInView:self.mainImageView];
  NSTimeInterval time = [touch timestamp];
  [self.points addObject:[[MLKStrokePoint alloc] initWithX:currentPoint.x
                                                         y:currentPoint.y
                                                         t:time * kMillisecondsPerTimeInterval]];
  [self drawLineFrom:self.lastPoint to:currentPoint];
  self.lastPoint = currentPoint;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event {
  UITouch *touch = [touches anyObject];
  CGPoint currentPoint = [touch locationInView:self.mainImageView];
  NSTimeInterval time = [touch timestamp];
  [self.points addObject:[[MLKStrokePoint alloc] initWithX:currentPoint.x
                                                         y:currentPoint.y
                                                         t:time * kMillisecondsPerTimeInterval]];
  [self drawLineFrom:self.lastPoint to:currentPoint];
  self.lastPoint = currentPoint;
  if (self.strokes == nil) {
    self.strokes = [NSMutableArray array];
  }
  [self.strokes addObject:[[MLKStroke alloc] initWithPoints:self.points]];
  self.points = nil;
  [self doRecognition];
}

মনে রাখবেন যে কোড স্নিপেটে UIImageView- এ স্ট্রোক আঁকার জন্য একটি নমুনা ফাংশন রয়েছে, যা আপনার অ্যাপ্লিকেশনের জন্য প্রয়োজন অনুসারে অভিযোজিত করা উচিত। আমরা লাইন সেগমেন্টগুলি আঁকার সময় রাউন্ডক্যাপ ব্যবহার করার পরামর্শ দিচ্ছি যাতে শূন্য দৈর্ঘ্যের সেগমেন্টগুলি একটি বিন্দু হিসাবে আঁকা হয় (ছোট হাতের অক্ষর i-এর বিন্দুটি মনে করুন)। প্রতিটি স্ট্রোক লেখার পরে doRecognition() ফাংশনটি কল করা হয় এবং নীচে সংজ্ঞায়িত করা হবে।

DigitalInkRecognizer এর একটি উদাহরণ পান

স্বীকৃতি প্রদানের জন্য আমাদের Ink অবজেক্টটি একটি DigitalInkRecognizer ইনস্ট্যান্সে পাস করতে হবে। DigitalInkRecognizer ইনস্ট্যান্স পেতে, আমাদের প্রথমে পছন্দসই ভাষার জন্য স্বীকৃতি মডেলটি ডাউনলোড করতে হবে এবং মডেলটি RAM-এ লোড করতে হবে। এটি নিম্নলিখিত কোড স্নিপেট ব্যবহার করে সম্পন্ন করা যেতে পারে, যা সরলতার জন্য viewDidLoad() পদ্ধতিতে স্থাপন করা হয়েছে এবং একটি হার্ডকোডেড ভাষার নাম ব্যবহার করে। ব্যবহারকারীকে উপলব্ধ ভাষার তালিকা কীভাবে দেখানো যায় এবং নির্বাচিত ভাষাটি কীভাবে ডাউনলোড করা যায় তার একটি উদাহরণের জন্য quickstart অ্যাপটি দেখুন।

সুইফট

override func viewDidLoad() {
  super.viewDidLoad()
  let languageTag = "en-US"
  let identifier = DigitalInkRecognitionModelIdentifier(forLanguageTag: languageTag)
  if identifier == nil {
    // no model was found or the language tag couldn't be parsed, handle error.
  }
  let model = DigitalInkRecognitionModel.init(modelIdentifier: identifier!)
  let modelManager = ModelManager.modelManager()
  let conditions = ModelDownloadConditions.init(allowsCellularAccess: true,
                                         allowsBackgroundDownloading: true)
  modelManager.download(model, conditions: conditions)
  // Get a recognizer for the language
  let options: DigitalInkRecognizerOptions = DigitalInkRecognizerOptions.init(model: model)
  recognizer = DigitalInkRecognizer.digitalInkRecognizer(options: options)
}

অবজেক্টিভ-সি

- (void)viewDidLoad {
  [super viewDidLoad];
  NSString *languagetag = @"en-US";
  MLKDigitalInkRecognitionModelIdentifier *identifier =
      [MLKDigitalInkRecognitionModelIdentifier modelIdentifierForLanguageTag:languagetag];
  if (identifier == nil) {
    // no model was found or the language tag couldn't be parsed, handle error.
  }
  MLKDigitalInkRecognitionModel *model = [[MLKDigitalInkRecognitionModel alloc]
                                          initWithModelIdentifier:identifier];
  MLKModelManager *modelManager = [MLKModelManager modelManager];
  [modelManager downloadModel:model conditions:[[MLKModelDownloadConditions alloc]
                                                initWithAllowsCellularAccess:YES
                                                allowsBackgroundDownloading:YES]];
  MLKDigitalInkRecognizerOptions *options =
      [[MLKDigitalInkRecognizerOptions alloc] initWithModel:model];
  self.recognizer = [MLKDigitalInkRecognizer digitalInkRecognizerWithOptions:options];
}

কুইকস্টার্ট অ্যাপগুলিতে অতিরিক্ত কোড অন্তর্ভুক্ত থাকে যা দেখায় যে কীভাবে একই সময়ে একাধিক ডাউনলোড পরিচালনা করতে হয় এবং সমাপ্তির বিজ্ঞপ্তিগুলি পরিচালনা করে কোন ডাউনলোড সফল হয়েছে তা কীভাবে নির্ধারণ করতে হয়।

একটি Ink বস্তু চিনুন

এরপর আমরা doRecognition() ফাংশনে আসি, যা সরলতার জন্য touchesEnded() থেকে ডাকা হয়। অন্যান্য অ্যাপ্লিকেশনগুলিতে কেউ হয়তো টাইমআউট হওয়ার পরে অথবা ব্যবহারকারী যখন রিকগনিশন ট্রিগার করার জন্য একটি বোতাম টিপে তখনই রিকগনিশন চালু করতে চাইতে পারে।

সুইফট

func doRecognition() {
  let ink = Ink.init(strokes: strokes)
  recognizer.recognize(
    ink: ink,
    completion: {
      [unowned self]
      (result: DigitalInkRecognitionResult?, error: Error?) in
      var alertTitle = ""
      var alertText = ""
      if let result = result, let candidate = result.candidates.first {
        alertTitle = "I recognized this:"
        alertText = candidate.text
      } else {
        alertTitle = "I hit an error:"
        alertText = error!.localizedDescription
      }
      let alert = UIAlertController(title: alertTitle,
                                  message: alertText,
                           preferredStyle: UIAlertController.Style.alert)
      alert.addAction(UIAlertAction(title: "OK",
                                    style: UIAlertAction.Style.default,
                                  handler: nil))
      self.present(alert, animated: true, completion: nil)
    }
  )
}

অবজেক্টিভ-সি

- (void)doRecognition {
  MLKInk *ink = [[MLKInk alloc] initWithStrokes:self.strokes];
  __weak typeof(self) weakSelf = self;
  [self.recognizer
      recognizeInk:ink
        completion:^(MLKDigitalInkRecognitionResult *_Nullable result,
                     NSError *_Nullable error) {
    typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf == nil) {
      return;
    }
    NSString *alertTitle = nil;
    NSString *alertText = nil;
    if (result.candidates.count > 0) {
      alertTitle = @"I recognized this:";
      alertText = result.candidates[0].text;
    } else {
      alertTitle = @"I hit an error:";
      alertText = [error localizedDescription];
    }
    UIAlertController *alert =
        [UIAlertController alertControllerWithTitle:alertTitle
                                            message:alertText
                                     preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"OK"
                                              style:UIAlertActionStyleDefault
                                            handler:nil]];
    [strongSelf presentViewController:alert animated:YES completion:nil];
  }];
}

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

আমরা ইতিমধ্যেই দেখেছি কিভাবে একটি স্বীকৃতি মডেল ডাউনলোড করতে হয়। নিম্নলিখিত কোড স্নিপেটগুলি দেখায় যে কীভাবে একটি মডেল ইতিমধ্যেই ডাউনলোড করা হয়েছে কিনা তা পরীক্ষা করা যায়, অথবা যখন স্টোরেজ স্পেস পুনরুদ্ধারের আর প্রয়োজন হয় না তখন একটি মডেল মুছে ফেলা যায়।

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

সুইফট

let model : DigitalInkRecognitionModel = ...
let modelManager = ModelManager.modelManager()
modelManager.isModelDownloaded(model)

অবজেক্টিভ-সি

MLKDigitalInkRecognitionModel *model = ...;
MLKModelManager *modelManager = [MLKModelManager modelManager];
[modelManager isModelDownloaded:model];

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

সুইফট

let model : DigitalInkRecognitionModel = ...
let modelManager = ModelManager.modelManager()

if modelManager.isModelDownloaded(model) {
  modelManager.deleteDownloadedModel(
    model!,
    completion: {
      error in
      if error != nil {
        // Handle error
        return
      }
      NSLog(@"Model deleted.");
    })
}

অবজেক্টিভ-সি

MLKDigitalInkRecognitionModel *model = ...;
MLKModelManager *modelManager = [MLKModelManager modelManager];

if ([self.modelManager isModelDownloaded:model]) {
  [self.modelManager deleteDownloadedModel:model
                                completion:^(NSError *_Nullable error) {
                                  if (error) {
                                    // Handle error.
                                    return;
                                  }
                                  NSLog(@"Model deleted.");
                                }];
}

টেক্সট শনাক্তকরণের নির্ভুলতা উন্নত করার টিপস

টেক্সট শনাক্তকরণের নির্ভুলতা বিভিন্ন ভাষায় ভিন্ন হতে পারে। নির্ভুলতা লেখার ধরণের উপরও নির্ভর করে। যদিও ডিজিটাল ইঙ্ক শনাক্তকরণ বিভিন্ন ধরণের লেখার ধরণ পরিচালনা করার জন্য প্রশিক্ষিত, ফলাফল ব্যবহারকারী থেকে ব্যবহারকারীতে ভিন্ন হতে পারে।

টেক্সট রিকগনাইজারের নির্ভুলতা উন্নত করার কিছু উপায় এখানে দেওয়া হল। মনে রাখবেন যে এই কৌশলগুলি ইমোজি, অটোড্র এবং শেপের জন্য অঙ্কন শ্রেণীবিভাগের ক্ষেত্রে প্রযোজ্য নয়।

লেখার ক্ষেত্র

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

রিকগনিনারকে লেখার জায়গার প্রস্থ এবং উচ্চতা জানালে সঠিকতা বৃদ্ধি পেতে পারে। তবে, রিকগনিনার ধরে নেন যে লেখার জায়গাটিতে কেবল একটি লাইন লেখা আছে। যদি ভৌত ​​লেখার জায়গাটি ব্যবহারকারীকে দুই বা ততোধিক লাইন লেখার সুযোগ করে দেওয়ার জন্য যথেষ্ট বড় হয়, তাহলে আপনি একটি WritingArea উচ্চতা দিয়ে ভালো ফলাফল পেতে পারেন যা আপনার একক লাইনের টেক্সটের উচ্চতার সর্বোত্তম অনুমান। আপনি যে WritingArea অবজেক্টটি রিকগনিনারকে দেবেন তা স্ক্রিনের ভৌত লেখার জায়গার সাথে হুবহু মিলতে হবে না। এইভাবে WritingArea উচ্চতা পরিবর্তন করা কিছু ভাষায় অন্যদের তুলনায় ভালো কাজ করে।

লেখার ক্ষেত্রটি নির্দিষ্ট করার সময়, স্ট্রোক স্থানাঙ্কের মতো একই এককের মধ্যে এর প্রস্থ এবং উচ্চতা নির্দিষ্ট করুন। x,y স্থানাঙ্ক আর্গুমেন্টের কোনও ইউনিটের প্রয়োজন নেই - API সমস্ত ইউনিটকে স্বাভাবিক করে তোলে, তাই একমাত্র গুরুত্বপূর্ণ বিষয় হল স্ট্রোকের আপেক্ষিক আকার এবং অবস্থান। আপনার সিস্টেমের জন্য যে স্কেলেই যুক্তিসঙ্গত হোক না কেন, আপনি স্থানাঙ্কগুলি পাস করতে স্বাধীন।

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

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

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

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

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

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

সুইফট

let ink: Ink = ...;
let recognizer: DigitalInkRecognizer =  ...;
let preContext: String = ...;
let writingArea = WritingArea.init(width: ..., height: ...);

let context: DigitalInkRecognitionContext.init(
    preContext: preContext,
    writingArea: writingArea);

recognizer.recognizeHandwriting(
  from: ink,
  context: context,
  completion: {
    (result: DigitalInkRecognitionResult?, error: Error?) in
    if let result = result, let candidate = result.candidates.first {
      NSLog("Recognized \(candidate.text)")
    } else {
      NSLog("Recognition error \(error)")
    }
  })

অবজেক্টিভ-সি

MLKInk *ink = ...;
MLKDigitalInkRecognizer *recognizer = ...;
NSString *preContext = ...;
MLKWritingArea *writingArea = [MLKWritingArea initWithWidth:...
                                              height:...];

MLKDigitalInkRecognitionContext *context = [MLKDigitalInkRecognitionContext
       initWithPreContext:preContext
       writingArea:writingArea];

[recognizer recognizeHandwritingFromInk:ink
            context:context
            completion:^(MLKDigitalInkRecognitionResult
                         *_Nullable result, NSError *_Nullable error) {
                               NSLog(@"Recognition result %@",
                                     result.candidates[0].text);
                         }];

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

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

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

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

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

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