تولید تصویر روی دستگاه در اندروید با MediaPipe

۱. مقدمه

مدیاپایپ چیست؟

MediaPipe Solutions به شما امکان می‌دهد راه‌حل‌های یادگیری ماشینی (ML) را در برنامه‌های خود اعمال کنید. این ابزار چارچوبی برای پیکربندی خطوط پردازش از پیش ساخته شده فراهم می‌کند که خروجی فوری، جذاب و مفید را به کاربران ارائه می‌دهد. شما حتی می‌توانید بسیاری از این راه‌حل‌ها را با MediaPipe Model Maker سفارشی کنید تا مدل‌های پیش‌فرض را به‌روزرسانی کنید.

تولید متن به تصویر یکی از چندین وظیفه یادگیری ماشینی است که MediaPipe Solutions ارائه می‌دهد.

در این Codelab، شما با یک برنامه اندروید تقریباً ساده شروع خواهید کرد، سپس مراحل مختلفی را طی خواهید کرد تا بتوانید تصاویر جدید را مستقیماً روی دستگاه اندروید خود تولید کنید.

آنچه یاد خواهید گرفت

  • نحوه پیاده‌سازی تبدیل متن به تصویر به صورت محلی در یک برنامه اندروید با MediaPipe Tasks .

آنچه نیاز دارید

  • یک نسخه نصب شده از اندروید استودیو (این آزمایشگاه کد با Android Studio Giraffe نوشته و آزمایش شده است).
  • یک دستگاه اندرویدی با حداقل ۸ گیگابایت رم.
  • آشنایی اولیه با توسعه اندروید و توانایی اجرای اسکریپت‌های پایتون از پیش نوشته شده.

۲. وظایف MediaPipe را به برنامه اندروید اضافه کنید

برنامه شروع اندروید را دانلود کنید

این آزمایشگاه کد با یک نمونه از پیش ساخته شده شامل رابط کاربری که برای نسخه اولیه تولید تصویر استفاده خواهد شد، شروع می‌شود. می‌توانید برنامه شروع را در مخزن رسمی MediaPipe Samples اینجا پیدا کنید. مخزن را کپی کنید یا فایل زیپ را با کلیک روی Code > Download ZIP دانلود کنید.

وارد کردن برنامه به اندروید استودیو

  1. اندروید استودیو را باز کنید.
  2. از صفحه خوش آمدید به اندروید استودیو ، گزینه Open را در گوشه بالا سمت راست انتخاب کنید.

a0b5b070b802e4ea.png

  1. به جایی که مخزن را کلون یا دانلود کرده‌اید بروید و دایرکتوری codelabs/image_generation_basic/android/start را باز کنید.
  2. در این مرحله، برنامه نباید کامپایل شود زیرا هنوز وابستگی MediaPipe Tasks را اضافه نکرده‌اید.

شما می‌توانید با رفتن به فایل build.gradle و اسکرول کردن به پایین تا رسیدن به // Step 1 - Add dependency، برنامه را تعمیر کرده و آن را اجرا کنید. از آنجا، خط زیر را وارد کنید و سپس دکمه Sync Now را که در بنر بالای اندروید استودیو ظاهر می‌شود، بزنید.

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

پس از اتمام همگام‌سازی، با کلیک روی فلش سبز رنگ () تأیید کنید که همه چیز به درستی باز و نصب شده است. 7e15a9c9e1620fe7.png ) در بالا سمت راست اندروید استودیو. باید برنامه را در صفحه‌ای با دو دکمه رادیویی و دکمه‌ای با برچسب INITIALIZE باز کنید. اگر روی آن دکمه کلیک کنید، باید بلافاصله به یک رابط کاربری جداگانه شامل یک اعلان متنی و گزینه‌های دیگر در کنار دکمه‌ای با برچسب GENERATE منتقل شوید.

83c31de8e8a320ee.png78b8765e832024e3.png

متأسفانه این تقریباً مربوط به برنامه‌ی آغازین است، بنابراین وقت آن رسیده است که یاد بگیرید چگونه این برنامه را تمام کنید و شروع به تولید تصاویر جدید در دستگاه خود کنید!

۳. راه‌اندازی مولد تصویر

برای این مثال، بخش عمده‌ی کار تولید تصویر در فایل ImageGenerationHelper.kt انجام می‌شود. وقتی این فایل را باز می‌کنید، متوجه متغیری در بالای کلاس به نام imageGenerator خواهید شد. این شیء Task است که کارهای سنگین را در برنامه‌ی تولید تصویر شما انجام می‌دهد.

درست زیر آن شیء، تابعی به نام initializeImageGenerator() با توضیح زیر خواهید دید: // مرحله 2 - مقداردهی اولیه مولد تصویر. همانطور که ممکن است حدس بزنید، اینجا جایی است که شما شیء ImageGenerator را مقداردهی اولیه می‌کنید. بدنه آن تابع را با کد زیر جایگزین کنید تا مسیر مدل تولید تصویر را تنظیم کرده و شیء ImageGenerator را مقداردهی اولیه کنید:

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

