Bilder mit Imagen generieren

Imagen ist ein Bildgenerierungsmodell. Es kann verwendet werden, um benutzerdefinierte Avatare für Nutzerprofile zu generieren oder personalisierte visuelle Assets in vorhandene Bildschirmabläufe zu integrieren, um die Nutzerinteraktion zu steigern.

Sie können über das Firebase AI Logic SDK von Ihrer Android-App aus auf Imagen-Modelle zugreifen. Imagen-Modelle sind mit beiden Firebase AI Logic API-Anbietern verfügbar: Gemini Developer API (für die meisten Entwickler empfohlen) und Vertex AI.

Ein Diagramm, das die Architektur einer Firebase AI Logic-Integration für den Zugriff auf die Gemini Developer API veranschaulicht. Eine Android-App verwendet das Firebase Android SDK, um eine Verbindung zu Firebase herzustellen. Firebase interagiert dann mit der Gemini Developer API, die auf Gemini Pro und Flash in der Cloud zugreift.
Abbildung 1 Zugriff auf Imagen-Modelle mit Firebase AI Logic.

Mit Prompts experimentieren

Das Erstellen der idealen Prompts erfordert oft mehrere Versuche. Sie können in Google AI Studio mit Bild-Prompts experimentieren . Google AI Studio ist eine IDE für das Prompt-Design und die Prototyperstellung. Tipps zur Verbesserung Ihrer Prompts finden Sie im Leitfaden Prompts und Bildattribute.

Ein Screenshot der Google AI Studio-Benutzeroberfläche mit vier generierten Bildern eines T-Rex mit einem blauen Rucksack in einem prähistorischen Wald.
Abbildung 2 Mit Google AI Studio können Sie Ihre Prompts zur Bildgenerierung optimieren.

Firebase-Projekt einrichten und App verbinden

Folgen Sie der Anleitung in der Firebase-Dokumentation, um Ihrem Android-Projekt Firebase hinzuzufügen.

Gradle-Abhängigkeit hinzufügen

Fügen Sie der Datei build.gradle die folgenden Abhängigkeiten hinzu:

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")
}

Bild generieren

Wenn Sie in Ihrer Android-App ein Bild generieren möchten, instanziieren Sie zuerst ein ImagenModel mit einer optionalen Konfiguration.

Mit dem generationConfig Parameter können Sie einen negativen Prompt, die Anzahl der Bilder, das Seitenverhältnis des Ausgabebilds und das Bildformat definieren sowie ein Wasserzeichen hinzufügen. Mit dem safetySettings Parameter können Sie die Filter für Sicherheit und Personen konfigurieren.

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))
);

Sobald Ihr ImagenModel instanziiert ist, können Sie Bilder generieren, indem Sie generateImages aufrufen:

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();
}

Bilder mit Imagen bearbeiten

Die Firebase AI Logic SDKs bieten erweiterte Bildbearbeitungsfunktionen über das Imagen-Modell. Damit können Sie Folgendes tun:

  • Bilder anhand von Masken bearbeiten , einschließlich Aktionen wie das Einfügen oder Entfernen von Objekten, das Erweitern von Bildinhalten über die ursprünglichen Grenzen hinaus und das Ändern von Hintergründen.
  • Bilder anpassen , indem Sie bestimmte Stile anwenden (Muster, Texturen oder Künstlerstile), sich auf verschiedene Motive konzentrieren (z. B. Produkte, Personen oder Tiere) oder verschiedene Steuerelemente verwenden (z. B. eine handgezeichnete Skizze, ein Canny-Edge-Bild oder ein Gesichtsnetz).

Modellinitialisierung

Wenn Sie die Imagen-Bearbeitungsfunktionen verwenden möchten, geben Sie ein Imagen-Modell an, das die Bild bearbeitung unterstützt, z. B. imagen-3.0-capability-001:

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

Maskenbasierte Bearbeitung

Mit der maskenbasierten Bearbeitung von Imagen können Sie Bilder ändern, indem Sie bestimmte Bereiche definieren, die das Modell bearbeiten soll. Diese Funktion ermöglicht eine Reihe von Aktionen, darunter das Erstellen und Anwenden von Masken, das Einfügen oder Entfernen von Objekten und das Erweitern von Bildinhalten über die ursprünglichen Grenzen hinaus.

Maske erstellen

Für die maskenbasierte Bearbeitung, z. B. das Einfügen oder Entfernen von Objekten, müssen Sie den Bereich definieren, der vom Modell bearbeitet werden soll, die Maske.

Sie können eine Maske automatisch vom Modell generieren lassen, indem Sie ImagenBackgroundMask() oder ImagenSemanticMask() verwenden und eine Klassen ID übergeben.

Sie können die Maske auch manuell auf dem Bildschirm zeichnen, indem Sie eine Maskenbitmap generieren und in ein ImagenRawMask umwandeln. Mit detectDragGestures und Canvas können Sie in Ihrer App eine Benutzeroberfläche zum Zeichnen von Masken mit Jetpack Compose implementieren:

//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")
        }
    }
}

Sie können dann die Maskenbitmap erstellen, indem Sie die Pfade auf die Canvas zeichnen:

