ML Kit'in dijital mürekkep tanıma özelliğiyle tek bir cihazda el yazısıyla yazılmış metinleri yüzlerce dilde dijital yüzeyler oluşturabilir ve çizimleri sınıflandırabilir.
Deneyin
- Örnek uygulamayı kullanarak bu API'nin örnek kullanımını inceleyin.
Başlamadan önce
Aşağıdaki ML Kit kitaplıklarını Podfile'ınıza ekleyin:
pod 'GoogleMLKit/DigitalInkRecognition', '3.2.0'
Projenizin kapsüllerini yükledikten veya güncelledikten sonra Xcode projenizi açın (
.xcworkspace
) kullanılıyor. Makine Öğrenimi Kiti, Xcode sürümünde desteklenir 13.2.1 veya daha sonraki sürümler.
Artık Ink
nesnedeki metinleri tanımaya başlamak için hazırsınız.
Ink
nesnesi oluşturun
Bir Ink
nesnesi oluşturmanın ana yolu, onu dokunmatik ekranda çizmektir. iOS'te
birlikte bir UIImageView öğesini kullanabilirsiniz
dokunma etkinliği
işleyiciler
Çizgileri çizen ve aynı zamanda çizgilerin kurulacak puan
Ink
nesnesini ifade eder. Bu genel kalıp aşağıdaki kodda gösterilmiştir
snippet'i Hızlı başlangıç kılavuzuna göz atın
uygulamasını
ile dokunma etkinliği işlemeyi, ekran çizimini,
ve kulaç veri yönetimi.
Swift
@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() }
Objective-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]; }
Kod snippet'inin, çizgiyi içine çizebileceğiniz örnek bir işlev içerdiğini unutmayın.
UIImageView,
ve uygulamanız gereken şekilde uyarlanmalıdır. Önerilerimiz
çizgi segmentlerini sıfır uzunluktaki segmentler çizecek şekilde yuvarlak resim
bir nokta olarak çizilir (küçük i harfinin üzerindeki noktayı düşünün). doRecognition()
işlevi, her çizgi yazıldıktan sonra çağrılır ve aşağıda tanımlanacaktır.
DigitalInkRecognizer
örneği al
Tanımayı gerçekleştirmek için Ink
nesnesini bir
DigitalInkRecognizer
örneği. DigitalInkRecognizer
örneğini almak için
önce istenen dil için tanıyıcı modeli indirmemiz ve
modeli RAM'e yükler. Bu işlem, aşağıdaki kod kullanılarak yapılabilir.
snippet'i kullanır. Bu snippet, basitlik için viewDidLoad()
yöntemine yerleştirilir ve
dilinin adını yazın. Hızlı başlangıç kılavuzuna göz atın
uygulamasını
kullanılabilir dillerin listesini kullanıcıya gösterme ve indirme örneği
seçilen dili seçin.
Swift
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) }
Objective-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]; }
Hızlı başlangıç uygulamaları, birden fazla öğenin nasıl ele alınacağını gösteren ek kod içerir. aynı anda nasıl indirildiğini görebilir ve hangi indirme işleminin tamamlama bildirimlerini devre dışı bırakabilirsiniz.
Ink
nesnesini tanı
Şimdi doRecognition()
işlevine geliyor. Bu işlev, basitlik için
touchesEnded()
başlangıç fiyatıyla. Diğer uygulamalarda bir
zaman aşımına uğradıktan sonra ya da kullanıcı, Google Ad Manager'ı tetiklemek için
teşekkür ederiz.
Swift
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) } ) }
Objective-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]; }]; }
Model indirmelerini yönetme
Tanıma modelinin nasıl indirileceğini zaten görmüştük. Aşağıdaki kod snippet'leri, bir modelin önceden indirilip indirilmediğini veya simgesini tıklayın.
Bir modelin önceden indirilip indirilmediğini kontrol etme
Swift
let model : DigitalInkRecognitionModel = ... let modelManager = ModelManager.modelManager() modelManager.isModelDownloaded(model)
Objective-C
MLKDigitalInkRecognitionModel *model = ...; MLKModelManager *modelManager = [MLKModelManager modelManager]; [modelManager isModelDownloaded:model];
İndirilen bir modeli silme
Swift
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."); }) }
Objective-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."); }]; }
Metin tanıma doğruluğunu iyileştirmeye yönelik ipuçları
Metin tanımanın doğruluğu diller arasında farklılık gösterebilir. Doğruluk oranı şuna da bağlıdır: ele alacağız. Dijital Mürekkep Tanıma özelliği, pek çok yazma stilini işleyecek şekilde eğitilir. sonuç kullanıcıya göre değişebilir.
Metin tanıyıcının doğruluğunu artırmanın bazı yolları aşağıda verilmiştir. Bu tekniklerin emoji, otomatik çizim ve şekillere yönelik çizim sınıflandırıcılarında geçerli değildir.
Yazma alanı
Birçok uygulamanın, kullanıcı girişi için iyi tanımlanmış bir yazma alanı vardır. Bir simgenin anlamı boyutu, onu içeren yazı alanının boyutuna göre kısmen belirlenir. Örneğin, "o" harfi ile küçük veya büyük harf veya "c", virgül ve a eğik çizgi.
Tanıyıcıya, yazma alanının genişliğini ve yüksekliğini söylemek doğruluğu artırabilir. Ancak, tanıyıcı, yazma alanında yalnızca tek bir satırlık metin olduğunu varsayar. Fiziksel yazma alanı kullanıcının iki veya daha fazla satır yazmasına izin verecek kadar büyük olursa yüksekliğini en iyi tahmin ettiğiniz yükseklikle bir WriteArea'dan geçerek sonuçlar elde edebilirsiniz. tek satırlık metin kullanarak ekleyebilirsiniz. Tanıyıcıya ilettiğiniz WriteArea nesnesinin karşılık gelmesine gerek yok ekrandaki fiziksel yazı alanıyla aynı olur. WriteArea yüksekliğini bu şekilde değiştirme bazı dillerde daha iyi çalışır.
Yazma alanını belirlerken genişliğini ve yüksekliğini fırçayla aynı birimlerde belirtin koordinatlar. x, y koordinat bağımsız değişkenlerinin birim gereksinimleri yoktur; API, olduğundan, önemli olan tek şey çizgilerin göreceli boyutu ve konumudur. İsterseniz sisteminiz için uygun olan ölçekte koordinat verebilir.
Bağlam öncesi
Bağlam ön bilgisi, eklediğiniz Ink
içindeki fırçalardan hemen önce gelen metindir
birlikte çalışır. Tanıyıcıya bağlam öncesi hakkında bilgi vererek yardımcı olabilirsiniz.
Örneğin, "n" şeklinde el yazısı harfleri ve "u" genellikle birbiriyle karıştırılır. Kullanıcı "arg" kısmi kelimesini girmişlerse "ument" veya "nment" olur. Bağlam öncesi "arg" değerini belirtme muğlaklığı giderir çünkü "bağımsız değişken" "bağımsız değişken"den daha olasıdır.
Bağlam ön bilgisi, tanıyıcının kelime sonlarını, yani kelimeler arasındaki boşlukları belirlemesine de yardımcı olabilir. Şunları yapabilirsiniz: boşluk karakteri giriyor ama çizemiyorsun, o halde tanıyıcı bir kelimenin ne zaman sona erdiğini nasıl belirleyebilir bir sonraki başlıyor mu? Kullanıcı zaten "merhaba" yazdıysa ve şöyle devam eder: "world" değerini, bağlam öncesi olmadan tanıyıcı "world" dizesini döndürür. Ancak, bağlam öncesi "merhaba" ise model, " "merhaba"dan itibaren baştaki boşlukla dünya" "merhabakelime"den daha anlamlıdır.
Mümkün olan en uzun bağlam öncesi dizesini (en fazla 20 karakter dahil) sağlamanız gerekir. alanlar'a dokunun. Dize daha uzunsa tanıyıcı yalnızca son 20 karakteri kullanır.
Aşağıdaki kod örneğinde bir yazma alanının nasıl tanımlanacağı ve
RecognitionContext
nesnesini ifade eder.
Swift
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)") } })
Objective-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); }];
Çizgi sıralama
Tanıma doğruluğu kulaçların sırasına göre hassastır. Tanıyıcılar darbelerin kullanıcıların doğal olarak yazdıkları sırayla olmasıdır; örneğin İngilizce için soldan sağa. Tüm durumlar bu kalıptan ayrılırsa örneğin son kelimeyle başlayan İngilizce bir cümle yazın. doğruluk oranı daha düşük sonuçlar verir.
Başka bir örnek de Ink
kelimesinin ortasındaki bir kelime kaldırılıp
ekleyebilirsiniz. Revizyon muhtemelen bir cümlenin ortasındadır, ancak düzeltmedeki fırça darbeleri
çizgi dizisinin sonundadır.
Bu durumda, yeni yazılan kelimeyi API'ye ayrı olarak göndermenizi ve
ve kendi mantığınızı kullanarak
önceki tanımalardan geleceksiniz.
Belirsiz şekillerle başa çıkma
Tanıyıcıya sağlanan şeklin anlamının belirsiz olduğu durumlar vardır. Örneğin, Örneğin, kenarları çok yuvarlanmış bir dikdörtgen, dikdörtgen ya da elips olarak görülebilir.
Net olmayan bu destek kayıtları, mevcut olduğunda tanıma puanları kullanılarak ele alınabilir. Yalnızca
şekil sınıflandırıcılar puanlar sağlar. Model çok güveniyorsa en iyi sonucun puanı
ikinci en iyisinden çok daha iyi. Belirsizlik varsa ilk iki sonuca ait puanlar
yakın olun. Ayrıca, şekil sınıflandırıcılarının tüm Ink
öğesini bir
tek şekil. Örneğin, Ink
her birinin yanında bir dikdörtgen ve bir elips içeriyorsa,
tanıyıcı, bu uyarının diğerini (ya da tamamen farklı bir şeyi)
Çünkü tek bir tanıma adayı iki şekli temsil edemez.