1. مقدمة
ما هي MediaPipe؟
تتيح لك حلول MediaPipe تطبيق حلول تعلُّم الآلة (ML) على تطبيقاتك. يوفر هذا المنتج إطار عمل لإعداد مسارات معالجة مسبقة الإنشاء تقدّم للمستخدمين نتائج فورية وجذابة ومفيدة. يمكنك حتى تخصيص هذه الحلول باستخدام MediaPipe Model Maker لتعديل النماذج التلقائية.
تصنيف الصور هو إحدى مهام رؤية تعلُّم الآلة التي تقدّمها "حلول MediaPipe". تتوفّر MediaPipe Tasks على Android وiOS وPython (بما في ذلك Raspberry Pi!) والويب.
في هذا الدرس التطبيقي حول الترميز، ستبدأ بتطبيق Android يتيح لك رسم أرقام على الشاشة، ثم ستضيف وظيفة تصنّف هذه الأرقام المرسومة كقيمة واحدة من 0 إلى 9.
أهداف الدورة التعليمية
- كيفية دمج مهمة تصنيف الصور في تطبيق Android باستخدام MediaPipe Tasks
المتطلبات
- إصدار مثبَّت من استوديو Android (تمت كتابة هذا الدرس التطبيقي واختباره باستخدام الإصدار Giraffe من "استوديو Android").
- جهاز Android أو محاكي لتشغيل التطبيق
- معرفة أساسية بتطوير تطبيقات Android (هذا ليس برنامج "Hello World"، ولكنّه ليس بعيدًا عنه كثيرًا).
2. إضافة MediaPipe Tasks إلى تطبيق Android
تنزيل تطبيق Android الأوّلي
سيبدأ هذا الدرس العملي بنموذج مُعدّ مسبقًا يتيح لك الرسم على الشاشة. يمكنك العثور على تطبيق البدء هذا في مستودع نماذج MediaPipe الرسمي هنا. استنسِخ المستودع أو نزِّل ملف ZIP بالنقر على "الرمز" > "تنزيل ملف ZIP".
استيراد التطبيق إلى "استوديو Android"
- افتح "استوديو Android".
- من شاشة مرحبًا بك في استوديو Android، انقر على فتح (Open) في أعلى يسار الشاشة.

- انتقِل إلى المكان الذي نسخت فيه المستودع أو نزّلته منه، وافتح دليل codelabs/digitclassifier/android/start.
- تأكَّد من أنّ كل شيء تم فتحه بشكل صحيح من خلال النقر على سهم التشغيل الأخضر (
) في أعلى يسار "استوديو Android". - من المفترض أن يفتح التطبيق مع شاشة سوداء يمكنك الرسم عليها، بالإضافة إلى زر محو لإعادة ضبط هذه الشاشة. يمكنك الرسم على هذه الشاشة، ولكنّها لا تقدّم الكثير من الميزات الأخرى، لذا سنبدأ في إصلاح ذلك الآن.

