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
- Bu API'nin örnek kullanımını görmek için örnek uygulamayı inceleyin.
Başlamadan önce
- Proje düzeyindeki
build.gradle
dosyanızda, Google'ın Maven deposunu hembuildscript
hem deallprojects
bölümlerinize eklediğinizden emin olun. - ML Kit Android kitaplıklarının bağımlılıklarını modülünüzün uygulama düzeyindeki Gradle dosyasına ekleyin. Bu dosya genellikle
app/build.gradle
:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}
Artık Ink
nesnedeki metinleri tanımaya başlamak için hazırsınız.
Ink
nesnesi oluşturma
Ink
nesnesi oluşturmanın en iyi yolu, dokunmatik ekranda çizmektir. Android'de bu amaçla tuval kullanabilirsiniz. Sizin
dokunma etkinliği işleyicileri
addNewTouchEvent()
yöntemi, fırça fırçalarındaki noktaları depolamak için aşağıdaki kod snippet'ini
Ink
nesnesini çeker.
Bu genel kalıp aşağıdaki kod snippet'inde gösterilmektedir. Bkz. ML Kit hızlı başlangıç örneği inceleyebilirsiniz.
Kotlin
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()
Java
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 örneği alma
Tanımayı gerçekleştirmek için Ink
örneğini bir DigitalInkRecognizer
nesnesine gönderin. Aşağıdaki kodda, bir BCP-47 etiketinden böyle bir tanımlayıcının nasıl oluşturulacağı gösterilmektedir.
Kotlin
// 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())
Java
// 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
nesnesini işleme
Kotlin
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") }
Java
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));
Yukarıdaki örnek kodda, tanıma modelinin sonraki bölümde açıklandığı gibi önceden indirilmiş olduğu varsayılır.
Model indirmelerini yönetme
Dijital mürekkep tanıma API'si yüzlerce dili desteklese de her dilin tanınmadan önce bazı verilerin indirilmesi gerekir. Dil başına yaklaşık 20 MB depolama alanı gerekir. Bu işlem
RemoteModelManager
nesne algılandı.
Yeni model indirin
Kotlin
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") }
Java
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));
Bir modelin daha önce indirilip indirilmediğini kontrol etme
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
İndirilen bir modeli silme
Bir modelin cihazın depolama alanından kaldırılması yer açar.
Kotlin
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));
Metin tanıma doğruluğunu iyileştirmeye yönelik ipuçları
Metin tanımanın doğruluğu farklı diller arasında değişiklik gösterebilir. Doğruluk, yazma stiline de bağlıdır. 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ımlayı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 sembolün anlamı kısmen, onu içeren yazı alanının boyutuna göre 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ükse tek bir metin satırının yüksekliğiyle ilgili en iyi tahmininizin yüksekliğine sahip bir WritingArea ileterek daha iyi sonuçlar elde edebilirsiniz. Tanımlayıcıya ilettiğiniz WritingArea nesnesinin ekrandaki fiziksel yazma alanına tam olarak karşılık gelmesi gerekmez. WritingArea yüksekliğinin bu şekilde değiştirilmesi bazı dillerde diğerlerinden daha iyi sonuç verir.
Yazma alanını belirlerken genişliğini ve yüksekliğini fırçayla aynı birimlerde belirtin koordinatlarıyla birlikte çalışır. x, y koordinatı bağımsız değişkenleri için birim şartı yoktur. API tüm birimleri normalleştirir. Bu nedenle, tek önemli nokta vuruşların göreceli boyutu ve konumudur. İsterseniz sisteminiz için uygun olan ölçekte koordinat verebilir.
Bağlam öncesi
Önceki bağlam, tanımaya çalıştığınız Ink
içindeki vuruşlardan hemen önce gelen metindir. Tanıyıcıya bağlam öncesi hakkında bilgi vererek yardımcı olabilirsiniz.
Örneğin, italik "n" ve "u" harfleri genellikle birbirine karıştırılır. Kullanıcı "arg" kısmi kelimesini zaten girdiyse "ument" veya "nment" olarak algılanabilir vuruşlarla devam edebilir. Bağlam öncesi "arg" değerini belirtme muğlaklığı giderir çünkü "argument" "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 "merhaba" ön bağlamını belirtirseniz model, "merhabadünya" "merhabasöz"den daha anlamlı olduğu için "dünya" dizesini başında boşluk olacak şekilde döndürü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.
Kotlin
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)
Java
String preContext = ...; float width = ...; float height = ...; RecognitionContext recognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(new WritingArea(width, height)) .build(); recognizer.recognize(ink, recognitionContext);
Ç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. Bu kalıptan farklı olan her durum (ör. İngilizce bir cümleyi son kelimeyle başlamak) daha az doğru sonuçlar verir.
Başka bir örnek de Ink
kelimesinin ortasındaki bir kelime kaldırılıp
ekleyebilirsiniz. Düzeltme muhtemelen bir cümlenin ortasındadır ancak düzeltmeyle ilgili vuruşların bulunduğu vuruş dizisi sona doğrudur.
Bu durumda, yeni yazılan kelimeyi API'ye ayrı olarak göndermenizi ve sonucu kendi mantığınızı kullanarak önceki tanımlarla birleştirmenizi öneririz.
Muğlak şekillerle başa çıkma
Tanımlayı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ı puan sağlar. Model çok güveniliyorsa en yüksek 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ğer bir özelliğinden (ya da tamamen farklı bir şey)
Çünkü tek bir tanıma adayı iki şekli temsil edemez.