1. Introducción
¿Qué es MediaPipe?
Las MediaPipe Solutions te permiten aplicar soluciones de aprendizaje automático (AA) en tus apps. Proporcionan un framework para que configures canalizaciones de procesamiento compiladas previamente que arrojan resultados inmediatos, pertinentes y útiles para los usuarios. Incluso puedes personalizar muchas de estas soluciones con MediaPipe Model Maker para actualizar los modelos predeterminados.
La generación de texto a imagen es una de las varias tareas de AA que ofrece MediaPipe Solutions.
En este codelab, comenzarás con una app para Android casi sin elementos y, luego, avanzarás por varios pasos hasta que puedas generar imágenes nuevas directamente en tu dispositivo Android.
Qué aprenderás
- Cómo implementar la generación de texto a imagen que se ejecuta de forma local en una app para Android con MediaPipe Tasks
Requisitos
- Una versión instalada de Android Studio (este codelab se escribió y probó con Android Studio Giraffe).
- Un dispositivo Android con al menos 8 GB de RAM
- Conocimientos básicos sobre el desarrollo de Android y la capacidad de ejecutar una secuencia de comandos de Python escrita previamente
2. Agrega MediaPipe Tasks a la app para Android
Descarga la app de partida para Android
Este codelab comenzará con una muestra prediseñada que consta de la IU que se usará para una versión básica de la generación de imágenes. Puedes encontrar esa app de inicio en el repositorio oficial de Samples de MediaPipe aquí. Para clonar el repositorio o descargar el archivo ZIP, haz clic en Code > Download ZIP.
Importa la app a Android Studio
- Abre Android Studio.
- En la pantalla Welcome to Android Studio, selecciona Open en la esquina superior derecha.
- Navega a la ubicación donde clonaste o descargaste el repositorio y abre el directorio codelabs/image_generation_basic/android/start.
- En esta etapa, la app no debería compilarse porque aún no incluyes la dependencia de MediaPipe Tasks.
Para corregir la app y hacer que se ejecute, ve al archivo build.gradle y desplázate hacia abajo hasta // Paso 1: Agrega una dependencia. Desde allí, incluye la siguiente línea y, luego, presiona el botón Sync Now que aparece en el banner de la parte superior de Android Studio.
// Step 1 - Add dependency
implementation 'com.google.mediapipe:tasks-vision-image-generator:latest.release'
Una vez que se complete la sincronización, haz clic en la flecha verde de ejecutar ( ) en la parte superior derecha de Android Studio para verificar que todo se haya abierto y se haya instalado correctamente. Deberías ver que la app se abre en una pantalla con dos botones de selección y un botón etiquetado como INITIALIZE. Si haces clic en ese botón, deberías ir de inmediato a una IU independiente que consta de una instrucción de texto y otras opciones junto a un botón etiquetado como GENERAR.
Lamentablemente, eso es todo lo que puedes hacer con la app de partida, así que es hora de que aprendas cómo terminarás esta app y comenzarás a generar imágenes nuevas en tu dispositivo.
3. Cómo configurar el generador de imágenes
En este ejemplo, la mayor parte del trabajo de generación de imágenes se realizará en el archivo ImageGenerationHelper.kt. Cuando abras este archivo, verás una variable en la parte superior de la clase llamada imageGenerator. Este es el objeto Task que hará el trabajo pesado en tu app de generación de imágenes.
Justo debajo de ese objeto, verás una función llamada initializeImageGenerator() con el siguiente comentario: // Paso 2: Inicializa el generador de imágenes. Como puedes suponer, aquí es donde inicializarás el objeto ImageGenerator. Reemplaza ese cuerpo de función por el siguiente código para establecer la ruta de acceso del modelo de generación de imágenes y, luego, inicializa el objeto ImageGenerator:
// Step 2 - initialize the image generator
val options = ImageGeneratorOptions.builder()
.setImageGeneratorModelDirectory(modelPath)
.build()
imageGenerator = ImageGenerator.createFromOptions(context, options)
Debajo, verás otra función llamada setInput(). Esta acepta tres parámetros: una cadena prompt que se usará para definir la imagen generada, la cantidad de iteraciones que debe realizar la tarea mientras se genera la imagen nueva y un valor de inicial que se puede usar para crear versiones nuevas de una imagen según la misma instrucción mientras se genera la misma imagen cuando se usa la misma inicial. El propósito de esta función es establecer estos parámetros iniciales para el generador de imágenes cuando intentas crear una imagen que sí muestre pasos intermedios.
Continúa y reemplaza el cuerpo de setInput() (donde verás el comentario // Step 3 - accept inputs) por esta línea:
// Step 3 - accept inputs
imageGenerator.setInputs(prompt, iteration, seed)
En los siguientes dos pasos, se produce la generación. La función generate() acepta las mismas entradas que setInput, pero crea una imagen como una llamada única que no muestra ninguna imagen de paso intermedio. Puedes reemplazar el cuerpo de esta función (que incluye el comentario // Paso 4: Generar sin mostrar iteraciones) por lo siguiente:
// Step 4 - generate without showing iterations
val result = imageGenerator.generate(prompt, iteration, seed)
val bitmap = BitmapExtractor.extract(result?.generatedImage())
return bitmap
Es importante saber que esta tarea se realiza de forma síncrona, por lo que deberás llamar a la función desde un subproceso en segundo plano. Obtendrás más información sobre eso más adelante en este codelab.
El último paso que darás en este archivo es completar la función execute() (etiquetada como paso 5). Esta función aceptará un parámetro que le indique si debe mostrar una imagen intermedia o no para el único paso de generación que se realizará con la función execute() de ImageGenerator. Reemplaza el cuerpo de la función por este código:
// 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
Eso es todo para el archivo de ayuda. En la siguiente sección, completarás el archivo ViewModel que controla la lógica de este ejemplo.
4. Cómo unir la app
El archivo MainViewModel controlará los estados de la IU y otra lógica relacionada con esta app de ejemplo. Adelante, ábrelo ahora.
Cerca de la parte superior del archivo, deberías ver el comentario // Paso 6: Establece la ruta de acceso del modelo. Aquí es donde le indicarás a tu app dónde puede encontrar los archivos de modelo necesarios para la generación de imágenes. En este ejemplo, establecerás el valor en /data/local/tmp/image_generator/bins/.
// Step 6 - set model path
private val MODEL_PATH = "/data/local/tmp/image_generator/bins/"
Desde allí, desplázate hacia abajo hasta la función generateImage(). Hacia la parte inferior de esta función, verás los pasos 7 y 8, que se usarán para generar imágenes con iteraciones que se muestran o sin ellas, respectivamente. Como ambas operaciones se realizan de forma síncrona, notarás que están combinadas en una corrutina. Puedes comenzar por reemplazar // Paso 7: Generar sin mostrar iteraciones con este bloque de código para llamar a generate() desde el archivo ImageGenerationHelper y, luego, actualizar el estado de la IU.
// Step 7 - Generate without showing iterations
val result = helper?.generate(prompt, iteration, seed)
_uiState.update {
it.copy(outputBitmap = result)
}
El paso 8 es un poco más complicado. Debido a que la función execute() solo realiza un paso en lugar de todos los pasos para la generación de imágenes, deberás llamar a cada paso de forma individual a través de un bucle. También deberás determinar si se debe mostrar el paso actual al usuario. Por último, actualizarás el estado de la IU si se debe mostrar la iteración actual. Puedes hacer todo esto ahora.
// 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)",
)
}
}
}
En este punto, deberías poder instalar tu app, inicializar el generador de imágenes y, luego, crear una imagen nueva a partir de una instrucción de texto.
… excepto que ahora la app falla cuando intentas inicializar el generador de imágenes. El motivo es que debes copiar los archivos del modelo en tu dispositivo. Para obtener la información más actualizada sobre los modelos de terceros que se sabe que funcionan, convertirlos para esta tarea de MediaPipe y copiarlos en tu dispositivo, puedes revisar esta sección de la documentación oficial.
Además de copiar archivos directamente en tu dispositivo de desarrollo, también puedes configurar el almacenamiento de Firebase para que descargue los archivos necesarios directamente en el dispositivo del usuario durante el tiempo de ejecución.
5. Implementa y prueba la app
Después de todo eso, deberías tener una app que funcione y que pueda aceptar una instrucción de texto y generar imágenes nuevas completamente en el dispositivo. Continúa y, luego, implementa la app en un dispositivo Android físico para probarla. Sin embargo, recuerda que te recomendamos que lo hagas en un dispositivo con al menos 8 GB de memoria.
- Haz clic en Run (
) en la barra de herramientas de Android Studio para ejecutar la app.
- Selecciona el tipo de pasos de generación (final o con iteraciones) y, luego, presiona el botón INITIALIZE.
- En la siguiente pantalla, establece las propiedades que quieras y haz clic en el botón GENERAR para ver qué genera la herramienta.
6. ¡Felicitaciones!
¡Lo lograste! En este codelab, aprendiste a agregar la generación de texto a imagen integrada en el dispositivo a una app para Android.
Próximos pasos
Puedes hacer mucho más con la tarea de generación de imágenes, por ejemplo:
- con una imagen base para estructurar las imágenes generadas a través de complementos, o bien entrena tus propias ponderaciones adicionales de LoRA a través de Vertex AI.
- Usa Almacenamiento de Firebase para recuperar archivos de modelos en tu dispositivo sin necesidad de usar la herramienta ADB.
Esperamos ver todo lo que crees con esta tarea experimental. No te pierdas los próximos codelabs y contenido del equipo de MediaPipe.