الطراز
عند تشغيل التطبيق لأول مرة، قد تلاحظ أنّه يتم تنزيل ملف باسم mnist.tflite وتخزينه في دليل assets الخاص بتطبيقك. لتبسيط الأمر، أخذنا نموذجًا معروفًا، وهو MNIST، يصنّف الأرقام، وأضفناه إلى التطبيق باستخدام النص البرمجي download_models.gradle في المشروع. إذا قرّرت تدريب نموذج مخصّص خاص بك، مثل نموذج للأحرف المكتوبة بخط اليد، عليك إزالة الملف download_models.gradle وحذف الإشارة إليه في ملف build.gradle على مستوى التطبيق، وتغيير اسم النموذج لاحقًا في الرمز (تحديدًا في الملف DigitClassifierHelper.kt).
تعديل ملف build.gradle
قبل أن تتمكّن من بدء استخدام MediaPipe Tasks، عليك استيراد المكتبة.
- افتح ملف build.gradle الموجود في وحدة التطبيق، ثم انتقِل للأسفل إلى قسم dependencies.
- يجب أن يظهر تعليق في أسفل هذا القسم يقول // STEP 1 Dependency Import.
- استبدِل هذا السطر بالتنفيذ التالي
implementation("com.google.mediapipe:tasks-vision:latest.release")
- انقر على الزر المزامنة الآن الذي يظهر في البانر أعلى "استوديو Android" لتنزيل هذه التبعية.
3- إنشاء أداة مساعدة لتصنيف الأرقام في MediaPipe Tasks
في الخطوة التالية، ستملأ فئة ستتولى عملية التصنيف المعقّدة للتعلم الآلي. افتح الملف DigitClassifierHelper.kt ولنبدأ.
- ابحث عن التعليق في أعلى الصف الذي يقول // STEP 2 Create listener
- استبدِل هذا السطر بالرمز التالي. سيؤدي ذلك إلى إنشاء متتبِّع سيتم استخدامه لنقل النتائج من فئة DigitClassifierHelper إلى أي مكان يستمع إلى هذه النتائج (في هذه الحالة ستكون فئة DigitCanvasFragment، ولكن سنتناول ذلك قريبًا).
// STEP 2 Create listener
interface DigitClassifierListener {
fun onError(error: String)
fun onResults(
results: ImageClassifierResult,
inferenceTime: Long
)
}
- عليك أيضًا قبول DigitClassifierListener كمَعلمة اختيارية للفئة:
class DigitClassifierHelper(
val context: Context,
val digitClassifierListener: DigitClassifierListener?
) {
- انتقِل إلى السطر الذي يظهر فيه // STEP 3 define classifier، وأضِف السطر التالي لإنشاء عنصر نائب لـ ImageClassifier الذي سيتم استخدامه لهذا التطبيق:
// الخطوة 3: تحديد المصنّف
private var digitClassifier: ImageClassifier? = null
- أضِف الدالة التالية في المكان الذي يظهر فيه التعليق // STEP 4 set up classifier:
// STEP 4 set up classifier
private fun setupDigitClassifier() {
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath("mnist.tflite")
// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
.setRunningMode(RunningMode.IMAGE)
.setBaseOptions(baseOptionsBuilder.build())
try {
digitClassifier =
ImageClassifier.createFromOptions(
context,
optionsBuilder.build()
)
} catch (e: IllegalStateException) {
digitClassifierListener?.onError(
"Image classifier failed to initialize. See error logs for " +
"details"
)
Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
}
}
تتضمّن الفقرة أعلاه بعض التفاصيل، لذا لنلقِ نظرة على أجزاء أصغر لفهم ما يحدث بشكل أفضل.
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath("mnist.tflite")
// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
.setRunningMode(RunningMode.IMAGE)
.setBaseOptions(baseOptionsBuilder.build())
ستحدّد هذه الكتلة المَعلمات التي يستخدمها ImageClassifier. ويشمل ذلك النموذج المخزّن داخل تطبيقك (mnist.tflite) ضمن BaseOptions وRunningMode ضمن ImageClassifierOptions، وهو IMAGE في هذه الحالة، ولكن VIDEO وLIVE_STREAM هما خياران إضافيان متاحان. المَعلمتان الأخريان المتاحتان هما MaxResults، التي تحدّ من عدد النتائج التي يعرضها النموذج، وScoreThreshold، التي تحدّد الحدّ الأدنى من الثقة التي يحتاج إليها النموذج في النتيجة قبل عرضها.
try {
digitClassifier =
ImageClassifier.createFromOptions(
context,
optionsBuilder.build()
)
} catch (e: IllegalStateException) {
digitClassifierListener?.onError(
"Image classifier failed to initialize. See error logs for " +
"details"
)
Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
}
بعد إنشاء خيارات الإعداد، يمكنك إنشاء أداة تصنيف الصور الجديدة من خلال تمرير سياق وخيارات. إذا حدث خطأ في عملية الإعداد هذه، سيتم عرض رسالة خطأ من خلال DigitClassifierListener.
- بما أنّنا نريد إعداد ImageClassifier قبل استخدامه، يمكنك إضافة كتلة init لاستدعاء setupDigitClassifier().
init {
setupDigitClassifier()
}
- أخيرًا، انتقِل للأسفل إلى التعليق الذي يقول // STEP 5 create classify function وأضِف الرمز التالي. ستقبل هذه الدالة صورة نقطية، وهي في هذه الحالة الرقم المرسوم، وتحوّله إلى عنصر صورة MediaPipe (MPImage)، ثم تصنّف هذه الصورة باستخدام ImageClassifier، بالإضافة إلى تسجيل المدة التي يستغرقها الاستنتاج، قبل عرض هذه النتائج من خلال DigitClassifierListener.
// STEP 5 create classify function
fun classify(image: Bitmap) {
if (digitClassifier == null) {
setupDigitClassifier()
}
// Convert the input Bitmap object to an MPImage object to run inference.
// Rotating shouldn't be necessary because the text is being extracted from
// a view that should always be correctly positioned.
val mpImage = BitmapImageBuilder(image).build()
// Inference time is the difference between the system time at the start and finish of the
// process
val startTime = SystemClock.uptimeMillis()
// Run image classification using MediaPipe Image Classifier API
digitClassifier?.classify(mpImage)?.also { classificationResults ->
val inferenceTimeMs = SystemClock.uptimeMillis() - startTime
digitClassifierListener?.onResults(classificationResults, inferenceTimeMs)
}
}
هذا كل ما يتعلق بملف المساعد. في القسم التالي، ستملأ الخطوات النهائية لبدء تصنيف الأرقام التي تم رسمها.
4. تنفيذ الاستدلال باستخدام MediaPipe Tasks
يمكنك بدء هذا القسم بفتح فئة DigitCanvasFragment في "استوديو Android"، حيث سيتم تنفيذ كل العمل.
- في أسفل هذا الملف، يجب أن يظهر تعليق يقول // STEP 6 Set up listener. ستضيف هنا الدالتَين onResults() وonError() المرتبطتَين بمعالج الأحداث.
// STEP 6 Set up listener
override fun onError(error: String) {
activity?.runOnUiThread {
Toast.makeText(requireActivity(), error, Toast.LENGTH_SHORT).show()
fragmentDigitCanvasBinding.tvResults.text = ""
}
}
override fun onResults(
results: ImageClassifierResult,
inferenceTime: Long
) {
activity?.runOnUiThread {
fragmentDigitCanvasBinding.tvResults.text = results
.classificationResult()
.classifications().get(0)
.categories().get(0)
.categoryName()
fragmentDigitCanvasBinding.tvInferenceTime.text = requireActivity()
.getString(R.string.inference_time, inferenceTime.toString())
}
}
إنّ الدالة onResults() مهمة بشكل خاص لأنّها ستعرض النتائج التي تم تلقّيها من ImageClassifier. بما أنّ دالة معاودة الاتصال هذه يتم تشغيلها من سلسلة تعليمات في الخلفية، عليك أيضًا تشغيل تعديلات واجهة المستخدم على سلسلة تعليمات واجهة المستخدم في Android.
- بما أنّك تضيف دوال جديدة من واجهة في الخطوة أعلاه، عليك أيضًا إضافة بيان التنفيذ في أعلى الفئة.
class DigitCanvasFragment : Fragment(), DigitClassifierHelper.DigitClassifierListener
- في أعلى الصف، من المفترض أن يظهر تعليق يقول // STEP 7a Initialize classifier. هذا هو المكان الذي ستضع فيه تعريف DigitClassifierHelper.
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
- بالانتقال إلى // STEP 7b Initialize classifier, يمكنك إعداد digitClassifierHelper ضِمن الدالة onViewCreated().
// STEP 7b Initialize classifier
// Initialize the digit classifier helper, which does all of the
// ML work. This uses the default values for the classifier.
digitClassifierHelper = DigitClassifierHelper(
context = requireContext(), digitClassifierListener = this
)
- بالنسبة إلى الخطوات الأخيرة، ابحث عن التعليق // STEP 8a*: classify* وأضِف الرمز التالي لاستدعاء دالة جديدة ستضيفها بعد قليل. ستؤدي مجموعة الرموز هذه إلى بدء عملية التصنيف عند رفع إصبعك عن مساحة الرسم في التطبيق.
// STEP 8a: classify
classifyDrawing()
- أخيرًا، ابحث عن التعليق // STEP 8b classify لإضافة الدالة الجديدة classifyDrawing(). سيؤدي ذلك إلى استخراج صورة نقطية من لوحة الرسم، ثم تمريرها إلى DigitClassifierHelper لإجراء التصنيف من أجل تلقّي النتائج في دالة واجهة onResults().
// STEP 8b classify
private fun classifyDrawing() {
val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()
digitClassifierHelper.classify(bitmap)
}
5- نشر التطبيق واختباره
بعد كل ذلك، من المفترض أن يكون لديك تطبيق يعمل ويمكنه تصنيف الأرقام المرسومة على الشاشة. يمكنك الآن نشر التطبيق على "محاكي Android" أو جهاز Android فعلي لاختباره.
- انقر على "تشغيل" (
) في شريط أدوات "استوديو Android" لتشغيل التطبيق. - ارسم أي رقم على لوحة الرسم لمعرفة ما إذا كان بإمكان التطبيق التعرّف عليه. يجب أن يعرض التطبيق الرقم الذي يعتقد النموذج أنّه تم رسمه، بالإضافة إلى المدة التي استغرقها النموذج للتنبؤ بهذا الرقم.

6. تهانينا!
أحسنت! في هذا الدرس التطبيقي حول الترميز، تعلّمت كيفية إضافة تصنيف الصور إلى تطبيق Android، وتحديدًا كيفية تصنيف الأرقام المرسومة باليد باستخدام نموذج MNIST.
الخطوات التالية
- بعد أن أصبح بإمكانك تصنيف الأرقام، قد تحتاج إلى تدريب نموذجك الخاص لتصنيف الحروف المرسومة أو الحيوانات أو عدد لا نهائي من العناصر الأخرى. يمكنك العثور على مستندات حول تدريب نموذج جديد لتصنيف الصور باستخدام MediaPipe Model Maker على صفحة developers.google.com/mediapipe.
- تعرَّف على مهام MediaPipe الأخرى المتاحة على Android، بما في ذلك "رصد نقاط الوجه" و"التعرّف على الإيماءات" و"تصنيف الصوت".
نتطلّع إلى رؤية كلّ الإنجازات الرائعة التي ستحقّقها.