Membuat gambar dengan Imagen

Imagen adalah model pembuatan gambar. Fitur ini dapat digunakan untuk membuat avatar kustom untuk profil pengguna atau untuk mengintegrasikan aset visual yang dipersonalisasi ke dalam alur layar yang ada untuk meningkatkan interaksi pengguna.

Anda dapat mengakses model Imagen dari aplikasi Android menggunakan Firebase AI Logic SDK. Model Imagen tersedia menggunakan penyedia API Firebase AI Logic: Gemini Developer API (direkomendasikan untuk sebagian besar developer) dan Vertex AI.

Diagram yang menggambarkan arsitektur integrasi Firebase AI Logic
       untuk mengakses Gemini Developer API. Aplikasi Android menggunakan Firebase Android SDK untuk terhubung ke Firebase. Kemudian, Firebase berinteraksi dengan
       Gemini Developer API, yang mengakses Gemini Pro & Flash dalam
       cloud.
Gambar 1. Mengakses model Imagen menggunakan Firebase AI Logic.

Bereksperimen dengan perintah

Membuat perintah yang ideal sering kali memerlukan beberapa upaya. Anda dapat bereksperimen dengan perintah gambar di Google AI Studio, IDE untuk desain dan pembuatan prototipe perintah. Untuk mendapatkan tips tentang cara meningkatkan kualitas perintah, tinjau panduan atribut gambar dan perintah.

Screenshot antarmuka Google AI Studio,
      yang menampilkan empat gambar T-Rex dengan ransel biru di hutan prasejarah.
Gambar 2. Google AI Studio dapat membantu Anda menyempurnakan perintah pembuatan gambar.

Menyiapkan project Firebase dan menghubungkan aplikasi Anda

Ikuti langkah-langkah dalam dokumentasi Firebase untuk menambahkan Firebase ke project Android Anda.

Menambahkan dependensi Gradle

Tambahkan dependensi berikut ke file build.gradle Anda:

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

Buat gambar

Untuk membuat gambar di aplikasi Android, mulailah dengan membuat instance ImagenModel dengan konfigurasi opsional.

Anda dapat menggunakan parameter generationConfig untuk menentukan perintah negatif, jumlah gambar, rasio aspek gambar output, format gambar, dan menambahkan watermark. Anda dapat menggunakan parameter safetySettings untuk mengonfigurasi filter keselamatan dan orang.

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

Setelah ImagenModel di-instance, Anda dapat membuat gambar dengan memanggil 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();
}

Mengedit gambar dengan Imagen

Firebase AI Logic SDK menawarkan kemampuan pengeditan gambar tingkat lanjut melalui model Imagen, sehingga Anda dapat:

  • Mengedit gambar berdasarkan penyamaran, yang mencakup tindakan seperti menyisipkan atau menghapus objek, memperluas konten gambar di luar batas aslinya, dan mengubah latar belakang.
  • Sesuaikan gambar melalui penerapan gaya tertentu (pola, tekstur, atau gaya artis), dengan berfokus pada berbagai subjek (seperti produk, orang, atau hewan), atau dengan mematuhi berbagai kontrol (seperti sketsa yang digambar tangan, gambar tepi canny, atau mesh wajah).

Inisialisasi model

Untuk menggunakan fitur pengeditan Imagen, tentukan model Imagen yang mendukung pengeditan gambar, seperti imgen-3.0-capability-001. versi model:

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

Pengeditan berbasis mask

Pengeditan berbasis mask Imagen memungkinkan modifikasi pada gambar dengan menentukan area tertentu yang akan dimanipulasi oleh model. Kemampuan ini memungkinkan berbagai tindakan, termasuk membuat dan menerapkan mask, menyisipkan atau menghapus objek, dan memperluas konten gambar di luar batas aslinya.

Membuat masker

Untuk melakukan pengeditan berbasis mask seperti menyisipkan atau menghapus objek, Anda perlu menentukan area yang perlu diedit oleh model, yaitu mask.

Untuk membuat mask, Anda dapat meminta model membuatnya secara otomatis menggunakan ImagenBackgroundMask() atau ImagenSemanticMask(), dengan meneruskan ID kelas .

Anda juga dapat menggambar mask secara manual di layar, dengan membuat bitmap mask dan mengonversinya menjadi ImagenRawMask. Dengan menggunakan detectDragGestures dan Canvas, Anda dapat menerapkan antarmuka pengguna untuk menggambar mask dengan Jetpack Compose di aplikasi Anda sebagai berikut:

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

Kemudian, Anda dapat membuat bitmap mask dengan menggambar jalur di kanvas:

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
}

