Segmentation d'images accélérée en temps réel sur Android avec LiteRT

1. Avant de commencer

Taper du code est un excellent moyen de développer votre mémoire musculaire et d'approfondir votre compréhension du contenu. Bien que le copier-coller puisse vous faire gagner du temps, investir dans cette pratique peut vous permettre de gagner en efficacité et d'améliorer vos compétences en programmation à long terme.

Dans cet atelier de programmation, vous allez apprendre à créer une application Android qui effectue une segmentation d'image en temps réel sur un flux vidéo en direct à l'aide du nouveau runtime de Google pour TensorFlow Lite, LiteRT. Vous allez prendre une application Android de démarrage et y ajouter des fonctionnalités de segmentation d'images. Nous aborderons également les étapes de prétraitement, d'inférence et de post-traitement. Vous découvrirez comment :

  • Créer une application Android qui segmente les images en temps réel.
  • Intégrer un modèle de segmentation d'images LiteRT pré-entraîné
  • Prétraitez l'image d'entrée pour le modèle.
  • Utilisez l'environnement d'exécution LiteRT pour l'accélération du CPU et du GPU.
  • Comprendre comment traiter la sortie du modèle pour afficher le masque de segmentation.
  • Découvrez comment ajuster la caméra avant.

Au final, vous obtiendrez un résultat semblable à l'image ci-dessous :

Application terminée

Prérequis

Cet atelier de programmation s'adresse aux développeurs mobiles expérimentés qui souhaitent acquérir de l'expérience dans le domaine du machine learning. Vous devez maîtriser les éléments suivants :

  • Développement Android avec Kotlin et Android Studio
  • Concepts de base du traitement des images

Points abordés

  • Comment intégrer et utiliser le runtime LiteRT dans une application Android.
  • Comment effectuer une segmentation d'image à l'aide d'un modèle LiteRT pré-entraîné.
  • Comment prétraiter l'image d'entrée pour le modèle.
  • Comment exécuter l'inférence pour le modèle.
  • Comment traiter la sortie d'un modèle de segmentation pour visualiser les résultats.
  • Utiliser CameraX pour le traitement du flux de caméra en temps réel.

Prérequis

  • Une version récente d'Android Studio (testée sur la version 2025.1.1).
  • Un appareil Android physique. Il est préférable de le tester sur des appareils Galaxy et Pixel.
  • Exemple de code (depuis GitHub).
  • Connaissances de base du développement Android en Kotlin.

2. Segmentation d'image

La segmentation d'images est une tâche de vision par ordinateur qui consiste à partitionner une image en plusieurs segments ou régions. Contrairement à la détection d'objets, qui dessine un cadre de délimitation autour d'un objet, la segmentation d'image attribue une classe ou une étiquette spécifique à chaque pixel de l'image. Cela permet de comprendre de manière beaucoup plus détaillée et précise le contenu de l'image, en vous indiquant la forme et les limites exactes de chaque objet.

Par exemple, au lieu de simplement savoir qu'une personne se trouve dans une boîte, vous pouvez savoir exactement quels pixels appartiennent à cette personne. Ce tutoriel explique comment effectuer une segmentation d'image en temps réel sur un appareil Android à l'aide d'un modèle de machine learning pré-entraîné.

Exemple de segmentation

LiteRT : repousser les limites du ML sur l'appareil

LiteRT est une technologie clé qui permet une segmentation haute fidélité en temps réel sur les appareils mobiles. LiteRT est l'environnement d'exécution hautes performances de nouvelle génération de Google pour TensorFlow Lite. Il est conçu pour exploiter tout le potentiel du matériel sous-jacent.

Pour ce faire, il utilise de manière intelligente et optimisée des accélérateurs matériels tels que le GPU (Graphics Processing Unit) et le NPU (Neural Processing Unit). En déchargeant la charge de travail de calcul intensive du modèle de segmentation du CPU à usage général vers ces processeurs spécialisés, LiteRT réduit considérablement le temps d'inférence. Cette accélération permet d'exécuter des modèles complexes de manière fluide sur un flux de caméras en direct, ce qui repousse les limites de ce que nous pouvons accomplir avec le machine learning directement sur votre téléphone. Sans ce niveau de performances, la segmentation en temps réel serait trop lente et saccadée pour offrir une bonne expérience utilisateur.

3. Configuration

Cloner le dépôt

Commencez par cloner le dépôt pour LiteRT :

git clone https://github.com/google-ai-edge/LiteRT.git

