Genera imágenes con Imagen

Imagen es un modelo de generación de imágenes. Se puede usar para generar avatares personalizados para perfiles de usuario o para integrar recursos visuales personalizados en los flujos de pantallas existentes para aumentar la participación de los usuarios.

Puedes acceder a los modelos de Imagen desde tu app para Android con el SDK de Firebase AI Logic. Los modelos de Imagen están disponibles con ambos proveedores de la API de Firebase AI Logic : la API de Gemini Developer (recomendada para la mayoría de los desarrolladores) y Vertex AI.

Un diagrama que ilustra una arquitectura de integración de Firebase AI Logic para acceder a la API de Gemini Developer. Una app para Android utiliza el SDK de Firebase para Android para conectarse a Firebase. Luego, Firebase interactúa con la API de Gemini Developer, que accede a Gemini Pro y Flash en la nube.
Figura 1. Accede a los modelos de Imagen con Firebase AI Logic.

Experimenta con instrucciones

Crear las instrucciones ideales suele requerir varios intentos. Puedes experimentar con instrucciones de imágenes en Google AI Studio, un IDE para el diseño y la creación de prototipos de instrucciones. Para obtener sugerencias sobre cómo mejorar tus instrucciones, consulta la guía de atributos de imágenes e instrucciones.

Una captura de pantalla de la interfaz de Google AI Studio, en la que se muestran cuatro imágenes generadas de un tiranosaurio rex con una mochila azul en un bosque prehistórico.
Figura 2. Google AI Studio puede ayudarte a mejorar tus instrucciones de generación de imágenes.

Configura un proyecto de Firebase y conecta tu app

Sigue los pasos de la documentación de Firebase para agregar Firebase a tu proyecto de Android.

Agrega la dependencia de Gradle

Agrega las siguientes dependencias a tu archivo build.gradle:

dependencies {
  // Import the BoM for the Firebase platform
  implementation(platform("com.google.firebase:firebase-bom:34.15.0"))

  // Add the dependency for the Firebase AI Logic library. When using the BoM,
  // you don't specify versions in Firebase library dependencies
  implementation("com.google.firebase:firebase-ai")
}

Generar una imagen

Para generar una imagen en tu app para Android, comienza por crear una instancia de ImagenModel con una configuración opcional.

Puedes usar el parámetro generationConfig para definir una instrucción negativa, la cantidad de imágenes, la relación de aspecto de la imagen de salida, el formato de imagen y agregar una marca de agua. Puedes usar el safetySettings parámetro para configurar los filtros de seguridad y de personas.

Kotlin

val config = ImagenGenerationConfig(
    numberOfImages = 2,
    aspectRatio = ImagenAspectRatio.LANDSCAPE_16x9,
    imageFormat = ImagenImageFormat.jpeg(compressionQuality = 100),
    addWatermark = false,
)

// Initialize the Gemini Developer API backend service
// For Vertex AI use Firebase.ai(backend = GenerativeBackend.vertexAI())
val model = Firebase.ai(backend = GenerativeBackend.googleAI()).imagenModel(
    modelName = "imagen-4.0-generate-001",
    generationConfig = config,
    safetySettings = ImagenSafetySettings(
        safetyFilterLevel = ImagenSafetyFilterLevel.BLOCK_LOW_AND_ABOVE,
        personFilterLevel = ImagenPersonFilterLevel.BLOCK_ALL
    ),
)

Java

ImagenGenerationConfig config = new ImagenGenerationConfig.Builder()
        .setNumberOfImages(2)
        .setAspectRatio(ImagenAspectRatio.LANDSCAPE_16x9)
        .setImageFormat(ImagenImageFormat.jpeg(100))
        .setAddWatermark(false)
        .build();

