باستخدام ميزة التعرّف على الحبر الرقمي من ML Kit، يمكنك التعرّف على النص المكتوب بخط اليد على سطح رقمي بمئات اللغات، بالإضافة إلى تصنيف الرسومات.
جرّبه الآن
- يمكنك تجربة نموذج التطبيق من أجل يمكنك الاطّلاع على مثال حول استخدام واجهة برمجة التطبيقات هذه.
قبل البدء
- في ملف
build.gradle
على مستوى المشروع، تأكَّد من تضمين مستودع Maven التابع لشركة Google في كلٍّ من القسمَين "buildscript
" و"allprojects
". - أضِف الاعتماديات الخاصة بمكتبات ML Kit على Android إلى ملف Gradle الخاص بالوحدة على مستوى التطبيق، ويكون عادةً
app/build.gradle
:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}
يمكنك الآن التعرّف على النص في كائنات Ink
.
إنشاء عنصر Ink
إنّ الطريقة الأساسية لإنشاء كائن Ink
هي من خلال رسمه على شاشة تعمل باللمس. مشغَّلة
في Android، يمكنك استخدام
لوحة الرسم لـ
لهذا الغرض.
معالِجات أحداث اللمس
من المفترض أن يستدعي addNewTouchEvent()
مقتطف الرمز التالي لتخزين النقاط في الضربات
يجذب المستخدم العنصر Ink
.
يتم توضيح هذا النمط العام في مقتطف الرمز التالي. يمكنك الاطّلاع على نموذج للبدء السريع في حزمة تعلّم الآلة للحصول على مثال أكثر اكتمالاً.
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
للتعرّف على المحتوى، يجب إرسال مثيل Ink
إلى
كائن DigitalInkRecognizer
. يوضح الرمز أدناه كيفية إنشاء مثيل
أداة التعرُّف من علامة BCP-47.
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
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));
يفترض الرمز النموذجي أعلاه أن نموذج التعرف قد تم بالفعل تنزيل التطبيق، كما هو موضح في القسم التالي.
إدارة عمليات تنزيل النماذج
على الرغم من أن واجهة برمجة التطبيقات للتعرف على الحبر الرقمي تتوافق مع مئات اللغات،
اللغة تتطلب تنزيل بعض البيانات قبل أي عملية التعرف عليها. في الجوار
يجب توفير 20 ميغابايت من مساحة التخزين لكل لغة. يتم التعامل مع هذا بواسطة
عنصر RemoteModelManager
.
تنزيل نموذج جديد
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));
التحقق مما إذا كان قد تم تنزيل نموذج من قبل
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
حذف نموذج تم تنزيله
تؤدي إزالة نموذج من مساحة تخزين الجهاز إلى إخلاء مساحة.
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));
نصائح لتحسين دقة التعرّف على النص
تختلف دقة التعرّف على النص باختلاف اللغات. تعتمد الدقة أيضًا على أسلوب الكتابة. في حين يتم تدريب تقنية "التعرّف الرقمي على الحبر" على التعامل مع العديد من أنواع أنماط الكتابة، يمكن أن تختلف النتائج من مستخدم إلى آخر.
في ما يلي بعض الطرق لتحسين دقة أداة التعرّف على النص. لاحظ أن هذه الأساليب ولن ينطبق على مصنِّفات الرسم للرموز التعبيرية والرسم التلقائي والأشكال.
منطقة الكتابة
تحتوي العديد من التطبيقات على منطقة كتابة محددة جيدًا لإدخال المستخدم. معنى الرمز هو ويتم تحديدها جزئيًا من خلال حجمها بالنسبة إلى حجم مساحة الكتابة التي تحتوي عليها. على سبيل المثال، الفرق بين الحرفين الصغير والكبير "o" أو "c"، والفاصلة مقابل شرطة مائلة للأمام.
يمكن أن يؤدي تحديد أداة التعرُّف بعرض مساحة الكتابة وارتفاعها إلى تحسين الدقة. ومع ذلك، تفترض أداة التعرف أن منطقة الكتابة تحتوي فقط على سطر واحد من النص. إذا كانت القيمة المادية مساحة الكتابة كبيرة بما يكفي للسماح للمستخدم بكتابة سطرين أو أكثر، فقد تتحسن عن طريق تمرير WriteArea مع ارتفاع يُعد أفضل تقدير لارتفاع سطر واحد من النص. ليس من الضروري أن يتطابق كائن WriteArea الذي تنقله إلى أداة التعرُّف بالضبط مع منطقة الكتابة الفعلية على الشاشة. تغيير ارتفاع WriteArea بهذه الطريقة يعمل بشكل أفضل في بعض اللغات أكثر من غيرها.
عند تحديد منطقة الكتابة، حدِّد عرضها وارتفاعها في الوحدات نفسها المستخدَمة في الحد الخارجي الإحداثيات. لا توجد متطلبات وحدة للوسيطات الإحداثية x وy، وستعمل واجهة برمجة التطبيقات على تسوية جميع لذا، فإن الشيء الوحيد المهم هو الحجم النسبي للحدود وموضعها. لك مطلق الحرية في ومرر الإحداثيات بأي مقياس يناسب نظامك.
ما قبل السياق
ما قبل السياق هو النص الذي يسبق الضغطات مباشرةً في Ink
الذي
يحاولون التعرف عليها. يمكنك مساعدة أداة التعرّف من خلال إخبارها بالسياق السابق.
على سبيل المثال، الأحرف التدوينية "n" و"u" وغالبًا ما يتم الخلط بينها وبين بعضها البعض. إذا كان لدى المستخدم سبق له إدخال الكلمة الجزئية "arg"، فقد تستمر بالضغط على المفاتيح التي يمكن التعرف عليها "ument" أو "اسم". تحديد "الوسيطة" التي تسبق السياق وتحل الغموض، نظرًا لأن كلمة "وسيطة" أكثر احتمالاً من "التأثر".
يمكن أن يساعد التعرّف على السياق المسبق أيضًا أداة التعرّف على فواصل الكلمات، أي المسافات بين الكلمات. يمكنك كتابة مسافة، ولكن لا يمكنك رسم حرف، فكيف يمكن لأداة التعرّف تحديد وقت انتهاء الكلمة وتبدأ المرحلة التالية؟ إذا كتب المستخدم "مرحبًا" بالفعل ويستمر بالكلمة المكتوبة "world"، بدون سياق ما قبل السياق، تعرض أداة التعرّف على السلسلة "world". ومع ذلك، إذا حددت "مرحبًا" قبل السياق، سيعرض النموذج السلسلة " العالم"، مع مساحة رائدة، منذ "مرحبًا العالم أكثر منطقية من "helloword".
يجب تقديم أطول سلسلة ممكنة قبل السياق، بحد أقصى 20 حرفًا، ويشمل ذلك مسافات. إذا كانت السلسلة أطول، لا تستخدم أداة التعرُّف إلا آخر 20 حرفًا.
يوضح نموذج التعليمات البرمجية أدناه كيفية تحديد منطقة الكتابة واستخدام
RecognitionContext
لتحديد السياق المسبق.
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);
ترتيب حركة السكتة الدماغية
تتأثر دقة التعرف على ترتيب المفاتيح. تتوقع أدوات التعرف على السكتات الدماغية بالترتيب الذي يكتبه الأشخاص بشكل طبيعي؛ على سبيل المثال من اليسار إلى اليمين للغة الإنجليزية. جميع الحالات التي تغادر هذا النمط، مثل كتابة جملة إنجليزية تبدأ بالكلمة الأخيرة، التي تقدّم نتائج أقل دقة
ومن الأمثلة الأخرى عندما تتم إزالة كلمة في منتصف Ink
واستبدالها
كلمة أخرى. من المحتمل أن تكون المراجعة في منتصف الجملة، لكن الضغطات الخاصة بالمراجعة
تكون في نهاية تسلسل الخط.
وفي هذه الحالة، نوصي بإرسال الكلمة المكتوبة حديثًا بشكل منفصل إلى واجهة برمجة التطبيقات ودمج
ونتج مع الإقرارات السابقة باستخدام منطقك الخاص.
التعامل مع الأشكال الغامضة
هناك حالات يكون فيها معنى الشكل المقدم إلى أداة التعرف غامضًا. بالنسبة على سبيل المثال، يمكن اعتبار مستطيل ذو حواف دائرية للغاية إما مستطيلاً أو قطعًا ناقصًا.
يمكن التعامل مع هذه الحالات غير الواضحة باستخدام درجات التقدير عندما تكون متاحة. فقط
توفر مصنفات الأشكال النتائج. فإذا كان النموذج واثقًا جدًا من ذلك، ستكون نتيجة أعلى نتيجة
أفضل بكثير من ثاني الأفضل. إذا كان هناك عدم يقين، فإن نتائج أعلى نتيجتين
أن تكون قريبًا. علاوةً على ذلك، ضع في اعتبارك أن أدوات تصنيف الأشكال تفسّر Ink
بالكامل على أنه
شكل واحد. على سبيل المثال، إذا كان Ink
يحتوي على مستطيل وقطع ناقص بجانب كل منهما.
الآخر، فإن أداة التعرف قد تعرض أحدهما أو الآخر (أو شيئًا مختلفًا تمامًا)
نظرًا لأن مرشحًا واحدًا للاعتراف لا يمكن أن يمثل شكلين.