Guía de generación de imágenes para Android

La tarea del generador de imágenes de MediaPipe te permite generar imágenes a partir de una instrucción de texto. En esta tarea, se usa un modelo de texto a imagen para generar imágenes con técnicas de difusión.

La tarea acepta una instrucción de texto como entrada, junto con una imagen de condición opcional que el modelo puede mejorar y usar como referencia para la generación. El generador de imágenes también puede generar imágenes basadas en conceptos específicos que se proporcionan al modelo durante el entrenamiento o la nueva capacitación. Para obtener más información, consulta Cómo personalizar con LoRa.

La muestra de código que se describe en estas instrucciones está disponible en GitHub. Para obtener más información sobre las funciones, los modelos y las opciones de configuración de esta tarea, consulta la descripción general.

Ejemplo de código

El código de ejemplo de MediaPipe Tasks es una implementación básica de una app de generador de imágenes para Android. Puedes usar la app como punto de partida para tu propia app para Android o consultarla cuando modifiques una app existente. El código de ejemplo del generador de imágenes se aloja en GitHub.

Descarga el código

En las siguientes instrucciones, se muestra cómo crear una copia local del código de ejemplo con la herramienta de línea de comandos git.

Para descargar el código de ejemplo, sigue estos pasos:

  1. Clona el repositorio de git con el siguiente comando:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. De manera opcional, configura tu instancia de git para usar el control de revisión disperso, de modo que tengas solo los archivos de la app de ejemplo de Image Generator:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_generation/android
    

Después de crear una versión local del código de ejemplo, puedes importar el proyecto a Android Studio y ejecutar la app. Para obtener instrucciones, consulta la Guía de configuración para Android.

Componentes clave

Los siguientes archivos contienen el código fundamental de esta aplicación de ejemplo de generación de imágenes:

  • ImageGenerationHelper.kt: Inicia la tarea y controla la generación de imágenes.
  • DiffusionActivity.kt: Genera imágenes cuando no están habilitados los complementos ni las ponderaciones de LoRA.
  • PluginActivity.kt: Implementa los modelos de complementos, lo que permite a los usuarios proporcionar una imagen de condición como entrada.
  • LoRAWeightActivity.kt: Accede a los pesos de LoRA y los controla, que se usan para personalizar los modelos de base y permitirles generar imágenes de conceptos específicos.

Configuración

En esta sección, se describen los pasos clave para configurar tu entorno de desarrollo y proyectos de código específicamente para usar Image Generator. Si deseas obtener información general sobre cómo configurar tu entorno de desarrollo para usar tareas de MediaPipe, incluidos los requisitos de la versión de la plataforma, consulta la Guía de configuración para Android.

Dependencias

La tarea del generador de imágenes usa la biblioteca com.google.mediapipe:tasks-vision-image-generator. Agrega esta dependencia al archivo build.gradle de tu app para Android:

dependencies {
    implementation 'com.google.mediapipe:tasks-vision-image-generator:latest.release'
}

En el caso de los dispositivos con Android 12 (nivel de API 31) o versiones posteriores, agrega la dependencia de la biblioteca nativa de OpenCL. Para obtener más información, consulta la documentación sobre la etiqueta uses-native-library.

Agrega las siguientes etiquetas uses-native-library al archivo AndroidManifest.xml:

<uses-native-library android:name="libOpenCL.so" android:required="false" />
<uses-native-library android:name="libOpenCL-car.so" android:required="false"/>
<uses-native-library android:name="libOpenCL-pixel.so" android:required="false" />

Modelo

La tarea del generador de imágenes de MediaPipe requiere un modelo de base entrenado que sea compatible con esta tarea. Después de descargar un modelo, instala las dependencias requeridas y conviértelo en un formato adecuado. Luego, envía el modelo convertido al dispositivo Android.

Para obtener más información sobre los modelos entrenados disponibles para Image Generator, consulta la sección Modelos de la descripción general de la tarea.

Descarga el modelo de base

El generador de imágenes requiere que el modelo de base coincida con el formato del modelo runwayml/stable-diffusion-v1-5 EMA-only, según el siguiente modelo: runwayml/stable-diffusion-v1-5.

Instala las dependencias y convierte el modelo

$ pip install torch typing_extensions numpy Pillow requests pytorch_lightning absl-py