// For Vertex AI use Firebase.ai(backend = GenerativeBackend.vertexAI())
ImagenModelFutures model = ImagenModelFutures.from(
        FirebaseAI.getInstance(GenerativeBackend.googleAI()).imagenModel(
                "imagen-4.0-generate-001",
                config,
                new ImagenSafetySettings(
                        ImagenSafetyFilterLevel.BLOCK_LOW_AND_ABOVE,
                        ImagenPersonFilterLevel.BLOCK_ALL))
);

Una vez que se crea una instancia de ImagenModel, puedes generar imágenes llamando a generateImages:

Kotlin

val imageResponse = model.generateImages(
    prompt = "A hyper realistic picture of a t-rex with a blue bagpack in a prehistoric forest",
)
val image = imageResponse.images.first()
val bitmapImage = image.asBitmap()

Java

ListenableFuture<ImagenGenerationResponse<ImagenInlineImage>> futureResponse =
        model.generateImages(
                "A hyper realistic picture of a t-rex with a blue bagpack in a prehistoric forest");

try {
    ImagenGenerationResponse<ImagenInlineImage> imageResponse = futureResponse.get();
    List<ImagenInlineImage> images = null;
    if (imageResponse != null) {
        images = imageResponse.getImages();
    }
    if (images != null && !images.isEmpty()) {
        ImagenInlineImage image = images.get(0);
        Bitmap bitmapImage = image.asBitmap();
        // Use bitmapImage
    }
} catch (ExecutionException | InterruptedException e) {
    e.printStackTrace();
}

Edita imágenes con Imagen

Los SDKs de Firebase AI Logic ofrecen capacidades avanzadas de edición de imágenes a través del modelo de Imagen, lo que te permite hacer lo siguiente:

  • Editar imágenes basadas en máscaras , lo que incluye acciones como insertar o quitar objetos, extender el contenido de la imagen más allá de sus límites originales y cambiar fondos
  • Personalizar imágenes mediante la aplicación de estilos específicos (patrones, texturas o estilos de artistas), enfocándose en varios temas (como productos, personas o animales) o respetando diferentes controles (como un boceto dibujado a mano, una imagen de borde nítido o una malla facial).

Inicialización del modelo

Para usar las funciones de edición de Imagen, especifica un modelo de Imagen que admita la edición de imágenes, como imagen-3.0-capability-001:

val imagenModel = Firebase.ai(backend = GenerativeBackend.vertexAI())
    .imagenModel("imagen-3.0-capability-001")

Edición basada en máscaras

La edición basada en máscaras de Imagen permite realizar modificaciones en las imágenes definiendo áreas específicas para que el modelo las manipule. Esta capacidad permite una variedad de acciones, como crear y aplicar máscaras, insertar o quitar objetos y expandir el contenido de la imagen más allá de los límites originales.

Crea una máscara

Para realizar la edición basada en máscaras, como insertar o quitar objetos, debes definir el área que el modelo debe editar, la máscara.

Para crear una máscara, puedes hacer que el modelo la genere automáticamente con ImagenBackgroundMask() o ImagenSemanticMask(), pasando un ID de clase.

También puedes dibujar la máscara de forma manual en la pantalla. Para ello, genera un mapa de bits de máscara y conviértelo en un ImagenRawMask. Con detectDragGestures y Canvas, puedes implementar una interfaz de usuario de dibujo de máscaras con Jetpack Compose en tu app de la siguiente manera:

//import androidx.compose.ui.graphics.Color as ComposeColor

