יצירת תמונות במכשיר ב-Android עם MediaPipe

1. מבוא

מהו MediaPipe?

MediaPipe Solutions מאפשר לכם להחיל פתרונות של למידת מכונה (ML) על האפליקציות שלכם. הוא מספק מסגרת להגדרת צינורות עיבוד מוכנים מראש שמספקים למשתמשים פלט מיידי, מעניין ומועיל. אפשר גם להתאים אישית הרבה מהפתרונות האלה באמצעות MediaPipe Model Maker כדי לעדכן את מודלי ברירת המחדל.

יצירת תמונות מטקסט היא אחת מכמה משימות למידת מכונה ש-MediaPipe Solutions מציעה.

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

מה תלמדו

  • איך מטמיעים יצירת תמונות מטקסט שפועלת באופן מקומי באפליקציה ל-Android באמצעות MediaPipe Tasks.

מה נדרש

  • גרסת Android Studio מותקנת (הקודלאב הזה נכתב ונבדק באמצעות Android Studio Giraffe).
  • מכשיר Android עם זיכרון RAM בנפח 8GB לפחות.
  • ידע בסיסי בפיתוח ל-Android ויכולת להריץ סקריפט Python שנכתב מראש.

2. הוספת משימות של MediaPipe לאפליקציה ל-Android

הורדת אפליקציית ההתחלה ל-Android

סדנת הקוד הזו תתחיל בתוכנית לדוגמה שנוצרה מראש, שכוללת את ממשק המשתמש שישמש ליצירת גרסת תמונה בסיסית. האפליקציה הזו מופיעה במאגר הרשמי של דוגמאות ל-MediaPipe כאן. כדי לשכפל את המאגר או להוריד את קובץ ה-zip, לוחצים על Code (קוד) > Download ZIP (הורדת קובץ ZIP).

ייבוא האפליקציה ל-Android Studio

  1. פותחים את Android Studio.
  2. במסך Welcome to Android Studio, בוחרים באפשרות Open (פתיחה) בפינה השמאלית העליונה.

a0b5b070b802e4ea.png

  1. עוברים למקום שבו שכפלתם או הורדת את המאגר ופותחים את הספרייה codelabs/image_generation_basic/android/start.
  2. בשלב הזה לא אמורה להתבצע הידור של האפליקציה כי עדיין לא הוספתם את התלות ב-MediaPipe Tasks.

כדי לתקן את האפליקציה ולהפעיל אותה, נכנסים לקובץ build.gradle וגלילים למטה אל // Step 1 - Add dependency. מכאן, כוללים את השורה הבאה ולוחצים על הלחצן Sync Now (סנכרון עכשיו) שמופיע בבאנר בחלק העליון של Android Studio.

// Step 1 - Add dependency
implementation 'com.google.mediapipe:tasks-vision-image-generator:latest.release'

בסיום הסנכרון, לוחצים על החץ הירוק run ( 7e15a9c9e1620fe7.png) בפינה השמאלית העליונה של Android Studio כדי לוודא שכל הקבצים נפתחו ושהותקנו בצורה תקינה. האפליקציה אמורה להיפתח למסך עם שני לחצני בחירה ולחצן עם הכיתוב INITIALIZE. אם לוחצים על הלחצן הזה, אמורה להופיע מיד הודעה בממשק משתמש נפרד עם הנחיה לטקסט ואפשרויות אחרות לצד הלחצן יצירה.

83c31de8e8a320ee.png 78b8765e832024e3.png

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

3. הגדרת מחולל התמונות

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

מתחת לאובייקט הזה תופיע פונקציה שנקראת initializeImageGenerator() עם ההערה הבאה: // Step 2 - initialize the image generator. כפי שאפשר לנחש, כאן תבצעו את האיניציאליזציה של האובייקט ImageGenerator. מחליפים את גוף הפונקציה בקוד הבא כדי להגדיר את הנתיב של מודל יצירת התמונות ולאתחל את האובייקט ImageGenerator:

// Step 2 - initialize the image generator
val options = ImageGeneratorOptions.builder()
    .setImageGeneratorModelDirectory(modelPath)
    .build()

imageGenerator = ImageGenerator.createFromOptions(context, options)

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

