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

המשימה 'זיהוי ציוני דרך של ידיים' ב-MediaPipe מאפשרת לזהות את ציוני הדרך של הידיים בתמונה. בהוראות הבאות מוסבר איך להשתמש ב-Hand Landmarker עם אפליקציות ל-Android. דוגמת הקוד שמתוארת בהוראות האלה זמינה ב-GitHub.

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

קוד לדוגמה

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

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

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

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

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

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

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

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

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

  • HandLandmarkerHelper.kt – הפונקציה מפעילה את השירות לזיהוי נקודות ציון ביד ומטפלת בבחירת המודל והנציג.
  • MainActivity.kt – הטמעת האפליקציה, כולל קריאה ל-HandLandmarkerHelper.

הגדרה

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

יחסי תלות

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

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

דגם

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

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

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

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

baseOptionBuilder.setModelAssetPath(MP_HAND_LANDMARKER_TASK)

יצירת המשימה

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

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

תמונה

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

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.IMAGE)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

וידאו

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

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

שידור חי

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

val optionsBuilder =
    HandLandmarker.HandLandmarkerOptions.builder()
        .setBaseOptions(baseOptions)
        .setMinHandDetectionConfidence(minHandDetectionConfidence)
        .setMinTrackingConfidence(minHandTrackingConfidence)
        .setMinHandPresenceConfidence(minHandPresenceConfidence)
        .setNumHands(maxNumHands)
        .setResultListener(this::returnLivestreamResult)
        .setErrorListener(this::returnLivestreamError)
        .setRunningMode(RunningMode.VIDEO)

val options = optionsBuilder.build()

handLandmarker =
    HandLandmarker.createFromOptions(context, options)
    

הטמעת הקוד לדוגמה של Hand Landmarker מאפשרת למשתמש לעבור בין מצבי העיבוד. הגישה הזו הופכת את הקוד ליצירת המשימות למורכב יותר, ויכול להיות שהיא לא מתאימה לתרחיש לדוגמה שלכם. הקוד הזה מופיע בפונקציה setupHandLandmarker() בקובץ HandLandmarkerHelper.kt.

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

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

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

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

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

LIVE_STREAM: המצב של סטרימינג בשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, צריך להפעיל את resultListener כדי להגדיר מאזין שיקבל את התוצאות באופן אסינכרוני.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numHands המספר המקסימלי של ידיים שזוהו על ידי הגלאי של נקודות ציון ביד. Any integer > 0 1
minHandDetectionConfidence ציון האמון המינימלי שדרוש כדי שזיהוי היד ייחשב מוצלח במודל לזיהוי כף היד. 0.0 - 1.0 0.5
minHandPresenceConfidence דירוג האמון המינימלי של דירוג נוכחות היד במודל לזיהוי ציוני ציון ביד. במצב וידאו ובמצב שידור חי, אם דירוג האמינות של נוכחות היד ממודל ציון הנקודות ביד נמוך מהסף הזה, הכלי לזיהוי נקודות ביד מפעיל את מודל זיהוי כף היד. אחרת, אלגוריתם קל למעקב אחר הידיים קובע את המיקום שלהן לצורך זיהוי של נקודות ציון בהמשך. 0.0 - 1.0 0.5
minTrackingConfidence דירוג האמון המינימלי שדרוש כדי שהמעקב אחר הידיים יחשב כהצלחה. זהו סף IoU של תיבת ההיקף בין הידיים בפריים הנוכחי לבין הפריים האחרון. במצב וידאו ובמצב סטרימינג של Hand Landmarker, אם המעקב נכשל, התכונה Hand Landmarker מפעילה זיהוי של היד. אחרת, המערכת תדלג על זיהוי היד. 0.0 - 1.0 0.5
resultListener מגדיר את מאזין התוצאות כך שיקבל את תוצאות הזיהוי באופן אסינכרוני כשכלי הסימון של כף היד נמצא במצב של שידור חי. רלוונטי רק כשמצב ההפעלה מוגדר כ-LIVE_STREAM לא רלוונטי לא רלוונטי
errorListener הגדרת מאזין אופציונלי לשגיאות. לא רלוונטי לא רלוונטי

הכנת הנתונים

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

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

תמונה

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()
    

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

הרצת המשימה

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

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

תמונה

val result = handLandmarker?.detect(mpImage)
    

וידאו

val timestampMs = i * inferenceIntervalMs

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

שידור חי

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

handLandmarker?.detectAsync(mpImage, frameTime)
    

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

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

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

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

ה-Hand Landmarker יוצר אובייקט תוצאה של Hand Landmarker לכל הפעלה של זיהוי. אובייקט התוצאה מכיל ציוני ציון של היד בקואורדינטות התמונה, ציוני ציון של היד בקואורדינטות העולם ואת היד הדומיננטית(יד ימין/יד שמאל) של הידיים שזוהו.

בהמשך מוצגת דוגמה לנתוני הפלט של המשימה הזו:

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

  • יד דומיננטית

    היד הדומיננטית מייצגת אם הידיים שזוהו הן יד שמאל או יד ימין.

  • ציוני דרך

    יש 21 נקודות ציון ביד, כל אחת מורכבת מהקואורדינטות x, ‏ y ו-z. הקואורדינטות x ו-y מתכווננות לטווח [0.0, 1.0] לפי רוחב התמונה וגובהה, בהתאמה. הקואורדינטה z מייצגת את עומק ציון הדרך, כאשר עומק פרק כף היד הוא המקור. ככל שהערך קטן יותר, כך ציון הדרך קרוב יותר למצלמה. הערך של z משתמש בערך באותו סולם כמו x.

  • אתרים חשובים בעולם

    21 נקודות הציון של היד מוצגות גם בקואורדינטות גלובליות. כל ציון דרך מורכב מ-x, ‏ y ו-z, שמייצגים קואורדינטות תלת-ממדיות בעולם האמיתי במטרים, כאשר המקור נמצא במרכז הגיאומטרי של היד.

HandLandmarkerResult:
  Handedness:
    Categories #0:
      index        : 0
      score        : 0.98396
      categoryName : Left
  Landmarks:
    Landmark #0:
      x            : 0.638852
      y            : 0.671197
      z            : -3.41E-7
    Landmark #1:
      x            : 0.634599
      y            : 0.536441
      z            : -0.06984
    ... (21 landmarks for a hand)
  WorldLandmarks:
    Landmark #0:
      x            : 0.067485
      y            : 0.031084
      z            : 0.055223
    Landmark #1:
      x            : 0.063209
      y            : -0.00382
      z            : 0.020920
    ... (21 world landmarks for a hand)

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

יד עם אגודל מורם ותצוגה של מבנה השלד של היד

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