1. 소개
MediaPipe란 무엇인가요?
MediaPipe 솔루션을 사용하면 머신러닝(ML) 솔루션을 앱에 적용할 수 있습니다. 이 솔루션으로 제공되는 프레임워크를 통해 사용자에게 즉각적이고, 매력적이고, 유용한 출력을 제공하는 사전 빌드된 처리 파이프라인을 구성할 수 있습니다. MediaPipe Model Maker를 사용해서 이러한 솔루션을 맞춤설정하여 기본 모델을 업데이트할 수도 있습니다.
텍스트 이미지 변환 생성은 MediaPipe 솔루션이 제공하는 여러 ML 태스크 중 하나입니다.
이 Codelab에서는 거의 비어 있는 Android 앱으로 시작하여 Android 기기에서 직접 새 이미지를 생성할 수 있을 때까지 여러 단계를 진행합니다.
학습할 내용
- MediaPipe 태스크를 사용하여 Android 앱에서 로컬로 실행되는 텍스트 이미지 변환을 구현하는 방법
필요한 항목
- 설치된 버전의 Android 스튜디오 (이 Codelab은 Android 스튜디오 Giraffe로 작성 및 테스트됨)
- RAM이 8GB 이상인 Android 기기
- Android 개발에 관한 기본 지식과 사전 작성된 Python 스크립트를 실행하는 기능
2. Android 앱에 MediaPipe Tasks 추가
Android 시작 앱 다운로드
이 Codelab에서는 기본 버전의 이미지 생성에 사용되는 UI로 구성된 사전 제작된 샘플로 시작합니다. 시작 앱은 공식 MediaPipe 샘플 저장소 여기에서 확인할 수 있습니다. Code > Download ZIP을 클릭하여 저장소를 클론하거나 zip 파일을 다운로드합니다.
앱을 Android 스튜디오로 가져오기
- Android 스튜디오를 엽니다.
- Android 스튜디오 시작 화면에서 오른쪽 상단의 열기를 선택합니다.
- 저장소를 클론하거나 다운로드한 위치로 이동하여 codelabs/image_generation_basic/android/start 디렉터리를 엽니다.
- 아직 MediaPipe Tasks 종속 항목을 포함하지 않았으므로 이 단계에서는 앱이 컴파일되지 않아야 합니다.
build.gradle 파일로 이동하여 // 1단계 - 종속 항목 추가로 스크롤하여 앱을 수정하고 실행합니다. 그런 다음 다음 줄을 포함하고 Android 스튜디오 상단의 배너에 표시되는 Sync Now 버튼을 누릅니다.
// Step 1 - Add dependency
implementation 'com.google.mediapipe:tasks-vision-image-generator:latest.release'
동기화가 완료되면 Android 스튜디오의 오른쪽 상단에 있는 녹색 실행 화살표 ( )를 클릭하여 모든 항목이 열리고 올바르게 설치되었는지 확인합니다. 앱이 열리면 라디오 버튼 2개와 INITIALIZE라는 라벨이 지정된 버튼이 있는 화면이 표시됩니다. 이 버튼을 클릭하면 텍스트 프롬프트와 생성 라벨이 지정된 버튼과 함께 다른 옵션으로 구성된 별도의 UI로 즉시 이동합니다.
안타깝게도 시작 앱은 여기까지입니다. 이제 이 앱을 완성하고 기기에서 새 이미지를 생성하는 방법을 알아볼 차례입니다.
3. 이미지 생성기 설정
이 예시에서는 대부분의 이미지 생성 작업이 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()이라는 또 다른 함수가 표시됩니다. 이 함수는 생성된 이미지를 정의하는 데 사용되는 프롬프트 문자열, 새 이미지를 생성하는 동안 태스크가 거쳐야 하는 반복 횟수, 동일한 시드를 사용할 때 동일한 이미지를 생성하는 동안 동일한 프롬프트를 기반으로 이미지의 새 버전을 만드는 데 사용할 수 있는 시드 값을 3개의 매개변수로 받습니다. 이 함수의 목적은 중간 단계를 표시하는 이미지를 만들려고 할 때 이미지 생성기에 이러한 초기 매개변수를 설정하는 것입니다.
setInput() 본문(// 3단계 - 입력 수락 주석이 표시됨)을 다음 줄로 바꿉니다.
// Step 3 - accept inputs
imageGenerator.setInputs(prompt, iteration, seed)
다음 두 단계에서 생성이 이루어집니다. generate() 함수는 setInput과 동일한 입력을 허용하지만 중간 단계 이미지를 반환하지 않는 일회성 호출로 이미지를 만듭니다. 이 함수의 본문(// 4단계 - 반복을 표시하지 않고 생성 주석 포함)을 다음과 같이 바꿀 수 있습니다.
// 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 상태 및 기타 로직을 처리합니다. 지금 열어 보세요.
파일 상단에 // 6단계 - 모델 경로 설정 주석이 표시됩니다. 여기에서 앱에 이미지 생성에 필요한 모델 파일을 찾을 수 있는 위치를 알려줍니다. 이 예에서는 값을 /data/local/tmp/image_generator/bins/로 설정합니다.
// Step 6 - set model path
private val MODEL_PATH = "/data/local/tmp/image_generator/bins/"
그런 다음 generateImage() 함수까지 아래로 스크롤합니다. 이 함수 하단에는 반환된 반복이 있거나 없는 이미지를 각각 생성하는 데 사용되는 7단계와 8단계가 모두 표시됩니다. 두 작업 모두 동기식으로 실행되므로 코루틴으로 래핑됩니다. 먼저 // 7단계 - 반복을 표시하지 않고 생성하기를 이 코드 블록으로 바꾸어 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 이상인 기기에서 테스트하는 것이 좋습니다.
- Android 스튜디오 툴바에서 Run(실행) 아이콘(
)을 클릭하여 앱을 실행합니다.
- 생성 단계 유형 (최종 또는 반복 포함)을 선택한 다음 INITIALIZE 버튼을 누릅니다.
- 다음 화면에서 원하는 속성을 설정하고 생성 버튼을 클릭하여 도구가 생성한 결과를 확인합니다.
6. 축하합니다.
축하합니다. 이 Codelab에서는 Android 앱에 기기 내 텍스트 대 이미지 생성을 추가하는 방법을 알아봤습니다.
다음 단계
이미지 생성 태스크로 할 수 있는 작업은 다음을 포함하여 더 많습니다.
- 기본 이미지를 사용하여 플러그인을 통해 생성된 이미지를 구성하거나 Vertex AI를 통해 자체 LoRA 가중치를 추가로 학습할 수 있습니다.
- Firebase Storage를 사용하여 ADB 도구를 사용하지 않고도 기기에서 모델 파일을 가져옵니다.
이 실험용 태스크로 만들어 보실 멋진 결과물을 기대하며 MediaPipe팀의 더 많은 Codelab과 콘텐츠도 기대해 주세요.