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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 שבהמשך מוצגת תנוחת היוגה 'לוחם 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]];