@Composable
fun ImagenEditingMaskEditor(
    sourceBitmap: Bitmap,
    onMaskFinalized: (Bitmap) -> Unit,
) {

    val paths = remember { mutableStateListOf<Path>() }
    var currentPath by remember { mutableStateOf<Path?>(null) }
    var scale by remember { mutableFloatStateOf(1f) }
    var offsetX by remember { mutableFloatStateOf(0f) }
    var offsetY by remember { mutableFloatStateOf(0f) }

    Column(
        modifier = Modifier.fillMaxSize(),
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .pointerInput(Unit) {
                    detectDragGestures(
                        onDragStart = { startOffset ->
                            val transformedStart = Offset(
                                (startOffset.x - offsetX) / scale,
                                (startOffset.y - offsetY) / scale,
                            )
                            currentPath = Path().apply { moveTo(transformedStart.x, transformedStart.y) }
                        },
                        onDrag = { change, _ ->
                            currentPath?.let {
                                val transformedChange = Offset(
                                    (change.position.x - offsetX) / scale,
                                    (change.position.y - offsetY) / scale,
                                )
                                it.lineTo(transformedChange.x, transformedChange.y)
                                currentPath = Path().apply { addPath(it) }
                            }
                            change.consume()
                        },
                        onDragEnd = {
                            currentPath?.let { paths.add(it) }
                            currentPath = null
                        },
                    )
                },
        ) {
            Image(
                bitmap = sourceBitmap.asImageBitmap(),
                contentDescription = null,
                modifier = Modifier.fillMaxSize(),
                contentScale = ContentScale.Fit,
            )
            Canvas(modifier = Modifier.fillMaxSize()) {
                val canvasWidth = size.width
                val canvasHeight = size.height
                val bitmapWidth = sourceBitmap.width.toFloat()
                val bitmapHeight = sourceBitmap.height.toFloat()
                scale = min(canvasWidth / bitmapWidth, canvasHeight / bitmapHeight)
                offsetX = (canvasWidth - bitmapWidth * scale) / 2
                offsetY = (canvasHeight - bitmapHeight * scale) / 2
                withTransform(
                    {
                        translate(left = offsetX, top = offsetY)
                        scale(scale, scale, pivot = Offset.Zero)
                    },
                ) {
                    val strokeWidth = 70f / scale
                    val stroke = Stroke(width = strokeWidth, cap = StrokeCap.Round, join = StrokeJoin.Round)
                    val pathColor = ComposeColor.White.copy(alpha = 0.5f)
                    paths.forEach { path ->
                        drawPath(path = path, color = pathColor, style = stroke)
                    }
                    currentPath?.let { path ->
                        drawPath(path = path, color = pathColor, style = stroke)
                    }
                }
            }
        }
        Button(
            onClick = {
                val maskBitmap = createMaskBitmap(sourceBitmap, paths)
                onMaskFinalized(maskBitmap)
            },
        ) {
            Text("Save mask")
        }
    }
}

Luego, puedes crear el mapa de bits de máscara dibujando las rutas de acceso en el lienzo:

// import android.graphics.Color as AndroidColor
// import android.graphics.Paint

private fun createMaskBitmap(
    sourceBitmap: Bitmap,
    paths: SnapshotStateList<Path>,
): Bitmap {
    val maskBitmap = Bitmap.createBitmap(sourceBitmap.width, sourceBitmap.height, Bitmap.Config.ARGB_8888)
    val canvas = android.graphics.Canvas(maskBitmap)
    val paint = Paint().apply {
        color = AndroidColor.RED
        strokeWidth = 70f
        style = Paint.Style.STROKE
        strokeCap = Paint.Cap.ROUND
        strokeJoin = Paint.Join.ROUND
        isAntiAlias = true
    }
    paths.forEach { path -> canvas.drawPath(path.asAndroidPath(), paint) }

    return maskBitmap
}

Asegúrate de que la máscara tenga el mismo tamaño que la imagen fuente. Consulta los ejemplos del catálogo de IA de Imagen para obtener más detalles.

Inserta objetos

Puedes insertar un objeto o contenido nuevo en una imagen existente, lo que también se conoce como retoque. El modelo generará e insertará el contenido nuevo en el área enmascarada especificada.

Para lograrlo, usa la función editImage(). Deberás proporcionar la imagen original, una máscara, y una instrucción de texto que describa el contenido que deseas insertar. Además, pasa un objeto ImagenEditingConfig y asegúrate de que su propiedad editMode esté configurada como ImagenEditMode.INPAINT_INSERTION.

