Tạo hình ảnh trên thiết bị trên Android bằng MediaPipe

1. Giới thiệu

MediaPipe là gì?

Giải pháp MediaPipe cho phép bạn áp dụng các giải pháp học máy (ML) cho ứng dụng của mình. Thư viện này cung cấp một khung để định cấu hình quy trình xử lý tạo sẵn, giúp mang lại kết quả tức thì, hấp dẫn và hữu ích cho người dùng. Bạn thậm chí có thể tuỳ chỉnh nhiều giải pháp trong số này bằng Trình tạo mô hình MediaPipe để cập nhật các mô hình mặc định.

Tạo văn bản thành hình ảnh là một trong số các tác vụ học máy mà MediaPipe Solutions cung cấp.

Trong lớp học lập trình này, bạn sẽ bắt đầu với một ứng dụng Android gần như trống, sau đó tiến hành qua nhiều bước cho đến khi có thể tạo hình ảnh mới ngay trên thiết bị Android của mình.

Kiến thức bạn sẽ học được

  • Cách triển khai tính năng tạo văn bản thành hình ảnh chạy cục bộ trong ứng dụng Android bằng Tác vụ MediaPipe.

Bạn cần có

  • Phiên bản Android Studio đã cài đặt (lớp học lập trình này được viết và kiểm thử bằng Android Studio Giraffe).
  • Thiết bị Android có RAM tối thiểu 8 GB.
  • Kiến thức cơ bản về cách phát triển Android và khả năng chạy tập lệnh Python được viết sẵn.

2. Thêm tác vụ MediaPipe vào ứng dụng Android

Tải ứng dụng khởi động Android

Lớp học lập trình này sẽ bắt đầu bằng một mẫu tạo sẵn bao gồm giao diện người dùng sẽ được dùng cho phiên bản tạo hình ảnh cơ bản. Bạn có thể tìm thấy ứng dụng khởi động đó trong kho lưu trữ MediaPipe Samples chính thức tại đây. Sao chép kho lưu trữ hoặc tải tệp zip xuống bằng cách nhấp vào Mã > Tải ZIP xuống.

Nhập ứng dụng vào Android Studio

  1. Mở Android Studio
  2. Trên màn hình Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy chọn Open (Mở) ở góc trên cùng bên phải.

a0b5b070b802e4ea.png

  1. Chuyển đến vị trí bạn đã sao chép hoặc tải kho lưu trữ xuống rồi mở thư mục codelabs/image_generation_basic/android/start.
  2. Ở giai đoạn này, ứng dụng không được biên dịch vì bạn chưa thêm phần phụ thuộc MediaPipe Tasks.

Bạn sẽ khắc phục và chạy ứng dụng bằng cách chuyển đến tệp build.gradle rồi di chuyển xuống // Bước 1 – Thêm phần phụ thuộc. Từ đó, hãy thêm dòng sau rồi nhấn nút Sync Now (Đồng bộ hoá ngay) xuất hiện trong biểu ngữ ở đầu Android Studio.

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

Sau khi quá trình đồng bộ hoá hoàn tất, hãy xác minh rằng mọi thứ đã mở và cài đặt chính xác bằng cách nhấp vào mũi tên chạy màu xanh lục ( 7e15a9c9e1620fe7.png) ở trên cùng bên phải của Android Studio. Bạn sẽ thấy ứng dụng mở ra một màn hình có hai nút chọn và một nút có nhãn INITIALIZE (KHỞI ĐỘNG). Nếu nhấp vào nút đó, bạn sẽ được chuyển ngay đến một giao diện người dùng riêng biệt bao gồm một lời nhắc văn bản và các tuỳ chọn khác cùng với một nút có nhãn TẠO.

83c31de8e8a320ee.png 78b8765e832024e3.png

Rất tiếc, đó là toàn bộ ứng dụng khởi động. Giờ là lúc bạn tìm hiểu cách hoàn tất ứng dụng này và bắt đầu tạo hình ảnh mới trên thiết bị của mình!

3. Thiết lập Trình tạo hình ảnh