LiteRT/litert/samples/image_segmentation est le répertoire contenant toutes les ressources dont vous aurez besoin. Pour cet atelier de programmation, vous n'aurez besoin que du projet kotlin_cpu_gpu/android_starter. Si vous êtes bloqué, vous pouvez consulter le projet finalisé : kotlin_cpu_gpu/android

Remarque sur les chemins d'accès aux fichiers

Ce tutoriel spécifie les chemins d'accès aux fichiers au format Linux/macOS. Si vous utilisez Windows, vous devrez ajuster les chemins en conséquence.

Il est également important de noter la distinction entre la vue du projet Android Studio et une vue standard du système de fichiers. La vue du projet Android Studio est une représentation structurée des fichiers de votre projet, organisée pour le développement Android. Les chemins d'accès aux fichiers de ce tutoriel font référence aux chemins d'accès au système de fichiers, et non à ceux de la vue du projet Android Studio.

Importer l'application de départ

Commençons par importer l'application de démarrage dans Android Studio.

  1. Ouvrez Android Studio et sélectionnez Open (Ouvrir).

Ouvrir Android Studio

  1. Accédez au répertoire kotlin_cpu_gpu/android_starter et ouvrez-le.

Android Starter

Pour vous assurer que toutes les dépendances sont disponibles pour votre application, vous devez synchroniser votre projet avec les fichiers Gradle une fois le processus d'importation terminé.

  1. Sélectionnez Sync Project with Gradle Files (Synchroniser le projet avec les fichiers Gradle) dans la barre d'outils Android Studio.

Synchronisation des menus

  1. Veuillez ne pas ignorer cette étape. Si elle ne fonctionne pas, le reste du tutoriel n'aura pas de sens.

Exécuter l'application de départ

Maintenant que vous avez importé le projet dans Android Studio, vous êtes prêt à exécuter l'application pour la première fois.

Connectez votre appareil Android à votre ordinateur via USB, puis cliquez sur Run (Exécuter) dans la barre d'outils Android Studio.

Bouton d'exécution

L'application doit se lancer sur votre appareil. Un flux vidéo en direct s'affiche, mais aucune segmentation n'est encore effectuée. Toutes les modifications de fichiers que vous effectuerez dans ce tutoriel se trouveront dans le répertoire LiteRT/litert/samples/image_segmentation/kotlin_cpu_gpu/android_starter/app/src/main/java/com/google/aiedge/examples/image_segmentation (vous savez maintenant pourquoi Android Studio le restructure 😃).

Répertoire du projet

Vous verrez également des commentaires TODO dans les fichiers ImageSegmentationHelper.kt, MainViewModel.kt et view/SegmentationOverlay.kt. Dans les étapes suivantes, vous allez implémenter la fonctionnalité de segmentation d'image en complétant ces TODO.

4. Comprendre l'application de démarrage

L'application de démarrage possède déjà une UI de base et une logique de gestion de la caméra. Voici un aperçu rapide des fichiers clés :

  • app/src/main/java/com/google/aiedge/examples/image_segmentation/MainActivity.kt : il s'agit du point d'entrée principal de l'application. Il configure l'UI à l'aide de Jetpack Compose et gère les autorisations de l'appareil photo.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/MainViewModel.kt : ce ViewModel gère l'état de l'UI et orchestre le processus de segmentation d'image.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/ImageSegmentationHelper.kt : c'est ici que nous ajouterons la logique de base pour la segmentation d'image. Il gérera le chargement du modèle, le traitement des images de la caméra et l'exécution de l'inférence.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/view/CameraScreen.kt : cette fonction composable affiche l'aperçu de la caméra et la superposition de la segmentation.
  • app/src/main/assets/selfie_multiclass.tflite : il s'agit du modèle de segmentation d'image TensorFlow Lite pré-entraîné que nous allons utiliser.

5. Comprendre LiteRT et ajouter des dépendances

Ajoutons maintenant la fonctionnalité de segmentation d'image à l'application de démarrage.

1. Ajouter la dépendance LiteRT

Vous devez d'abord ajouter la bibliothèque LiteRT à votre projet. Il s'agit de la première étape cruciale pour activer le machine learning sur l'appareil avec le runtime optimisé de Google.

Ouvrez le fichier app/build.gradle.kts et ajoutez la ligne suivante au bloc dependencies :

// LiteRT for on-device ML
implementation(libs.litert)

Après avoir ajouté la dépendance, synchronisez votre projet avec les fichiers Gradle en cliquant sur le bouton Sync Now (Synchroniser maintenant) qui s'affiche en haut à droite d'Android Studio.