suspend fun insertFlowersIntoImage(
    model: ImagenModel,
    originalImage: Bitmap,
    mask: ImagenMaskReference
): ImagenGenerationResponse<ImagenInlineImage> {
    val prompt = "a vase of flowers"

    // Pass the original image, a mask, the prompt, and an editing configuration.
    val editedImage = model.editImage(
        referenceImages = listOf(
            ImagenRawImage(originalImage.toImagenInlineImage()),
            mask,
        ),
        prompt = prompt,
        // Define the editing configuration for inpainting and insertion.
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_INSERTION)
    )
    return editedImage
}

Quita objetos

El retoque te permite quitar objetos no deseados de una imagen. Para ello, usa la función editImage. Deberás proporcionar la imagen original y una máscara que destaque el objeto que deseas quitar. De manera opcional, puedes incluir una instrucción de texto para describir el objeto, lo que puede ayudar al modelo a identificarlo con precisión. Además, debes configurar editMode dentro de ImagenEditingConfig como ImagenEditMode.INPAINT_REMOVAL.

suspend fun removeBallFromImage(
    model: ImagenModel,
    originalImage: Bitmap,
    mask: ImagenMaskReference
): ImagenGenerationResponse<ImagenInlineImage> {

    // Optional: provide the prompt describing the content to be removed.
    val prompt = "a ball"

    // Pass the original image, a mask, the prompt, and an editing configuration.
    val editedImage = model.editImage(
        referenceImages = listOf(
            ImagenRawImage(originalImage.toImagenInlineImage()),
            mask
        ),
        prompt = prompt,
        // Define the editing configuration for inpainting and removal.
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_REMOVAL)
    )

    return editedImage
}

Expande el contenido de la imagen

Puedes expandir una imagen más allá de sus límites originales, lo que se conoce como extensión de imagen, con la función outpaintImage(). Esta función requiere la imagen original y las Dimensions necesarias de la imagen expandida. De manera opcional, puedes incluir una instrucción descriptiva para la expansión y especificar el ImagenImagePlacement de la imagen original dentro de la nueva imagen generada:

suspend fun expandImage(originalImage: Bitmap, imagenModel: ImagenModel): ImagenGenerationResponse<ImagenInlineImage> {

    // Optionally describe what should appear in the expanded area.
    val prompt = "a sprawling sandy beach next to the ocean"

    val editedImage = imagenModel.outpaintImage(
        originalImage.toImagenInlineImage(),
        Dimensions(1024, 1024),
        prompt = prompt,
        newPosition = ImagenImagePlacement.LEFT_CENTER
    )


    return editedImage
}

Reemplaza el fondo

Puedes reemplazar el fondo de una imagen y conservar el sujeto en primer plano. Para ello, usa la función editImage. Pasa la imagen original, un objeto ImagenBackgroundMask (que contiene una instrucción de texto para el fondo nuevo) y un ImagenEditingConfig con su propiedad editMode configurada como ImagenEditMode.INPAINT_INSERTION.

suspend fun replaceBackground(model: ImagenModel, originalImage: Bitmap): ImagenGenerationResponse<ImagenInlineImage> {
    // Provide the prompt describing the new background.
    val prompt = "space background"

    // Pass the original image, a mask, the prompt, and an editing configuration.
    val editedImage = model.editImage(
        referenceImages = listOf(
            ImagenRawImage(originalImage.toImagenInlineImage()),
            ImagenBackgroundMask(),
        ),
        prompt = prompt,
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_INSERTION)
    )

    return editedImage
}

Personalización

Puedes usar la capacidad de personalización de Imagen para generar o editar imágenes basadas en imágenes de referencia que especifican un tema, un control o un estilo. Para ello, proporciona una instrucción de texto junto con una o más imágenes de referencia para guiar al modelo.

Personaliza según un tema

Puedes generar imágenes nuevas de un tema específico a partir de una imagen de referencia (por ejemplo, un producto, una persona o un animal). Solo proporciona una instrucción de texto y al menos una imagen de referencia del tema. Por ejemplo, puedes subir una foto de tu mascota y generar una imagen nueva en un entorno completamente diferente.

