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

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

وضع التصنيف واحتساب التكرار باستخدام الخوارزمية التصنيفية (k-NN)

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

سنشرح في هذا القسم كيف أنشأنا مصنِّفًا مخصصًا للأوضاع باستخدام MediaPipe Colab، ونعرض مثالاً على مصنِّف عامل في نموذج تطبيق ML Kit.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4. الدمج مع تطبيق ML Kit للبدء السريع

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

يمكنك تجربة تصنيف الوضعية باستخدام نماذج مجمّعة في تطبيق التشغيل السريع.

  • يمكنك الحصول على مشروع تطبيق البدء السريع لـ 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 لحساب الزاوية بين جزأين من الجسم. لهذا النهج في التصنيف بعض القيود. من خلال تحديد 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]];