מדריך לזיהוי של ציוני דרך ב-Android

המשימה 'MediaPipe Pose Scoreer' מאפשרת לזהות ציוני דרך של גופות אנושיות בתמונה או בסרטון. אפשר להשתמש במשימה כדי לזהות מיקומים מרכזיים בגוף, לנתח את היציבה ולסווג תנועות. במשימה הזו נעשה שימוש במודלים של למידת מכונה (ML) שעובדים עם תמונות או סרטונים בודדים. הפלט של המשימה מהגוף מייצג ציוני דרך בקואורדינטות של תמונה ובקואורדינטות בעולם תלת-ממדי.

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

קוד לדוגמה

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

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

הורדת הקוד

בהוראות הבאות מוסבר איך ליצור עותק מקומי של הקוד לדוגמה באמצעות כלי שורת הפקודה git.

כדי להוריד את הקוד לדוגמה:

  1. משכפלים את מאגר ה-Git באמצעות הפקודה הבאה:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. לחלופין, אפשר להגדיר את מכונת ה-Git לשימוש בקופה עם נפח נתונים נמוך, כדי שיהיו לכם רק את הקבצים של האפליקציה לדוגמה של Pose שבהמשך:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/pose_landmarker/android
    

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

רכיבים עיקריים

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

  • PoseLandmarkerHelper.kt – מפעיל את מנצימציית התנוחה ומטפל במודל ובבחירת הנציגים.
  • CameraFragment.kt – שימוש במצלמת המכשיר ועיבוד של נתוני קלט התמונה והווידאו.
  • GalleryFragment.kt – מקיים אינטראקציה עם OverlayView כדי להציג את התמונה או הסרטון בפלט.
  • OverlayView.kt – מיישמת את התצוגה לתנוחות שזוהו.

הגדרה

בקטע הזה מתוארים שלבי המפתח להגדרת סביבת הפיתוח ופרויקטים של קוד באופן ספציפי לשימוש ב-Pose Scoreer. במדריך ההגדרה ל-Android תוכלו לקרוא מידע כללי על הגדרת סביבת הפיתוח לשימוש במשימות של MediaPipe, כולל הדרישות לגבי גרסת הפלטפורמה.

יחסי תלות

המשימה 'Pose שבהמשך' משתמשת בספרייה com.google.mediapipe:tasks-vision. מוסיפים את התלות הזו לקובץ build.gradle באפליקציה ל-Android:

dependencies {
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
}

מודל

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

בוחרים ומורידים את המודל, ומאחסנים אותו בספריית הפרויקט:

<dev-project-root>/src/main/assets

מציינים את הנתיב של המודל בתוך הפרמטר ModelAssetPath. בקוד לדוגמה, המודל מוגדר בקובץ PoseLandmarkerHelper.kt:

val modelName = "pose_landmarker_lite.task"
baseOptionsBuilder.setModelAssetPath(modelName)

יצירת המשימה

כדי להגדיר את המשימה, MediaPipe Pose לעשות שימוש בפונקציה createFromOptions(). הפונקציה createFromOptions() מקבלת ערכים לאפשרויות התצורה. מידע נוסף על אפשרויות ההגדרה זמין במאמר אפשרויות תצורה.

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

תמונה

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(modelName)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder = 
    poseLandmarker.poseLandmarkerOptions.builder()
        .setBaseOptions(baseOptionsBuilder.build())
        .setMinPoseDetectionConfidence(minPoseDetectionConfidence)
        .setMinTrackingConfidence(minPoseTrackingConfidence)
        .setMinPosePresenceConfidence(minposePresenceConfidence)
        .setNumPoses(maxNumPoses)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()
poseLandmarker = poseLandmarker.createFromOptions(context, options)
    

וידאו

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(modelName)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder = 
    poseLandmarker.poseLandmarkerOptions.builder()
        .setBaseOptions(baseOptionsBuilder.build())
        .setMinPoseDetectionConfidence(minPoseDetectionConfidence)
        .setMinTrackingConfidence(minPoseTrackingConfidence)
        .setMinPosePresenceConfidence(minposePresenceConfidence)
        .setNumPoses(maxNumPoses)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()
poseLandmarker = poseLandmarker.createFromOptions(context, options)
    

שידור חי

val baseOptionsBuilder = BaseOptions.builder().setModelAssetPath(modelName)
val baseOptions = baseOptionBuilder.build()

val optionsBuilder = 
    poseLandmarker.poseLandmarkerOptions.builder()
        .setBaseOptions(baseOptionsBuilder.build())
        .setMinPoseDetectionConfidence(minPoseDetectionConfidence)
        .setMinTrackingConfidence(minPoseTrackingConfidence)
        .setMinPosePresenceConfidence(minposePresenceConfidence)
        .setNumPoses(maxNumPoses)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.LIVE_STREAM)

val options = optionsBuilder.build()
poseLandmarker = poseLandmarker.createFromOptions(context, options)
    

הטמעת הקוד לדוגמה של Pose Scoreer מאפשרת למשתמש לעבור בין מצבי עיבוד. הגישה הזו יוצרת את הקוד ליצירת משימה ועשויה לא להתאים לתרחיש שלכם לדוגמה. אפשר לראות את הקוד הזה בפונקציה setupPoseLandmarker() בקובץ PoseLandmarkerHelper.kt.

אפשרויות הגדרה

המשימה כוללת את אפשרויות ההגדרה הבאות לאפליקציות ל-Android:

שם האפשרות תיאור טווח ערכים ערך ברירת מחדל
runningMode מגדיר את מצב הריצה של המשימה. יש שלושה מצבים:

