Lebenszyklus der Medienoptimierung im Bitmap-Modus

Die Media Enhancement API bietet eine latenzarme, datenschutzfreundliche KI-Lösung auf dem Gerät, die die Hardwarebeschleunigung nutzt, um hochwertige Medienverbesserungen ohne APK-Bloatware zu liefern. Weitere Informationen finden Sie unter Informationen zu den Funktionen zur Medienverbesserung.

Der statische Bitmap-Modus (EnhancementMode.BITMAP) ist für die Verarbeitung statischer, decodierter Bilder konzipiert.

Im Bitmap-Modus muss das Betriebssystem unkomprimierte Pixeldaten serialisieren und sie aus dem CPU-Speicher über den Systembus in den GPU-Speicher kopieren. Der verarbeitete Frame wird dann durch eine umgekehrte Kopie zurückgegeben. Er ist für die Ausführung einzelner Frames optimiert und nicht mit Video-Streaming in Echtzeit kompatibel.

Bei diesem Workflow wird eine Sitzung erstellt, eine einzelne Bitmap damit verarbeitet und dann das Ergebnis verarbeitet. Die Codebeispiele aus den vorherigen Abschnitten decken diesen Anwendungsfall im Detail ab.

  1. Optionen konfigurieren: Erstellen Sie ein EnhancementOptions-Objekt und stellen Sie sicher, dass enhancementMode auf EnhancementMode.BITMAP festgelegt ist.
  2. Sitzung erstellen: Verwenden Sie den zuvor definierten Wrapper createSessionAsync, um eine EnhancementSession zu erstellen. Dies ist ein ressourcenintensives Objekt, daher sollten Sie es nur bei Bedarf erstellen.
  3. Bild verarbeiten: Rufen Sie die Sitzung, die Eingabe-Bitmap und die gewünschten Optionen auf.
  4. Ergebnis verarbeiten: Die Suspend-Funktion gibt bei Erfolg eine neue, verbesserte Bitmap zurück oder löst bei einem Fehler eine Ausnahme aus.
  5. Sitzung freigeben: Rufen Sie unbedingt session.release() auf, wenn Sie fertig sind, um GPU-Ressourcen freizugeben.

Die EnhancementSession ist ein ressourcenintensives Kontextobjekt, das eine persistente GPU- oder NPU-Speicherpipeline verwaltet. Es weist dedizierten Video-RAM (VRAM) und native Systemhandles zu. Um schwerwiegende Speicherlecks und potenzielle OutOfMemoryError-Abstürze zu vermeiden, halten Sie sich an die folgenden Lebenszyklusprinzipien:

  1. Lazy Instantiation: Erstellen Sie erst dann eine Sitzung, wenn der Nutzer eine Aktion zur Verbesserung startet.
  2. Strategische Wiederverwendung: Behalten Sie eine einzelne Sitzungsinstanz bei und verwenden Sie sie wieder, wenn Sie mehrere Bilder mit identischen Konfigurationen (Abmessungen und aktivierten Optionen) verarbeiten.
  3. Schnelle Beendigung: Rufen Sie session.release() sofort auf, wenn visuelle Aufgaben beendet werden, um freigegebene Hardwareressourcen freizugeben.

Engine zur Verbesserung initialisieren

Diese Methode orchestriert eine zweistufige Prüfung. Sie prüft, ob die Hardware des Geräts die Beschleunigung unterstützt, und stellt dann sicher, dass die erforderlichen Module für maschinelles Lernen vorhanden sind.

Wenn Sie diesen Schritt als Voraussetzung ausführen, werden Fehler bei der Laufzeitinitialisierung vermieden, da die Funktionen validiert werden, bevor Ihre App versucht, Medien zu verarbeiten.

class MediaSetupViewModel(application: Application) : AndroidViewModel(application) {
    private val enhancementClient = Enhancement.getClient(application)
    fun initializeEnhancementEngine() {
        viewModelScope.launch {
            try {
                // 1. Verify hardware capability
                val isSupported = enhancementClient.isDeviceSupportedAsync()
                if (!isSupported) {
                    notifyUiDeviceIncompatible()
                    return@launch
                }
                // 2. Verify and download the Google Play services ML modules
                val isInstalled = enhancementClient.isModuleInstalledAsync()
                if (!isInstalled) {
                    notifyUiDownloadingModels()
                    enhancementClient.installModule().await() 
                }
                notifyUiEngineReady()
            } catch (e: Exception) {

                // Handle potential errors during session creation or image processing.
                handleInitializationError(e)
            }
        }
    }
}

Wrapper für Sitzungs- und Bitmap-Verarbeitung erstellen

Verwenden Sie diese Kotlin-Coroutine-Wrapper, um aufgabenbasierte Client-Callbacks in standardmäßige Suspend-Funktionen umzuwandeln, die eine sauberere und sequenzielle Ausführung ermöglichen.

