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

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

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

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

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

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

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

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

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

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

איור 1. בתנוחות של שכיבות סמיכה למעלה ולמטה

2. הפעלת זיהוי תנוחה בתמונות שבדגימה

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

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

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

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

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

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

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

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

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

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

  • מורידים מ-GitHub את פרויקט האפליקציה למתחילים של ML Kit ל-Android ומוודאים שהוא פועל כמו שצריך.
  • צריך לעבור אל 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]];