IMAGE: המצב שבו ניתן להזין תמונה יחידה.

וידאו: המצב של פריימים מפוענחים של סרטון.

LIVE_STREAM: המצב עבור סטרימינג בשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, יש להפעיל את resultListener כדי להגדיר מאזין לקבלת תוצאות באופן אסינכרוני.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numposes המספר המקסימלי של תנוחות ש-Pose Scoreer יכול לזהות. Integer > 0 1
minPoseDetectionConfidence ציון המהימנות המינימלי שזיהוי התנוחה ייחשב כמוצלח. Float [0.0,1.0] 0.5
minPosePresenceConfidence ציון המהימנות המינימלי של ציון הנוכחות לתנוחה בזיהוי מיקום התנוחה. Float [0.0,1.0] 0.5
minTrackingConfidence ציון הסמך המינימלי שצריך לעמוד בו כדי לעקוב אחרי התנוחה כדי להיחשב בהצלחה. Float [0.0,1.0] 0.5
outputSegmentationMasks האם Pose לעשות פלט של מסכת פילוח לתנוחה שזוהתה. Boolean False
resultListener מגדיר את ה-listener כך שיקבל את התוצאות של ציוני הדרך באופן אסינכרוני כש-Pose landmarker נמצא במצב שידור חי. אפשר להשתמש רק כשמצב ריצה מוגדר ל-LIVE_STREAM ResultListener N/A
errorListener מגדירה האזנה לשגיאות כאופציונלי. ErrorListener N/A

הכנת הנתונים

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

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

תמונה

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(image).build()
    

וידאו

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

val argb8888Frame =
    if (frame.config == Bitmap.Config.ARGB_8888) frame
    else frame.copy(Bitmap.Config.ARGB_8888, false)

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(argb8888Frame).build()
    

שידור חי

import com.google.mediapipe.framework.image.BitmapImageBuilder
import com.google.mediapipe.framework.image.MPImage

// Convert the input Bitmap object to an MPImage object to run inference
val mpImage = BitmapImageBuilder(rotatedBitmap).build()
    

בקוד לדוגמה של Pose Scoreer, הכנת הנתונים מתבצעת בקובץ PoseLandmarkerHelper.kt.

מריצים את המשימה.

בהתאם לסוג הנתונים שאתם עובדים איתם, צריך להשתמש בשיטה poseLandmarker.detect...() שספציפית לסוג הנתונים הזה. צריך להשתמש ב-detect() לתמונות בודדות, ב-detectForVideo() לפריימים בקובצי וידאו וב-detectAsync() לסטרימינג של וידאו. כשאתם מבצעים זיהוי בשידור וידאו, הקפידו להריץ את הזיהויים בשרשור נפרד כדי למנוע חסימה של שרשור התגובות של המשתמש.

דוגמאות הקוד הבאות מדגימות איך להפעיל את Pose Scoreer במצבי הנתונים השונים:

תמונה

val result = poseLandmarker.detect(mpImage)
    

וידאו

val timestampMs = i * inferenceIntervalMs

poseLandmarker.detectForVideo(mpImage, timestampMs)
    .let { detectionResult ->
        resultList.add(detectionResult)
    }
    

שידור חי

val mpImage = BitmapImageBuilder(rotatedBitmap).build()
val frameTime = SystemClock.uptimeMillis()

poseLandmarker.detectAsync(mpImage, frameTime)
    

שימו לב לנקודות הבאות:

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

בקוד לדוגמה של Pose Targeter, הפונקציות detect, detectForVideo ו-detectAsync מוגדרות בקובץ PoseLandmarkerHelper.kt.

טיפול בתוצאות והצגתן

ה-Pose Scoreer מחזיר אובייקט poseLandmarkerResult בכל הרצת זיהוי. אובייקט התוצאה מכיל קואורדינטות עבור כל ציון דרך של תנוחה.

דוגמה לנתוני הפלט מהמשימה הזאת:

PoseLandmarkerResult:
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : 0.129959
      visibility   : 0.9999997615814209
      presence     : 0.9999984502792358
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
      visibility   : 0.999909
      presence     : 0.999958
    ... (33 landmarks per pose)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
      visibility   : 0.9999997615814209
      presence     : 0.9999984502792358
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
      visibility   : 0.999976
      presence     : 0.999998
    ... (33 world landmarks per pose)
  SegmentationMasks:
    ... (pictured below)

הפלט מכיל גם קואורדינטות מנורמלות (Landmarks) וגם קואורדינטות עולמיות (WorldLandmarks) לכל ציון דרך.

הפלט מכיל את הקואורדינטות המנורמלות הבאות (Landmarks):

  • x ו-y: קואורדינטות של ציוני דרך מנורמלות בין 0.0 ל-1.0 ברוחב התמונה (x) ובגובה (y).

  • z: העומק לציון דרך, כשהעומק בנקודת האמצע של הירכיים הוא המקור. ככל שהערך קטן יותר, ציון הדרך קרוב יותר למצלמה. הגודל של z משתמש בערך באותו קנה מידה כמו x.

  • visibility: הסבירות שציון הדרך גלוי בתמונה.

הפלט מכיל את הקואורדינטות הבאות מהעולם (WorldLandmarks):

  • x, y ו-z: קואורדינטות תלת-ממדיות בעולם האמיתי במטרים, עם נקודת האמצע של מותניים.

  • visibility: הסבירות שציון הדרך גלוי בתמונה.

התמונה הבאה מציגה המחשה של פלט המשימה:

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

הקוד לדוגמה של Pose Lander מדגים איך להציג את התוצאות שהוחזרו מהמשימה. פרטים נוספים זמינים בכיתה OverlayView.