Ejecuta la secuencia de comandos convert.py:

$ python3 convert.py --ckpt_path <ckpt_path> --output_path <output_path>

Envía el modelo convertido al dispositivo

Envía el contenido de la carpeta <output_path> al dispositivo Android.

$ adb shell rm -r /data/local/tmp/image_generator/ # Remove any previously loaded weights
$ adb shell mkdir -p /data/local/tmp/image_generator/
$ adb push <output_path>/. /data/local/tmp/image_generator/bins

Descarga modelos de complementos y agrega pesos de LoRA (opcional)

Si deseas usar un modelo de complemento, verifica si se debe descargar. En el caso de los complementos que requieren un modelo adicional, los modelos de complementos deben estar empaquetados en el APK o descargarse a pedido. Los modelos de complementos son ligeros (~23 MB) y se pueden agrupar directamente en el APK. Sin embargo, te recomendamos que descargues modelos de complementos a pedido.

Si personalizaste un modelo con LoRA, descárgalos a pedido. Para obtener más información, consulta el modelo de complemento de pesos de LoRA.

Crea la tarea

La tarea del generador de imágenes de MediaPipe usa la función createFromOptions() para configurar la tarea. La función createFromOptions() acepta valores para las opciones de configuración. Para obtener más información sobre las opciones de configuración, consulta Opciones de configuración.

Opciones de configuración

Esta tarea tiene las siguientes opciones de configuración para apps para Android:

Nombre de la opción Descripción Rango de valores
imageGeneratorModelDirectory Es el directorio del modelo del generador de imágenes que almacena los pesos del modelo. PATH
loraWeightsFilePath Establece la ruta de acceso al archivo de pesos de LoRA. Es opcional y solo se aplica si el modelo se personalizó con LoRA. PATH
errorListener Establece un objeto de escucha de errores opcional. N/A

La tarea también admite modelos de complementos, lo que permite a los usuarios incluir imágenes de condiciones en la entrada de la tarea, que el modelo de base puede aumentar y usar como referencia para la generación. Estas imágenes de condiciones pueden ser puntos de referencia faciales, contornos de bordes y estimaciones de profundidad, que el modelo usa como contexto y como información adicional para generar imágenes.

Cuando agregues un modelo de complemento al modelo de base, también configura las opciones del complemento. El complemento de punto de referencia de rostro usa faceConditionOptions, el complemento de borde Canny usa edgeConditionOptions y el complemento de profundidad usa depthConditionOptions.

Opciones de bordes definidos

Configura las siguientes opciones en edgeConditionOptions.

Nombre de la opción Descripción Rango de valores Valor predeterminado
threshold1 Primer umbral para el procedimiento de histéresis. Float 100
threshold2 Segundo umbral para el procedimiento de histéresis. Float 200
apertureSize Es el tamaño de la apertura para el operador Sobel. El rango típico es de 3 a 7. Integer 3
l2Gradient Indica si se usa la norma L2 para calcular la magnitud del gradiente de la imagen, en lugar de la norma L1 predeterminada. BOOLEAN False
EdgePluginModelBaseOptions El objeto BaseOptions que establece la ruta de acceso para el modelo del complemento Objeto BaseOptions N/A

Para obtener más información sobre el funcionamiento de estas opciones de configuración, consulta Detector de bordes de Canny.

Opciones de puntos de referencia faciales

Configura las siguientes opciones en faceConditionOptions.

Nombre de la opción Descripción Rango de valores Valor predeterminado
minFaceDetectionConfidence La puntuación de confianza mínima para que la detección de rostros se considere exitosa. Float [0.0,1.0] 0.5
minFacePresenceConfidence Es la puntuación de confianza mínima de la puntuación de presencia de rostro en la detección de puntos de referencia del rostro. Float [0.0,1.0] 0.5
faceModelBaseOptions El objeto BaseOptions que establece la ruta de acceso para el modelo que crea la imagen de la condición Objeto BaseOptions N/A
FacePluginModelBaseOptions El objeto BaseOptions que establece la ruta de acceso para el modelo del complemento Objeto BaseOptions N/A

Para obtener más información sobre el funcionamiento de estas opciones de configuración, consulta la tarea de Face Landmarker.

Opciones de profundidad

