خيارات تصنيف الوضع

باستخدام واجهة برمجة التطبيقات ML Kit Pose Detection، يمكنك الحصول على تفسيرات مفيدة للوضعية من خلال التحقق من المواضع النسبية لمختلف أجزاء الجسم. توضِّح هذه الصفحة بعض الأمثلة.

تصنيف الوضعيات واحتساب التكرار باستخدام خوارزمية الجار الأقرب

من بين التطبيقات الأكثر شيوعًا لميزة "رصد الوضع" تتبُّع اللياقة البدنية. يمكن أن يكون إنشاء مصنّف للوضعيات يتعرّف على أوضاع لياقة بدنية معيّنة ويحصي تكرارات التمارين مهمة صعبة للمطوّرين.

في هذا القسم، نوضّح كيفية إنشاء أسلوب مخصّص لتحديد الوضع باستخدام MediaPipe Colab، وسنوضّح أحد أساليب تحديد الوضع في تطبيق ML Kit النموذجي.

إذا لم تكن على دراية بخدمة Google Colaboratory، يُرجى الاطّلاع على دليل التعريف.

من أجل التعرّف على الوضعيات، نستخدم خوارزمية الجار الأقرب (k-NN) لأنّها بسيطة وسهلة البدء. تحدد الخوارزمية فئة الكائن بناءً على العينات الأقرب في التطبيق.

اتّبِع الخطوات التالية لإنشاء المعرّف وتدريبه:

1. جمع عيّنات من الصور

جمعنا عيّنات من الصور للتمارين الهدف من مصادر مختلفة. اختَرنا بضع مئات من الصور لكل تمرين، مثل الوضعَين "أعلى" و"أسفل" للتمارين الضغط. من المهم جمع العيّنات التي تغطي كاميرا مختلفة الزوايا والظروف البيئية وأشكال الجسم والاختلافات في التمارين الرياضية.

الشكل 1 أوضاع تمارين الضغط للأعلى وللأسفل

2. شغِّل ميزة "رصد الوضعية" على عيّنات الصور

يؤدي ذلك إلى إنشاء مجموعة من معالم الوضعية لاستخدامها في عملية التدريب. لسنا تهتم باكتشاف الوضعية نفسها، حيث إننا سنتدرب على نموذجك الخاص في الخطوة التالية.

تتطلّب خوارزمية k-NN التي اخترناها لتصنيف الوضعيات المخصّصة استخدام تمثيل متّجهات السمات لكل عيّنة ومقياس لاحتساب المسافة بين المتّجهَين للعثور على الهدف الأقرب إلى عيّنة الوضعية. هذا يعني أنه يجب علينا تحويل المعالم ذات الوضعية التي حصلنا عليها للتو.

لتحويل معالم وضعية إلى متجه الخصائص، نستخدم المسافات الزوجية بين القوائم المحددة مسبقًا لمفاصل وضعية الجسم، مثل المسافة بين المعصم والكتف والكاحل والورك، والمعصمَين الأيسر والأيمن. نظرًا لأن مقياس الصور يمكن أن يختلف، قمنا بضبط الوضعيات ليكون لها نفس حجم الجذع والجذع العمودي الاتجاه قبل تحويل المعالم.

3- تدريب النموذج واحتساب مرات التكرار

استخدمنا MediaPipe Colab للوصول إلى رمز المصنّف و تدريب النموذج.

لحساب عمليات التكرار، استخدمنا خوارزمية أخرى في Colab لمراقبة الحدّ الأدنى لاحتمالية وضعية الهدف. على سبيل المثال:

  • عندما تتجاوز احتمالية فئة الوضع "منخفض" حدًا معيّنًا للمرة الأولى، تُشير الخوارزمية إلى أنّه تم إدخال فئة الوضع "منخفض".
  • فعندما تنخفض الاحتمالية إلى أقل من الحد الأدنى، تحدد الخوارزمية "أسفل" تم الخروج من فئة الوضع وزادت من العدّاد.
الشكل 2. مثال على احتساب التكرار

4. الدمج مع تطبيق البدء السريع في حزمة تعلّم الآلة

يُنشئ تطبيق Colab أعلاه ملف CSV يمكنك ملؤه بكل نماذج الوضعيات . في هذا القسم، ستتعرّف على كيفية دمج ملف CSV مع تطبيق بدء استخدام ML Kit لنظام التشغيل Android للاطّلاع على تصنيف الوضعيات المخصّصة في الوقت الفعلي.

تجربة تصنيف الصور باستخدام عيّنات مضمَّنة في تطبيق Quickstart

  • احصل على مشروع تطبيق بدء التشغيل السريع لـ ML Kit على Android من GitHub وتأكَّد من أنّه يتم إنشاؤه وتشغيله بشكلٍ جيد.
  • انتقِل إلى LivePreviewActivity وفعِّل ميزة "كشف الوضع" Run classification من صفحة "الإعدادات". الآن ينبغي أن تكون قادرًا على تصنيف تمارين الضغط والقرفصاء.

إضافة ملف CSV الخاص بك

  • أضِف ملف CSV إلى مجلد مواد عرض التطبيق.
  • في PoseClassifierProcessor، عدِّل متغيّري POSE_SAMPLES_FILE وPOSE_CLASSES لمطابقة ملف CSV ووضع النماذج.
  • أنشئ التطبيق وشغِّله.

لاحظ أن التصنيف قد لا يعمل بشكل جيد إذا لم تكن هناك عينات كافية. بشكل عام، تحتاج إلى حوالي 100 عينة لكل فئة موضع.

لمزيد من المعلومات وتجربة هذه الميزة بنفسك، يمكنك الاطّلاع على MediaPipe Colab. ودليل تصنيف MediaPipe.

