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

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

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

קוד לדוגמה

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

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

להורדת הקוד

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

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

  1. משכפלים את מאגר ה-Git באמצעות הפקודה הבאה:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. אפשר גם להגדיר את ה-Git למכונת ה-Git לשימוש בקופה מועטה, יהיו לכם רק את הקבצים של האפליקציה לדוגמה של מסווג התמונות:
    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 – טיפול בתוצאות ובעיצוב שלהן.

הגדרה

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

יחסי תלות

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

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

דגם

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

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

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

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

ב קוד לדוגמה של מסווג תמונות, המודל מוגדר ב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: המצב לקלט של תמונה יחידה.

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

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

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

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