دليل التعرّف على المعالم في نظام Android يدويًا

تتيح لك مهمة "وضع علامات على أيدي الأشخاص" من MediaPipe رصد معالم الأيدي في الصورة. توضِّح لك هذه التعليمات كيفية استخدام ميزة "وضع علامات على أجزاء اليد" مع تطبيقات Android. يتوفّر نموذج الرمز البرمجي الموضّح في هذه التعليمات على GitHub.

لمزيد من المعلومات عن الإمكانات والنماذج وخيارات الضبط لهذه المهمة، اطّلِع على نظرة عامة.

مثال على الرمز البرمجي

مثال رمز MediaPipe Tasks هو تطبيق بسيط لنظام التشغيل Android يحدِّد معالم اليد. يستخدم المثال الكاميرا على جهاز Android مادي لرصد معالم اليد باستمرار، ويمكنه أيضًا استخدام الصور والفيديوهات من معرض الصور على الجهاز لرصد معالم اليد بشكل ثابت.

يمكنك استخدام التطبيق كنقطة بداية لتطبيق Android الخاص بك، أو الرجوع إليه عند تعديل تطبيق حالي. يتم استضافة رمز مثال أداة وضع علامات على أجزاء اليد على GitHub.

تنزيل الرمز

توضِّح لك التعليمات التالية كيفية إنشاء نسخة محلية من مثال الرمز البرمجي باستخدام أداة سطر الأوامر git.

لتنزيل نموذج الرمز البرمجي:

  1. استنسِخ مستودع git باستخدام الأمر التالي:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. يمكنك اختياريًا ضبط مثيل git لاستخدام ميزة "الفحص الخفيف"، لكي لا يتوفّر لديك سوى ملفات مثال تطبيق "وضع علامات على أجزاء اليد":
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/hand_landmarker/android
    

بعد إنشاء نسخة محلية من رمز المثال، يمكنك استيراد المشروع إلى "استوديو Android" وتشغيل التطبيق. للحصول على التعليمات، اطّلِع على دليل الإعداد لنظام التشغيل Android.

المكونات الرئيسية

تحتوي الملفات التالية على الرمز البرمجي المهم لهذا المثال على تطبيق رصد معالم اليد:

  • HandLandmarkerHelper.kt: تُستخدَم لإعداد أداة رصد معالم اليد ومعالجة اختيار النموذج والوكيل.
  • MainActivity.kt: ينفِّذ التطبيق، بما في ذلك استدعاء HandLandmarkerHelper.

ضبط إعدادات الجهاز

يوضّح هذا القسم الخطوات الرئيسية لإعداد بيئة التطوير ومشاريع الترميز خصيصًا لاستخدام أداة "وضع علامات على أجزاء اليد". للحصول على معلومات عامة عن إعداد بيئة التطوير لاستخدام مهام MediaPipe، بما في ذلك متطلبات إصدار النظام الأساسي، يُرجى الاطّلاع على دليل الإعداد لنظام التشغيل Android.

التبعيات

تستخدِم مهمة "مُحدِّد المواقع الجغرافية لليد" مكتبة com.google.mediapipe:tasks-vision. أضِف هذه التبعية إلى ملف build.gradle لتطبيق Android:

dependencies {
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
}

الطراز

تتطلّب مهمة MediaPipe Hand Landmarker حِزمة نموذج مدرَّب متوافقة مع هذه المهمة. لمزيد من المعلومات عن النماذج المدربة المتاحة لميزة "وضع علامات على أجزاء اليد"، اطّلِع على نظرة عامة على المهمة في قسم "النماذج".

اختَر النموذج ونزِّله واحفظه في دليل مشروعك:

<dev-project-root>/src/main/assets

حدِّد مسار النموذج ضمن المَعلمة ModelAssetPath. في المثال التالي على الرمز، يتم تحديد التصميم في ملف HandLandmarkerHelper.kt:

baseOptionBuilder.setModelAssetPath(MP_HAND_LANDMARKER_TASK)

إنشاء المهمة

تستخدِم مهمة MediaPipe Hand Landmarker الدالة createFromOptions() لإعداد المهمة. تقبل الدالة createFromOptions() قيمًا لإعدادات options. لمزيد من المعلومات حول خيارات الضبط، يُرجى الاطّلاع على خيارات الضبط.

تتيح ميزة "وضع علامات على أجزاء اليد" استخدام 3 أنواع من بيانات الإدخال: الصور الثابتة وملفات الفيديو والبث المباشر. يجب تحديد وضع التشغيل المقابل لنوع data input عند إنشاء المهمة. اختَر علامة التبويب التي تتوافق مع نوع بيانات الإدخال للاطّلاع على كيفية إنشاء المهمة وتنفيذ الاستنتاج.