Synchroniser maintenant

2. Comprendre les principales API LiteRT

OuvrirImageSegmentationHelper.kt

Avant d'écrire le code d'implémentation, il est important de comprendre les composants principaux de l'API LiteRT que vous allez utiliser. Assurez-vous d'importer à partir du package com.google.ai.edge.litert, puis ajoutez les importations suivantes en haut de ImageSegmentationHelper.kt :

import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
  • CompiledModel : classe centrale pour interagir avec votre modèle TFLite. Il s'agit d'un modèle précompilé et optimisé pour un accélérateur matériel spécifique (comme le processeur ou le GPU). Cette précompilation est une fonctionnalité clé de LiteRT qui permet une inférence plus rapide et plus efficace.
  • CompiledModel.Options : vous utilisez cette classe de compilateur pour configurer CompiledModel. Le paramètre le plus important consiste à spécifier l'accélérateur matériel que vous souhaitez utiliser pour exécuter votre modèle.
  • Accelerator : cette énumération vous permet de choisir le matériel pour l'inférence. Le projet de démarrage est déjà configuré pour gérer ces options :
    • Accelerator.CPU : pour exécuter le modèle sur le processeur de l'appareil. Il s'agit de l'option la plus universellement compatible.
    • Accelerator.GPU : pour exécuter le modèle sur le GPU de l'appareil. C'est souvent beaucoup plus rapide que le processeur pour les modèles basés sur des images.
  • Tampons d'entrée et de sortie (TensorBuffer) : LiteRT utilise TensorBuffer pour les entrées et sorties du modèle. Cela vous permet de contrôler précisément la mémoire et d'éviter les copies de données inutiles. Vous obtiendrez ces tampons directement à partir de votre instance CompiledModel à l'aide de model.createInputBuffers() et model.createOutputBuffers(), puis vous y écrirez vos données d'entrée et y lirez les résultats.
  • model.run() : fonction qui exécute l'inférence. Vous lui transmettez les tampons d'entrée et de sortie, et LiteRT gère la tâche complexe d'exécution du modèle sur l'accélérateur matériel sélectionné.

6. Terminer l'implémentation initiale d'ImageSegmentationHelper

Il est maintenant temps d'écrire du code. Vous allez effectuer l'implémentation initiale de ImageSegmentationHelper.kt. Cela implique de configurer la classe privée Segmenter pour contenir le modèle LiteRT et d'implémenter la fonction cleanup() pour le libérer correctement.

  1. Terminez la classe Segmenter et la fonction cleanup() : dans le fichier ImageSegmentationHelper.kt, vous trouverez le squelette d'une classe privée nommée Segmenter et d'une fonction nommée cleanup(). Tout d'abord, complétez la classe Segmenter en définissant son constructeur pour contenir le modèle, en créant des propriétés pour les tampons d'entrée/sortie et en ajoutant une méthode close() pour libérer le modèle. Implémentez ensuite la fonction cleanup() pour appeler cette nouvelle méthode close().Remplacez la classe Segmenter et la fonction cleanup() existantes par les éléments suivants (ligne 83 environ) :
    private class Segmenter(
        // Add this argument
        private val model: CompiledModel,
        private val coloredLabels: List<ColoredLabel>,
    ) {
        // Add these private vals
        private val inputBuffers: = model.createInputBuffers()
        private val outputBuffers: = model.createOutputBuffers()
    
        fun cleanup() {
          // cleanup buffers
          inputBuffers.forEach { it.close() }
          outputBuffers.forEach { it.close() }
          // cleanup model
          model.close()
        }
    }
    
  2. Définissez la méthode toAccelerator : cette méthode mappe les énumérations d'accélérateur définies à partir du menu d'accélérateur aux énumérations d'accélérateur spécifiques aux modules LiteRT importés (ligne 225 environ) :
    fun toAccelerator(acceleratorEnum: AcceleratorEnum): Accelerator {
      return when (acceleratorEnum) {
        AcceleratorEnum.CPU -> Accelerator.CPU
        AcceleratorEnum.GPU -> Accelerator.GPU
      }
    }
    
  3. Initialisez CompiledModel : recherchez maintenant la fonction initSegmenter. C'est là que vous allez créer l'instance CompiledModel et l'utiliser pour instancier votre classe Segmenter désormais définie. Ce code configure le modèle avec l'accélérateur spécifié (CPU ou GPU) et le prépare pour l'inférence. Remplacez TODO dans initSegmenter par l'implémentation suivante (Cmd/Ctrl+f "initSegmenter" ou ligne 62) :
    cleanup()
    try {
      withContext(singleThreadDispatcher) {
        val model =
          CompiledModel.create(
            context.assets,
            "selfie_multiclass.tflite",
            CompiledModel.Options(toAccelerator(acceleratorEnum)),
            null,
          )
        segmenter = Segmenter(model, coloredLabels)
        Log.d(TAG, "Created an image segmenter")
      }
    } catch (e: Exception) {
      Log.i(TAG, "Create LiteRT from selfie_multiclass is failed: ${e.message}")
      _error.emit(e)
    }
    