Đối với ví dụ này, phần lớn công việc tạo hình ảnh sẽ diễn ra trong tệp ImageGenerationHelper.kt. Khi mở tệp này, bạn sẽ thấy một biến ở đầu lớp có tên là imageGenerator. Đây là đối tượng Task (Nhiệm vụ) sẽ thực hiện các tác vụ nặng trong ứng dụng tạo hình ảnh.

Ngay bên dưới đối tượng đó, bạn sẽ thấy một hàm có tên initializeImageGenerator() với chú thích sau: // Bước 2 – khởi chạy trình tạo hình ảnh. Như bạn có thể đoán, đây là nơi bạn sẽ khởi tạo đối tượng ImageGenerator. Thay thế phần thân hàm đó bằng mã sau để đặt đường dẫn mô hình tạo hình ảnh và khởi chạy đối tượng ImageGenerator:

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

imageGenerator = ImageGenerator.createFromOptions(context, options)

Bên dưới, bạn sẽ thấy một hàm khác có tên setInput(). Hàm này chấp nhận 3 tham số: một chuỗi lời nhắc sẽ được dùng để xác định hình ảnh được tạo, số lần lặp lại mà Tác vụ sẽ thực hiện trong khi tạo hình ảnh mới và một giá trị mẫu có thể dùng để tạo các phiên bản mới của hình ảnh dựa trên cùng một lời nhắc trong khi tạo cùng một hình ảnh khi sử dụng cùng một mẫu. Mục đích của hàm này là đặt các tham số ban đầu này cho trình tạo hình ảnh khi bạn cố gắng tạo một hình ảnh hiển thị các bước trung gian.

Hãy tiếp tục và thay thế phần nội dung setInput() (nơi bạn sẽ thấy nhận xét // Bước 3 – chấp nhận dữ liệu đầu vào) bằng dòng sau:

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

Hai bước tiếp theo là nơi diễn ra quá trình tạo. Hàm generate() chấp nhận các dữ liệu đầu vào giống như setInput, nhưng tạo hình ảnh dưới dạng lệnh gọi một lần không trả về hình ảnh nào ở bước trung gian. Bạn có thể thay thế phần nội dung của hàm này (bao gồm cả nhận xét // Bước 4 – tạo mà không hiển thị các vòng lặp) bằng nội dung sau:

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

Điều quan trọng cần biết là tác vụ này diễn ra đồng bộ, vì vậy, bạn cần gọi hàm từ luồng trong nền. Bạn sẽ tìm hiểu thêm về điều đó trong phần sau của lớp học lập trình này.

Bước cuối cùng bạn sẽ thực hiện trong tệp này là điền vào hàm execute() (được gắn nhãn là Bước 5). Hàm này sẽ chấp nhận một tham số cho biết liệu có trả về hình ảnh trung gian hay không cho một bước tạo hình ảnh sẽ được thực hiện bằng hàm execute() của ImageGenerator. Thay thế phần thân hàm bằng mã sau:

// 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

Đó là tất cả những gì cần làm đối với tệp trình trợ giúp. Trong phần tiếp theo, bạn sẽ điền vào tệp ViewModel để xử lý logic cho ví dụ này.

4. Kết hợp ứng dụng

Tệp MainViewModel sẽ xử lý các trạng thái giao diện người dùng và logic khác liên quan đến ứng dụng mẫu này. Hãy tiếp tục và mở tệp đó ngay.

Ở đầu tệp, bạn sẽ thấy nhận xét // Bước 6 – đặt đường dẫn mô hình. Đây là nơi bạn sẽ cho ứng dụng biết vị trí có thể tìm thấy các tệp mô hình cần thiết để tạo hình ảnh. Trong ví dụ này, bạn sẽ đặt giá trị thành /data/local/tmp/image_generator/bins/.

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

Từ đó, hãy di chuyển xuống hàm generateImage(). Ở cuối hàm này, bạn sẽ thấy cả Bước 7 và Bước 8, tương ứng sẽ được dùng để tạo hình ảnh có hoặc không có số lần lặp được trả về. Vì cả hai thao tác này đều diễn ra đồng bộ, nên bạn sẽ thấy rằng chúng được gói trong một coroutine. Bạn có thể bắt đầu bằng cách thay thế // Bước 7 – Tạo mà không hiển thị các vòng lặp bằng khối mã này để gọi generate() từ tệp ImageGenerationHelper, sau đó cập nhật trạng thái giao diện người dùng.

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

Bước 8 sẽ khó khăn hơn một chút. Vì hàm execute() chỉ thực hiện một bước thay vì tất cả các bước để tạo hình ảnh, nên bạn cần gọi từng bước riêng lẻ thông qua một vòng lặp. Bạn cũng cần xác định xem có nên hiển thị bước hiện tại cho người dùng hay không. Cuối cùng, bạn sẽ cập nhật trạng thái giao diện người dùng nếu cần hiển thị vòng lặp hiện tại. Bạn có thể làm tất cả những việc này ngay bây giờ.

// 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)",
            )
        }
    }
}

