Bilder mit Imagen generieren

Imagen ist ein Modell zur Bildgenerierung. Damit lassen sich benutzerdefinierte Avatare für Nutzerprofile erstellen oder personalisierte visuelle Assets in bestehende Bildschirmabläufe einbinden, um die Nutzerinteraktion zu steigern.

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

Ein Diagramm zur Veranschaulichung einer Firebase AI Logic-Integrationsarchitektur für den Zugriff auf die Gemini Developer API. Eine Android-App nutzt 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: Mit Firebase AI Logic auf Imagen-Modelle zugreifen

Prompts ausprobieren

Das Erstellen der idealen Prompts erfordert oft mehrere Versuche. Sie können mit Bild-Prompts in Google AI Studio experimentieren, einer IDE für Prompt-Design und Prototyping. Tipps zur Verbesserung Ihrer Prompts finden Sie im Leitfaden zu Prompts und Bildattributen.

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

Firebase-Projekt einrichten und App verbinden

Folgen Sie der Anleitung in der Firebase-Dokumentation, um Firebase zu Ihrem Android-Projekt 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.4.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 ein Bild in Ihrer Android-App generieren möchten, instanziieren Sie zuerst ein ImagenModel mit einer optionalen Konfiguration.

Mit dem Parameter generationConfig 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 Parameter safetySettings können Sie die Sicherheits- und Personenfilter 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.ai(backend = GenerativeBackend.googleAI()).imagenModel(
       "imagen-4.0-generate-001",
       config,
       ImagenSafetySettings.builder()
          .setSafetyFilterLevel(ImagenSafetyFilterLevel.BLOCK_LOW_AND_ABOVE)
          .setPersonFilterLevel(ImagenPersonFilterLevel.BLOCK_ALL)
          .build())
);

Sobald Ihre 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

CompletableFuture<GenerateContentResponse> futureResponse =
    model.generateContent(
        Content.newBuilder()
            .addParts(
                Part.newBuilder()
                    .setText("A hyper realistic picture of a t-rex with a blue bagpack in a prehistoric forest")
                    .build())
            .build());

try {
  GenerateContentResponse imageResponse = futureResponse.get();
  List<GeneratedImage> images =
      imageResponse
          .getCandidates(0)
          .getContent()
          .getParts(0)
          .getInlineData()
          .getImagesList();

  if (!images.isEmpty()) {
    GeneratedImage 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:

  • Bilder anhand von Masken bearbeiten: Dazu gehören 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 – durch Anwenden bestimmter Stile (Muster, Texturen oder Künstlerstile), durch Fokussieren auf verschiedene Motive (z. B. Produkte, Personen oder Tiere) oder durch Einhalten verschiedener Steuerelemente (z. B. handgezeichnete Skizze, Canny-Edge-Bild oder Gesichts-Mesh).

Modellinitialisierung

Wenn Sie die Imagen-Bearbeitungsfunktionen verwenden möchten, geben Sie ein Imagen-Modell an, das die Bildbearbeitung unterstützt, z. B. imgen-3.0-capability-001. Version des Modells:

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

Maskenbasierte Bearbeitung

Mit der maskenbasierte Bearbeitung von Imagen können Bilder bearbeitet werden, indem bestimmte Bereiche für das Modell definiert werden. 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. zum Einfügen oder Entfernen von Objekten, müssen Sie den Bereich definieren, der vom Modell bearbeitet werden soll – die Maske.

Wenn Sie eine Maske erstellen möchten, können Sie sie vom Modell automatisch 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 Masken-Bitmap generieren und in eine ImagenRawMask konvertieren. 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 = createMask(sourceBitmap, paths)
                onMaskFinalized(maskBitmap)
            },
        ) {
            Text("Save mask")
        }
    }
}

Anschließend können Sie die Masken-Bitmap erstellen, indem Sie die Pfade auf das Canvas zeichnen:

import android.graphics.Color as AndroidColor
import android.graphics.Paint
[...]

private fun createMaskBitmap(
    sourceBitmap: Bitmap,
    paths: SnapshotStateList<Path>,
): Bitmap {
    val maskBitmap = createBitmap(sourceBitmap.width, sourceBitmap.height)
    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 unter Imagen AI-Katalogbeispiele.

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 die neuen Inhalte und fügt sie in den angegebenen maskierten Bereich ein.

Verwenden Sie dazu die Funktion editImage(). Sie müssen das Originalbild, eine Maske und einen Text-Prompt mit einer Beschreibung der Inhalte, die Sie einfügen möchten, angeben. Übergeben Sie außerdem ein ImagenEditingConfig-Objekt und achten Sie darauf, dass dessen editMode-Property auf ImagenEditMode.INPAINT_INSERTION festgelegt 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(
        sources = listOf(
            ImagenRawImage(originalImage),
            mask),
        prompt = prompt,
        // Define the editing configuration for inpainting and insertion.
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_INSERTION)
    )

    return editedImage
}