7. Démarrer la segmentation et le prétraitement

Maintenant que nous avons un modèle, nous devons déclencher le processus de segmentation et préparer les données d'entrée pour le modèle.

Segmentation des déclencheurs

Le processus de segmentation commence dans MainViewModel.kt, qui reçoit les frames de la caméra.

OuvrirMainViewModel.kt

  1. Déclencher la segmentation à partir des images de la caméra : les fonctions segment dans MainViewModel sont le point d'entrée de notre tâche de segmentation. Elles sont appelées chaque fois qu'une nouvelle image est disponible depuis l'appareil photo ou sélectionnée dans la galerie. Ces fonctions appellent ensuite la méthode segment dans notre ImageSegmentationHelper. Remplacez les TODO dans les deux fonctions segment par ce qui suit (ligne 107 environ) :
    // For ImageProxy (from CameraX)
    fun segment(imageProxy: ImageProxy) {
        segmentJob =
            viewModelScope.launch {
                imageSegmentationHelper.segment(imageProxy.toBitmap(), imageProxy.imageInfo.rotationDegrees)
                imageProxy.close()
            }
    }
    
    // For Bitmaps (from gallery)
    fun segment(bitmap: Bitmap, rotationDegrees: Int) {
        segmentJob =
            viewModelScope.launch {
                val argbBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
                imageSegmentationHelper.segment(argbBitmap, rotationDegrees)
            }
    }
    

Prétraiter l'image

Revenons maintenant à ImageSegmentationHelper.kt pour gérer le prétraitement des images.

OuvrirImageSegmentationHelper.kt

  1. Implémenter la fonction publique segment : cette fonction sert de wrapper qui appelle la fonction privée segment dans la classe Segmenter. Remplacez TODO par (~ligne 95) :
    try {
      withContext(singleThreadDispatcher) {
        segmenter?.segment(bitmap, rotationDegrees)?.let { if (isActive) _segmentation.emit(it) }
      }
    } catch (e: Exception) {
      Log.i(TAG, "Image segment error occurred: ${e.message}")
      _error.emit(e)
    }
    
  2. Implémenter le prétraitement : la fonction privée segment à l'intérieur de la classe Segmenter est l'endroit où nous effectuerons les transformations nécessaires sur l'image d'entrée pour la préparer au modèle. Cela inclut la mise à l'échelle, la rotation et la normalisation de l'image. Cette fonction appellera ensuite une autre fonction privée segment pour effectuer l'inférence. Remplacez TODO dans la fonction segment(bitmap: Bitmap, ...) par ce qui suit (ligne 121 environ) :
    val totalStartTime = SystemClock.uptimeMillis()
    val rotation = -rotationDegrees / 90
    val (h, w) = Pair(256, 256)
    
    // Preprocessing
    val preprocessStartTime = SystemClock.uptimeMillis()
    var image = bitmap.scale(w, h, true)
    image = rot90Clockwise(image, rotation)
    val inputFloatArray = normalize(image, 127.5f, 127.5f)
    Log.d(TAG, "Preprocessing time: ${SystemClock.uptimeMillis() - preprocessStartTime} ms")
    
    // Inference
    val inferenceStartTime = SystemClock.uptimeMillis()
    val segmentResult = segment(inputFloatArray)
    Log.d(TAG, "Inference time: ${SystemClock.uptimeMillis() - inferenceStartTime} ms")
    
    Log.d(TAG, "Total segmentation time: ${SystemClock.uptimeMillis() - totalStartTime} ms")
    return SegmentationResult(segmentResult, SystemClock.uptimeMillis() - inferenceStartTime)
    

8. Inférence principale avec LiteRT

Une fois les données d'entrée prétraitées, nous pouvons exécuter l'inférence principale à l'aide de LiteRT.

