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

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

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

קוד לדוגמה

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

אפשר להשתמש באפליקציה כנקודת התחלה לאפליקציה משלכם ל-Android, או להיעזר בה כשמשנים אפליקציה קיימת. קוד הדוגמה של Pose Landmarker מתארח ב-GitHub.

מורידים את הקוד

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

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

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

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

רכיבים מרכזיים

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

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

הגדרה

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

יחסי תלות

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

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

דגם

כדי לבצע את המשימה 'זיהוי נקודות ציון של תנוחות' ב-MediaPipe, נדרש חבילת מודל מאומנת שתואמת למשימה הזו. מידע נוסף על המודלים המאומנים הזמינים ל-Pose Landmarker זמין בקטע 'מודלים' בסקירה הכללית של המשימה.

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

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

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

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

יצירת המשימה

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

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

תמונה

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 Landmarker מאפשרת למשתמש לעבור בין מצבי העיבוד. הגישה הזו הופכת את הקוד ליצירת המשימות למורכב יותר, ויכול להיות שהיא לא מתאימה לתרחיש לדוגמה שלכם. הקוד הזה מופיע בפונקציה setupPoseLandmarker() בקובץ PoseLandmarkerHelper.kt.

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

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

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

IMAGE: המצב להזנת תמונה אחת.

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

LIVE_STREAM: המצב של סטרימינג בשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, צריך להפעיל את resultListener כדי להגדיר מאזין שיקבל את התוצאות באופן אסינכרוני.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numposes המספר המקסימלי של תנוחות שאפשר לזהות באמצעות התכונה Pose Landmarker. 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 Landmarker מפיקה מסכת פילוח לתנוחה שזוהתה. Boolean False
resultListener מגדיר את מאזין התוצאות לקבל את תוצאות ה-landmarker באופן אסינכרוני כש-Pose Landmarker נמצא במצב של שידור חי. אפשר להשתמש בה רק כשמצב ההפעלה מוגדר כ-LIVE_STREAM ResultListener N/A
errorListener הגדרת מאזין אופציונלי לשגיאות. ErrorListener N/A

הכנת הנתונים

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

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

תמונה

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 Landmarker, הכנת הנתונים מתבצעת בקובץ PoseLandmarkerHelper.kt.

הרצת המשימה

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

בדוגמאות הקוד הבאות מפורטות דוגמאות פשוטות להפעלת Pose Landmarker במצבי הנתונים השונים האלה:

תמונה

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 Landmarker.
  • כשהיא פועלת במצב תמונה או בסרטון, המשימה Pose Landmarker חוסמת את השרשור הנוכחי עד שהיא מסיימת לעבד את התמונה או את הפריים של הקלט. כדי למנוע חסימת המשתמש, מריצים את העיבוד בשרשור רקע.
  • כשהיא פועלת במצב של שידור חי, המשימה Pose Landmarker מחזירה תשובה באופן מיידי ולא חוסמת את השרשור הנוכחי. הוא יפעיל את מאזין התוצאות עם תוצאת הזיהוי בכל פעם שהוא יסיים לעבד פריים קלט.

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

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

ה-Pose Landmarker מחזיר אובייקט 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 Landmarker מוסבר איך להציג את התוצאות שמוחזרות מהמשימה. פרטים נוספים זמינים בכיתה OverlayView.