MediaPipe를 사용하여 Android에서 기기 내 이미지 생성

1. 소개

MediaPipe란 무엇인가요?

MediaPipe 솔루션을 사용하면 머신러닝(ML) 솔루션을 앱에 적용할 수 있습니다. 이 솔루션은 사용자에게 즉각적이고, 매력적이고, 유용한 출력을 제공하는 사전 빌드된 처리 파이프라인을 구성하는 프레임워크를 제공합니다. MediaPipe Model Maker를 사용해서 이러한 솔루션을 맞춤설정하여 기본 모델을 업데이트할 수도 있습니다.

텍스트 이미지 변환 생성은 MediaPipe 솔루션이 제공하는 여러 ML 작업 중 하나입니다.

이 Codelab에서는 거의 비어 있는 Android 앱으로 시작하여 Android 기기에서 직접 새 이미지를 생성할 수 있을 때까지 여러 단계를 진행합니다.

학습할 내용

  • MediaPipe Tasks를 사용하여 Android 앱에서 로컬로 실행되는 텍스트 이미지 변환 생성을 구현하는 방법

필요한 항목

  • Android 스튜디오 설치 버전 (이 Codelab은 Android 스튜디오 Giraffe로 작성되고 테스트됨)
  • RAM이 8GB 이상인 Android 기기
  • Android 개발에 관한 기본 지식과 미리 작성된 Python 스크립트를 실행할 수 있는 능력

2. Android 앱에 MediaPipe Tasks 추가

Android 스타터 앱 다운로드

이 Codelab은 이미지 생성의 기본 버전에 사용될 UI로 구성된 사전 제작된 샘플로 시작합니다. 공식 MediaPipe 샘플 저장소(여기)에서 시작 앱을 확인할 수 있습니다. 코드 > ZIP 다운로드를 클릭하여 저장소를 클론하거나 zip 파일을 다운로드합니다.

Android 스튜디오로 앱 가져오기

  1. Android 스튜디오를 엽니다.
  2. Welcome to Android Studio 화면에서 오른쪽 상단의 Open을 선택합니다.

a0b5b070b802e4ea.png

  1. 저장소를 클론하거나 다운로드한 위치로 이동하여 codelabs/image_generation_basic/android/start directory를 엽니다.
  2. 이 단계에서는 아직 MediaPipe Tasks 종속 항목을 포함하지 않았으므로 앱이 컴파일되지 않아야 합니다.

build.gradle 파일로 이동하여 // Step 1 - Add dependency.까지 아래로 스크롤하면 앱을 수정하고 실행할 수 있습니다. 여기에서 다음 줄을 포함한 다음 Android 스튜디오 상단의 배너에 표시되는 Sync Now 버튼을 누릅니다.

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

동기화가 완료되면 Android 스튜디오의 오른쪽 상단에 있는 녹색 실행 화살표 ( 7e15a9c9e1620fe7.png)를 클릭하여 모든 항목이 올바르게 열리고 설치되었는지 확인합니다. 두 개의 라디오 버튼과 INITIALIZE라는 라벨이 지정된 버튼이 있는 화면에 앱이 열립니다. 이 버튼을 클릭하면 텍스트 프롬프트와 기타 옵션, 생성이라는 라벨이 지정된 버튼으로 구성된 별도의 UI로 즉시 이동합니다.

83c31de8e8a320ee.png 78b8765e832024e3.png

아쉽게도 스타터 앱은 여기까지입니다. 이제 이 앱을 완성하고 기기에서 새 이미지를 생성하는 방법을 알아볼 시간입니다.

3. 이미지 생성기 설정

이 예시에서는 이미지 생성 작업의 대부분이 ImageGenerationHelper.kt 파일에서 이루어집니다. 이 파일을 열면 클래스 상단에 imageGenerator라는 변수가 표시됩니다. 이미지 생성 앱에서 힘든 작업을 처리할 Task 객체입니다.

이 객체 바로 아래에 다음 주석이 있는 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

이 작업은 동기적으로 실행되므로 백그라운드 스레드에서 함수를 호출해야 합니다. 이 Codelab의 뒷부분에서 자세히 알아봅니다.

이 파일에서 마지막으로 수행할 단계는 execute() 함수 (5단계로 표시됨)를 작성하는 것입니다. ImageGenerator execute() 함수로 실행될 생성의 단일 단계에 대해 중간 이미지를 반환해야 하는지 여부를 알려주는 매개변수를 허용합니다. 함수 본문을 다음 코드로 바꿉니다.

// 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 파일은 이 예시 앱과 관련된 UI 상태 및 기타 로직을 처리합니다. 지금 열어 보세요.

파일 상단에 // 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단계가 모두 표시됩니다. 이 단계는 각각 반환된 반복 또는 반환된 반복이 없는 이미지를 생성하는 데 사용됩니다. 이 두 작업은 모두 동기식으로 발생하므로 코루틴으로 래핑되어 있습니다. 먼저 // Step 7 - Generate without showing iterations를 이 코드 블록으로 바꿔 ImageGenerationHelper 파일에서 generate()를 호출한 다음 UI 상태를 업데이트합니다.

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

8단계는 약간 더 까다롭습니다. execute() 함수는 이미지 생성의 모든 단계가 아닌 하나의 단계만 실행하므로 루프를 통해 각 단계를 개별적으로 호출해야 합니다. 또한 현재 단계를 사용자에게 표시해야 하는지 여부도 확인해야 합니다. 마지막으로 현재 반복을 표시해야 하는 경우 UI 상태를 업데이트합니다. 이제 이 모든 작업을 할 수 있습니다.

// 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. Android 스튜디오 툴바에서 실행 ( 7e15a9c9e1620fe7.png)을 클릭하여 앱을 실행합니다.
  2. 생성 단계 유형 (최종 또는 반복 포함)을 선택한 다음 INITIALIZE 버튼을 누릅니다.
  3. 다음 화면에서 원하는 속성을 설정하고 생성 버튼을 클릭하여 도구에서 생성한 내용을 확인합니다.

e46cfaeb9d3fc235.gif

6. 축하합니다.

축하합니다. 이 Codelab에서는 Android 앱에 온디바이스 텍스트-이미지 생성 기능을 추가하는 방법을 알아봤습니다.

다음 단계

이미지 생성 작업을 통해 다음과 같은 작업을 할 수 있습니다.

  • 기본 이미지를 사용하여 플러그인을 통해 생성된 이미지를 구조화하거나 Vertex AI를 통해 자체 추가 LoRA 가중치를 학습시킬 수 있습니다.
  • Firebase Storage를 사용하여 ADB 도구를 사용하지 않고 기기에서 모델 파일을 가져옵니다.

이 실험적 작업을 통해 멋진 콘텐츠를 많이 만들어 주시기를 기대하며, MediaPipe팀에서 제공하는 더 많은 Codelab과 콘텐츠도 기대해 주세요.