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

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

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

קוד לדוגמה

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

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

הורדת הקוד

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

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

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

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

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

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

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

הגדרה

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

יחסי תלות

המשימה 'ציוני דרך ביד' משתמשת בספרייה 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' משתמשת בפונקציה createFromOptions() כדי להגדיר את המשימה. הפונקציה createFromOptions() מקבלת ערכים לאפשרויות התצורה. מידע נוסף על אפשרויות ההגדרה זמין במאמר אפשרויות תצורה.

Hand Scoreer תומך ב-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 Lander מאפשרת למשתמש לעבור בין מצבי עיבוד. הגישה הזו יוצרת את הקוד ליצירת משימה ועשויה לא להתאים לתרחיש שלכם לדוגמה. אפשר לראות את הקוד הזה בפונקציה setupHandLandmarker() בקובץ HandLandmarkerHelper.kt.

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

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

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

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

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

LIVE_STREAM: המצב עבור סטרימינג בשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, יש להפעיל את resultListener כדי להגדיר מאזין לקבלת תוצאות באופן אסינכרוני.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
numHands מספר הידיים המקסימלי שזוהה על ידי מזהה המקום של Hand. Any integer > 0 1
minHandDetectionConfidence ציון המהימנות המינימלי שזיהוי היד ייחשב כמוצלח במודל של זיהוי כף היד. 0.0 - 1.0 0.5
minHandPresenceConfidence ציון המהימנות המינימלי של ציון הנוכחות של היד במודל הזיהוי של ציון הדרך. במצב וידאו ובמצב שידור חי, אם דירוג המהימנות של הנוכחות של היד של המודל של ציון הדרך במכשיר נמוך מהסף הזה, ה-hand המצבer מפעיל את המודל לזיהוי כף היד. אחרת, נעשה שימוש באלגוריתם קליל למעקב אחר הידיים כדי לקבוע את המיקום של הידיים כדי לזהות ציוני דרך עתידיים. 0.0 - 1.0 0.5
minTrackingConfidence ציון הסמך המינימלי שצריך לעמוד בו כדי שהמעקב אחרי היד ייחשבו בהצלחה. זהו סף ה-IoU של התיבה התוחמת בין הידיים במסגרת הנוכחית לפריים האחרון. במצב וידאו ובמצב 'סטרימינג' של Hand שבהמשך יהיה, אם המעקב ייכשל, ה-Handhander יפעיל את זיהוי היד. אחרת, יתבצע דילוג על זיהוי היד. 0.0 - 1.0 0.5
resultListener מגדיר את הכלי להאזנה לתוצאות כך שיקבל את תוצאות הזיהוי באופן אסינכרוני כאשר ה-placeholder של היד נמצא במצב שידור חי. רלוונטי רק כשמצב ריצה מוגדר ל-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()
    

בקוד לדוגמה של Hand Lander, הכנת הנתונים מטופלת בקובץ HandLandmarkerHelper.kt.

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

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

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

תמונה

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

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

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

ה-Handhander יוצר אובייקט תוצאה של placeholder של יד לכל הרצה של זיהוי. אובייקט התוצאה מכיל ציוני דרך של ידיים בקואורדינטות של תמונות, סימנים לציון ידיים בקואורדינטות בעולם והידיות השמאלית/ימנית של הידיים שזוהו.

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

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