Skip to content

Commit 433ce0a

Browse files
author
David Motsonashvili
committed
Added options to inpainting and outpainting, also added downscaling to attached images to speed up generation time
1 parent 5658225 commit 433ce0a

File tree

5 files changed

+87
-11
lines changed

5 files changed

+87
-11
lines changed

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

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import com.google.firebase.ai.type.GenerativeBackend
1111
import com.google.firebase.ai.type.ImagenBackgroundMask
1212
import com.google.firebase.ai.type.ImagenEditMode
1313
import com.google.firebase.ai.type.ImagenEditingConfig
14+
import com.google.firebase.ai.type.ImagenForegroundMask
15+
import com.google.firebase.ai.type.ImagenImagePlacement
1416
import com.google.firebase.ai.type.ImagenMaskReference
1517
import com.google.firebase.ai.type.ImagenRawImage
18+
import com.google.firebase.ai.type.ImagenRawMask
1619
import com.google.firebase.ai.type.ImagenStyleReference
1720
import com.google.firebase.ai.type.ImagenSubjectReference
1821
import com.google.firebase.ai.type.ImagenSubjectReferenceType
@@ -150,7 +153,7 @@ val FIREBASE_AI_SAMPLES = listOf(
150153
)
151154
},
152155
allowEmptyPrompt = false,
153-
generateImages = { model: ImagenModel, inputText: String, _: Bitmap? ->
156+
generateImages = { model: ImagenModel, inputText: String, _: Bitmap?, _ ->
154157
model.generateImages(
155158
inputText
156159
)
@@ -166,9 +169,14 @@ val FIREBASE_AI_SAMPLES = listOf(
166169
initialPrompt = content { text("A sunny beach") },
167170
includeAttach = true,
168171
allowEmptyPrompt = true,
169-
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
172+
radioOptions = listOf("Background", "Foreground"),
173+
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap?, selectedRadioOption:String? ->
174+
val mask = when(selectedRadioOption) {
175+
"Foreground" -> ImagenForegroundMask()
176+
else -> ImagenBackgroundMask()
177+
}
170178
model.editImage(
171-
listOf(ImagenRawImage(bitmap!!.toImagenInlineImage()), ImagenBackgroundMask()),
179+
listOfNotNull(ImagenRawImage(bitmap!!.toImagenInlineImage()), mask),
172180
inputText,
173181
ImagenEditingConfig(ImagenEditMode.INPAINT_INSERTION)
174182
)
@@ -184,10 +192,23 @@ val FIREBASE_AI_SAMPLES = listOf(
184192
initialPrompt = content { text("") },
185193
includeAttach = true,
186194
allowEmptyPrompt = true,
187-
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
195+
radioOptions = listOf("Center", "Top", "Bottom", "Left", "Right"),
196+
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap?, selectedRadioOption: String? ->
197+
val position = when(selectedRadioOption) {
198+
"Top" -> ImagenImagePlacement.TOP_CENTER
199+
"Bottom" -> ImagenImagePlacement.BOTTOM_CENTER
200+
"Left" -> ImagenImagePlacement.LEFT_CENTER
201+
"Right" -> ImagenImagePlacement.RIGHT_CENTER
202+
else -> ImagenImagePlacement.CENTER
203+
}
188204
val dimensions = Dimensions(bitmap!!.width * 2, bitmap.height * 2)
205+
val (sourceImage, mask) = ImagenMaskReference.generateMaskAndPadForOutpainting(
206+
bitmap.toImagenInlineImage(),
207+
dimensions,
208+
position
209+
)
189210
model.editImage(
190-
ImagenMaskReference.generateMaskAndPadForOutpainting(bitmap.toImagenInlineImage(), dimensions),
211+
listOf(sourceImage, ImagenRawMask(mask.image!!, 0.05)),
191212
inputText,
192213
ImagenEditingConfig(ImagenEditMode.OUTPAINT)
193214
)
@@ -203,7 +224,7 @@ val FIREBASE_AI_SAMPLES = listOf(
203224
initialPrompt = content { text("<subject> flying through space") },
204225
includeAttach = true,
205226
allowEmptyPrompt = false,
206-
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
227+
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap?, _ ->
207228
model.editImage(
208229
listOf(
209230
ImagenSubjectReference(
@@ -228,7 +249,7 @@ val FIREBASE_AI_SAMPLES = listOf(
228249
initialPrompt = content { text("A picture of a cat") },
229250
includeAttach = true,
230251
allowEmptyPrompt = true,
231-
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap? ->
252+
generateImages = { model: ImagenModel, inputText: String, bitmap: Bitmap?, _ ->
232253
model.editImage(
233254
listOf(
234255
ImagenRawImage(MainActivity.catImage.toImagenInlineImage()),

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,21 @@ import androidx.activity.result.contract.ActivityResultContracts
88
import androidx.compose.foundation.Image
99
import androidx.compose.foundation.layout.Box
1010
import androidx.compose.foundation.layout.Column
11+
import androidx.compose.foundation.layout.Row
1112
import androidx.compose.foundation.layout.fillMaxWidth
1213
import androidx.compose.foundation.layout.padding
1314
import androidx.compose.foundation.lazy.grid.GridCells
1415
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
1516
import androidx.compose.foundation.lazy.grid.items
17+
import androidx.compose.foundation.selection.selectable
18+
import androidx.compose.foundation.selection.selectableGroup
1619
import androidx.compose.material3.Card
1720
import androidx.compose.material3.CardDefaults
1821
import androidx.compose.material3.CircularProgressIndicator
1922
import androidx.compose.material3.ElevatedCard
2023
import androidx.compose.material3.MaterialTheme
2124
import androidx.compose.material3.OutlinedTextField
25+
import androidx.compose.material3.RadioButton
2226
import androidx.compose.material3.Text
2327
import androidx.compose.material3.TextButton
2428
import androidx.compose.runtime.Composable
@@ -31,6 +35,7 @@ import androidx.compose.ui.Alignment
3135
import androidx.compose.ui.Modifier
3236
import androidx.compose.ui.graphics.asImageBitmap
3337
import androidx.compose.ui.platform.LocalContext
38+
import androidx.compose.ui.semantics.Role
3439
import androidx.compose.ui.unit.dp
3540
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3641
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -54,6 +59,8 @@ fun ImagenScreen(
5459
val includeAttach by imagenViewModel.includeAttach.collectAsStateWithLifecycle()
5560
val allowEmptyPrompt by imagenViewModel.allowEmptyPrompt.collectAsStateWithLifecycle()
5661
val attachedImage by imagenViewModel.attachedImage.collectAsStateWithLifecycle()
62+
val radioOptions by imagenViewModel.radioOptions.collectAsStateWithLifecycle()
63+
val selectedOption by imagenViewModel.selectedRadioOption.collectAsStateWithLifecycle()
5764
val context = LocalContext.current
5865
val contentResolver = context.contentResolver
5966
val scope = rememberCoroutineScope()
@@ -98,6 +105,33 @@ fun ImagenScreen(
98105
.padding(16.dp)
99106
.fillMaxWidth()
100107
)
108+
if (radioOptions.isNotEmpty()) {
109+
Column(modifier = Modifier.selectableGroup()) {
110+
radioOptions.forEach {option ->
111+
Row(
112+
Modifier
113+
.fillMaxWidth()
114+
.selectable(
115+
selected = (option == selectedOption),
116+
onClick = { imagenViewModel.selectRadio(option) },
117+
role = Role.RadioButton
118+
)
119+
.padding(horizontal = 16.dp),
120+
verticalAlignment = Alignment.CenterVertically
121+
) {
122+
RadioButton(
123+
selected = (option == selectedOption),
124+
onClick = null // null recommended for accessibility with screen readers
125+
)
126+
Text(
127+
text = option,
128+
style = MaterialTheme.typography.bodyLarge,
129+
modifier = Modifier.padding(start = 16.dp)
130+
)
131+
}
132+
}
133+
}
134+
}
101135
if (includeAttach) {
102136
if (attachedImage != null) {
103137
AttachmentsList(listOf(Attachment("", attachedImage)))

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ class ImagenViewModel(
4242
private val _includeAttach = MutableStateFlow(sample.includeAttach)
4343
val includeAttach: StateFlow<Boolean> = _includeAttach
4444

45+
private val _radioOptions = MutableStateFlow(sample.radioOptions)
46+
val radioOptions: StateFlow<List<String>> = _radioOptions
47+
48+
private val _selectedRadioOption = MutableStateFlow<String?>(null)
49+
val selectedRadioOption: StateFlow<String?> = _selectedRadioOption
50+
4551
private val _allowEmptyPrompt = MutableStateFlow(sample.allowEmptyPrompt)
4652
val allowEmptyPrompt: StateFlow<Boolean> = _allowEmptyPrompt
4753

@@ -76,7 +82,8 @@ class ImagenViewModel(
7682
viewModelScope.launch {
7783
_isLoading.value = true
7884
try {
79-
val imageResponse = sample.generateImages!!(imagenModel, inputText, attachedImage.first())
85+
val imageResponse =
86+
sample.generateImages!!(imagenModel, inputText, attachedImage.first(), selectedRadioOption.first())
8087
_generatedBitmaps.value = imageResponse.images.map { it.asBitmap() }
8188
_errorMessage.value = null // clear error message
8289
} catch (e: Exception) {
@@ -90,6 +97,19 @@ class ImagenViewModel(
9097
suspend fun attachImage(
9198
fileInBytes: ByteArray,
9299
) {
93-
_attachedImage.emit(BitmapFactory.decodeByteArray(fileInBytes, 0, fileInBytes.size))
100+
val originalBitmap = BitmapFactory.decodeByteArray(fileInBytes, 0, fileInBytes.size)
101+
val resizedBitmap = Bitmap.createScaledBitmap(
102+
originalBitmap,
103+
512,
104+
(originalBitmap.height * (512.0 / originalBitmap.width)).toInt(),
105+
true
106+
)
107+
_attachedImage.emit(resizedBitmap)
108+
}
109+
110+
fun selectRadio(selection: String) {
111+
viewModelScope.launch {
112+
_selectedRadioOption.emit(selection)
113+
}
94114
}
95115
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ data class Sample(
4040
val tools: List<Tool>? = null,
4141
val includeAttach: Boolean = false,
4242
val allowEmptyPrompt: Boolean = false,
43-
val generateImages: (suspend (ImagenModel, String, Bitmap?) -> ImagenGenerationResponse<ImagenInlineImage>)? = null
43+
val radioOptions: List<String> = emptyList(),
44+
val generateImages: (suspend (ImagenModel, String, Bitmap?, String?) -> ImagenGenerationResponse<ImagenInlineImage>)? = null
4445
)

firebase-ai/gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ composeBom = "2024.09.00"
55
composeNavigation = "2.9.0"
66
coreKtx = "1.16.0"
77
espressoCore = "3.6.1"
8-
firebaseBom = "33.15.0"
8+
firebaseBom = "34.1.0"
99
junit = "4.13.2"
1010
junitVersion = "1.2.1"
1111
kotlin = "2.0.21"

0 commit comments

Comments
 (0)