صورة

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

فيديو

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

بث مباشر

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(MP_HAND_LANDMARKER_TASK)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

يتيح نموذج تنفيذ رمز Hand Landmarker للمستخدم التبديل بين أوضاع المعالجة. تؤدي هذه الطريقة إلى جعل رمز إنشاء المهام أكثر تعقيدًا، وقد لا يكون مناسبًا لحالة الاستخدام. يمكنك الاطّلاع على هذا الرمز في دالة setupHandLandmarker() في ملف HandLandmarkerHelper.kt.

خيارات الضبط

تتضمّن هذه المهمة خيارات الضبط التالية لتطبيقات Android:

اسم الخيار الوصف نطاق القيمة القيمة التلقائية
runningMode لضبط وضع التشغيل للمهمة هناك ثلاثة أوضاع:

IMAGE: وضع الإدخالات باستخدام صورة واحدة.

‫VIDEO: وضع الإطارات التي تم فك ترميزها في الفيديو

LIVE_STREAM: وضع البث المباشر لبيانات الإدخال ، مثل بيانات الكاميرا في هذا الوضع، يجب استدعاء resultListener لإعداد مستمع لتلقّي النتائج بشكل غير متزامن.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numHands الحد الأقصى لعدد الأيدي التي يرصدها "كاشف معالم اليد" Any integer > 0 1
minHandDetectionConfidence الحد الأدنى لنتيجة الثقة لرصد اليد ليكون ناجحًا في نموذج رصد راحة اليد 0.0 - 1.0 0.5
minHandPresenceConfidence الحد الأدنى لنتيجة الثقة لنتيجة توفُّر اليد في نموذج رصد معالم اليد في وضع "الفيديو" ووضع "البث المباشر"، إذا كانت نتيجة الثقة في توفّر اليد من نموذج معالِم اليد أقل من هذا الحدّ الأدنى، يشغِّل "معالِم اليد" نموذج "اكتشاف راحة اليد". بخلاف ذلك، تحدِّد خوارزمية تتبُّع اليدين الخفيفة وزنًا موقع اليدَين لرصد المعالم اللاحقة. 0.0 - 1.0 0.5
minTrackingConfidence الحد الأدنى لنتيجة الثقة ليعتبر تتبع اليد ناجحًا هذا هو الحدّ الأدنى لمقياس IoU للحدود المربّعة بين اليدين في الإطار الحالي والإطار الأخير. في وضعَي "الفيديو" و"البث" في "أداة وضع علامات على أيدي المستخدمين"، إذا تعذّر التتبّع، تبدأ أداة وضع علامات على أيدي المستخدمين في رصد اليد. وفي حال عدم توفّر هذه البيانات، يتم تخطي ميزة "رصد اليد". 0.0 - 1.0 0.5
resultListener ضبط مستمع النتائج لتلقّي نتائج رصد العناصر بشكل غير متزامن عندما يكون علامة موضع اليد في وضع البث المباشر لا ينطبق ذلك إلا عند ضبط وضع التشغيل على LIVE_STREAM لا ينطبق لا ينطبق
errorListener تُستخدَم لضبط مستمع أخطاء اختياري. لا ينطبق لا ينطبق

إعداد البيانات

تعمل ميزة "وضع علامات على أجزاء اليد" مع الصور وملفات الفيديو والفيديوهات المباشرة. تعالج المهمة المعالجة المُسبَقة لإدخال البيانات، بما في ذلك تغيير الحجم والدوران ومعالجة قيم البيانات.

يوضّح الرمز البرمجي التالي كيفية تسليم البيانات لمعالجتها. تتضمّن هذه النماذج تفاصيل عن كيفية التعامل مع البيانات من الصور وملفات الفيديو وعمليات البث المباشر للفيديو.

صورة

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(image).build()
    

فيديو

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

val argb8888Frame =
    if (frame.config == Bitmap.Config.ARGB_8888) frame
    else frame.copy(Bitmap.Config.ARGB_8888, false)

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(argb8888Frame).build()
    

بث مباشر

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(rotatedBitmap).build()
    

في مثال رمز برمجي لـ Hand Landmarker، يتم التعامل مع إعداد البيانات في ملف HandLandmarkerHelper.kt.

تنفيذ المهمة

استنادًا إلى نوع البيانات التي تعمل معها، استخدِم طريقة HandLandmarker.detect...() الخاصة بهذا النوع من البيانات. استخدِم ‎ detect() للصور الفردية و‎ detectForVideo() للإطارات في ملفات الفيديو و‎ detectAsync() لأحداث بث الفيديو. عند إجراء عمليات رصد في مجرى فيديو، تأكَّد من تشغيل عمليات الرصد في سلسلة محادثات منفصلة لتجنُّب حظر سلسلة محادثات واجهة المستخدم.

