Skip to content

Commit a86a475

Browse files
authored
Batch generation implementation (#122)
* Batch implementation | Patch 1 * Batch implementation | Patch 2 * Update README.md * Update README.md
1 parent 96cdc75 commit a86a475

File tree

25 files changed

+284
-80
lines changed

25 files changed

+284
-80
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Stable Diffusion AI is an easy-to-use app that lets you quickly generate images
1616

1717
- Can use server environment powered by [AI Horde](https://stablehorde.net/) (a crowdsourced distributed cluster of Stable Diffusion workers)
1818
- Can use server environment powered by [Stable-Diffusion-WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) (AUTOMATIC1111)
19+
- Can use local environment powered by LocalDiffusion (Beta)
1920
- Supports original Txt2Img, Img2Img modes
2021
- **Positive** and **negative** prompt support
2122
- Support dynamic **size** in range from 64 to 2048 px (for width and height)
@@ -26,7 +27,8 @@ Stable Diffusion AI is an easy-to-use app that lets you quickly generate images
2627
- **Restore faces** option
2728
- ( Img2Img ONLY ) : Image selection from device gallery _(requires user permission)_
2829
- ( Img2Img ONLY ) : Capture input image from camera _(requires user permission)_
29-
- ( Img2Img ONLY ) : Fetching random image for the input
30+
- ( Img2Img ONLY ) : Fetching random image for the input
31+
- Batch generation with maximum of 20 images (for A1111 and Horde)
3032
- In-app Gallery, stored locally, contains all AI generated images
3133
- Displays generated images grid
3234
- Image detail view: Zoom, Pinch, Generation Info.
@@ -37,6 +39,7 @@ Stable Diffusion AI is an easy-to-use app that lets you quickly generate images
3739
- Active SD Model selection
3840
- Server availability monitoring (http-ping method)
3941
- Enable/Disable auto-saving of generated images
42+
- Enable/Disable saving generated images to `Download/SDAI` android MediaStore folder
4043
- Clear gallery / app cache
4144

4245
## Setup instruction
@@ -60,6 +63,12 @@ If for some reason you have no ability to run your server instance, you can togg
6063

6164
AI Horde requires to use API KEY, this mobile app alows to use either default API KEY (which is "0000000000"), or type your own. You can sign up and get your own AI Horde API KEY [here](https://stablehorde.net/register).
6265

66+
### Option 3: Local Diffusion (Beta)
67+
68+
Only **txt2img** mode is supported.
69+
70+
Allows to use phone resources to generate images.
71+
6372
## Supported languages
6473

6574
App uses the language provided by OS default settings.

app/src/main/java/com/shifthackz/aisdv1/app/AiStableDiffusionClientApp.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import org.koin.android.ext.koin.androidContext
2020
import org.koin.core.context.startKoin
2121
import timber.log.Timber
2222

23-
2423
class AiStableDiffusionClientApp : Application() {
2524

2625
override fun onCreate() {
@@ -50,8 +49,8 @@ class AiStableDiffusionClientApp : Application() {
5049

5150
private fun initializeLogging() {
5251
Timber.plant(FileLoggingTree())
53-
// if (BuildConfig.DEBUG) {
52+
if (BuildConfig.DEBUG) {
5453
Timber.plant(Timber.DebugTree())
55-
// }
54+
}
5655
}
5756
}

domain/src/main/java/com/shifthackz/aisdv1/domain/entity/ImageToImagePayload.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ data class ImageToImagePayload(
1515
val subSeedStrength: Float,
1616
val sampler: String,
1717
val nsfw: Boolean,
18+
val batchCount: Int,
1819
)

domain/src/main/java/com/shifthackz/aisdv1/domain/entity/TextToImagePayload.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ data class TextToImagePayload(
1313
val subSeedStrength: Float,
1414
val sampler: String,
1515
val nsfw: Boolean,
16+
val batchCount: Int,
1617
)

domain/src/main/java/com/shifthackz/aisdv1/domain/usecase/generation/ImageToImageUseCase.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ import com.shifthackz.aisdv1.domain.entity.ImageToImagePayload
55
import io.reactivex.rxjava3.core.Single
66

77
interface ImageToImageUseCase {
8-
operator fun invoke(payload: ImageToImagePayload): Single<AiGenerationResult>
8+
operator fun invoke(payload: ImageToImagePayload): Single<List<AiGenerationResult>>
99
}

domain/src/main/java/com/shifthackz/aisdv1/domain/usecase/generation/ImageToImageUseCaseImpl.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.shifthackz.aisdv1.domain.entity.ServerSource
66
import com.shifthackz.aisdv1.domain.preference.PreferenceManager
77
import com.shifthackz.aisdv1.domain.repository.HordeGenerationRepository
88
import com.shifthackz.aisdv1.domain.repository.StableDiffusionGenerationRepository
9+
import io.reactivex.rxjava3.core.Observable
910
import io.reactivex.rxjava3.core.Single
1011

1112
internal class ImageToImageUseCaseImpl(
@@ -14,9 +15,12 @@ internal class ImageToImageUseCaseImpl(
1415
private val preferenceManager: PreferenceManager,
1516
) : ImageToImageUseCase {
1617

17-
override fun invoke(payload: ImageToImagePayload) = execute(payload)
18+
override fun invoke(payload: ImageToImagePayload) = Observable
19+
.range(1, payload.batchCount)
20+
.flatMapSingle { generate(payload) }
21+
.toList()
1822

19-
private fun execute(payload: ImageToImagePayload): Single<AiGenerationResult> {
23+
private fun generate(payload: ImageToImagePayload): Single<AiGenerationResult> {
2024
if (preferenceManager.source == ServerSource.HORDE) {
2125
return hordeGenerationRepository.generateFromImage(payload)
2226
}

domain/src/main/java/com/shifthackz/aisdv1/domain/usecase/generation/TextToImageUseCase.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ import com.shifthackz.aisdv1.domain.entity.TextToImagePayload
55
import io.reactivex.rxjava3.core.Single
66

77
interface TextToImageUseCase {
8-
operator fun invoke(payload: TextToImagePayload): Single<AiGenerationResult>
8+
operator fun invoke(payload: TextToImagePayload): Single<List<AiGenerationResult>>
99
}
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.shifthackz.aisdv1.domain.usecase.generation
22

3-
import com.shifthackz.aisdv1.domain.entity.AiGenerationResult
43
import com.shifthackz.aisdv1.domain.entity.ServerSource
54
import com.shifthackz.aisdv1.domain.entity.TextToImagePayload
65
import com.shifthackz.aisdv1.domain.preference.PreferenceManager
76
import com.shifthackz.aisdv1.domain.repository.HordeGenerationRepository
87
import com.shifthackz.aisdv1.domain.repository.LocalDiffusionGenerationRepository
98
import com.shifthackz.aisdv1.domain.repository.StableDiffusionGenerationRepository
10-
import io.reactivex.rxjava3.core.Single
9+
import io.reactivex.rxjava3.core.Observable
1110

1211
internal class TextToImageUseCaseImpl(
1312
private val stableDiffusionGenerationRepository: StableDiffusionGenerationRepository,
@@ -16,13 +15,14 @@ internal class TextToImageUseCaseImpl(
1615
private val preferenceManager: PreferenceManager,
1716
) : TextToImageUseCase {
1817

19-
override operator fun invoke(payload: TextToImagePayload) = execute(payload)
18+
override operator fun invoke(payload: TextToImagePayload) = Observable
19+
.range(1, payload.batchCount)
20+
.flatMapSingle { generate(payload) }
21+
.toList()
2022

21-
private fun execute(payload: TextToImagePayload): Single<AiGenerationResult> {
22-
return when (preferenceManager.source) {
23-
ServerSource.HORDE -> hordeGenerationRepository.generateFromText(payload)
24-
ServerSource.LOCAL -> localDiffusionGenerationRepository.generateFromText(payload)
25-
else -> stableDiffusionGenerationRepository.generateFromText(payload)
26-
}
23+
private fun generate(payload: TextToImagePayload) = when (preferenceManager.source) {
24+
ServerSource.HORDE -> hordeGenerationRepository.generateFromText(payload)
25+
ServerSource.LOCAL -> localDiffusionGenerationRepository.generateFromText(payload)
26+
else -> stableDiffusionGenerationRepository.generateFromText(payload)
2727
}
2828
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.shifthackz.aisdv1.presentation.core
2+
3+
import com.shifthackz.aisdv1.core.ui.MviEffect
4+
5+
sealed interface GenerationMviEffect : MviEffect {
6+
data class LaunchGalleryDetail(val itemId: Long) : GenerationMviEffect
7+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.shifthackz.aisdv1.presentation.core
2+
3+
import androidx.compose.runtime.Composable
4+
import com.shifthackz.aisdv1.core.ui.MviScreen
5+
import com.shifthackz.aisdv1.core.ui.MviState
6+
import com.shifthackz.aisdv1.core.viewmodel.MviViewModel
7+
8+
abstract class GenerationMviScreen<S: MviState, E: GenerationMviEffect>(
9+
viewModel: MviViewModel<S, E>,
10+
private val launchGalleryDetail: (Long) -> Unit,
11+
) : MviScreen<S, E>(viewModel) {
12+
13+
override fun processEffect(effect: E) = when (effect) {
14+
is GenerationMviEffect.LaunchGalleryDetail -> launchGalleryDetail(effect.itemId)
15+
else -> super.processEffect(effect)
16+
}
17+
18+
@Composable
19+
override fun ApplySystemUiColors() = Unit
20+
}

0 commit comments

Comments
 (0)