imageGenerator = ImageGenerator.createFromOptions(context, options)

در زیر آن تابع دیگری به نام setInput() را مشاهده خواهید کرد. این تابع سه پارامتر را می‌پذیرد: یک رشته اعلان که برای تعریف تصویر تولید شده استفاده می‌شود، تعداد تکرارهایی که Task باید هنگام تولید تصویر جدید طی کند، و یک مقدار اولیه که می‌تواند برای ایجاد نسخه‌های جدید از یک تصویر بر اساس همان اعلان هنگام تولید همان تصویر هنگام استفاده از همان seed استفاده شود. هدف این تابع تنظیم این پارامترهای اولیه برای مولد تصویر است، زمانی که سعی در ایجاد تصویری دارید که مراحل میانی را نمایش می‌دهد .

ادامه دهید و بدنه‌ی setInput() (جایی که کامنت // مرحله ۳ - پذیرش ورودی‌ها را خواهید دید) را با این خط جایگزین کنید:

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

دو مرحله بعدی جایی است که تولید رخ می‌دهد. تابع generate() همان ورودی‌های setInput را می‌پذیرد، اما تصویری را به صورت یک فراخوانی یک‌باره ایجاد می‌کند که هیچ تصویر مرحله میانی را برنمی‌گرداند. می‌توانید بدنه این تابع (که شامل کامنت // مرحله ۴ - تولید بدون نمایش تکرارها است) را با کد زیر جایگزین کنید:

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

مهم است بدانید که این کار به صورت همزمان انجام می‌شود، بنابراین باید تابع را از یک نخ پس‌زمینه فراخوانی کنید. کمی بعد در این آزمایشگاه کد، اطلاعات بیشتری در مورد آن خواهید آموخت.

آخرین مرحله‌ای که در این فایل انجام خواهید داد، پر کردن تابع execute() (با برچسب مرحله ۵) است. این تابع پارامتری را می‌پذیرد که به آن می‌گوید آیا باید یک تصویر میانی را برای تک مرحله تولید که با تابع 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 را که منطق این مثال را مدیریت می‌کند، پر خواهید کرد.

۴. یکپارچه‌سازی برنامه

فایل 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() برسید. در پایین این تابع، هر دو مرحله ۷ و ۸ را خواهید دید که به ترتیب برای تولید تصاویر با تکرارهای برگشتی یا بدون تکرار استفاده می‌شوند. از آنجایی که هر دوی این عملیات به صورت همزمان اتفاق می‌افتند، متوجه خواهید شد که آنها در یک کوروتین قرار گرفته‌اند. می‌توانید با جایگزینی // مرحله ۷ - تولید بدون نمایش تکرارها با این بلوک کد شروع کنید تا generate() را از فایل ImageGenerationHelper فراخوانی کنید، سپس وضعیت رابط کاربری را به‌روزرسانی کنید.

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

مرحله ۸ کمی پیچیده‌تر می‌شود. از آنجا که تابع 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 را طوری تنظیم کرد که فایل‌های لازم را مستقیماً در زمان اجرا روی دستگاه کاربر دانلود کند.

۵. اپلیکیشن را مستقر و آزمایش کنید

بعد از همه این‌ها، شما باید یک برنامه‌ی کاربردی داشته باشید که بتواند یک پیام متنی را بپذیرد و تصاویر جدید را کاملاً روی دستگاه تولید کند! برنامه را برای آزمایش روی یک دستگاه اندروید فیزیکی مستقر کنید، البته به یاد داشته باشید که باید این کار را با دستگاهی با حداقل ۸ گیگابایت حافظه امتحان کنید.

  1. روی اجرا کلیک کنید ( 7e15a9c9e1620fe7.png ) را در نوار ابزار اندروید استودیو برای اجرای برنامه انتخاب کنید.
  2. نوع مراحل تولید (نهایی یا با تکرار) را انتخاب کنید و سپس دکمه INITIALIZE را فشار دهید.
  3. در صفحه بعد، هر ویژگی که می‌خواهید را تنظیم کنید و روی دکمه GENERATE کلیک کنید تا ببینید ابزار چه چیزی را نمایش می‌دهد.

e46cfaeb9d3fc235.gif

۶. تبریک می‌گویم!

شما موفق شدید! در این آزمایشگاه کد، یاد گرفتید که چگونه قابلیت تبدیل متن به تصویر را به یک برنامه اندروید اضافه کنید.

مراحل بعدی

کارهای بیشتری می‌توانید با وظیفه تولید تصویر انجام دهید، از جمله

  • با استفاده از یک تصویر پایه برای ساختاردهی تصاویر تولید شده از طریق افزونه‌ها، یا آموزش وزن‌های اضافی LoRA خود از طریق Vertex AI.
  • از Firebase Storage برای بازیابی فایل‌های مدل روی دستگاه خود بدون نیاز به استفاده از ابزار ADB استفاده کنید.

ما مشتاقانه منتظر دیدن همه چیزهای جالبی هستیم که با این کار آزمایشی می‌سازید، و منتظر آزمایشگاه‌های کد و محتوای بیشتر از تیم MediaPipe باشید!