Configura las siguientes opciones en depthConditionOptions.

Nombre de la opción Descripción Rango de valores Valor predeterminado
depthModelBaseOptions El objeto BaseOptions que establece la ruta de acceso para el modelo que crea la imagen de la condición Objeto BaseOptions N/A
depthPluginModelBaseOptions El objeto BaseOptions que establece la ruta de acceso para el modelo del complemento Objeto BaseOptions N/A

Crea solo con el modelo de base

val options = ImageGeneratorOptions.builder()
    .setImageGeneratorModelDirectory(modelPath)
    .build()

imageGenerator = ImageGenerator.createFromOptions(context, options)

Crea con complementos

Si aplicas un modelo de complemento opcional, configura las opciones básicas del modelo de complemento con setPluginModelBaseOptions. Si el modelo del complemento requiere un modelo descargado adicional para crear la imagen de la condición, especifica la ruta de acceso en BaseOptions.

Punto de referencia facial

val options = ImageGeneratorOptions.builder()
    .setImageGeneratorModelDirectory(modelPath)
    .build()

val faceModelBaseOptions = BaseOptions.builder()
    .setModelAssetPath("face_landmarker.task")
    .build()

val facePluginModelBaseOptions = BaseOptions.builder()
    .setModelAssetPath("face_landmark_plugin.tflite")
    .build()

val faceConditionOptions = FaceConditionOptions.builder()
    .setFaceModelBaseOptions(faceModelBaseOptions)
    .setPluginModelBaseOptions(facePluginModelBaseOptions)
    .setMinFaceDetectionConfidence(0.3f)
    .setMinFacePresenceConfidence(0.3f)
    .build()

val conditionOptions = ConditionOptions.builder()
    .setFaceConditionOptions(faceConditionOptions)
    .build()

imageGenerator =
    ImageGenerator.createFromOptions(context, options, conditionOptions)
    

Borde Canny

val options = ImageGeneratorOptions.builder()
    .setImageGeneratorModelDirectory(modelPath)
    .build()

val edgePluginModelBaseOptions = BaseOptions.builder()
    .setModelAssetPath("canny_edge_plugin.tflite")
    .build()

val edgeConditionOptions = EdgeConditionOptions.builder()
    .setThreshold1(100.0f)
    .setThreshold2(100.0f)
    .setApertureSize(3)
    .setL2Gradient(false)
    .setPluginModelBaseOptions(edgePluginModelBaseOptions)
    .build()

val conditionOptions = ConditionOptions.builder()
    .setEdgeConditionOptions(edgeConditionOptions)
    .build()

imageGenerator =
    ImageGenerator.createFromOptions(context, options, conditionOptions)
    

Profundidad

val options = ImageGeneratorOptions.builder()
    .setImageGeneratorModelDirectory(modelPath)
    .build()

val depthModelBaseOptions = BaseOptions.builder()
    .setModelAssetPath("depth_model.tflite")
    .build()

val depthPluginModelBaseOptions = BaseOptions.builder()
    .setModelAssetPath("depth_plugin.tflite")
    .build()

val depthConditionOptions =
    ConditionOptions.DepthConditionOptions.builder()
        .setDepthModelBaseOptions(depthModelBaseOptions)
        .setPluginModelBaseOptions(depthPluginModelBaseOptions)
        .build()

val conditionOptions = ConditionOptions.builder()
    .setDepthConditionOptions(depthConditionOptions)
    .build()

imageGenerator =
    ImageGenerator.createFromOptions(context, options, conditionOptions)
    

Crea con pesos de LoRA

Si incluyes pesos de LoRA, usa el parámetro loraWeightsFilePath para apuntar a la ubicación de la ruta.

val options = ImageGeneratorOptions.builder()
    .setLoraWeightsFilePath(weightsPath)
    .setImageGeneratorModelDirectory(modelPath)
    .build()

imageGenerator = ImageGenerator.createFromOptions(context, options)

Preparar los datos

El generador de imágenes acepta las siguientes entradas:

  • prompt (obligatorio): Es la instrucción de texto que describe la imagen que se generará.
  • iterations (obligatorio): Es la cantidad total de iteraciones para generar la imagen. Un buen punto de partida es 20.
  • seed (obligatorio): Es el valor inicial aleatorio que se usa durante la generación de imágenes.
  • imagen de condición (opcional): Es la imagen que el modelo usa como referencia para la generación. Solo se aplica cuando se usa un modelo de complemento.
  • type of condition (opcional): Es el tipo de modelo de complemento que se usa con la tarea. Solo se aplica cuando se usa un modelo de complemento.