OuvrirImageSegmentationHelper.kt

  1. Implémenter l'exécution du modèle : la fonction privée segment(inputFloatArray: FloatArray) est l'endroit où nous interagissons directement avec la méthode run() de LiteRT. Nous écrivons nos données prétraitées dans le tampon d'entrée, exécutons le modèle et lisons les résultats du tampon de sortie. Remplacez TODO dans cette fonction par (~ligne 188) :
    val (h, w, c) = Triple(256, 256, 6)
    
    // MODEL EXECUTION PHASE
    val modelExecStartTime = SystemClock.uptimeMillis()
    
    // Write input data - measure time
    val bufferWriteStartTime = SystemClock.uptimeMillis()
    inputBuffers[0].writeFloat(inputFloatArray)
    val bufferWriteTime = SystemClock.uptimeMillis() - bufferWriteStartTime
    Log.d(TAG, "Buffer write time: $bufferWriteTime ms")
    
    // Optional tensor inspection
    logTensorStats("Input tensor", inputFloatArray)
    
    // Run model inference - measure time
    val modelRunStartTime = SystemClock.uptimeMillis()
    model.run(inputBuffers, outputBuffers)
    val modelRunTime = SystemClock.uptimeMillis() - modelRunStartTime
    Log.d(TAG, "Model.run() time: $modelRunTime ms")
    
    // Read output data - measure time
    val bufferReadStartTime = SystemClock.uptimeMillis()
    val outputFloatArray = outputBuffers[0].readFloat()
    val outputBuffer = FloatBuffer.wrap(outputFloatArray)
    val bufferReadTime = SystemClock.uptimeMillis() - bufferReadStartTime
    Log.d(TAG, "Buffer read time: $bufferReadTime ms")
    
    val modelExecTime = SystemClock.uptimeMillis() - modelExecStartTime
    Log.d(TAG, "Total model execution time: $modelExecTime ms")
    
    // Optional tensor inspection
    logTensorStats("Output tensor", outputFloatArray)
    
    // POSTPROCESSING PHASE
    val postprocessStartTime = SystemClock.uptimeMillis()
    
    // Process mask from model output
    val inferenceData = InferenceData(width = w, height = h, channels = c, buffer = outputBuffer)
    val mask = processImage(inferenceData)
    
    val postprocessTime = SystemClock.uptimeMillis() - postprocessStartTime
    Log.d(TAG, "Postprocessing time (mask creation): $postprocessTime ms")
    
    return Segmentation(
      listOf(Mask(mask, inferenceData.width, inferenceData.height)),
      coloredLabels,
    )
    

9. Post-traitement et affichage de la superposition

Après l'exécution de l'inférence, nous obtenons une sortie brute du modèle. Nous devons traiter cette sortie pour créer un masque de segmentation visuelle, puis l'afficher à l'écran.

OuvrirImageSegmentationHelper.kt

  1. Implémenter le traitement des sorties : la fonction processImage convertit la sortie brute à virgule flottante du modèle en ByteBuffer qui représente le masque de segmentation. Pour ce faire, il recherche la classe ayant la probabilité la plus élevée pour chaque pixel. Remplacez son TODO par (~ligne 238) :
    val mask = ByteBuffer.allocateDirect(inferenceData.width * inferenceData.height)
    for (i in 0 until inferenceData.height) {
        for (j in 0 until inferenceData.width) {
            val offset = inferenceData.channels * (i * inferenceData.width + j)
    
            var maxIndex = 0
            var maxValue = inferenceData.buffer.get(offset)
    
            for (index in 1 until inferenceData.channels) {
                if (inferenceData.buffer.get(offset + index) > maxValue) {
                    maxValue = inferenceData.buffer.get(offset + index)
                    maxIndex = index
                }
            }
            mask.put(i * inferenceData.width + j, maxIndex.toByte())
        }
    }
    return mask
    

