使用 Imagen 生成图片

Imagen 是一种图片生成模型。该功能可用于为用户个人资料生成自定义头像,或将个性化视觉资源集成到现有界面流程中,以提高用户互动度。

您可以使用 Firebase AI Logic SDK 从 Android 应用访问 Imagen 模型。Imagen 模型可通过以下两种 Firebase AI Logic API 提供方使用:Gemini Developer API(建议大多数开发者使用)和 Vertex AI。

一张图表,展示了用于访问 Gemini Developer API 的 Firebase AI Logic 集成架构。Android 应用利用 Firebase Android SDK 连接到 Firebase。然后,Firebase 会与 Gemini Developer API 进行交互,后者会访问云端中的 Gemini Pro 和 Gemini 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 边缘图片或面部网格)。

模型初始化

如需使用 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,并将该 ImagenEditingConfigeditMode 属性设置为 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,其中 ImagenEditingConfigeditSteps 的数量(值越高,质量越好):

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,其中 ImagenEditingConfig 包含 editSteps 的数量(值越高,质量越好):

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 
}

后续步骤