מדריך לסיווג תמונות ב-Android

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

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

קוד לדוגמה

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

אפשר להשתמש באפליקציה כנקודת התחלה לאפליקציית Android משלכם, או להיעזר בה כשמשנים אפליקציה קיימת. קוד הדוגמה של Image Classifier מתארח ב-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/image_classification/android
    

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

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

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

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

הגדרה

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

יחסי תלות

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

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

דגם

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

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

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

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

בקוד לדוגמה של Image Classifier, המודל מוגדר בקובץ ImageClassifierHelper.kt.

יצירת המשימה

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

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

תמונה

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.IMAGE)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

וידאו

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.VIDEO)
    .setMaxResults(5)
    .build();
imageClassifier = ImageClassifier.createFromOptions(context, options);
    

שידור חי

ImageClassifierOptions options =
  ImageClassifierOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setMaxResults(5)
    .setResultListener((result, inputImage) -> {
         // Process the classification result here.
    })
    .setErrorListener((result, inputImage) -> {
         // Process the classification errors here.
    })
    .build()
imageClassifier = ImageClassifier.createFromOptions(context, options)
    

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

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

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

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

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

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

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

הכנת הנתונים

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

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

תמונה

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 value. Youll need them
// 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()
Image mpImage = new MediaImageBuilder(mediaImage).build();
    

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

הרצת המשימה

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

תמונה

ImageClassifierResult classifierResult = imageClassifier.classify(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.
ImageClassifierResult classifierResult =
    imageClassifier.classifyForVideo(image, frameTimestampMs);
    

שידור חי

// Run inference on the frame. The classifications results will be available 
// via the `resultListener` provided in the `ImageClassifierOptions` when 
// the image classifier was created.
imageClassifier.classifyAsync(image, frameTimestampMs);
    

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

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

בקוד לדוגמה של סיווג תמונות, הפונקציות classify מוגדרות בקובץ ImageClassifierHelper.kt.

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

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

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

ImageClassifierResult:
 Classifications #0 (single classification head):
  head index: 0
  category #0:
   category name: "/m/01bwb9"
   display name: "Passer domesticus"
   score: 0.91406
   index: 671
  category #1:
   category name: "/m/01bwbt"
   display name: "Passer montanus"
   score: 0.00391
   index: 670

התוצאה הזו התקבלה בהפעלה של Bird Classifier:

תמונה תקריב של דרור בית

בקוד לדוגמה של סיווג תמונות, הכיתה ClassificationResultsAdapter בקובץ ClassificationResultsAdapter.kt מטפלת בתוצאות:

fun updateResults(imageClassifierResult: ImageClassifierResult? = null) {
    categories = MutableList(adapterSize) { null }
    if (imageClassifierResult != null) {
        val sortedCategories = imageClassifierResult.classificationResult()
            .classifications()[0].categories().sortedBy { it.index() }
        val min = kotlin.math.min(sortedCategories.size, categories.size)
        for (i in 0 until min) {
            categories[i] = sortedCategories[i]
        }
    }
}