Tạo hình ảnh bằng Imagen

Imagen là một mô hình tạo hình ảnh. Bạn có thể dùng mô hình này để tạo hình đại diện tuỳ chỉnh cho hồ sơ người dùng hoặc để tích hợp các thành phần hình ảnh được cá nhân hoá vào luồng màn hình hiện có nhằm tăng mức độ tương tác của người dùng.

Bạn có thể truy cập vào các mô hình Imagen từ ứng dụng Android bằng SDK Firebase AI Logic. Bạn có thể sử dụng các mô hình Imagen bằng cả hai nhà cung cấp Firebase AI Logic API: Gemini Developer API (nên dùng cho hầu hết nhà phát triển) và Vertex AI.

Sơ đồ minh hoạ cấu trúc tích hợp Firebase AI Logic để truy cập vào Gemini Developer API. Ứng dụng Android sử dụng Firebase Android SDK để kết nối với Firebase. Sau đó, Firebase sẽ tương tác với Gemini Developer API để truy cập vào Gemini Pro và Flash trong đám mây.
Hình 1. Truy cập vào các mô hình Imagen bằng Firebase AI Logic.

Thử nghiệm với câu lệnh

Bạn thường phải thử nhiều lần mới tạo được câu lệnh lý tưởng. Bạn có thể thử nghiệm với câu lệnh hình ảnh trong Google AI Studio, một IDE để thiết kế và tạo nguyên mẫu câu lệnh. Để biết các mẹo về cách cải thiện câu lệnh, hãy xem hướng dẫn về câu lệnh và thuộc tính hình ảnh.

Ảnh chụp màn hình giao diện Google AI Studio, cho thấy 4 hình ảnh được tạo về một con khủng long bạo chúa có ba lô màu xanh dương trong một khu rừng thời tiền sử.
Hình 2. Google AI Studio có thể giúp bạn tinh chỉnh câu lệnh tạo hình ảnh prompts.

Thiết lập dự án Firebase và kết nối ứng dụng

Làm theo các bước trong tài liệu về Firebase để thêm Firebase vào dự án Android.

Thêm phần phụ thuộc vào Gradle

Thêm các phần phụ thuộc sau vào tệp build.gradle:

dependencies {
  // Import the BoM for the Firebase platform
  implementation(platform("com.google.firebase:firebase-bom:34.11.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")
}

Tạo hình ảnh

Để tạo hình ảnh trong ứng dụng Android, hãy bắt đầu bằng cách tạo thực thể ImagenModel với một cấu hình không bắt buộc.

Bạn có thể sử dụng tham số generationConfig để xác định câu lệnh phủ định, số lượng hình ảnh, tỷ lệ khung hình của hình ảnh đầu ra, định dạng hình ảnh và thêm hình mờ. Bạn có thể sử dụng tham số safetySettings để định cấu hình bộ lọc an toàn và bộ lọc người.

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

Sau khi tạo thực thể ImagenModel, bạn có thể tạo hình ảnh bằng cách gọi 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();
}

Chỉnh sửa hình ảnh bằng Imagen

SDK Firebase AI Logic cung cấp các tính năng chỉnh sửa hình ảnh nâng cao thông qua mô hình Imagen, cho phép bạn:

  • Chỉnh sửa hình ảnh dựa trên mặt nạ, bao gồm các thao tác như chèn hoặc xoá đối tượng, mở rộng nội dung hình ảnh vượt ra ngoài ranh giới ban đầu và thay đổi nền.
  • Tuỳ chỉnh hình ảnh thông qua việc áp dụng các kiểu cụ thể (mẫu, kết cấu hoặc kiểu của nghệ sĩ), bằng cách tập trung vào nhiều đối tượng (chẳng hạn như sản phẩm, người hoặc động vật) hoặc bằng cách tuân thủ các chế độ kiểm soát khác nhau (chẳng hạn như bản phác thảo vẽ tay, hình ảnh cạnh Canny hoặc lưới khuôn mặt).

Khởi chạy mô hình

Để sử dụng các tính năng chỉnh sửa của Imagen, hãy chỉ định một mô hình Imagen hỗ trợ chỉnh sửa hình ảnh, chẳng hạn như imagen-3.0-capability-001:

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

Chỉnh sửa dựa trên mặt nạ

Tính năng chỉnh sửa dựa trên mặt nạ của Imagen cho phép sửa đổi hình ảnh bằng cách xác định các vùng cụ thể để mô hình thao tác. Tính năng này cho phép thực hiện một loạt thao tác, bao gồm tạo và áp dụng mặt nạ, chèn hoặc xoá đối tượng và mở rộng nội dung hình ảnh vượt ra ngoài ranh giới ban đầu.

Tạo mặt nạ

Để thực hiện chỉnh sửa dựa trên mặt nạ, chẳng hạn như chèn hoặc xoá đối tượng, bạn cần xác định vùng cần chỉnh sửa bằng mô hình, tức là mặt nạ.

Để tạo mặt nạ, bạn có thể để mô hình tự động tạo mặt nạ bằng cách sử dụng ImagenBackgroundMask() hoặc ImagenSemanticMask(), truyền mã lớp.

Bạn cũng có thể vẽ mặt nạ theo cách thủ công trên màn hình bằng cách tạo một bitmap mặt nạ và chuyển đổi bitmap đó thành ImagenRawMask. Bằng cách sử dụng detectDragGesturesCanvas, bạn có thể triển khai giao diện người dùng vẽ mặt nạ bằng Jetpack Compose trong ứng dụng như sau:

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

Sau đó, bạn có thể tạo bitmap mặt nạ bằng cách vẽ các đường dẫn trên canvas:

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