Tại thời điểm này, bạn sẽ có thể cài đặt ứng dụng, khởi chạy trình tạo hình ảnh, sau đó tạo hình ảnh mới dựa trên câu lệnh văn bản

... ngoại trừ việc ứng dụng gặp sự cố khi bạn cố gắng khởi chạy trình tạo hình ảnh. Lý do xảy ra vấn đề này là bạn cần sao chép tệp mô hình vào thiết bị. Để biết thông tin mới nhất về các mô hình bên thứ ba đã biết là hoạt động, chuyển đổi các mô hình đó cho tác vụ MediaPipe này và sao chép các mô hình đó vào thiết bị, bạn có thể xem phần này của tài liệu chính thức.

Ngoài việc sao chép tệp trực tiếp vào thiết bị phát triển, bạn cũng có thể thiết lập Bộ nhớ Firebase để tải trực tiếp các tệp cần thiết xuống thiết bị của người dùng trong thời gian chạy.

5. Triển khai và kiểm thử ứng dụng

Sau tất cả, bạn sẽ có một ứng dụng đang hoạt động có thể chấp nhận lời nhắc văn bản và tạo hình ảnh mới hoàn toàn trên thiết bị! Hãy tiếp tục triển khai ứng dụng cho một thiết bị Android thực để kiểm thử, nhưng hãy nhớ rằng bạn nên thử nghiệm với một thiết bị có bộ nhớ tối thiểu 8 GB.

  1. Nhấp vào biểu tượng Chạy ( 7e15a9c9e1620fe7.png) trong thanh công cụ của Android Studio để chạy ứng dụng.
  2. Chọn loại bước tạo (cuối cùng hoặc có lặp lại) rồi nhấn nút INITIALIZE (KHỞI ĐỘNG).
  3. Trên màn hình tiếp theo, hãy thiết lập bất kỳ thuộc tính nào bạn muốn rồi nhấp vào nút GENERATE (TẠO) để xem kết quả của công cụ.

e46cfaeb9d3fc235.gif

6. Xin chúc mừng!

Các bạn đã làm được! Trong lớp học lập trình này, bạn đã tìm hiểu cách thêm tính năng tạo văn bản thành hình ảnh trên thiết bị vào ứng dụng Android.

Các bước tiếp theo

Bạn có thể làm nhiều việc hơn với tác vụ tạo hình ảnh, bao gồm:

  • sử dụng hình ảnh cơ sở để tạo cấu trúc cho hình ảnh được tạo thông qua các trình bổ trợ hoặc huấn luyện các trọng số LoRA bổ sung của riêng bạn thông qua Vertex AI.
  • Sử dụng Bộ nhớ Firebase để truy xuất tệp mô hình trên thiết bị mà không cần sử dụng công cụ ADB.

Chúng tôi rất mong được xem tất cả những điều thú vị mà bạn tạo ra bằng nhiệm vụ thử nghiệm này. Hãy chú ý theo dõi các lớp học lập trình và nội dung khác của nhóm MediaPipe!