التعرف على الإيماءات البسيطة من خلال حساب مسافة المعالم

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

الشكل 3. تفسير وضعية

التعرّف على وضعية يوغا باستخدام إشارات إرشادية للزوايا

يمكنك تحديد وضعية اليوغا من خلال احتساب زوايا المفاصل المختلفة. بالنسبة على سبيل المثال، يوضح الشكل 2 أدناه وضعية يوغا المحارب الثاني. يتم كتابة الزوايا التقريبية التي تحدِّد هذه الوضعية على النحو التالي:

الشكل 4 تقسيم وضعية إلى زوايا

يمكن وصف هذه الوضعية على أنّها مجموعة الزوايا التقريبية التالية ل أجزاء الجسم:

  • زاوية 90 درجة عند كلا الكتفَين
  • 180 درجة في كلا المرفقَين
  • بزاوية 90 درجة للساق الأمامية والخصر
  • زاوية 180 درجة في الركبة الخلفية
  • زاوية 135 درجة عند الخصر

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

بعد حساب جميع الزوايا اللازمة لتحديد الوضعية، يمكنك التحقق من لترى ما إذا كان هناك تطابق، وفي هذه الحالة تعرفت على الوضعية.

يوضّح مقتطف الرمز البرمجي أدناه كيفية استخدام الإحداثيَين X وY لحساب الزاوية بين جزءَين من الجسم. هناك بعض القيود على هذا المنهج للتصنيف. من خلال فحص س و ص فقط، تختلف الزوايا المحسوبة وفقًا للزاوية بين الهدف والكاميرا. ستحصل على أفضل النتائج باستخدام صورة مستوية ومباشرة ومواجهة للكاميرا. يمكنك أيضًا حاول توسيع هذه الخوارزمية بالاستفادة من الإحداثي Z ونرى ما إذا كان يتلاءم بشكل أفضل مع حالة الاستخدام.

حساب زوايا المعالم على Android

تحسب الطريقة التالية الزاوية بين أي ثلاثة معالم. ويضمن أن تكون الزاوية المعروضة بين 0 و180 درجة.

Kotlin

fun getAngle(firstPoint: PoseLandmark, midPoint: PoseLandmark, lastPoint: PoseLandmark): Double {
        var result = Math.toDegrees(atan2(lastPoint.getPosition().y - midPoint.getPosition().y,
                lastPoint.getPosition().x - midPoint.getPosition().x)
                - atan2(firstPoint.getPosition().y - midPoint.getPosition().y,
                firstPoint.getPosition().x - midPoint.getPosition().x))
        result = Math.abs(result) // Angle should never be negative
        if (result > 180) {
            result = 360.0 - result // Always get the acute representation of the angle
        }
        return result
    }

Java

static double getAngle(PoseLandmark firstPoint, PoseLandmark midPoint, PoseLandmark lastPoint) {
  double result =
        Math.toDegrees(
            atan2(lastPoint.getPosition().y - midPoint.getPosition().y,
                      lastPoint.getPosition().x - midPoint.getPosition().x)
                - atan2(firstPoint.getPosition().y - midPoint.getPosition().y,
                      firstPoint.getPosition().x - midPoint.getPosition().x));
  result = Math.abs(result); // Angle should never be negative
  if (result > 180) {
      result = (360.0 - result); // Always get the acute representation of the angle
  }
  return result;
}

في ما يلي كيفية احتساب الزاوية في الورك الأيمن:

Kotlin

val rightHipAngle = getAngle(
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE))

Java

double rightHipAngle = getAngle(
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE));

حساب زوايا المعالم في نظام التشغيل iOS

تحسب الطريقة التالية الزاوية بين أي ثلاثة معالم. ويضمن أن تكون الزاوية المعروضة بين 0 و180 درجة.

Swift

func angle(
      firstLandmark: PoseLandmark,
      midLandmark: PoseLandmark,
      lastLandmark: PoseLandmark
  ) -> CGFloat {
      let radians: CGFloat =
          atan2(lastLandmark.position.y - midLandmark.position.y,
                    lastLandmark.position.x - midLandmark.position.x) -
            atan2(firstLandmark.position.y - midLandmark.position.y,
                    firstLandmark.position.x - midLandmark.position.x)
      var degrees = radians * 180.0 / .pi
      degrees = abs(degrees) // Angle should never be negative
      if degrees > 180.0 {
          degrees = 360.0 - degrees // Always get the acute representation of the angle
      }
      return degrees
  }

Objective-C

(CGFloat)angleFromFirstLandmark:(MLKPoseLandmark *)firstLandmark
                      midLandmark:(MLKPoseLandmark *)midLandmark
                     lastLandmark:(MLKPoseLandmark *)lastLandmark {
    CGFloat radians = atan2(lastLandmark.position.y - midLandmark.position.y,
                            lastLandmark.position.x - midLandmark.position.x) -
                      atan2(firstLandmark.position.y - midLandmark.position.y,
                            firstLandmark.position.x - midLandmark.position.x);
    CGFloat degrees = radians * 180.0 / M_PI;
    degrees = fabs(degrees); // Angle should never be negative
    if (degrees > 180.0) {
        degrees = 360.0 - degrees; // Always get the acute representation of the angle
    }
    return degrees;
}

في ما يلي طريقة حساب الزاوية عند الورك الأيمن:

Swift

let rightHipAngle = angle(
      firstLandmark: pose.landmark(ofType: .rightShoulder),
      midLandmark: pose.landmark(ofType: .rightHip),
      lastLandmark: pose.landmark(ofType: .rightKnee))

Objective-C

CGFloat rightHipAngle =
    [self angleFromFirstLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightShoulder]
                     midLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightHip]
                    lastLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightKnee]];