Pastikan masker berukuran sama dengan gambar sumber. Lihat Contoh Katalog Imagen AI untuk mengetahui detail selengkapnya.

Menyisipkan objek

Anda dapat menyisipkan objek atau konten baru ke dalam gambar yang sudah ada, yang juga disebut inpainting. Model akan membuat dan menyisipkan konten baru ke dalam area bertopeng yang ditentukan.

Untuk melakukannya, gunakan fungsi editImage(). Anda harus memberikan gambar asli, masker, dan perintah teks yang menjelaskan konten yang ingin Anda sisipkan. Selain itu, teruskan objek ImagenEditingConfig, pastikan properti editMode-nya disetel ke 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
}

Menghapus objek

Pengeditan memungkinkan Anda menghapus objek yang tidak diinginkan dari gambar. Untuk melakukannya, gunakan fungsi editImage. Anda harus memberikan gambar asli dan masker yang menandai objek yang ingin Anda hapus. Secara opsional, Anda dapat menyertakan perintah teks untuk mendeskripsikan objek, yang dapat membantu model dalam mengidentifikasi secara akurat. Selain itu, Anda harus menetapkan editMode dalam ImagenEditingConfig ke 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
}

Memperluas konten gambar

Anda dapat memperluas gambar di luar batas aslinya, yang dikenal sebagai outpainting, dengan menggunakan fungsi outpaintImage(). Fungsi ini memerlukan gambar asli dan Dimensions yang diperlukan dari gambar yang diperluas. Jika ingin, Anda dapat menyertakan perintah deskriptif untuk perluasan dan menentukan ImagenImagePlacement gambar asli dalam gambar baru yang dihasilkan:

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
}

Mengganti latar belakang

Anda dapat mengganti latar belakang gambar sambil mempertahankan subjek latar depan. Untuk melakukannya, gunakan fungsi editImage. Teruskan gambar asli, objek ImagenBackgroundMask (yang berisi perintah teks untuk latar belakang baru), dan ImagenEditingConfig dengan properti editMode yang disetel ke 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
}

Penyesuaian

Anda dapat menggunakan kemampuan penyesuaian dari Imagen untuk membuat atau mengedit gambar berdasarkan gambar referensi yang menentukan subjek, kontrol, atau gaya. Hal ini dilakukan dengan memberikan perintah teks bersama dengan satu atau beberapa gambar referensi untuk memandu model.

Menyesuaikan berdasarkan subjek

Anda dapat membuat gambar baru dari subjek tertentu dari gambar referensi (misalnya, produk, orang, atau hewan). Cukup berikan perintah teks dan setidaknya satu gambar referensi subjek. Misalnya, Anda dapat mengupload foto hewan peliharaan Anda dan membuat gambar baru hewan tersebut di lingkungan yang sama sekali berbeda.

Untuk melakukannya, tentukan referensi subjek menggunakan ImagenSubjectReference, lalu teruskan ke editImage bersama dengan perintah Anda. Selain itu, sertakan ImagenEditingConfig yang menentukan jumlah editSteps; nilai editSteps yang lebih tinggi umumnya menghasilkan kualitas yang lebih baik:

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
}

Menyesuaikan berdasarkan kontrol

Teknik ini menghasilkan gambar baru berdasarkan gambar referensi kontrol, seperti sketsa yang digambar tangan ("coretan"), gambar tepi Canny, atau mesh wajah. Model ini menggunakan gambar kontrol sebagai panduan struktural untuk tata letak dan komposisi gambar baru, sementara perintah teks memberikan detail seperti warna dan tekstur.

Tentukan referensi kontrol dengan ImagenControlReference dan berikan ke editImage bersama dengan perintah dan ImagenEditingConfig dengan jumlah editSteps (nilai yang lebih tinggi dapat meningkatkan kualitas):

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
}

Menyesuaikan berdasarkan gaya

Anda dapat membuat atau mengedit gambar agar sesuai dengan gaya tertentu dari gambar referensi, seperti pola, tekstur, atau desainnya. Model ini menggunakan gambar referensi untuk memahami estetika yang diperlukan dan menerapkannya ke gambar baru yang dijelaskan dalam perintah teks. Misalnya, Anda dapat membuat gambar kucing dengan gaya lukisan terkenal dengan memberikan gambar lukisan tersebut.

Tentukan referensi gaya dengan ImagenStyleReference dan berikan ke editImage bersama dengan perintah dan ImagenEditingConfig dengan jumlah editSteps (nilai yang lebih tinggi dapat meningkatkan kualitas):

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 
}

Langkah berikutnya