Objekte entfernen

Mit der Funktion „Übermalen“ können Sie unerwünschte Objekte aus einem Bild entfernen. Verwenden Sie dazu die Funktion editImage. Sie müssen das Originalbild und eine Maske bereitstellen, die das Objekt hervorhebt, das Sie entfernen möchten. Optional können Sie einen Text-Prompt einfü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(
        sources = listOf(
            ImagenRawImage(originalImage),
            mask
        ),
        prompt = prompt,
        // Define the editing configuration for inpainting and removal.
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_REMOVAL)
    )

    return editedImage
}

Bildinhalte maximieren

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 Originalbild und die erforderlichen Dimensions des erweiterten Bildes erforderlich. Optional können Sie einen beschreibenden Prompt für die Erweiterung einfügen und die ImagenImagePlacement des Originalbilds im neu generierten Bild angeben:

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 = model.outpaintImage(
        ImagenRawImage(originalImage),
        Dimension(width, height),
        prompt = prompt,
        newPosition = ImagenImagePlacement.LEFT_CENTER
    )


    return editedImage
}

Hintergrund ersetzen

Sie können den Hintergrund eines Bildes ersetzen und dabei das Vordergrundmotiv beibehalten. Verwenden Sie dazu die Funktion editImage. Übergeben Sie das Originalbild, ein ImagenBackgroundMask-Objekt (mit einem Textprompt für den neuen Hintergrund) und ein ImagenEditingConfig-Objekt, dessen editMode-Attribut auf ImagenEditMode.INPAINT_INSERTION festgelegt 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(
        sources = listOf(
            ImagenRawImage(originalImage),
            ImagenBackgroundMask(),
        ),
        prompt = prompt,
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_INSERTION)
    )

    return editedImage
}

Personalisierung

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

Anpassen basierend auf einem Thema

Sie können neue Bilder eines bestimmten Motivs aus einem Referenzbild generieren, z. B. eines Produkts, einer Person oder eines Tieres. 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 lassen.

Dazu definieren Sie den Themenbezug mit ImagenSubjectReference und übergeben ihn dann zusammen mit Ihrem Prompt an editImage. Geben Sie außerdem eine ImagenEditingConfig an, die die Anzahl der editSteps angibt. Ein höherer editSteps-Wert führt in der Regel zu Ergebnissen von besserer Qualität:

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

    // Define the subject reference using the reference image.
    val subjectReference = ImagenSubjectReference(
        image = referenceCatImage,
        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(
        references = listOf(subjectReference),
        prompt = prompt,
        config = ImagenEditingConfig(
            editSteps = 50 // Number of editing steps, a higher value can improve quality
        )
    )

    return editedImage
}

Anpassung basierend auf einer Kontrollvariablen

Bei dieser Technik wird ein neues Bild auf Grundlage eines Kontrollreferenzbilds generiert, z. B. einer handgezeichneten Skizze („Scribble“), eines Canny-Edge-Bilds oder eines Gesichts-Mesh. Das Modell verwendet das Kontrollbild als strukturelle Orientierungshilfe für das Layout und die Komposition des neuen Bildes, während der Text-Prompt Details wie Farbe und Textur liefert.

Definieren Sie eine Kontrollreferenz mit ImagenControlReference und stellen Sie sie editImage zusammen mit einem Prompt und ImagenEditingConfig mit der Anzahl der editSteps zur Verfügung (ein höherer Wert kann die Qualität verbessern):

suspend fun customizeCatImageByControl(model: ImagenModel, referenceCatImage: Bitmap): ImagenGenerationResponse<ImagenInlineImage> {
 
   // Define the subject reference using the reference image.
    val controlReference = ImagenControlReference(
        image = referenceImage,
        referenceID = 1,
        controlType = CONTROL_TYPE_SCRIBBLE
    )

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

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

    return editedImage
}

Anpassung basierend auf einem Stil

Sie können ein Bild generieren oder bearbeiten, sodass es einem bestimmten Stil eines Referenzbilds entspricht, z. B. dessen Muster, Textur oder Design. Das Modell verwendet das Referenzbild, um die gewünschte Ä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 erstellen, indem Sie ein Bild dieses Gemäldes bereitstellen.

Definieren Sie mit ImagenStyleReference eine Stilreferenz und stellen Sie sie editImage zusammen mit einem Prompt und ImagenEditingConfig mit der Anzahl der editSteps zur Verfügung (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,
        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(
        references = listOf(styleReference),
        prompt = prompt,
        config = ImagenEditingConfig(
            editSteps = 50 // Number of editing steps, a higher value can improve quality
        )
    )

    return editedImage 
}

Nächste Schritte