מדריך להטמעת תמונות ל-Android

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

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

קוד לדוגמה

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

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

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

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

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

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

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

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

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

  • ImageEmbedderHelper.kt: הפונקציה הזו מפעילה את הכלי להטמעת תמונות ומטפלת בבחירת המודל וההענקה.
  • MainActivity.kt: הטמעת האפליקציה והרכבת רכיבי ממשק המשתמש.

הגדרה

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

יחסי תלות

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

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

דגם

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

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

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

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

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

יצירת המשימה

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

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

תמונה

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.IMAGE)
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

וידאו

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.VIDEO)
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

שידור חי

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setResultListener((result, inputImage) -> {
         // Process the embedding result here.
    })
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

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

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

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

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

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

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

LIVE_STREAM: המצב של סטרימינג בשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, צריך להפעיל את resultListener כדי להגדיר מאזין שיקבל את התוצאות באופן אסינכרוני.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
l2_normalize האם לבצע נורמליזציה של וקטור המאפיינים המוחזר באמצעות נורמלי L2. משתמשים באפשרות הזו רק אם המודל כבר לא מכיל פונקציית L2_NORMALIZATION מקורית של TFLite. ברוב המקרים, המצב הזה כבר קיים, ולכן נורמליזציה של L2 מתבצעת באמצעות היסק של TFLite ללא צורך באפשרות הזו. Boolean False
quantize האם להצפין את הטמעת הנתונים שחוזרת לבייטים באמצעות קידוד סקלר. ההנחה לגבי הטמעות היא שהן נורמליות ליחידה, ולכן לכל מאפיין מובטח ערך בטווח [-1.0, 1.0]. אם זה לא המצב, צריך להשתמש באפשרות l2_normalize. Boolean False
resultListener מגדיר את מאזין התוצאות לקבל את תוצאות ההטמעה באופן אסינכרוני כשהכלי להטמעת תמונות נמצא במצב של שידור חי. אפשר להשתמש בה רק כשמצב ההפעלה מוגדר כ-LIVE_STREAM לא רלוונטי לא מוגדר
errorListener הגדרת מאזין אופציונלי לשגיאות. לא רלוונטי לא מוגדר

הכנת הנתונים

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

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

תמונה

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

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

הרצת המשימה

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

תמונה

ImageEmbedderResult embedderResult = imageEmbedder.embed(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.
ImageEmbedderResult embedderResult =
    imageEmbedder.embedForVideo(image, frameTimestampMs);
    

שידור חי

// Run inference on the frame. The embedding results will be available
// via the `resultListener` provided in the `ImageEmbedderOptions` when
// the image embedder was created.
imageEmbedder.embedAsync(image, frameTimestampMs);
    

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

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

בקוד לדוגמה, הפונקציה embed מוגדרת בקובץ ImageEmbedderHelper.kt.

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

לאחר הפעלת ההסקה, המשימה Image Embedder מחזירה אובייקט ImageEmbedderResult שמכיל רשימה של הטמעות (נקודות צפות או סקלר-קוונטיזציה) של תמונת הקלט.

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

ImageEmbedderResult:
  Embedding #0 (sole embedding head):
    float_embedding: {0.0, 0.0, ..., 0.0, 1.0, 0.0, 0.0, 2.0}
    head_index: 0

התוצאה הזו התקבלה על ידי הטמעת התמונה הבאה:

צילום בינוני של חתול אקזוטי

אפשר להשוות את הדמיון בין שני הטמעות באמצעות הפונקציה ImageEmbedder.cosineSimilarity. דוגמה לקוד:

// Compute cosine similarity.
double similarity = ImageEmbedder.cosineSimilarity(
  result.embeddingResult().embeddings().get(0),
  otherResult.embeddingResult().embeddings().get(0));