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

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

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

קוד לדוגמה

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

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

הורדת הקוד

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

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

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

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

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

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

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

הגדרה

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

יחסי תלות

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

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

מודל

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

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

<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: המצב שבו ניתן להזין תמונה יחידה.

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

LIVE_STREAM: המצב עבור סטרימינג בשידור חי של נתוני קלט, למשל ממצלמה. במצב הזה, יש להפעיל את resultListener כדי להגדיר מאזין לקבלת תוצאות באופן אסינכרוני.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
l2_normalize האם לנרמל את וקטור התכונה שמוחזר עם נורמת L2. יש להשתמש באפשרות הזו רק אם המודל עדיין לא מכיל L2_NORMALIZATION TFLite Op. . ברוב המקרים, זה כבר המצב, וכתוצאה מכך מתקבל מסקנות של TFLite, ללא צורך באפשרות הזו. Boolean False
quantize האם יש לכמת את ההטמעה שמוחזרת לבייטים באמצעות קוונטיזציה סקלרית. ההנחה היא שההטמעות הן נורמת יחידה, ולכן לכל מאפיין מובטח שיהיה ערך [ -1.0, 1.0]. אם זה לא המצב, מומלץ להשתמש באפשרות l2_Normalize. Boolean False
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 user’s device as a Bitmap object using BitmapFactory.

// Convert an Android’s Bitmap object to a MediaPipe’s 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 video’s metadata, load the METADATA_KEY_DURATION and
// METADATA_KEY_VIDEO_FRAME_COUNT value. You’ll need them
// to calculate the timestamp of each frame later.

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

// Convert the Android’s Bitmap object to a MediaPipe’s Image object.
Image mpImage = new BitmapImageBuilder(frame).build();
    

שידור חי

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

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

// For each Android’s ImageProxy object received from the ImageAnalysis,
// extract the encapsulated Android’s Image object and convert it to
// a MediaPipe’s 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.

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

לאחר הרצה של ההסקה, המשימה של הטמעת תמונות מחזירה אובייקט 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));