מחליפים את גוף הקוד של setInput()‎ (שבו מופיעה ההערה // Step 3 - accept inputs) בשורה הבאה:

// Step 3 - accept inputs
imageGenerator.setInputs(prompt, iteration, seed)

בשני השלבים הבאים מתבצע היצירה. הפונקציה generate() מקבלת את אותם קלטים כמו setInput, אבל יוצרת תמונה כקריאה חד-פעמית שלא מחזירה תמונות של שלבים ביניים. אפשר להחליף את גוף הפונקציה הזו (שכולל את התגובה // Step 4 - generate without showing iterations) בקוד הבא:

// Step 4 - generate without showing iterations
val result = imageGenerator.generate(prompt, iteration, seed)
val bitmap = BitmapExtractor.extract(result?.generatedImage())
return bitmap

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

השלב האחרון בקובץ הזה הוא מילוי הפונקציה execute()‎ (שסומן בתור שלב 5). הפונקציה תקבל פרמטר שמציין אם היא צריכה להחזיר קובץ אימג' ביניים או לא עבור השלב היחיד של היצירה שיתבצע באמצעות הפונקציה execute()‎ של ImageGenerator. מחליפים את גוף הפונקציה בקוד הזה:

// Step 5 - generate with iterations
val result = imageGenerator.execute(showResult)

if (result == null || result.generatedImage() == null) {
    return Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888)
        .apply {
            val canvas = Canvas(this)
            val paint = Paint()
            paint.color = Color.WHITE
            canvas.drawPaint(paint)
        }
}

val bitmap =
    BitmapExtractor.extract(result.generatedImage())

return bitmap

זהו, סיימתם עם קובץ העזרה. בקטע הבא ימלא את קובץ ה-ViewModel שמטפל בלוגיקה של הדוגמה הזו.

4. איך משלבים את האפליקציות

הקובץ MainViewModel יטפל במצבים של ממשק המשתמש ובלוגיקה אחרת שקשורה לאפליקציית הדוגמה הזו. אפשר לפתוח אותו עכשיו.

בחלק העליון של הקובץ אמורה להופיע ההערה // Step 6 - set model path. כאן תגדירו לאפליקציה איפה היא יכולה למצוא את קובצי המודל הנחוצים ליצירת תמונות. בדוגמה הזו, מגדירים את הערך כ-‎ /data/local/tmp/image_generator/bins/.

// Step 6 - set model path
private val MODEL_PATH = "/data/local/tmp/image_generator/bins/"

משם, גוללים למטה אל הפונקציה generateImage(). לקראת החלק התחתון של הפונקציה הזו יופיעו שלב 7 ושלב 8, שישמשו ליצירת תמונות עם חזרות שהוחזרו או ללא חזרות, בהתאמה. שתי הפעולות האלה מתרחשות סינכרונית, ולכן הן עטופות ב-coroutine. אפשר להתחיל על ידי החלפת הקוד // Step 7 - Generate without showing iterations בקוד הבא כדי לקרוא ל-generate() מהקובץ ImageGenerationHelper, ולאחר מכן לעדכן את מצב ממשק המשתמש.

// Step 7 - Generate without showing iterations
val result = helper?.generate(prompt, iteration, seed)
_uiState.update {
    it.copy(outputBitmap = result)
}

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

// Step 8 - Generate with showing iterations
helper?.setInput(prompt, iteration, seed)
for (step in 0 until iteration) {
    isDisplayStep =
        (displayIteration > 0 && ((step + 1) % displayIteration == 0))
    val result = helper?.execute(isDisplayStep)

    if (isDisplayStep) {
        _uiState.update {
            it.copy(
                outputBitmap = result,
                generatingMessage = "Generating... (${step + 1}/$iteration)",
            )
        }
    }
}

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

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

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

5. פריסה ובדיקה של האפליקציה

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

  1. לוחצים על סמל ההפעלה ( 7e15a9c9e1620fe7.png) בסרגל הכלים של Android Studio כדי להפעיל את האפליקציה.
  2. בוחרים את סוג שלבי היצירה (סופיים או עם איטרציות) ולוחצים על הלחצן INITIALIZE.
  3. במסך הבא, מגדירים את המאפיינים הרצויים ולוחצים על הלחצן יצירה כדי לראות מה הכלי יוצר.

e46cfaeb9d3fc235.gif

6. מעולה!

כל הכבוד! בסדנת הקוד הזו למדתם איך להוסיף לאפליקציית Android יצירת תמונות מטקסט במכשיר.

השלבים הבאים

יש עוד דברים שאפשר לעשות עם המשימה של יצירת תמונות, כולל:

  • באמצעות תמונה בסיסית כדי לבנות את התמונות שנוצרו באמצעות יישומי פלאגין, או לאמן משקלים נוספים של LoRA באמצעות Vertex AI.
  • שימוש ב-Firebase Storage כדי לאחזר קובצי מודלים במכשיר בלי צורך בכלי ADB.

אנחנו מחכים לראות את כל הדברים המגניבים שתיצרו בעזרת המשימה הניסיונית הזו. כדאי לעקוב אחרי עוד קורסים ב-Codelab ותוכן של צוות MediaPipe.