تعرض عيّنات الرموز البرمجية التالية أمثلة بسيطة على كيفية تشغيل Hand Landmarker في أوضاع البيانات المختلفة التالية:

صورة

val result = handLandmarker?.detect(mpImage)
    

فيديو

val timestampMs = i * inferenceIntervalMs

handLandmarker?.detectForVideo(mpImage, timestampMs)
    ?.let { detectionResult ->
        resultList.add(detectionResult)
    }
    

بث مباشر

val mpImage = BitmapImageBuilder(rotatedBitmap).build()
val frameTime = SystemClock.uptimeMillis()

handLandmarker?.detectAsync(mpImage, frameTime)
    

يُرجى ملاحظة ما يلي:

  • عند التشغيل في وضع الفيديو أو وضع البث المباشر، يجب أيضًا تقديم الطابع الزمني لإطار الإدخال إلى مهمة "وضع علامات على أجزاء اليد".
  • عند التشغيل في وضع الصورة أو الفيديو، ستؤدي مهمة "وضع علامات على أجزاء اليد" إلى حظر سلسلة المهام الحالية إلى أن تنتهي من معالجة الصورة أو الإطار المُدخل. لتجنّب حظر واجهة المستخدم، نفِّذ المعالجة في سلسلسة مهام في الخلفية.
  • عند التشغيل في وضع البث المباشر، لا تحظر مهمة "وضع علامات على أجزاء اليد" المحادثة الحالية، بل تعود على الفور. سيستدعي مستمع النتائج نتيجة الكشف في كل مرة تنتهي فيها من معالجة ملف إطار الإدخال. إذا تمّ استدعاء دالة الكشف عندما تكون مهمة "مُحدِّد معالم اليد" مشغولة في معالجة إطار آخر، ستتجاهل المهمة إطار الإدخال الجديد.

في مثال التعليمات البرمجية لميزة "وضع علامات على أجزاء اليد"، تم تحديد الدوال detect وdetectForVideo و detectAsync في ملف HandLandmarkerHelper.kt.

معالجة النتائج وعرضها

ينشئ "مُحدِّد معالم اليد" عنصر نتيجة "مُحدِّد معالم اليد" لكل عملية رصد. يحتوي عنصر النتيجة على معالِم اليد في إحداثيات الصورة ومعالِم اليد في إحداثيات العالم واتجاه اليد(اليد اليسرى/اليمنى) لليدين التي تم رصدها.

في ما يلي مثال على بيانات الإخراج من هذه المهمة:

يحتوي الناتج HandLandmarkerResult على ثلاثة مكوّنات. كل مكوّن هو صفيف، حيث يحتوي كل عنصر على النتائج التالية ليد واحدة تم رصدها:

  • استخدام إحدى اليدين

    يشير مقياس اليد المفضّلة إلى ما إذا كانت الأيدي التي تم رصدها هي اليد اليمنى أو اليسرى.

  • معالم

    هناك 21 معلمًا يخصّ اليد، ويتكون كلّ منها من إحداثيات x وy وz. يتم توحيد إحداثيات x وy لتكون [0.0, 1.0] حسب عرض الصورة وارتفاعها على التوالي. يمثّل الإحداثي z عمق المَعلم، مع كون العميق عند الرسغ هو الأصل. وكلما كانت القيمة أصغر، كان المَعلم أقرب إلى الكاميرا. يستخدم حجم z المقياس نفسه تقريبًا الذي يستخدمه x.

  • معالم عالمية

    يتم أيضًا عرض 21 معلمًا يدويًا بالإحداثيات العالمية. يتألّف كل معلم من x وy وz، وهي تمثل إحداثيات ثلاثية الأبعاد في العالم الحقيقي بال مترات مع منشأ في المركز الهندسي لليد.

HandLandmarkerResult:
  Handedness:
    Categories #0:
      index        : 0
      score        : 0.98396
      categoryName : Left
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : -3.41E-7
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
    ... (21 landmarks for a hand)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
    ... (21 world landmarks for a hand)

تعرض الصورة التالية عرضًا مرئيًا لمعدّل تكرار المهمة:

يد في حركة إشارة جيدة مع تحديد الهيكل العظمي لليد

يوضّح مثال رمز Hand Landmarker كيفية عرض النتائج التي تم عرضها من المهمة، اطّلِع على فئة OverlayView لمزيد من التفاصيل.