Para ello, define la referencia del tema con ImagenSubjectReference y, luego, pásala a editImage junto con tu instrucción. Además, incluye un ImagenEditingConfig que especifique la cantidad de editSteps; un valor editSteps más alto suele generar resultados de mejor calidad:

suspend fun customizeCatImage(model: ImagenModel, referenceCatImage: Bitmap): ImagenGenerationResponse<ImagenInlineImage> {

    // Define the subject reference using the reference image.
    val subjectReference = ImagenSubjectReference(
        image = referenceCatImage.toImagenInlineImage(),
        referenceId = 1,
        description = "cat",
        subjectType = ImagenSubjectReferenceType.ANIMAL
    )

    // Provide a prompt that describes the final image.
    // The "[1]" links the prompt to the subject reference with ID 1.
    val prompt = "A cat[1] flying through outer space"

    // Use the editImage API to perform the subject customization.
    val editedImage = model.editImage(
        referenceImages = listOf(subjectReference),
        prompt = prompt,
        config = ImagenEditingConfig(
            editSteps = 50 // Number of editing steps, a higher value can improve quality
        )
    )

    return editedImage
}

Personaliza según un control

Esta técnica genera una imagen nueva basada en una imagen de referencia de control, como un boceto dibujado a mano ("garabato"), una imagen de borde nítido, o una malla facial. El modelo usa la imagen de control como guía estructural para el diseño y la composición de la imagen nueva, mientras que la instrucción de texto proporciona detalles como el color y la textura.

Define una referencia de control con ImagenControlReference y proporciónala a editImage junto con una instrucción y ImagenEditingConfig con la cantidad de editSteps (un valor más alto puede mejorar la calidad):

suspend fun customizeCatImageByControl(model: ImagenModel, referenceImage: Bitmap): ImagenGenerationResponse<ImagenInlineImage> {

    // Define the subject reference using the reference image.
    val controlReference = ImagenControlReference(
        image = referenceImage.toImagenInlineImage(),
        referenceId = 1,
        type = ImagenControlType.SCRIBBLE,
    )

    val prompt = "A cat flying through outer space arranged like the scribble map[1]"

    val editedImage = model.editImage(
        referenceImages = listOf(controlReference),
        prompt = prompt,
        config = ImagenEditingConfig(
            editSteps = 50
        ),
    )

    return editedImage
}

Personaliza según un estilo

Puedes generar o editar una imagen para que coincida con un estilo específico de una imagen de referencia, como su patrón, textura o diseño. El modelo usa la imagen de referencia para comprender la estética requerida y la aplica a la imagen nueva que se describe en la instrucción de texto. Por ejemplo, puedes generar una imagen de un gato con el estilo de una pintura famosa proporcionando una imagen de esa pintura.

Define una referencia de estilo con ImagenStyleReference y proporciónala a editImage junto con una instrucción y ImagenEditingConfig con la cantidad de editSteps (un valor más alto puede mejorar la calidad):

suspend fun customizeImageByStyle(model: ImagenModel, referenceVanGoghImage: Bitmap): ImagenGenerationResponse<ImagenInlineImage> {

    // Define the style reference using the reference image.
    val styleReference = ImagenStyleReference(
        image = referenceVanGoghImage.toImagenInlineImage(),
        referenceId = 1,
        description = "Van Gogh style"
    )

    // Provide a prompt that describes the final image.
    // The "1" links the prompt to the style reference with ID 1.
    val prompt = "A cat flying through outer space, in the Van Gogh style[1]"

    // Use the editImage API to perform the style customization.
    val editedImage = model.editImage(
        referenceImages = listOf(styleReference),
        prompt = prompt,
        config = ImagenEditingConfig(
            editSteps = 50 // Number of editing steps, a higher value can improve quality
        ),
    )

    return editedImage
}

Próximos pasos