מדריך לזיהוי אובייקטים ב-Android

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

קוד לדוגמה

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

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

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

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

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

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

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

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

הקבצים הבאים מכילים את הקוד החשוב לאפליקציית הדוגמה של Object Detector:

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

הגדרה

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

יחסי תלות

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

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

דגם

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

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

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

משתמשים ב-method‏ BaseOptions.Builder.setModelAssetPath() כדי לציין את הנתיב שבו המודל משתמש. דוגמה לקוד מופיעה בקטע הבא.

יצירת המשימה

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

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

תמונה

ObjectDetectorOptions options =
  ObjectDetectorOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath(‘model.tflite’).build())
    .setRunningMode(RunningMode.IMAGE)
    .setMaxResults(5)
    .build();
objectDetector = ObjectDetector.createFromOptions(context, options);
    

וידאו

ObjectDetectorOptions options =
  ObjectDetectorOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath(‘model.tflite’).build())
    .setRunningMode(RunningMode.VIDEO)
    .setMaxResults(5)
    .build();
objectDetector = ObjectDetector.createFromOptions(context, options);
    

שידור חי

ObjectDetectorOptions options =
  ObjectDetectorOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath(model.tflite).build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setMaxResults(5)
    .setResultListener((result, inputImage) -> {
      // Process the detection result here.
    })
    .setErrorListener((result, inputImage) -> {
      // Process the classification errors here.
    })
   .build();
objectDetector = ObjectDetector.createFromOptions(context, options);
    

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

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

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

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

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

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

LIVE_STREAM: המצב של סטרימינג בשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, צריך להפעיל את resultListener כדי להגדיר מאזין שיקבל את התוצאות באופן אסינכרוני.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
displayNamesLocales הגדרת השפה של התוויות לשימוש בשמות התצוגה שסופקו במטא-נתונים של מודל המשימה, אם הם זמינים. ברירת המחדל היא en לאנגלית. אפשר להוסיף תוויות מותאמות לשוק המקומי למטא-נתונים של מודל מותאם אישית באמצעות TensorFlow Lite Metadata Writer API קוד לוקאל en
maxResults מגדיר את המספר המקסימלי האופציונלי של תוצאות זיהוי עם הדירוג הגבוה ביותר שיוחזר. מספרים חיוביים -1 (כל התוצאות מוצגות)
scoreThreshold הגדרת הסף של ציון התחזית, שמבטל את הסף שצוין במטא-נתונים של המודל (אם יש כזה). תוצאות מתחת לערך הזה נדחות. כל ערך צף לא מוגדר
categoryAllowlist הגדרת רשימה אופציונלית של שמות קטגוריות מותרים. אם הערך לא ריק, תוצאות הזיהוי ששם הקטגוריה שלהן לא נמצא בקבוצה הזו יוסר. המערכת מתעלמת משמות קטגוריות כפולים או לא מוכרים. האפשרות הזו לא תואמת לאפשרות categoryDenylist, ושימוש בשתיהן גורם לשגיאה. מחרוזות כלשהן לא מוגדר
categoryDenylist הגדרת רשימה אופציונלית של שמות קטגוריות אסורים. אם הערך לא ריק, תוצאות הזיהוי ששם הקטגוריה שלהן נמצא בקבוצה הזו יסוננו. המערכת מתעלמת משמות קטגוריות כפולים או לא מוכרים. האפשרות הזו לא תואמת לאפשרות categoryAllowlist, ושימוש בשתיהן גורם לשגיאה. מחרוזות כלשהן לא מוגדר
resultListener מגדיר את מאזין התוצאות לקבלת תוצאות הזיהוי באופן אסינכרוני כשגלאי האובייקטים נמצא במצב של שידור חי. אפשר להשתמש באפשרות הזו רק כשמגדירים את runningMode לערך LIVE_STREAM. לא רלוונטי לא מוגדר

הכנת הנתונים

צריך להמיר את התמונה או את המסגרת של הקלט לאובייקט com.google.mediapipe.framework.image.MPImage לפני שמעבירים אותו ל-Object Detector.

הדוגמאות הבאות ממחישות איך להכין נתונים לעיבוד לכל אחד מסוגי הנתונים הזמינים:

תמונה

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

// Load an image on the users device as a Bitmap object using BitmapFactory.

// Convert an Androids Bitmap object to a MediaPipes Image object.
Image mpImage = new BitmapImageBuilder(bitmap).build();
    

וידאו

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

// Load a video file on the user's device using MediaMetadataRetriever

// From the videos metadata, load the METADATA_KEY_DURATION and
// METADATA_KEY_VIDEO_FRAME_COUNT values. Use these values
// to calculate the timestamp of each frame later.

// Loop through the video and load each frame as a Bitmap object.

// Convert the Androids Bitmap object to a MediaPipes Image object.
Image mpImage = new BitmapImageBuilder(frame).build();
    

שידור חי

import com.google.mediapipe.framework.image.MediaImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Create a CameraXs ImageAnalysis to continuously receive frames
// from the devices camera. Configure it to output frames in RGBA_8888
// format to match with what is required by the model.

// For each Androids ImageProxy object received from the ImageAnalysis,
// extract the encapsulated Androids Image object and convert it to
// a MediaPipes Image object.
android.media.Image mediaImage = imageProxy.getImage()
MPImage mpImage = new MediaImageBuilder(mediaImage).build();
    

בקוד לדוגמה של גלאי האובייקטים, הכנת הנתונים מתבצעת בכיתה ObjectDetectorHelper בפונקציות detectImage(),‏ detectVideoFile() ו-detectLivestreamFrame().

הרצת המשימה

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

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

תמונה

ObjectDetectorResult detectionResult = objectDetector.detect(image);
    

וידאו

// Calculate the timestamp in milliseconds of the current frame.
long frame_timestamp_ms = 1000 * video_duration * frame_index / frame_count;

// Run inference on the frame.
ObjectDetectorResult detectionResult =
    objectDetector.detectForVideo(image, frameTimestampMs);
    

שידור חי

// Run inference on the frame. The detection results will be available
// via the `resultListener` provided in the `ObjectDetectorOptions` when
// the object detector was created.
objectDetector.detectAsync(image, frameTimestampMs);
    

בדוגמת הקוד של גלאי האובייקטים מוצגות בפירוט ההטמעות של כל אחד מהמצבים האלה: detect(),‏ detectVideoFile() ו-detectAsync(). קוד הדוגמה מאפשר למשתמש לעבור בין מצבי עיבוד, ויכול להיות שהוא לא נדרש בתרחיש לדוגמה שלכם.

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

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

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

לאחר הפעלת ההסקה, המשימה 'זיהוי אובייקטים' מחזירה אובייקט ObjectDetectorResult שמתאר את האובייקטים שהיא מצאה בתמונה הקלט.

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

ObjectDetectorResult:
 Detection #0:
  Box: (x: 355, y: 133, w: 190, h: 206)
  Categories:
   index       : 17
   score       : 0.73828
   class name  : dog
 Detection #1:
  Box: (x: 103, y: 15, w: 138, h: 369)
  Categories:
   index       : 17
   score       : 0.73047
   class name  : dog

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

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

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