// Wraps the task-based createSession callback into a suspending function.
suspend fun EnhancementClient.createSessionAsync(
    options: EnhancementOptions,
    executor: Executor
): EnhancementSession = withContext(Dispatchers.Main) {
    suspendCancellableCoroutine { continuation ->

        // EnhancementSessionCallback handles session success or failure.
        val callback = object : EnhancementSessionCallback {
            override fun onSessionCreated(session: EnhancementSession) {
                continuation.resume(session)
            }
            override fun onSessionCreationFailed(status: Status) {
                continuation.resumeWithException(
                    Exception("Session creation failed: ${status.statusMessage} (${status.statusCode})")
                )
            }
            override fun onSessionDestroyed() {}
            override fun onSessionDisconnected(status: Status) {}
        }

        // Handles errors during the initial request trigger.
        this.createSession(options, callback).addOnFailureListener(executor) { e ->
            if (continuation.isActive) {
                continuation.resumeWithException(e)
            }
        }
    }
}

// Wraps this process in a suspending function for cleaner execution.
suspend fun EnhancementSession.processBitmapAsync(
    bitmap: Bitmap,
    options: EnhancementOptions
): Bitmap = suspendCancellableCoroutine { continuation ->

    // EnhancementCallback returns the processed bitmap or an error code.
    val callback = object : EnhancementCallback {
        override fun onBitmapProcessed(enhancedBitmap: Bitmap) {
            continuation.resume(enhancedBitmap)
        }
        override fun onError(statusCode: Int) {
            continuation.resumeWithException(
                Exception("Bitmap processing failed with status code: $statusCode")
            )
        }
        override fun onSurfaceProcessed(timestamp: Long) {}
    }
    this.process(bitmap, options, callback)
}

Bitmap-Pipeline in einem ViewModel ausführen

Wenn Sie die Pipeline zur Verbesserung in Ihre Anwendungsarchitektur einbinden möchten, verwenden Sie ein ViewModel, um den Lebenszyklus der Sitzung zu verwalten. So wird sichergestellt, dass die ressourcenintensiven GPU-Ressourcen freigegeben werden, wenn das ViewModel gelöscht wird.

// Define a data class to hold image information.
data class ImageInfo(val bitmap: Bitmap)
// Define a UI state class to hold loading status, errors, and enhanced image.
data class EnhancementUiState(
    val isLoading: Boolean = false,
    val enhancementError: String? = null,
    val enhancedImage: ImageInfo? = null
)

class EnhancementViewModel(application: Application) : AndroidViewModel(application) {

    // Backing field for UI state, initialized with default values.
    private val _uiState = MutableStateFlow(EnhancementUiState())
    // Publicly exposed UI state flow for observation.
    val uiState: StateFlow<EnhancementUiState> = _uiState.asStateFlow()

// Initialize client to interact with the Media Enhancement service.
    private val enhancementClient: EnhancementClient = Enhancement.getClient(application)

// Single-thread executor for processing background enhancement tasks.
    private val enhancementExecutor = Executors.newSingleThreadExecutor()

// Track session state to enable reuse across multiple processing calls.
    private var enhancementSession: EnhancementSession? = null

// Primary function to trigger the enhancement workflow for a provided bitmap.
    fun enhanceImage(bitmap: Bitmap) {
        viewModelScope.launch(Dispatchers.IO) {
            _uiState.update { it.copy(isLoading = true, enhancementError = null) }
            try {
                // 1. Establish the session lazily on demand

// Define enhancement options (for example, enable upscale, tonemapping) based
// on bitmap dimensions.
                if (enhancementSession == null) {
                    val options = EnhancementOptions(
                        bitmap.width,
                        bitmap.height,
                        EnhancementMode.BITMAP,
                        enableTonemap = true,
                        enableDeblurDenoise = true,
                        enableDenoiseOnly = false,
                        enableUpscale = false,
                    )
                    enhancementSession = enhancementClient.createSessionAsync(options, enhancementExecutor)
                }
                val session = enhancementSession ?: throw IllegalStateException("Session unavailable.")
                // 2. Dispatch image through the neural pipeline
                val enhancedBitmap = session.processBitmapAsync(bitmap, session.defaultOptions)
                // 3. Render output to UI
                _uiState.update {
                    it.copy(enhancedImage = ImageInfo(bitmap = enhancedBitmap))
                }
            } catch (e: Exception) {
                _uiState.update { it.copy(enhancementError = e.message) }
            } finally {

// Ensure loading state is reset regardless of the outcome.
                _uiState.update { it.copy(isLoading = false) }
            }
        }
    }
    override fun onCleared() {
        // 4. Critical: Release native GPU hardware resources
        enhancementSession?.release()
        enhancementSession = null
        enhancementExecutor.shutdown()
        super.onCleared()
    }
}