Entradas con solo el modelo de base

fun setInput(prompt: String, iteration: Int, seed: Int) {
    imageGenerator.setInputs(prompt, iteration, seed)
}

Entradas con complementos

Si aplicas un modelo de complemento opcional, usa también el parámetro conditionType para elegir el modelo de complemento y el parámetro sourceConditionImage para generar la imagen de la condición.

Nombre de la opción Descripción Valor
conditionType Es el modelo del complemento aplicado al modelo de base. {"FACE", "EDGE", "DEPTH"}
sourceConditionImage Es la imagen de origen que se usa para crear la imagen de condición. Objeto MPImage

Si usas un modelo de complemento, usa createConditionImage para crear la imagen de condición:

fun createConditionImage(
    inputImage: MPImage,
    conditionType: ConditionType
): Bitmap {
    val result =
        imageGenerator.createConditionImage(inputImage, conditionType)
    return BitmapExtractor.extract(result)
}

Después de crear la imagen de condición, inclúyela como entrada junto con la instrucción, la inicialización y la cantidad de iteraciones.

imageGenerator.setInputs(
    prompt,
    conditionalImage,
    conditionType,
    iteration,
    seed
)

Entradas con pesos de LoRA

Si usas pesos de LoRA, asegúrate de que el token esté en la instrucción de texto si deseas generar una imagen con el concepto específico que representan los pesos.

fun setInput(prompt: String, iteration: Int, seed: Int) {
    imageGenerator.setInputs(prompt, iteration, seed)
}

Ejecuta la tarea

Usa el método generate() para generar una imagen con las entradas proporcionadas en la sección anterior. Esto produce una sola imagen generada.

Genera solo con el modelo de base

fun generate(prompt: String, iteration: Int, seed: Int): Bitmap {
    val result = imageGenerator.generate(prompt, iteration, seed)
    val bitmap = BitmapExtractor.extract(result?.generatedImage())
    return bitmap
}

Generar con complementos

fun generate(
    prompt: String,
    inputImage: MPImage,
    conditionType: ConditionType,
    iteration: Int,
    seed: Int
): Bitmap {
    val result = imageGenerator.generate(
        prompt,
        inputImage,
        conditionType,
        iteration,
        seed
    )
    val bitmap = BitmapExtractor.extract(result?.generatedImage())
    return bitmap
}

Genera con pesos de LoRA

El proceso de generar imágenes con un modelo personalizado con pesos de LoRA es similar al proceso con un modelo de base estándar. Asegúrate de que el token se incluya en la instrucción y ejecuta el mismo código.

fun generate(prompt: String, iteration: Int, seed: Int): Bitmap {
    val result = imageGenerator.generate(prompt, iteration, seed)
    val bitmap = BitmapExtractor.extract(result?.generatedImage())
    return bitmap
}

Generación iterativa

El generador de imágenes también puede generar las imágenes intermedias generadas durante cada iteración, como se define en el parámetro de entrada iterations. Para ver estos resultados intermedios, llama al método setInputs y, luego, llama a execute() para ejecutar cada paso. Establece el parámetro showResult en true para mostrar los resultados intermedios.

fun execute(showResult: Boolean): Bitmap {
    val result = imageGenerator.execute(showResult)

    val bitmap =
        BitmapExtractor.extract(result.generatedImage())

    return bitmap
}

Cómo controlar y mostrar los resultados

El generador de imágenes muestra un ImageGeneratorResult, que incluye la imagen generada, una marca de tiempo del momento de la finalización y la imagen condicional si se proporcionó una como entrada.

val bitmap = BitmapExtractor.extract(result.generatedImage())

La siguiente imagen se generó a partir de las siguientes entradas, con solo un modelo de base.

Entradas:

  • Consigna: “Un mapache de caricatura colorido que lleva un sombrero de ala ancha y floja y camina por el bosque, animado, vista en tres cuartos, pintura”
  • Valor inicial: 312687592
  • Iteraciones: 20

Imagen generada: