Segmentación de imágenes acelerada en tiempo real en Android con LiteRT

1. Antes de comenzar

Escribir código es una excelente manera de desarrollar la memoria muscular y profundizar tu comprensión del material. Si bien copiar y pegar puede ahorrar tiempo, invertir en esta práctica puede generar una mayor eficiencia y mejores habilidades de programación a largo plazo.

En este codelab, aprenderás a compilar una aplicación para Android que realice la segmentación de imágenes en tiempo real en una transmisión de cámara en vivo con el nuevo tiempo de ejecución de Google para TensorFlow Lite, LiteRT. Tomarás una aplicación para Android de inicio y le agregarás capacidades de segmentación de imágenes. También analizaremos los pasos de preprocesamiento, inferencia y posprocesamiento. Harás lo siguiente:

  • Compila una app para Android que segmenta imágenes en tiempo real.
  • Integra un modelo de segmentación de imágenes de LiteRT entrenado previamente.
  • Preprocesa la imagen de entrada para el modelo.
  • Usa el tiempo de ejecución de LiteRT para la aceleración de CPU y GPU.
  • Comprende cómo procesar el resultado del modelo para mostrar la máscara de segmentación.
  • Comprende cómo realizar ajustes para la cámara frontal.

Al final, crearás algo similar a la siguiente imagen:

App finalizada

Requisitos previos

Este codelab se diseñó para desarrolladores de dispositivos móviles experimentados que desean adquirir experiencia en el aprendizaje automático. Debes estar familiarizado con lo siguiente:

  • Desarrollo para Android con Kotlin y Android Studio
  • Conceptos básicos del procesamiento de imágenes

Qué aprenderás

  • Cómo integrar y usar el tiempo de ejecución de LiteRT en una aplicación para Android
  • Cómo realizar la segmentación de imágenes con un modelo LiteRT previamente entrenado
  • Cómo preprocesar la imagen de entrada para el modelo
  • Es la forma de ejecutar la inferencia para el modelo.
  • Cómo procesar el resultado de un modelo de segmentación para visualizar los resultados
  • Cómo usar CameraX para el procesamiento de feeds de cámara en tiempo real

Requisitos

  • Una versión reciente de Android Studio (probada en la versión 2025.1.1)
  • Un dispositivo Android físico Se recomienda probarlo en dispositivos Galaxy y Pixel.
  • El código de muestra (de GitHub)
  • Conocimientos básicos sobre el desarrollo de Android en Kotlin

2. Segmentación de imágenes

La segmentación de imágenes es una tarea de visión artificial que implica dividir una imagen en varios segmentos o regiones. A diferencia de la detección de objetos, que dibuja un cuadro delimitador alrededor de un objeto, la segmentación de imágenes asigna una clase o etiqueta específica a cada píxel de la imagen. Esto proporciona una comprensión mucho más detallada y granular del contenido de la imagen, lo que te permite conocer la forma y el límite exactos de cada objeto.

Por ejemplo, en lugar de solo saber que hay una "persona" en un cuadro, puedes saber exactamente qué píxeles pertenecen a esa persona. En este instructivo, se demuestra cómo realizar la segmentación de imágenes en tiempo real en un dispositivo Android con un modelo de aprendizaje automático previamente entrenado.

Ejemplo de segmentación

LiteRT: Ampliamos los límites del AA integrado en el dispositivo

Una tecnología clave que permite la segmentación de alta fidelidad en tiempo real en dispositivos móviles es LiteRT. Como entorno de ejecución de alto rendimiento y próxima generación de Google para TensorFlow Lite, LiteRT se diseñó para obtener el mejor rendimiento absoluto del hardware subyacente.

Esto se logra a través del uso inteligente y optimizado de aceleradores de hardware, como la GPU (unidad de procesamiento gráfico) y la NPU (unidad de procesamiento neuronal). Al descargar la intensa carga de trabajo computacional del modelo de segmentación de la CPU de uso general a estos procesadores especializados, LiteRT reduce drásticamente el tiempo de inferencia. Esta aceleración es lo que permite ejecutar modelos complejos sin problemas en un feed de cámara en vivo, lo que amplía los límites de lo que podemos lograr con el aprendizaje automático directamente en tu teléfono. Sin este nivel de rendimiento, la segmentación en tiempo real sería demasiado lenta y entrecortada para brindar una buena experiencia del usuario.

3. Prepárate

Clona el repositorio

Primero, clona el repositorio de LiteRT:

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

LiteRT/litert/samples/image_segmentation es el directorio con todos los recursos que necesitarás. Para este codelab, solo necesitarás el proyecto kotlin_cpu_gpu/android_starter. Si te quedas atascado, te recomendamos que revises el proyecto terminado: kotlin_cpu_gpu/android

Nota sobre las rutas de acceso a archivos

En este instructivo, se especifican rutas de acceso a archivos en formato Linux/macOS. Si usas Windows, deberás ajustar las rutas según corresponda.

También es importante tener en cuenta la distinción entre la vista de proyecto de Android Studio y una vista estándar del sistema de archivos. La vista de proyecto de Android Studio es una representación estructurada de los archivos de tu proyecto, organizada para el desarrollo de Android. Las rutas de acceso a los archivos en este instructivo hacen referencia a las rutas de acceso del sistema de archivos, no a las rutas de acceso en la vista de proyecto de Android Studio.

Cómo importar la app de inicio

Comencemos por importar la app de inicio en Android Studio.

  1. Abre Android Studio y selecciona Open.

Apertura de Android Studio

  1. Navega al directorio kotlin_cpu_gpu/android_starter y ábrelo.

Android Starter

Para asegurarte de que todas las dependencias estén disponibles para tu app, debes sincronizar tu proyecto con los archivos de Gradle cuando finalice el proceso de importación.

  1. Selecciona Sync Project with Gradle Files en la barra de herramientas de Android Studio.

Sincronización de menús

  1. No omitas este paso, ya que, si no funciona, el resto del instructivo no tendrá sentido.

Cómo ejecutar la app de inicio

Ahora que importaste el proyecto a Android Studio, puedes ejecutar la app por primera vez.

Conecta tu dispositivo Android a la computadora a través de un cable USB y haz clic en Run en la barra de herramientas de Android Studio.

Botón Ejecutar

La app debería iniciarse en tu dispositivo. Verás una transmisión de la cámara en vivo, pero aún no se realizará la segmentación. Todas las ediciones de archivos que realizarás en este instructivo se encontrarán en el directorio LiteRT/litert/samples/image_segmentation/kotlin_cpu_gpu/android_starter/app/src/main/java/com/google/aiedge/examples/image_segmentation (ahora sabes por qué Android Studio reestructura esto 😃).

Directorio del proyecto

También verás comentarios de TODO en los archivos ImageSegmentationHelper.kt, MainViewModel.kt y view/SegmentationOverlay.kt. En los siguientes pasos, implementarás la funcionalidad de segmentación de imágenes completando estos TODO.

4. Información sobre la app de inicio

La app de partida ya tiene una IU básica y lógica de control de la cámara. A continuación, se incluye una breve descripción general de los archivos clave:

  • app/src/main/java/com/google/aiedge/examples/image_segmentation/MainActivity.kt: Es el punto de entrada principal de la aplicación. Configura la IU con Jetpack Compose y controla los permisos de la cámara.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/MainViewModel.kt: Este ViewModel administra el estado de la IU y coordina el proceso de segmentación de imágenes.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/ImageSegmentationHelper.kt: Aquí agregaremos la lógica principal para la segmentación de imágenes. Se encargará de cargar el modelo, procesar los fotogramas de la cámara y ejecutar la inferencia.
  • app/src/main/java/com/google/aiedge/examples/image_segmentation/view/CameraScreen.kt: Esta función de componibilidad muestra la vista previa de la cámara y la superposición de segmentación.
  • app/src/main/assets/selfie_multiclass.tflite: Este es el modelo de segmentación de imágenes de TensorFlow Lite previamente entrenado que usaremos.

5. Cómo comprender LiteRT y agregar dependencias

Ahora, agreguemos la funcionalidad de segmentación de imágenes a la app de inicio.

1. Agrega la dependencia de LiteRT

Primero, debes agregar la biblioteca de LiteRT a tu proyecto. Este es el primer paso fundamental para habilitar el aprendizaje automático en el dispositivo con el tiempo de ejecución optimizado de Google.

Abre el archivo app/build.gradle.kts y agrega la siguiente línea al bloque dependencies:

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

Después de agregar la dependencia, haz clic en el botón Sync Now que aparece en la esquina superior derecha de Android Studio para sincronizar tu proyecto con los archivos de Gradle.

Sincronizar ahora

2. Información sobre las APIs clave de LiteRT

Abrir ImageSegmentationHelper.kt

Antes de escribir el código de implementación, es importante comprender los componentes principales de la API de LiteRT que usarás. Asegúrate de importar desde el paquete com.google.ai.edge.litert y agrega las siguientes importaciones en la parte superior de ImageSegmentationHelper.kt:

import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
  • CompiledModel: Esta es la clase central para interactuar con tu modelo de TFLite. Representa un modelo que se compiló previamente y se optimizó para un acelerador de hardware específico (como la CPU o la GPU). Esta precompilación es una función clave de LiteRT que permite una inferencia más rápida y eficiente.
  • CompiledModel.Options: Usas esta clase de compilador para configurar el CompiledModel. El parámetro de configuración más importante es especificar el acelerador de hardware que deseas usar para ejecutar tu modelo.
  • Accelerator: Este enum te permite elegir el hardware para la inferencia. El proyecto inicial ya está configurado para controlar estas opciones:
    • Accelerator.CPU: Para ejecutar el modelo en la CPU del dispositivo Esta es la opción más compatible con todos los dispositivos.
    • Accelerator.GPU: Para ejecutar el modelo en la GPU del dispositivo Esto suele ser mucho más rápido que la CPU para los modelos basados en imágenes.
  • Buffers de entrada y salida (TensorBuffer): LiteRT usa TensorBuffer para las entradas y salidas del modelo. Esto te brinda un control detallado sobre la memoria y evita copias de datos innecesarias. Obtendrás estos búferes directamente de tu instancia de CompiledModel con model.createInputBuffers() y model.createOutputBuffers(), y, luego, escribirás tus datos de entrada en ellos y leerás los resultados.
  • model.run(): Esta es la función que ejecuta la inferencia. Le pasas los búferes de entrada y salida, y LiteRT se encarga de la compleja tarea de ejecutar el modelo en el acelerador de hardware seleccionado.

6. Finaliza la implementación inicial de ImageSegmentationHelper

Ahora es momento de escribir código. Completarás la implementación inicial de ImageSegmentationHelper.kt. Esto implica configurar la clase privada Segmenter para que contenga el modelo de LiteRT y, luego, implementar la función cleanup() para liberarlo correctamente.

  1. Completa la clase Segmenter y la función cleanup(): En el archivo ImageSegmentationHelper.kt, encontrarás un esqueleto para una clase privada llamada Segmenter y una función llamada cleanup(). Primero, completa la clase Segmenter definiendo su constructor para que contenga el modelo, creando propiedades para los búferes de entrada y salida, y agregando un método close() para liberar el modelo. Luego, implementa la función cleanup() para llamar a este nuevo método close().Reemplaza la clase Segmenter y la función cleanup() existentes por lo siguiente: (~línea 83)
    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. Define el método toAccelerator: Este método asigna los enums de acelerador definidos desde el menú de acelerador a los enums de acelerador específicos de los módulos LiteRT importados (línea 225 aproximadamente):
    fun toAccelerator(acceleratorEnum: AcceleratorEnum): Accelerator {
      return when (acceleratorEnum) {
        AcceleratorEnum.CPU -> Accelerator.CPU
        AcceleratorEnum.GPU -> Accelerator.GPU
      }
    }
    
  3. Inicializa CompiledModel: Ahora busca la función initSegmenter. Aquí crearás la instancia CompiledModel y la usarás para crear una instancia de la clase Segmenter que ahora definiste. Este código configura el modelo con el acelerador especificado (CPU o GPU) y lo prepara para la inferencia. Reemplaza el TODO en initSegmenter por la siguiente implementación (Cmd/Ctrl+f "initSegmenter" o línea 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. Inicia la segmentación y el preprocesamiento

Ahora que tenemos un modelo, debemos activar el proceso de segmentación y preparar los datos de entrada para el modelo.

Segmentación de activadores

El proceso de segmentación comienza en MainViewModel.kt, que recibe fotogramas de la cámara.

Abrir MainViewModel.kt

  1. Trigger Segmentation from Camera Frames: Las funciones segment en MainViewModel son el punto de entrada para nuestra tarea de segmentación. Se llaman cada vez que hay una imagen nueva disponible desde la cámara o se selecciona una de la galería. Luego, estas funciones llaman al método segment en nuestro ImageSegmentationHelper. Reemplaza los TODO en ambas funciones segment por lo siguiente (línea 107):
    // 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)
            }
    }
    

Preprocesa la imagen

Ahora, volvamos a ImageSegmentationHelper.kt para controlar el procesamiento previo de la imagen.

Abrir ImageSegmentationHelper.kt

  1. Implementa la función pública segment: Esta función actúa como un wrapper que llama a la función privada segment dentro de la clase Segmenter. Reemplaza TODO por lo siguiente (línea 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. Implementa el procesamiento previo: La función privada segment dentro de la clase Segmenter es donde realizaremos las transformaciones necesarias en la imagen de entrada para prepararla para el modelo. Esto incluye escalar, rotar y normalizar la imagen. Luego, esta función llamará a otra función segment privada para realizar la inferencia. Reemplaza TODO en la función segment(bitmap: Bitmap, ...) por lo siguiente (línea 121):
    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. Inferencia principal con LiteRT

Con los datos de entrada preprocesados, ahora podemos ejecutar la inferencia principal con LiteRT.

Abrir ImageSegmentationHelper.kt

  1. Implementación de la ejecución del modelo: La función privada segment(inputFloatArray: FloatArray) es donde interactuamos directamente con el método run() de LiteRT. Escribimos nuestros datos preprocesados en el búfer de entrada, ejecutamos el modelo y leemos los resultados del búfer de salida. Reemplaza TODO en esta función por lo siguiente (línea 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. Posprocesamiento y visualización de la superposición

Después de ejecutar la inferencia, obtenemos un resultado sin procesar del modelo. Debemos procesar este resultado para crear una máscara de segmentación visual y, luego, mostrarla en la pantalla.

Abrir ImageSegmentationHelper.kt

  1. Implementa el procesamiento de salida: La función processImage convierte la salida de punto flotante sin procesar del modelo en un ByteBuffer que representa la máscara de segmentación. Para ello, busca la clase con la probabilidad más alta para cada píxel. Reemplaza su TODO por lo siguiente (línea 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
    

Abrir MainViewModel.kt

  1. Collect and Process Segmentation Results: Ahora volvemos a MainViewModel para procesar los resultados de la segmentación de ImageSegmentationHelper. El segmentationUiShareFlow recopila el SegmentationResult, convierte la máscara en un Bitmap colorido y lo proporciona a la IU. Reemplaza el TODO en la propiedad segmentationUiShareFlow por (~línea 63). No reemplaces el código que ya está allí, solo completa el cuerpo:
    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) }
    }
    

Abrir view/SegmentationOverlay.kt

La última pieza es orientar correctamente la superposición de segmentación cuando el usuario cambia a la cámara frontal. El feed de la cámara frontal se refleja de forma natural, por lo que debemos aplicar el mismo volteo horizontal a nuestra superposición Bitmap para asegurarnos de que se alinee correctamente con la vista previa de la cámara.

  1. Handle Overlay Orientation: Busca TODO en el archivo SegmentationOverlay.kt y reemplázalo por el siguiente código. Este código verifica si la cámara frontal está activa y, si es así, aplica un volteo horizontal a la superposición Bitmap antes de que se dibuje en el Canvas. (~línea 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. Cómo ejecutar y usar la app final

Ahora completaste todos los cambios de código necesarios. Es hora de ejecutar la app y ver tu trabajo en acción.

  1. Ejecuta la app: Conecta tu dispositivo Android y haz clic en Run en la barra de herramientas de Android Studio.

Botón Ejecutar

  1. Prueba las funciones: Una vez que se inicie la app, deberías ver el feed de la cámara en vivo con una superposición de segmentación colorida.
    • Cambiar de cámara: Presiona el ícono de cambio de cámara en la parte superior para alternar entre la cámara frontal y la posterior. Observa cómo la capa superpuesta se orienta correctamente.
    • Cambiar acelerador: Presiona el botón "CPU" o "GPU" en la parte inferior para cambiar el acelerador de hardware. Observa el cambio en el Tiempo de inferencia que se muestra en la parte inferior de la pantalla. La GPU debería ser mucho más rápida.
    • Usar una imagen de la galería: Presiona la pestaña "Galería" en la parte superior para seleccionar una imagen de la galería de fotos de tu dispositivo. La app ejecutará la segmentación en la imagen estática seleccionada.

Otra IU

Ahora tienes una app de segmentación de imágenes en tiempo real completamente funcional con tecnología de LiteRT.

11. Avanzado (opcional): Cómo usar la NPU

Este repositorio también contiene una versión de la app optimizada para las unidades de procesamiento neuronal (NPU). La versión de la NPU puede proporcionar un aumento significativo del rendimiento en dispositivos que tienen una NPU compatible.

Para probar la versión de la NPU, abre el proyecto kotlin_npu/android en Android Studio. El código es muy similar a la versión de CPU/GPU y está configurado para usar el delegado de NPU.

Para usar el delegado de la NPU, deberás inscribirte en el Programa de acceso anticipado.

12. ¡Felicitaciones!

Creaste correctamente una app para Android que realiza la segmentación de imágenes en tiempo real con LiteRT. Aprendió a hacer lo siguiente:

  • Integra el tiempo de ejecución de LiteRT en una app para Android.
  • Carga y ejecuta un modelo de segmentación de imágenes de TFLite.
  • Procesa previamente la entrada del modelo.
  • Procesa el resultado del modelo para crear una máscara de segmentación.
  • Usa CameraX para una app de cámara en tiempo real.

Próximos pasos

  • Prueba con otro modelo de segmentación de imágenes.
  • Experimenta con diferentes delegados de LiteRT (CPU, GPU, NPU).

Más información