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

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

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

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

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

אם לא נתקלת ב-Google Colaboratory, כדאי לבדוק את במדריך המבוא.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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