Đảm bảo mặt nạ có cùng kích thước với hình ảnh nguồn. Xem Mẫu danh mục AI Imagen để biết thêm thông tin chi tiết.

Chèn đối tượng

Bạn có thể chèn một đối tượng hoặc nội dung mới vào hình ảnh hiện có, còn gọi là inpainting. Mô hình sẽ tạo và chèn nội dung mới vào vùng được che phủ đã chỉ định.

Để thực hiện việc này, hãy sử dụng hàm editImage(). Bạn cần cung cấp hình ảnh gốc, mặt nạvà câu lệnh dạng văn bản mô tả nội dung bạn muốn chèn. Ngoài ra, hãy truyền đối tượng ImagenEditingConfig, đảm bảo thuộc tính editMode của đối tượng này được đặt thành 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
}

Xoá đối tượng

Tính năng inpainting cho phép bạn xoá các đối tượng không mong muốn khỏi hình ảnh. Để thực hiện việc này, hãy sử dụng hàm editImage. Bạn cần cung cấp hình ảnh gốc và a mặt nạ làm nổi bật đối tượng bạn muốn xoá. Bạn có thể đưa vào câu lệnh dạng văn bản để mô tả đối tượng (không bắt buộc). Câu lệnh này có thể hỗ trợ mô hình xác định chính xác. Ngoài ra, bạn phải đặt editMode trong ImagenEditingConfig thành 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
}

Mở rộng nội dung hình ảnh

Bạn có thể mở rộng hình ảnh vượt ra ngoài ranh giới ban đầu, được gọi là outpainting, bằng cách sử dụng hàm outpaintImage(). Hàm này yêu cầu hình ảnh gốc và Dimensions cần thiết của hình ảnh được mở rộng. Bạn có thể đưa vào câu lệnh mô tả để mở rộng và chỉ định the ImagenImagePlacement của hình ảnh gốc trong hình ảnh mới được tạo (không bắt buộc):

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
}

Thay thế nền

Bạn có thể thay thế nền của hình ảnh trong khi vẫn giữ nguyên đối tượng ở tiền cảnh. Để thực hiện việc này, hãy sử dụng hàm editImage. Truyền hình ảnh gốc, đối tượng ImagenBackgroundMask (chứa câu lệnh dạng văn bản cho nền mới) và ImagenEditingConfig với thuộc tính editMode được đặt thành 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
}

Tùy chỉnh

Bạn có thể sử dụng tính năng tùy chỉnh của Imagen để tạo hoặc chỉnh sửa hình ảnh dựa trên hình ảnh tham khảo chỉ định đối tượng, chế độ kiểm soát hoặc kiểu. Để thực hiện việc này, hãy cung cấp câu lệnh dạng văn bản cùng với một hoặc nhiều hình ảnh tham khảo để hướng dẫn mô hình.

Tuỳ chỉnh dựa trên đối tượng

Bạn có thể tạo hình ảnh mới về một đối tượng cụ thể từ hình ảnh tham khảo (ví dụ: sản phẩm, người hoặc động vật). Chỉ cần cung cấp câu lệnh dạng văn bản và ít nhất một hình ảnh tham khảo về đối tượng. Ví dụ: bạn có thể tải ảnh thú cưng lên và tạo hình ảnh mới về thú cưng đó trong một môi trường hoàn toàn khác.

Để thực hiện việc này, hãy xác định thực thể tham chiếu bằng ImagenSubjectReference, sau đó truyền thực thể đó đến editImage cùng với câu lệnh. Ngoài ra, hãy đưa vào ImagenEditingConfig chỉ định số lượng editSteps; giá trị editSteps càng cao thì kết quả thường có chất lượng càng tốt:

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
}

Tuỳ chỉnh dựa trên chế độ kiểm soát

Kỹ thuật này tạo hình ảnh mới dựa trên hình ảnh tham khảo chế độ kiểm soát, chẳng hạn như bản phác thảo vẽ tay ("scribble"), hình ảnh cạnh Canny, hoặc lưới khuôn mặt. Mô hình sử dụng hình ảnh chế độ kiểm soát làm hướng dẫn cấu trúc cho bố cục và bố cục của hình ảnh mới, trong khi câu lệnh dạng văn bản cung cấp thông tin chi tiết như màu sắc và kết cấu.

Xác định tham chiếu chế độ kiểm soát bằng ImagenControlReference và cung cấp tham chiếu đó cho editImage cùng với câu lệnh và ImagenEditingConfig với số lượng editSteps (giá trị càng cao thì chất lượng càng có thể cải thiện):

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
}

Tuỳ chỉnh dựa trên kiểu

Bạn có thể tạo hoặc chỉnh sửa hình ảnh để khớp với một kiểu cụ thể từ hình ảnh tham khảo, chẳng hạn như mẫu, kết cấu hoặc thiết kế của hình ảnh đó. Mô hình sử dụng hình ảnh tham khảo để hiểu tính thẩm mỹ cần thiết và áp dụng tính thẩm mỹ đó cho hình ảnh mới được mô tả trong câu lệnh dạng văn bản. Ví dụ: bạn có thể tạo hình ảnh một chú mèo theo kiểu của một bức tranh nổi tiếng bằng cách cung cấp hình ảnh bức tranh đó.

Xác định tham chiếu kiểu bằng ImagenStyleReference và cung cấp tham chiếu đó cho editImage cùng với câu lệnh và ImagenEditingConfig với số lượng editSteps (giá trị càng cao thì chất lượng càng có thể cải thiện):

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
}

Các bước tiếp theo