אפשרויות לסיווג תנוחות

באמצעות ה-ML Kit Pose Detection API, תוכלו להסיק את הפרשנות המשמעותית של התנוחה על ידי בדיקת המיקומים היחסיים של חלקי הגוף השונים. הדף הזה מציג כמה דוגמאות.

סיווג תנוחות וספירת חזרות באמצעות האלגוריתם k-NN

אחד השימושים הנפוצים ביותר של זיהוי תנוחות הוא מעקב אחר הכושר. פיתוח סיווג של תנוחות שמאפשר לזהות תנוחות כושר ספציפיות ומספר חזרות הוא מאתגר למפתחים.

בסעיף הזה מתואר איך בנינו סיווג של תנוחות מותאמות אישית באמצעות MediaPipe Colab, ומראים מכשיר סיווג באפליקציית הדגימה שלנו ללמידת מכונה.

אם אינכם מכירים את Google Colaboratory, מומלץ לעיין במדריך ההיכרות.

כדי לזהות תנוחות, אנחנו משתמשים באלגוריתם השכן הקרוב ביותר (k-NN) כי הוא פשוט וקל להתחיל בו. האלגוריתם קובע את סיווג האובייקט על סמך הדגימות הקרובות ביותר בקבוצת האימון.

יש לבצע את השלבים הבאים כדי לפתח את המזהה ולהכין אותו:

1. איסוף דוגמאות של תמונות

אספנו דגימות תמונות של תרגילי היעד ממקורות שונים. אנחנו בוחרים כמה מאות תמונות לכל פעילות גופנית, כמו "up" and "down" חשוב לאסוף דוגמאות שמכסות זוויות צילום שונות, תנאים סביבתיים, צורות גוף והווריאציות של פעילות גופנית.

איור 1. מיקומים בדחיפה למעלה ולמטה

2. הרצה של זיהוי תנוחות בתמונות לדוגמה

כך נוצרת קבוצה של ציוני דרך לתנוחות המשמשים לאימון. אנחנו לא מתעניינים בזיהוי התנוחות עצמו, כי נלמד את המודל שלנו בשלב הבא.

האלגוריתם שלנו k-NN שבחרנו עבור סיווג תנוחות מותאמות אישית מחייב ייצוג וקטורי של תכונות עבור כל דגימה ומדד לחישוב המרחק בין שני וקטורים כדי למצוא את היעד הקרוב ביותר לדגימה של התנוחות. המשמעות היא שעלינו להמיר את המיקומים של התנוחות שהשגנו עכשיו.

כדי להמיר נקודות ציון של תנוחות לווקטור, אנחנו משתמשים במרחקים בין שתי רשימות מוגדרות מראש של מפרקים, כמו מרחק בין פרק כף היד לכתף, קרסול וגב, ועל פרק כף היד השמאלי והימני. מכיוון שקנה המידה של התמונות עשוי להשתנות, אנחנו מנרמלים את התנוחות עם אותו מידות טורו ואותה זווית אנכית לפני המרת ציוני הדרך.

3. אימון המודל וספירת חזרות

השתמשנו ב-MediaPipe Colab כדי לגשת לקוד המסווג ולמן את המודל.

כדי לספור חזרות, השתמשנו באלגוריתם אחר של Colab כדי לעקוב אחר ההסתברות של מיקום יעד בתנוחות היעד. למשל:

  • כאשר ההסתברות של ה-"down" של תנוחה עוברת סף מסוים בפעם הראשונה, האלגוריתם מציין שמחלקת "down" .
  • כאשר ההסתברות יורדת מתחת לסף, האלגוריתם מציין שסיווג התנוחות '&ציטוט' יוצא, ומגדיל את המונה.
איור 2. דוגמה לספירה של חזרות

4. שילוב עם האפליקציה למתחילים של ML Kit

ה-Colab שלמעלה מפיק קובץ CSV שאפשר להוסיף לכל הדגימות שלכם בתנוחות. בקטע הזה נסביר איך לשלב את קובץ ה-CSV עם האפליקציה ל-Android למתחילים ב-ML Kit, כדי לראות בזמן אמת את הסיווג של תנוחות מותאמות אישית.

כדאי לנסות סיווג של תפאורה עם דוגמאות בחבילה באפליקציה למתחילים

  • מורידים את פרויקט האפליקציה למתחילים של ML Kit ל-Android מ-GitHub ומוודאים שהיא פועלת היטב.
  • עוברים אל LivePreviewActivity ומפעילים את זיהוי התנוחה Run classification בדף 'הגדרות'. עכשיו אתם אמורים לסווג את הדחיפות והסקוואטים.

הוספת קובץ CSV

  • יש להוסיף את קובץ ה-CSV לתיקיית הנכס של האפליקציה.
  • ב-PoseClassifierProcessor מעדכנים את המשתנים POSE_SAMPLES_FILE ו-POSE_CLASSES בהתאם לקובץ ה-CSV ויוצרים דגימות.
  • יוצרים ומפעילים את האפליקציה.

לתשומת ליבכם, ייתכן שהסיווג לא יפעל כמו שצריך אם אין מספיק דוגמאות. באופן כללי, צריך כ-100 דוגמאות לכל שיעור לפי תנוחה.

תוכלו לקבל מידע נוסף ולנסות זאת בעצמכם, במדריך הסיווג של MediaPipe Colab ובמדריך הסיווג של MediaPipe.

זיהוי תנועות פשוטות על ידי חישוב מרחקים מפורסמים

אם שני ציוני דרך או יותר קרובים זה לזה, ניתן להשתמש בהם כדי לזהות תנועות. לדוגמה, כשציון הדרך של אצבע אחת או יותר על יד קרוב יותר לציון הדרך של האף, אפשר להסיק שהמשתמש ככל הנראה נוגע בפנים שלו.

איור 3. פירוש של תנוחות

זיהוי תנוחת יוגה עם כללים זוויתיים

ניתן לזהות תנוחת יוגה על ידי חישוב הזוויות של מפרקים שונים. לדוגמה, באיור 2 שבהמשך מוצגת תנוחת היורה של הלוחם השני II. הזוויות המשוערות שמזהה את התנוחות האלה נכתבות כך:

איור 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]];