OuvrirMainViewModel.kt

  1. Collecter et traiter les résultats de la segmentation : revenons maintenant à MainViewModel pour traiter les résultats de la segmentation de ImageSegmentationHelper. Le segmentationUiShareFlow collecte le SegmentationResult, convertit le masque en Bitmap coloré et le fournit à l'UI. Remplacez le TODO dans la propriété segmentationUiShareFlow par (~ligne 63). Ne remplacez pas le code déjà présent, remplissez simplement le corps :
    viewModelScope.launch {
      imageSegmentationHelper.segmentation
        .filter { it.segmentation.masks.isNotEmpty() }
        .map {
          val segmentation = it.segmentation
          val mask = segmentation.masks[0]
          val maskArray = mask.data
          val width = mask.width
          val height = mask.height
          val pixelSize = width * height
          val pixels = IntArray(pixelSize)
    
          val colorLabels =
            segmentation.coloredLabels.mapIndexed { index, coloredLabel ->
              ColorLabel(index, coloredLabel.label, coloredLabel.argb)
            }
          // Set color for pixels
          for (i in 0 until pixelSize) {
            val colorLabel = colorLabels[maskArray[i].toInt()]
            val color = colorLabel.getColor()
            pixels[i] = color
          }
          // Get image info
          val overlayInfo = OverlayInfo(pixels = pixels, width = width, height = height)
    
          val inferenceTime = it.inferenceTime
          Pair(overlayInfo, inferenceTime)
        }
        .collect { flow.emit(it) }
    }
    

Ouvrirview/SegmentationOverlay.kt

La dernière étape consiste à orienter correctement la superposition de segmentation lorsque l'utilisateur passe à la caméra frontale. Le flux de la caméra avant est naturellement mis en miroir. Nous devons donc appliquer la même inversion horizontale à notre superposition Bitmap pour nous assurer qu'elle s'aligne correctement sur l'aperçu de la caméra.

  1. Gérer l'orientation du calque de poignée : recherchez TODO dans le fichier SegmentationOverlay.kt et remplacez-le par le code suivant. Ce code vérifie si la caméra frontale est active et, si c'est le cas, applique une inversion horizontale à la superposition Bitmap avant qu'elle ne soit dessinée sur Canvas. (~ligne 42) :
    val orientedBitmap =
      if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
        // Create a matrix for horizontal flipping
        val matrix = Matrix().apply { preScale(-1f, 1f) }
        Bitmap.createBitmap(image, 0, 0, image.width, image.height, matrix, false).also {
          image.recycle()
        }
      } else {
        image
      }
    

10. Exécuter et utiliser l'application finale

Vous avez maintenant terminé toutes les modifications de code nécessaires. Il est temps d'exécuter l'application et de voir le résultat de votre travail.

  1. Exécuter l'application : connectez votre appareil Android et cliquez sur Run (Exécuter) dans la barre d'outils Android Studio.

Bouton d&#39;exécution

  1. Testez les fonctionnalités : une fois l'application lancée, vous devriez voir le flux vidéo en direct de la caméra avec une superposition de segmentation colorée.
    • Changer de caméra : appuyez sur l'icône de changement de caméra en haut de l'écran pour basculer entre la caméra avant et la caméra arrière. Notez comment la superposition s'oriente correctement.
    • Changer d'accélérateur : appuyez sur le bouton "CPU" ou "GPU" en bas de l'écran pour changer d'accélérateur matériel. Observez la modification du temps d'inférence affiché en bas de l'écran. Le GPU devrait être beaucoup plus rapide.
    • Utiliser une image de la galerie : appuyez sur l'onglet "Galerie" en haut de l'écran pour sélectionner une image dans la galerie photo de votre appareil. L'application exécutera la segmentation sur l'image statique sélectionnée.

Autre UI

Vous disposez désormais d'une application de segmentation d'images en temps réel entièrement fonctionnelle, optimisée par LiteRT.

11. Avancé (facultatif) : Utiliser la NPU

Ce dépôt contient également une version de l'application optimisée pour les unités de traitement neuronal (NPU). La version NPU peut améliorer considérablement les performances sur les appareils dotés d'un NPU compatible.

Pour essayer la version NPU, ouvrez le projet kotlin_npu/android dans Android Studio. Le code est très semblable à la version CPU/GPU et est configuré pour utiliser le délégué NPU.

Pour utiliser le délégué NPU, vous devez vous inscrire au programme d'accès anticipé.

12. Félicitations !

Vous avez créé une application Android qui effectue une segmentation d'images en temps réel à l'aide de LiteRT. Vous avez appris à effectuer les tâches suivantes :

  • Intégrez le runtime LiteRT à une application Android.
  • Chargez et exécutez un modèle TFLite de segmentation d'images.
  • Prétraitez l'entrée du modèle.
  • Traitez la sortie du modèle pour créer un masque de segmentation.
  • Utilisez CameraX pour une application d'appareil photo en temps réel.

Étapes suivantes

  • Essayez un autre modèle de segmentation d'images.
  • Testez différents délégués LiteRT (CPU, GPU, NPU).

En savoir plus