diff --git a/amethyst/build.gradle b/amethyst/build.gradle index fb4ddb6d8..ae6d0f09e 100644 --- a/amethyst/build.gradle +++ b/amethyst/build.gradle @@ -349,7 +349,6 @@ dependencies { // On-device alt-text suggestions: genai image description (preferred, descriptive sentences) // with image-labeling as a keyword-join fallback for devices without AICore. playImplementation libs.google.mlkit.genai.image.description - playImplementation libs.google.mlkit.image.labeling // PushNotifications playImplementation platform(libs.firebase.bom) diff --git a/amethyst/src/play/java/com/vitorpamplona/amethyst/service/ai/MLKitImageLabelService.kt b/amethyst/src/play/java/com/vitorpamplona/amethyst/service/ai/MLKitImageLabelService.kt index 25d044b34..57d734845 100644 --- a/amethyst/src/play/java/com/vitorpamplona/amethyst/service/ai/MLKitImageLabelService.kt +++ b/amethyst/src/play/java/com/vitorpamplona/amethyst/service/ai/MLKitImageLabelService.kt @@ -29,14 +29,8 @@ import com.google.mlkit.genai.imagedescription.ImageDescriber import com.google.mlkit.genai.imagedescription.ImageDescriberOptions import com.google.mlkit.genai.imagedescription.ImageDescription import com.google.mlkit.genai.imagedescription.ImageDescriptionRequest -import com.google.mlkit.vision.common.InputImage -import com.google.mlkit.vision.label.ImageLabeler -import com.google.mlkit.vision.label.ImageLabeling -import com.google.mlkit.vision.label.defaults.ImageLabelerOptions import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext -import kotlin.coroutines.resume /** * Unified alt-text suggestion service. @@ -47,15 +41,12 @@ import kotlin.coroutines.resume class MLKitImageLabelService( private val context: Context, ) { - private var labeler: ImageLabeler? = null private var describer: ImageDescriber? = null // FeatureStatus is an Int enum. Cached per-instance — describer availability does not flip // mid-session in practice, and one composer mount only needs to ask AICore once. @Volatile private var cachedGenAiStatus: Int? = null - private fun ensureLabeler(): ImageLabeler = labeler ?: ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS).also { labeler = it } - private fun ensureDescriber(): ImageDescriber? = describer ?: try { @@ -66,26 +57,7 @@ class MLKitImageLabelService( null } - suspend fun labelImage(uri: Uri): List> = - withContext(Dispatchers.IO) { - try { - val image = InputImage.fromFilePath(context, uri) - val client = ensureLabeler() - suspendCancellableCoroutine { cont -> - client - .process(image) - .addOnSuccessListener { labels -> - cont.resume(labels.map { it.text to it.confidence }) - }.addOnFailureListener { - cont.resume(emptyList()) - } - } - } catch (_: Exception) { - emptyList() - } - } - - suspend fun suggestAltText(uri: Uri): String? = describeWithGenAi(uri) ?: labelKeywords(uri) + suspend fun suggestAltText(uri: Uri): String? = describeWithGenAi(uri) private suspend fun describeWithGenAi(uri: Uri): String? = withContext(Dispatchers.IO) { @@ -100,20 +72,13 @@ class MLKitImageLabelService( .runInference(request) .get() .description - ?.trim() - ?.takeIf { it.isNotEmpty() } + .trim() + .takeIf { it.isNotEmpty() } } catch (_: Exception) { null } } - private suspend fun labelKeywords(uri: Uri): String? { - val labels = labelImage(uri) - val confident = labels.filter { it.second >= MIN_CONFIDENCE }.map { it.first } - if (confident.isEmpty()) return null - return confident.take(MAX_LABELS).joinToString(", ") - } - // Two-pass decode keeps a 12 MP camera shot from blowing past 40 MB of ARGB_8888 — the // on-device describer downscales internally anyway, so a ~1024 px input is plenty. private fun loadDownscaledBitmap(uri: Uri): Bitmap? = @@ -136,14 +101,12 @@ class MLKitImageLabelService( ): Int { if (width <= 0 || height <= 0) return 1 var sample = 1 - var maxDim = maxOf(width, height) + val maxDim = maxOf(width, height) while (maxDim / sample > target) sample *= 2 return sample } fun close() { - labeler?.close() - labeler = null describer?.close() describer = null cachedGenAiStatus = null diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1dc29b301..c51f30efd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,7 +41,6 @@ genaiProofreading = "1.0.0-beta1" genaiPrompt = "1.0.0-beta2" genaiRewriting = "1.0.0-beta1" genaiImageDescription = "1.0.0-beta1" -imageLabeling = "16.0.0" languageId = "17.0.6" lifecycleRuntimeKtx = "2.10.0" lightcompressor-enhanced = "2.2.1" @@ -151,7 +150,6 @@ google-mlkit-genai-proofreading = { group = "com.google.mlkit", name = "genai-pr google-mlkit-genai-prompt = { group = "com.google.mlkit", name = "genai-prompt", version.ref = "genaiPrompt" } google-mlkit-genai-rewriting = { group = "com.google.mlkit", name = "genai-rewriting", version.ref = "genaiRewriting" } google-mlkit-genai-image-description = { group = "com.google.mlkit", name = "genai-image-description", version.ref = "genaiImageDescription" } -google-mlkit-image-labeling = { group = "com.google.android.gms", name = "play-services-mlkit-image-labeling", version.ref = "imageLabeling" } google-mlkit-language-id = { group = "com.google.mlkit", name = "language-id", version.ref = "languageId" } google-mlkit-translate = { group = "com.google.mlkit", name = "translate", version.ref = "translate" } jackson-module-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin", version.ref = "jacksonModuleKotlin" }