ML Kit-এর ডিজিটাল কালি শনাক্তকরণের মাধ্যমে, আপনি শত শত ভাষায় ডিজিটাল পৃষ্ঠে হাতে লেখা টেক্সট চিনতে পারবেন, সেইসাথে স্কেচ শ্রেণীবদ্ধ করতে পারবেন।
চেষ্টা করে দেখুন
- এই API এর একটি উদাহরণ ব্যবহার দেখতে নমুনা অ্যাপের সাথে খেলুন।
আপনি শুরু করার আগে
আপনার পডফাইলে নিম্নলিখিত এমএল কিট লাইব্রেরিগুলি অন্তর্ভুক্ত করুন:
pod 'GoogleMLKit/DigitalInkRecognition', '15.5.0'
আপনি আপনার প্রোজেক্টের পড ইনস্টল বা আপডেট করার পরে, এটির
.xcworkspace
ব্যবহার করে আপনার Xcode প্রকল্পটি খুলুন। ML কিট 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() }
উদ্দেশ্য-C
// 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
এর একটি উদাহরণ পান
স্বীকৃতি সঞ্চালন করতে আমাদের একটি DigitalInkRecognizer
উদাহরণে Ink
অবজেক্টটি পাস করতে হবে। DigitalInkRecognizer
ইনস্ট্যান্স পেতে, আমাদের প্রথমে কাঙ্খিত ভাষার জন্য সনাক্তকারী মডেলটি ডাউনলোড করতে হবে এবং মডেলটিকে RAM এ লোড করতে হবে। এটি নিম্নলিখিত কোড স্নিপেট ব্যবহার করে সম্পন্ন করা যেতে পারে, যা সরলতার জন্য viewDidLoad()
পদ্ধতিতে স্থাপন করা হয় এবং একটি হার্ডকোড করা ভাষার নাম ব্যবহার করে। কিভাবে ব্যবহারকারীকে উপলব্ধ ভাষার তালিকা দেখাতে হয় এবং নির্বাচিত ভাষা ডাউনলোড করতে হয় তার উদাহরণের জন্য দ্রুতস্টার্ট অ্যাপটি দেখুন।
সুইফট
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) }
উদ্দেশ্য-C
- (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) } ) }
উদ্দেশ্য-C
- (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)
উদ্দেশ্য-C
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."); }) }
উদ্দেশ্য-C
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" প্রায়ই একে অপরের জন্য ভুল হয়। ব্যবহারকারী যদি ইতিমধ্যেই আংশিক শব্দ "আর্গ" প্রবেশ করে থাকে, তাহলে তারা স্ট্রোক চালিয়ে যেতে পারে যা "ument" বা "nment" হিসাবে স্বীকৃত হতে পারে। প্রাক-প্রসঙ্গ "আর্গ" নির্দিষ্ট করা অস্পষ্টতার সমাধান করে, যেহেতু "আর্গমেন্ট" শব্দটি "আর্গনমেন্ট" এর চেয়ে বেশি সম্ভাবনাময়।
প্রাক-প্রসঙ্গ শনাক্তকারীকে শব্দের বিরতি, শব্দের মধ্যে ফাঁকা স্থান সনাক্ত করতেও সাহায্য করতে পারে। আপনি একটি স্পেস অক্ষর টাইপ করতে পারেন কিন্তু আপনি একটি আঁকতে পারবেন না, তাহলে একজন শনাক্তকারী কীভাবে নির্ধারণ করতে পারে কখন একটি শব্দ শেষ হয় এবং পরেরটি শুরু হয়? যদি ব্যবহারকারী ইতিমধ্যেই "হ্যালো" লিখে থাকেন এবং লিখিত শব্দ "বিশ্ব" দিয়ে চালিয়ে যান, তবে প্রাক-প্রসঙ্গ ছাড়াই শনাক্তকারী স্ট্রিং "বিশ্ব" ফেরত দেয়। যাইহোক, আপনি যদি প্রাক-প্রসঙ্গ "হ্যালো" নির্দিষ্ট করেন, তাহলে মডেলটি একটি লিডিং স্পেস সহ "ওয়ার্ল্ড" স্ট্রিংটি ফিরিয়ে দেবে, যেহেতু "হ্যালো ওয়ার্ল্ড" "হ্যালোওয়ার্ড" এর চেয়ে বেশি অর্থবোধ করে।
আপনার স্পেস সহ 20টি অক্ষর পর্যন্ত সম্ভাব্য দীর্ঘতম প্রাক-প্রসঙ্গ স্ট্রিং প্রদান করা উচিত। স্ট্রিং দীর্ঘ হলে, সনাক্তকারী শুধুমাত্র শেষ 20টি অক্ষর ব্যবহার করে।
নীচের কোড নমুনা দেখায় কিভাবে একটি লেখার এলাকা সংজ্ঞায়িত করতে হয় এবং প্রাক-প্রসঙ্গ নির্দিষ্ট করতে একটি 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)") } })
উদ্দেশ্য-C
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
একটি আয়তক্ষেত্র এবং একে অপরের পাশে একটি উপবৃত্ত থাকে, তাহলে সনাক্তকারী একটি বা অন্যটি (বা সম্পূর্ণ ভিন্ন কিছু) ফলাফল হিসাবে ফিরিয়ে দিতে পারে, যেহেতু একটি একক স্বীকৃতি প্রার্থী দুটি আকার উপস্থাপন করতে পারে না।
ML Kit-এর ডিজিটাল কালি শনাক্তকরণের মাধ্যমে, আপনি শত শত ভাষায় ডিজিটাল পৃষ্ঠে হাতে লেখা টেক্সট চিনতে পারবেন, সেইসাথে স্কেচ শ্রেণীবদ্ধ করতে পারবেন।
চেষ্টা করে দেখুন
- এই API এর একটি উদাহরণ ব্যবহার দেখতে নমুনা অ্যাপের সাথে খেলুন।
আপনি শুরু করার আগে
আপনার পডফাইলে নিম্নলিখিত এমএল কিট লাইব্রেরিগুলি অন্তর্ভুক্ত করুন:
pod 'GoogleMLKit/DigitalInkRecognition', '15.5.0'
আপনি আপনার প্রোজেক্টের পড ইনস্টল বা আপডেট করার পরে, এটির
.xcworkspace
ব্যবহার করে আপনার Xcode প্রকল্পটি খুলুন। ML কিট 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() }
উদ্দেশ্য-C
// 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
এর একটি উদাহরণ পান
স্বীকৃতি সঞ্চালন করতে আমাদের একটি DigitalInkRecognizer
উদাহরণে Ink
অবজেক্টটি পাস করতে হবে। DigitalInkRecognizer
ইনস্ট্যান্স পেতে, আমাদের প্রথমে কাঙ্খিত ভাষার জন্য সনাক্তকারী মডেলটি ডাউনলোড করতে হবে এবং মডেলটিকে RAM এ লোড করতে হবে। এটি নিম্নলিখিত কোড স্নিপেট ব্যবহার করে সম্পন্ন করা যেতে পারে, যা সরলতার জন্য viewDidLoad()
পদ্ধতিতে স্থাপন করা হয় এবং একটি হার্ডকোড করা ভাষার নাম ব্যবহার করে। কিভাবে ব্যবহারকারীকে উপলব্ধ ভাষার তালিকা দেখাতে হয় এবং নির্বাচিত ভাষা ডাউনলোড করতে হয় তার উদাহরণের জন্য দ্রুতস্টার্ট অ্যাপটি দেখুন।
সুইফট
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) }
উদ্দেশ্য-C
- (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) } ) }
উদ্দেশ্য-C
- (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)
উদ্দেশ্য-C
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."); }) }
উদ্দেশ্য-C
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" প্রায়ই একে অপরের জন্য ভুল হয়। ব্যবহারকারী যদি ইতিমধ্যেই আংশিক শব্দ "আর্গ" প্রবেশ করে থাকে, তাহলে তারা স্ট্রোক চালিয়ে যেতে পারে যা "ument" বা "nment" হিসাবে স্বীকৃত হতে পারে। প্রাক-প্রসঙ্গ "আর্গ" নির্দিষ্ট করা অস্পষ্টতার সমাধান করে, যেহেতু "আর্গমেন্ট" শব্দটি "আর্গনমেন্ট" এর চেয়ে বেশি সম্ভাবনাময়।
প্রাক-প্রসঙ্গ শনাক্তকারীকে শব্দের বিরতি, শব্দের মধ্যে ফাঁকা স্থান সনাক্ত করতেও সাহায্য করতে পারে। আপনি একটি স্পেস অক্ষর টাইপ করতে পারেন কিন্তু আপনি একটি আঁকতে পারবেন না, তাহলে একজন শনাক্তকারী কীভাবে নির্ধারণ করতে পারে কখন একটি শব্দ শেষ হয় এবং পরেরটি শুরু হয়? যদি ব্যবহারকারী ইতিমধ্যেই "হ্যালো" লিখে থাকেন এবং লিখিত শব্দ "বিশ্ব" দিয়ে চালিয়ে যান, তবে প্রাক-প্রসঙ্গ ছাড়াই শনাক্তকারী স্ট্রিং "বিশ্ব" ফেরত দেয়। যাইহোক, আপনি যদি প্রাক-প্রসঙ্গ "হ্যালো" নির্দিষ্ট করেন, তাহলে মডেলটি একটি লিডিং স্পেস সহ "ওয়ার্ল্ড" স্ট্রিংটি ফিরিয়ে দেবে, যেহেতু "হ্যালো ওয়ার্ল্ড" "হ্যালোওয়ার্ড" এর চেয়ে বেশি অর্থবোধ করে।
আপনার স্পেস সহ 20টি অক্ষর পর্যন্ত সম্ভাব্য দীর্ঘতম প্রাক-প্রসঙ্গ স্ট্রিং প্রদান করা উচিত। স্ট্রিং দীর্ঘ হলে, সনাক্তকারী শুধুমাত্র শেষ 20টি অক্ষর ব্যবহার করে।
নীচের কোড নমুনা দেখায় কিভাবে একটি লেখার এলাকা সংজ্ঞায়িত করতে হয় এবং প্রাক-প্রসঙ্গ নির্দিষ্ট করতে একটি 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)") } })
উদ্দেশ্য-C
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
একটি আয়তক্ষেত্র এবং একে অপরের পাশে একটি উপবৃত্ত থাকে, তাহলে সনাক্তকারী একটি বা অন্যটি (বা সম্পূর্ণ ভিন্ন কিছু) ফলাফল হিসাবে ফিরিয়ে দিতে পারে, যেহেতু একটি একক স্বীকৃতি প্রার্থী দুটি আকার উপস্থাপন করতে পারে না।