// 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
}

Die Maske muss dieselbe Größe wie das Quellbild haben. Weitere Informationen finden Sie in den Imagen AI Catalog Samples.

Objekte einfügen

Sie können ein neues Objekt oder einen neuen Inhalt in ein vorhandenes Bild einfügen. Das wird auch als Inpainting bezeichnet. Das Modell generiert den neuen Inhalt und fügt ihn in den angegebenen maskierten Bereich ein.

Verwenden Sie dazu die Funktion editImage(). Sie müssen das Originalbild, eine Maskeund einen Text-Prompt angeben, der den einzufügenden Inhalt beschreibt. Übergeben Sie außerdem ein ImagenEditingConfig-Objekt und stellen Sie sicher, dass die Property editMode auf ImagenEditMode.INPAINT_INSERTION gesetzt ist.

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
}

Objekte entfernen

Mit Inpainting können Sie unerwünschte Objekte aus einem Bild entfernen. Verwenden Sie dazu die Funktion editImage. Sie müssen das Originalbild und eine Maske angeben, die das zu entfernende Objekt hervorhebt. Optional können Sie einen Text-Prompt hinzufügen, um das Objekt zu beschreiben. Das kann dem Modell helfen, es genau zu identifizieren. Außerdem müssen Sie editMode in ImagenEditingConfig auf ImagenEditMode.INPAINT_REMOVAL setzen.

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
}

Bildinhalte erweitern

Mit der Funktion outpaintImage() können Sie ein Bild über seine ursprünglichen Grenzen hinaus erweitern. Das wird als Outpainting bezeichnet. Für diese Funktion sind das Original bild und die erforderlichen Dimensions des erweiterten Bilds erforderlich. Optional können Sie einen beschreibenden Prompt für die Erweiterung angeben und die das ImagenImagePlacement des Originalbilds im neu generierten Bild festlegen:

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
}

Hintergrund ersetzen

Sie können den Hintergrund eines Bilds ersetzen und dabei das Vordergrundmotiv beibehalten. Verwenden Sie dazu die Funktion editImage. Übergeben Sie das Originalbild, ein ImagenBackgroundMask-Objekt (mit einem Text-Prompt für den neuen Hintergrund) und ein ImagenEditingConfig-Objekt, dessen Property editMode auf ImagenEditMode.INPAINT_INSERTION gesetzt ist.

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
}

Anpassung

Mit der Anpassungsfunktion von Imagen können Sie Bilder anhand von Referenzbildern generieren oder bearbeiten, die ein Motiv, ein Steuerelement oder einen Stil angeben. Dazu geben Sie einen Text-Prompt zusammen mit einem oder mehreren Referenzbildern an, um das Modell zu steuern.

Anpassung anhand eines Motivs

Sie können neue Bilder eines bestimmten Motivs aus einem Referenzbild generieren (z. B. ein Produkt, eine Person oder ein Tier). Geben Sie einfach einen Text-Prompt und mindestens ein Referenzbild des Motivs an. Sie könnten beispielsweise ein Bild Ihres Haustiers hochladen und ein neues Bild davon in einer völlig anderen Umgebung generieren.

Definieren Sie dazu die Motivreferenz mit ImagenSubjectReference und übergeben Sie sie zusammen mit Ihrem Prompt an editImage. Fügen Sie außerdem ein ImagenEditingConfig-Objekt hinzu, das die Anzahl der editSteps angibt. Ein höherer Wert für editSteps führt in der Regel zu besseren Ergebnissen:

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
}

Anpassung anhand eines Steuerelements

Mit dieser Technik wird ein neues Bild anhand eines Steuerungsreferenzbilds, z. B. einer handgezeichneten Skizze („Scribble“), eines Canny-Edge-Bilds, oder eines Gesichtsnetzes generiert. Das Modell verwendet das Steuerungsbild als strukturelle Anleitung für das Layout und die Komposition des neuen Bilds, während der Text-Prompt Details wie Farbe und Textur liefert.

Definieren Sie eine Steuerungsreferenz mit ImagenControlReference und übergeben Sie sie zusammen mit einem Prompt und ImagenEditingConfig mit der Anzahl der editSteps an editImage. Ein höherer Wert kann die Qualität verbessern:

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
}

Anpassung anhand eines Stils

Sie können ein Bild generieren oder bearbeiten, um es an einen bestimmten Stil aus einem Referenzbild anzupassen, z. B. an sein Muster, seine Textur oder sein Design. Das Modell verwendet das Referenzbild, um die erforderliche Ästhetik zu verstehen, und wendet sie auf das neue Bild an, das im Text-Prompt beschrieben wird. Sie könnten beispielsweise ein Bild einer Katze im Stil eines berühmten Gemäldes generieren, indem Sie ein Bild dieses Gemäldes angeben.

Definieren Sie eine Stilreferenz mit ImagenStyleReference und übergeben Sie sie zusammen mit einem Prompt und ImagenEditingConfig mit der Anzahl der editSteps an editImage. Ein höherer Wert kann die Qualität verbessern:

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
}

Nächste Schritte