Skip to content

Commit 55fa1ca

Browse files
author
David Motsonashvili
committed
fixes for comments, plus adding subject references and style customization
1 parent 3b4e977 commit 55fa1ca

File tree

6 files changed

+81
-26
lines changed

6 files changed

+81
-26
lines changed

firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/FirebaseAISamples.kt

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.google.firebase.quickstart.ai
22

3+
import android.content.Context
4+
import android.content.res.Resources
35
import android.graphics.Bitmap
6+
import android.graphics.BitmapFactory
47
import com.google.firebase.ai.ImagenModel
58
import com.google.firebase.ai.type.Dimensions
69
import com.google.firebase.ai.type.FunctionDeclaration
@@ -10,6 +13,9 @@ import com.google.firebase.ai.type.ImagenEditMode
1013
import com.google.firebase.ai.type.ImagenEditingConfig
1114
import com.google.firebase.ai.type.ImagenMaskReference
1215
import com.google.firebase.ai.type.ImagenRawImage
16+
import com.google.firebase.ai.type.ImagenStyleReference
17+
import com.google.firebase.ai.type.ImagenSubjectReference
18+
import com.google.firebase.ai.type.ImagenSubjectReferenceType
1319
import com.google.firebase.ai.type.PublicPreviewAPI
1420
import com.google.firebase.ai.type.ResponseModality
1521
import com.google.firebase.ai.type.Schema
@@ -142,21 +148,24 @@ val FIREBASE_AI_SAMPLES = listOf(
142148
text(
143149
"A photo of a modern building with water in the background"
144150
)
151+
},
152+
allowEmptyPrompt = false,
153+
generateImages = { model: ImagenModel, inputText: String, _: Bitmap? ->
154+
model.generateImages(
155+
inputText
156+
)
145157
}
146158
),
147159
Sample(
148160
title = "Imagen 3 - Inpainting",
149161
description = "Replace the background of an image using Imagen 3",
150-
modelName= "imagen-3.0-capability-001",
162+
modelName = "imagen-3.0-capability-001",
151163
backend = GenerativeBackend.vertexAI(),
152164
navRoute = "imagen",
153165
categories = listOf(Category.IMAGE),
154-
initialPrompt = content {
155-
text(
156-
"A sunny beach"
157-
)
158-
},
166+
initialPrompt = content { text("A sunny beach") },
159167
includeAttach = true,
168+
allowEmptyPrompt = true,
160169
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
161170
model.editImage(
162171
listOf(ImagenRawImage(bitmap!!.toImagenInlineImage()), ImagenBackgroundMask()),
@@ -168,16 +177,13 @@ val FIREBASE_AI_SAMPLES = listOf(
168177
Sample(
169178
title = "Imagen 3 - Outpainting",
170179
description = "Expand an image by drawing in more background",
171-
modelName= "imagen-3.0-capability-001",
180+
modelName = "imagen-3.0-capability-001",
172181
backend = GenerativeBackend.vertexAI(),
173182
navRoute = "imagen",
174183
categories = listOf(Category.IMAGE),
175-
initialPrompt = content {
176-
text(
177-
""
178-
)
179-
},
184+
initialPrompt = content { text("") },
180185
includeAttach = true,
186+
allowEmptyPrompt = true,
181187
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
182188
val dimensions = Dimensions(bitmap!!.width * 2, bitmap.height * 2)
183189
model.editImage(
@@ -187,6 +193,50 @@ val FIREBASE_AI_SAMPLES = listOf(
187193
)
188194
}
189195
),
196+
Sample(
197+
title = "Imagen 3 - Subject Reference",
198+
description = "generate an image using a referenced subject (must be an animal)",
199+
modelName = "imagen-3.0-capability-001",
200+
backend = GenerativeBackend.vertexAI(),
201+
navRoute = "imagen",
202+
categories = listOf(Category.IMAGE),
203+
initialPrompt = content { text("<subject> flying through space") },
204+
includeAttach = true,
205+
allowEmptyPrompt = false,
206+
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
207+
model.editImage(
208+
listOf(
209+
ImagenSubjectReference(
210+
referenceId = 1,
211+
image = bitmap!!.toImagenInlineImage(),
212+
subjectType = ImagenSubjectReferenceType.ANIMAL,
213+
description = "An animal"
214+
)
215+
),
216+
"Create an image about An animal [1] to match the description: " +
217+
inputText.replace("<subject>", "An animal [1]"),
218+
)
219+
}
220+
),
221+
Sample(
222+
title = "Imagen 3 - Style Transfer",
223+
description = "Change the art style of an cat picture using a reference",
224+
modelName = "imagen-3.0-capability-001",
225+
backend = GenerativeBackend.vertexAI(),
226+
navRoute = "imagen",
227+
categories = listOf(Category.IMAGE),
228+
initialPrompt = content { text("A picture of a cat") },
229+
includeAttach = true,
230+
allowEmptyPrompt = true,
231+
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
232+
model.editImage(
233+
listOf(
234+
ImagenRawImage(MainActivity.catImage.toImagenInlineImage()),
235+
ImagenStyleReference(bitmap!!.toImagenInlineImage(), 1, "an art style")),
236+
"Generate an image in an art style [1] based on the following caption: $inputText",
237+
)
238+
}
239+
),
190240
Sample(
191241
title = "Gemini 2.0 Flash - image generation",
192242
description = "Generate and/or edit images using Gemini 2.0 Flash",

firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/MainActivity.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.google.firebase.quickstart.ai
22

3+
import android.graphics.Bitmap
4+
import android.graphics.BitmapFactory
35
import android.os.Bundle
46
import androidx.activity.ComponentActivity
57
import androidx.activity.compose.setContent
@@ -22,6 +24,7 @@ import androidx.navigation.NavDestination
2224
import androidx.navigation.compose.NavHost
2325
import androidx.navigation.compose.composable
2426
import androidx.navigation.compose.rememberNavController
27+
import com.google.firebase.ai.type.toImagenInlineImage
2528
import com.google.firebase.quickstart.ai.feature.live.StreamRealtimeRoute
2629
import com.google.firebase.quickstart.ai.feature.live.StreamRealtimeScreen
2730
import com.google.firebase.quickstart.ai.feature.media.imagen.ImagenRoute
@@ -36,6 +39,7 @@ class MainActivity : ComponentActivity() {
3639
override fun onCreate(savedInstanceState: Bundle?) {
3740
super.onCreate(savedInstanceState)
3841
enableEdgeToEdge()
42+
catImage = BitmapFactory.decodeResource(applicationContext.resources, R.drawable.cat)
3943
setContent {
4044
val navController = rememberNavController()
4145

@@ -86,7 +90,7 @@ class MainActivity : ComponentActivity() {
8690
composable<ChatRoute> {
8791
ChatScreen()
8892
}
89-
// Imagn Samples
93+
// Imagen Samples
9094
composable<ImagenRoute> {
9195
ImagenScreen()
9296
}
@@ -110,4 +114,7 @@ class MainActivity : ComponentActivity() {
110114
})
111115
}
112116
}
117+
companion object{
118+
lateinit var catImage: Bitmap
119+
}
113120
}

firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/media/imagen/ImagenScreen.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ fun ImagenScreen(
4747
val isLoading by imagenViewModel.isLoading.collectAsStateWithLifecycle()
4848
val generatedImages by imagenViewModel.generatedBitmaps.collectAsStateWithLifecycle()
4949
val includeAttach by imagenViewModel.includeAttach.collectAsStateWithLifecycle()
50+
val allowEmptyPrompt by imagenViewModel.allowEmptyPrompt.collectAsStateWithLifecycle()
5051
val context = LocalContext.current
5152
val contentResolver = context.contentResolver
5253
val openDocument = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { optionalUri: Uri? ->
@@ -65,7 +66,7 @@ fun ImagenScreen(
6566

6667
contentResolver.openInputStream(uri)?.use { stream ->
6768
val bytes = stream.readBytes()
68-
imagenViewModel.attachImage(bytes, fileName)
69+
imagenViewModel.attachImage(bytes)
6970
}
7071
}
7172
}
@@ -96,13 +97,13 @@ fun ImagenScreen(
9697
modifier = Modifier
9798
.padding(end = 16.dp, bottom = 16.dp)
9899
.align(Alignment.End)
99-
100-
101100
) { Text("Attach") }
102101
}
103102
TextButton(
104103
onClick = {
105-
imagenViewModel.generateImages(imagenPrompt)
104+
if (allowEmptyPrompt || imagenPrompt.isNotBlank()) {
105+
imagenViewModel.generateImages(imagenPrompt)
106+
}
106107
},
107108
modifier = Modifier
108109
.padding(end = 16.dp, bottom = 16.dp)

firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/feature/media/imagen/ImagenViewModel.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class ImagenViewModel(
4141
private val _includeAttach = MutableStateFlow(sample.includeAttach)
4242
val includeAttach: StateFlow<Boolean> = _includeAttach
4343

44+
private val _allowEmptyPrompt = MutableStateFlow(sample.allowEmptyPrompt)
45+
val allowEmptyPrompt: StateFlow<Boolean> = _allowEmptyPrompt
46+
4447
private val _generatedBitmaps = MutableStateFlow(listOf<Bitmap>())
4548
val generatedBitmaps: StateFlow<List<Bitmap>> = _generatedBitmaps
4649

@@ -71,14 +74,7 @@ class ImagenViewModel(
7174
viewModelScope.launch {
7275
_isLoading.value = true
7376
try {
74-
val generateImages = sample.generateImages
75-
val imageResponse = if (generateImages == null) {
76-
imagenModel.generateImages(
77-
inputText
78-
)
79-
} else {
80-
generateImages(imagenModel, inputText, attachedImage)
81-
}
77+
val imageResponse = sample.generateImages!!(imagenModel, inputText, attachedImage)
8278
_generatedBitmaps.value = imageResponse.images.map { it.asBitmap() }
8379
_errorMessage.value = null // clear error message
8480
} catch (e: Exception) {
@@ -91,7 +87,6 @@ class ImagenViewModel(
9187

9288
fun attachImage(
9389
fileInBytes: ByteArray,
94-
fileName: String? = "Unnamed file"
9590
) {
9691
attachedImage = BitmapFactory.decodeByteArray(fileInBytes, 0, fileInBytes.size)
9792
}

firebase-ai/app/src/main/java/com/google/firebase/quickstart/ai/ui/navigation/Sample.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.google.firebase.quickstart.ai.ui.navigation
22

3+
import android.content.Context
34
import android.graphics.Bitmap
45
import com.google.firebase.ai.ImagenModel
56
import com.google.firebase.ai.type.Content
@@ -38,5 +39,6 @@ data class Sample(
3839
val chatHistory: List<Content> = emptyList(),
3940
val tools: List<Tool>? = null,
4041
val includeAttach: Boolean = false,
42+
val allowEmptyPrompt: Boolean = false,
4143
val generateImages: (suspend (ImagenModel, String, Bitmap?) -> ImagenGenerationResponse<ImagenInlineImage>)? = null
4244
)
385 KB
Loading

0 commit comments

Comments
 (0)