使用 Imagen 生成圖片

Imagen 是圖像生成模型,這項功能可為使用者個人資料生成自訂顯示圖片,或將個人化視覺素材整合至現有畫面流程,提高使用者參與度。

您可以使用 Firebase AI Logic SDK,從 Android 應用程式存取 Imagen 模型。您可以使用 Firebase AI Logic API 提供者,透過 Gemini Developer API (建議大多數開發人員使用) 和 Vertex AI,存取 Imagen 模型。

這張圖說明 Firebase AI Logic 整合架構,可存取 Gemini Developer API。Android 應用程式會使用 Firebase Android SDK 連線至 Firebase。Firebase 接著會與 Gemini 開發人員 API 互動,存取雲端中的 Gemini Pro 和 Flash。
圖 1. 使用 Firebase AI Logic 存取 Imagen 模型。

測試提示

通常需要多次嘗試,才能建立理想的提示。您可以在 Google AI Studio 中測試圖片提示,這個 IDE 可用於設計提示和製作原型。如需改善提示的訣竅,請參閱提示和圖像屬性指南

Google AI Studio 介面的螢幕截圖,顯示四張生成圖像,內容為在史前森林中背著藍色背包的暴龍。
圖 2. Google AI Studio 可協助你調整圖片生成提示。

設定 Firebase 專案並連結應用程式

按照 Firebase 說明文件中的步驟,將 Firebase 新增至 Android 專案

新增 Gradle 依附元件

build.gradle 檔案中新增下列依附元件:

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

生成圖片

如要在 Android 應用程式中生成圖片,請先例項化 ImagenModel,並視需要設定。

您可以使用 generationConfig 參數定義負面提示、圖片數量、輸出圖片的顯示比例、圖片格式,以及新增浮水印。您可以使用 safetySettings 參數設定安全和個人篩選器。

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

ImagenModel 例項化後,您就可以呼叫 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

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

使用 Imagen 編輯圖像

Firebase AI Logic SDK 透過 Imagen 模型提供進階圖片編輯功能,可讓您:

  • 根據遮罩編輯圖片,包括插入或移除物件、將圖片內容延伸至原始邊界之外,以及變更背景。
  • 自訂圖片:套用特定樣式 (圖案、紋理或藝術家風格)、著重於各種主題 (例如產品、人物或動物),或遵循不同控制項 (例如手繪草圖、canny edge 圖像或臉部網格)。

模型初始化

如要使用 Imagen 編輯功能,請指定支援圖片編輯的 Imagen 模型,例如 imgen-3.0-capability-001。模型版本:

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

遮罩型編輯

Imagen 的遮蓋型編輯功能可讓您定義模型要處理的特定區域,藉此修改圖像。這項功能可執行多種動作,包括建立及套用遮罩、插入或移除物件,以及將圖片內容擴展至原始邊界以外。

建立遮罩

如要執行遮罩型編輯 (例如插入或移除物件),請先使用遮罩定義需要模型編輯的區域。

如要建立遮罩,您可以透過 ImagenBackgroundMask()ImagenSemanticMask() 傳遞類別 ID,讓模型自動生成遮罩。

你也可以在畫面上手動繪製遮罩,方法是產生遮罩點陣圖,然後轉換成 ImagenRawMask。使用 detectDragGesturesCanvas,您可以在應用程式中透過 Jetpack Compose 實作遮罩繪製使用者介面,如下所示:

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

然後在畫布上繪製路徑,建立遮罩點陣圖:

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
}

請確認遮罩與來源圖片大小相同。詳情請參閱 Imagen AI 目錄範例

插入物件

您可以在現有圖片中插入新物件或內容,這也稱為修復。模型會生成新內容,並插入指定遮蓋區域。

如要達成這個目標,請使用 editImage() 函式。您需要提供原始圖片、遮罩,以及描述要插入內容的文字提示。此外,請傳遞 ImagenEditingConfig 物件,並確保其 editMode 屬性設為 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(
        sources = listOf(
            ImagenRawImage(originalImage),
            mask),
        prompt = prompt,
        // Define the editing configuration for inpainting and insertion.
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_INSERTION)
    )

    return editedImage
}

移除物件

塗改功能可移除圖片中不想要的物件。如要執行這項操作,請使用 editImage 函式。您需要提供原始圖片和遮罩,醒目顯示要移除的物件。您也可以選擇加入文字提示來描述物體,協助模型準確辨識。此外,您必須將 ImagenEditingConfig 中的 editMode 設為 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(
        sources = listOf(
            ImagenRawImage(originalImage),
            mask
        ),
        prompt = prompt,
        // Define the editing configuration for inpainting and removal.
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_REMOVAL)
    )

    return editedImage
}

擴展圖片內容

您可以使用 outpaintImage() 函式,將圖片擴展到原始邊界之外,也就是所謂的「外繪」。這項功能需要原始圖片和擴大圖片的必要 Dimensions。視需要,您可以加入擴展的描述性提示,並在生成的新圖片中指定原始圖片的 ImagenImagePlacement

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
}

更換背景

你可以更換圖片背景,同時保留前景主體。如要執行這項操作,請使用 editImage 函式。傳遞原始圖片、ImagenBackgroundMask 物件 (內含新背景的文字提示),以及 ImagenEditingConfig (其 editMode 屬性設為 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(
        sources = listOf(
            ImagenRawImage(originalImage),
            ImagenBackgroundMask(),
        ),
        prompt = prompt,
        config = ImagenEditingConfig(ImagenEditMode.INPAINT_INSERTION)
    )

    return editedImage
}

自訂

您可以使用 Imagen 的自訂功能,根據指定主題、控制項或樣式的參考圖片,生成或編輯圖片。方法是提供文字提示,以及一或多張參考圖片,引導模型生成圖片。

根據主題自訂

您可以根據參考圖片 (例如產品、人物或動物),生成特定主體的全新圖片。只要提供文字提示,以及至少一張主題的參考圖片,舉例來說,你可以上傳寵物的相片,然後生成寵物在完全不同環境中的新圖片。

如要這麼做,請使用 ImagenSubjectReference 定義主體參照,然後將其連同提示傳遞至 editImage。此外,請加入指定 editSteps 數量的 ImagenEditingConfigeditSteps 值越高,通常會產生品質越好的結果:

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
}

根據控制項自訂

這項技術會根據控制參照圖片生成新圖片,例如手繪草圖 (「塗鴉」)、Canny 邊緣圖片或臉部網格。模型會將控制圖片做為新圖片的版面配置和構圖結構指南,而文字提示則提供顏色和紋理等詳細資料。

使用 ImagenControlReference 定義控制項參照,並連同提示和 ImagenEditingConfig 提供給 editImage,並指定 editSteps 數量 (值越高,品質越好):

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
}

根據樣式自訂

你可以生成或編輯圖片,讓圖片的風格與參考圖片相符,例如圖片的圖案、紋理或設計。模型會使用參考圖片瞭解所需的美學風格,並套用至文字提示中描述的新圖片。舉例來說,只要提供著名畫作的圖片,就能生成該畫風的貓咪圖片。

使用 ImagenStyleReference 定義樣式參照,並連同提示和 ImagenEditingConfig 提供給 editImage,其中 ImagenEditingConfigeditSteps 的數量 (值越高越能提升品質):

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 
}

後續步驟