Apps foundation

Mejora las experiencias de la cámara en todos los dispositivos con CameraX y la inteligencia integrada en el dispositivo

Bocinas

Acerca de

Acerca de la charla

Los usuarios esperan que las apps de terceros capturen fotos y videos con la misma alta fidelidad que la cámara nativa de su dispositivo, independientemente de las condiciones de iluminación o el nivel de hardware. Ofrecer esta coherencia en el ecosistema Android en expansión conlleva consideraciones arquitectónicas clave. En esta sesión, se muestra cómo crear canalizaciones de captura sólidas y profesionales que maximicen las capacidades de los dispositivos y, al mismo tiempo, sigan siendo confiables en todos los factores de forma.

Exploraremos los avances más recientes en CameraX y te mostraremos cómo integrar funciones de vanguardia, como fotos en movimiento y mejoras con IA integrada en el dispositivo, a través de los Servicios de Google Play, junto con experiencias espaciales con el SDK de Jetpack XR. También veremos nuestra habilidad de CameraX, que permite que el agente de programación que elijas implemente automáticamente los conceptos de esta charla y mucho más.

Resumen

  • Jetpack CameraX es la biblioteca de cámara recomendada para las apps para Android, ya que Camera1 dejó de estar disponible. La aplicación de las políticas de Play comenzará a inhabilitar Camera1 el próximo año en Android 18.
  • CameraXViewfinder adapta las vistas previas de la cámara en diferentes factores de forma, como los dispositivos plegables. En CameraX 1.7, se agregan controles de gestos y compatibilidad con el flash de pantalla.
  • Los anteojos con audio y pantalla ejecutan apps en un teléfono host mientras exponen las cámaras de ambos dispositivos. Pasar ProjectedDeviceContext permite que CameraX capture imágenes directamente desde los lentes.
  • La API de Feature Group ayuda a las apps a seleccionar combinaciones compatibles de funciones de la cámara. A partir de CameraX 1.6, se admite la combinación de grabación en 4K con estabilización de video.
  • El aprendizaje automático integrado en el dispositivo analiza las vistas previas de la cámara y el contenido multimedia capturado a través de ML Kit y MediaPipe. Los desarrolladores pueden ejecutar tareas que van desde la detección de gestos en tiempo real hasta las descripciones de imágenes creadas con tecnología generativa.

Recursos

Gemini Notebook

Temas

  • camerax
  • android-camera
  • on-device-ml
  • gemini-nano
  • mediapipe
  • ml-kit
  • android-xr

Transcripción

Descargar transcripción

Introducción y descripción general de CameraX

  • Hola a todos.

  • Le damos la bienvenida a nuestra sesión.

  • Soy Aman y me acompaña Scott.

  • Hoy nos entusiasma hablar sobre cómo puedes mejorar tus experiencias con la cámara en todos los dispositivos con CameraX y la inteligencia integrada en el dispositivo.

  • Empecemos.

  • Piensa en cómo usas tus teléfonos.

  • A menudo, usamos la cámara del teléfono para capturar momentos y compartirlos con nuestros seres queridos.

  • La cámara se convirtió en un elemento central de la experiencia con smartphones.

  • De hecho, una buena cámara suele ser un factor clave a la hora de comprar smartphones.

  • Hoy, abordaremos dos temas generales para crear experiencias de cámara premium.

  • Primero, exploraremos algunas funciones de CameraX y, luego, aprenderemos sobre algunas técnicas de aprendizaje automático integrado en el dispositivo.

  • Comencemos con CameraX.

  • Jetpack CameraX potencia la experiencia de la cámara en Android.

  • Además de CameraX, también tenemos Camera1 y Camera2, que forman parte del framework de la plataforma de Android.

  • Entonces, ¿qué biblioteca deberías usar?

  • Camera1 fue la API de cámara original en Android.

  • Ahora está obsoleta, por lo que las apps nuevas deben usar Camera2 o CameraX.

  • Camera2 reemplazó a Camera1.

  • Por lo tanto, si necesitas un control de cámara de bajo nivel para casos de uso complejos, Camera2 es una buena opción.

  • Sin embargo, requiere que tú mismo manejes las configuraciones específicas del dispositivo.

  • Por último, Jetpack CameraX se basa en Camera2.

  • Proporciona una API coherente y controla automáticamente las configuraciones específicas de los dispositivos.

  • Es la opción recomendada para la mayoría de los desarrolladores.

  • Tenemos un aviso muy importante que darte.

  • Planeamos inhabilitar Camera1, y esto comenzará con la aplicación de las políticas de Play el próximo año en Android 18.

  • Si aún usas la API de Camera1 heredada, deberás migrar a CameraX o Camera2.

  • Entonces, ¿por qué deberías usar CameraX?

  • En primer lugar, admite Android 7 y versiones posteriores, por lo que es compatible con casi el 99% de los dispositivos Android activos.

  • En segundo lugar, es fácil de usar y proporciona APIs sencillas para casos de uso comunes, como la vista previa, la captura y la grabación con menos código.

  • En tercer lugar, proporciona una experiencia coherente en todos los dispositivos, ya que reduce los problemas específicos de cada uno, como el estiramiento de la vista previa, que es bastante común en dispositivos plegables y tablets.

  • Por último, simplifica la implementación de funciones avanzadas de OEM, como el modo nocturno y el modo retrato.

  • Ahora, me gustaría invitar a Scott para que hable sobre algunas de las funciones de CameraX.

  • Ahora tú, Scott.

  • Gracias, Aman.

  • Hola a todos.

  • Me llamo Scott.

  • Hoy, abordaremos varias funciones de CameraX para que puedas crear una experiencia de cámara de nivel profesional.

Funciones del visor adaptable

  • Primero, hablemos del Visor adaptable.

  • Está diseñada para eliminar por completo la dificultad de controlar la vista previa en diferentes factores de forma y tamaños de pantalla.

  • Ahora los usuarios tienen una variedad de dispositivos, incluidos plegables, teléfonos, laptops y dispositivos de RE.

  • Por lo tanto, es más importante que nunca crear una experiencia de cámara que se adapte a los nuevos factores de forma.

  • Además, la vista previa de la cámara es notoriamente difícil de lograr, en especial en los dispositivos plegables de pantalla panorámica.

  • En estos dispositivos, cuando están desplegados, la cámara interior se monta de forma horizontal, mientras que la cámara posterior se monta de forma vertical.

  • Por lo tanto, si asumes que todos los sensores de la cámara están montados verticalmente, se producirá una discrepancia espacial y se generará una distorsión o una imagen lateral.

  • Y CameraXViewfinder está diseñada para controlar por completo la complejidad, de modo que la vista previa de la cámara funcione sin problemas en todos los factores de forma.

  • Veamos cómo usar el elemento CameraXViewfinder que admite composición.

    var surfaceRequest by remember {
        mutableStateOf<SurfaceRequest?>(null)
    }
    
    LaunchedEffect(...) {
        preview.setSurfaceProvider { surfaceRequest = it }
        cameraProvider.bindToLifecycle(..., preview)
    }
    
    surfaceRequest?.let { request ->
        CameraXViewfinder(
            surfaceRequest = request,
            contentScale = ContentScale.Fit
        )
    }
  • Primero, declara la variable de contenido surfaceRequest para que contenga la solicitud de plataforma.

  • Lo siento.

  • En segundo lugar, en LaunchedEffect, configuras el proveedor de Surface para que capture un objeto de solicitud de Surface.

  • Luego, vinculas al ciclo de vida y, después, inicias la cámara.

  • Por último, cada vez que se actualice el estado con una solicitud de plataforma válida y se active la recomposición, establecerás la solicitud directamente en CameraXViewfinder.

  • Y aquí, puedes configurar el contentScale para que se ajuste o se recorte, según tus preferencias.

  • Eso es todo, ya está todo listo.

  • En CameraX 1.7, agregamos varias funciones nuevas a CameraXViewfinder.

  • Esto incluye gestos nuevos, como presionar para enfocar o pellizcar para hacer zoom.

  • También admite el flash de pantalla, que te permite usar una pantalla para iluminar tu selfie.

  • Implementar esta función es fácil.

    CameraXViewfinder(
        surfaceRequest = viewModel.surfaceRequest,
        contentScale = ContentScale.Fit,
        isTapToFocusEnabled = true,
        isPinchToZoomEnabled = true,
        onScreenFlashReady =
            {
               imageCapture.screenFlash = it
               imageCapture.flashMode = FLASH_MODE_SCREEN
            }
    )
  • Solo debes habilitar las opciones de presionar para enfocar y pellizcar para hacer zoom como verdaderas.

  • En el caso del flash de pantalla, obtienes la instancia de ScreenFlash de la devolución de llamada onScreenFlashReady y, luego, la pasas a ImageCapture y estableces un modo de flash en FLASH_MODE_SCREEN.

  • Eso es todo.

Captura de cámara con Android XR

  • Vimos cómo CameraX habilita la vista previa de la cámara que funciona sin problemas en todos los factores de forma.

  • Ahora, demos un paso más con una captura de cámara en Android XR.

  • Existen tres categorías de dispositivos de RE: primero, los visores de RE.

  • Son visores completamente cerrados y envolventes que usan una cámara para capturar el mundo físico y mostrarlo dentro de los visores.

  • Y, en segundo lugar, los lentes de RE con cable.

  • Hoy anunciamos el XREAL Aura en la presentación de esta categoría.

  • Como puedes ver en las presentaciones, estos son los lentes transparentes conectados al disco de procesamiento que te permiten ver la superposición de contenido virtual en el mundo real.

  • Y el tercero, lentes con audio y pantalla.

  • Estos son los anteojos livianos.

  • Pueden combinar audio, cámara y pantalla para brindar experiencias sin usar las manos y de un vistazo.

  • Y las dos primeras categorías, visor de RE y lentes de RE con cable, también admiten Camera2 y CameraX, como un teléfono normal.

  • Por lo tanto, hoy nos enfocaremos solo en los lentes con audio y con pantalla.

  • En estos lentes, las apps se ejecutan en el teléfono Android host, en lugar de en los lentes, pero aquí se accede tanto a la cámara del teléfono como a la de los lentes.

  • La pregunta clave es cómo sabes con qué cámara estás hablando.

  • Es simple.

  • Con el Context estándar, tu CameraX accede a la cámara del teléfono.

  • Y con ProjectedDeviceContext, CameraX accede a la cámara de los lentes.

  • Por lo tanto, un Context diferente generará una cámara de destino diferente.

  • Recuerda que los permisos de la cámara y de los lentes son independientes.

  • Los permisos para el teléfono y los lentes son independientes, por lo que debes solicitar el permiso para los lentes por separado.

  • La otra opción es usar la actividad proyectada, que también se ejecuta en los dispositivos Android host, pero la IU se proyecta en los lentes.

  • Y aquí la regla se invierte.

  • Si usas Context estándar, accederás a la cámara de los lentes.

  • Si quieres acceder a la cámara del teléfono, debes usar HostContext.

  • Y veamos el código, veamos cómo podemos capturar usando la cámara de los lentes.

    // In a coroutine
    val imageCapture = ImageCapture.Builder().build()
    
    ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
        .collectLatest { isConnected ->
            if (isConnected) {
                val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
                val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
                    .setRepeatingStreamForced(false)
                    .build()
                val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
                cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
            }
        }
    // Shutter button clicked
    imageCapture.takePicture(...)
  • Primero, invoca isProjectedDeviceConnected para ver si los lentes están conectados o no.

  • Luego, llamas a createProjectedDeviceContext para obtener un projectedContext.

  • En segundo lugar, aquí usas LifecycleCameraProvider para inicializar CameraX, en lugar de ProcessCameraProvider.

  • Por lo tanto, ProcessCameraProvider es un singleton en el que no puedes cambiar el contexto después de la inicialización.

  • Por lo tanto, usar LifecycleCameraProvider te permite cambiar el contexto en cualquier momento.

  • Luego, debes inhabilitar la transmisión repetida adicional en CameraX.

  • Esto es necesario porque los lentes actuales solo admiten un flujo.

  • Por el momento, la API es interna, pero pronto la haremos pública.

  • Eso es todo.

  • A partir de aquí, se usa CameraX estándar, solo vinculas ImageCapture y ahora capturas a través de los lentes.

Funciones y APIs de captura

  • Bien.

  • A continuación, veamos cómo CameraX te ayuda a encontrar la combinación de funciones que funcionan juntas y a aprovechar al máximo el dispositivo.

  • Las apps de cámara modernas se definen por funciones potentes, y los usuarios esperan que la cámara admita funciones como HDR, 60 FPS y estabilización de la vista previa, todo al mismo tiempo.

  • Sin embargo, como desarrolladores, sabemos que combinar estas funciones no siempre es sencillo.

  • Verificar la compatibilidad de las funciones de asistencia individuales no garantiza que funcionen juntas.

  • Esta incertidumbre obligó a los desarrolladores a ser conservadores y limitar lo que los usuarios pueden experimentar en dispositivos compatibles.

  • Para solucionar este problema, presentamos la API de Feature Group, que funciona con el framework de consultas de combinación de funciones de Camera2.

  • Existen dos maneras de usarla: primero, usa requiredFeatureGroup para verificar y habilitar la combinación de funciones cuando sea compatible.

  • En segundo lugar, puedes proporcionar la prioridad de tus funciones con preferredFeatureGroup y, luego, CameraX elegirá la mejor combinación compatible para ti.

  • Este es el ejemplo.

  • A la derecha, a la izquierda, perdón, sin habilitar nada, solo un teléfono normal, una grabación normal.

  • A la izquierda, habilitamos HDR, 60 FPS y la estabilización de la vista previa, todo junto.

  • Y puedes ver que la calidad mejora significativamente con un video más brillante, fluido y sin vibraciones.

  • Y veamos el código.

    cameraProvider.bindToLifecycle(
        lifecycleOwner,
        cameraSelector,
        SessionConfig(
            useCases = listOf(preview, videoCapture),
            // HDR_HLG10 > FPS_60 > Preview Stabilization.
            preferredFeatureGroup =
               listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
        )
    )
  • Por lo tanto, aquí usamos preferredFeatureGroup para proporcionar nuestra lista de prioridades de funciones, que es HDR, luego 60 FPS y, por último, la estabilización de la vista previa.

  • CameraX verificará la combinación y seleccionará las mejores opciones compatibles con el dispositivo.

  • Con CameraX 1.6 y versiones posteriores, agregamos la grabación en 4K y la estabilización de video directamente a la API de Feature Group.

    // CameraX 1.6+ supports:
    // 4K, video stabilization & Effect
    val sessionConfig = SessionConfig(
       useCases = listOf(preview, videoCapture),
       requiredFeatureGroup =
         setOf(UHD_RECORDING, VIDEO_STABILIZATION),
       effects = listOf(overlayEffect)
    )
  • Muchos dispositivos premium admiten la combinación de 4K y estabilización de video.

  • Ahora, con la API de Feature Group, puedes habilitarlas juntas con confianza.

  • Además, ahora tienes la flexibilidad de aplicar el efecto en tiempo real con la API de Feature Group, lo que antes no era posible.

  • Ahora, hablemos de cómo puedes obtener una captura de imágenes de alta calidad en CameraX.

  • CameraX ahora admite la captura de RAW en DNG.

  • Te brinda acceso a la imagen RAW sin procesar ni comprimir directamente desde el sensor de la cámara para que puedas realizar ediciones profesionales.

  • Además, mejoramos nuestra extensión de Modo nocturno.

  • Esta extensión permite capturar fotos nítidas y brillantes con poca luz sin usar el flash.

  • Además, presentamos una nueva API de indicador de modo nocturno.

  • Supervisará cuándo el entorno actual está oscuro y es adecuado para el modo nocturno, y puedes usarlo para mostrar el indicador del modo nocturno.

  • Además, Ultra HDR ahora es compatible con las extensiones de CameraX.

  • Esto aporta un rango dinámico más amplio y colores intensos al modo nocturno, que actualmente es compatible con Ultra HDR en muchos dispositivos premium recientes.

  • Aquí puedes ver la diferencia entre el modo normal, el modo nocturno y el modo nocturno con Ultra HDR.

  • Veamos cómo implementar esto.

    val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
            ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))
    
    val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)
    
    val imageCapture = ImageCapture.Builder().apply {
           if (imageCapabilities.supportedOutputFormats
                    .contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
               setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
           }
         }.build()
    
    val extensionSessionConfig = ExtensionSessionConfig(
        ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
    )
    
    cameraProvider.bindToLifecycle(...,  extensionSessionConfig)
  • Primero, crea un ExtensionSessionConfig para el modo nocturno y úsalo para obtener la instancia de CameraInfo.

  • Luego, usa CameraInfo para obtener la capacidad de captura de imágenes y, luego, consulta si se admite Ultra HDR.

  • Luego, establece el formato de salida en Ultra HDR.

  • Por último, vuelve a crear el ExtensionSessionConfig con los casos de uso y vincúlalo al ciclo de vida.

  • Ahora todo está listo para que captures fotos con el modo nocturno y Ultra HDR.

  • También incorporamos la compatibilidad con Fotos en movimiento a CameraX.

  • Las fotos en movimiento capturan el sonido y el movimiento alrededor de la foto, lo que preserva el momento, no solo una imagen estática.

  • Estandarizamos esto en diferentes teléfonos y dispositivos Android, y facilitamos la compatibilidad con las fotos en movimiento.

  • Las fotos en movimiento llegarán a CameraX en el primer trimestre de 2027.

  • Una vez que esté disponible, simplemente configura el modo de Fotos en movimiento en ImageCapture.Builder y, luego, podrás comenzar a capturar fotos en movimiento.

    val imageCapture = ImageCapture.Builder()
            .setMotionPhotoMode(
                 ImageCapture.MOTION_PHOTO_MODE_ON,
                 customConfig
             )
            .build()
    
    cameraProvider.bindToLifecycle(.. imageCapture)
    imageCapture.takePicture(...)
  • A continuación, exploremos cómo CameraX habilita la grabación de video de alta velocidad.

  • Grabar acciones de movimiento rápido con claridad es una función esencial de las apps de cámara modernas.

  • Es especialmente útil para situaciones de ritmo rápido, como los deportes.

  • Esto se logra a través de la captura de alta velocidad a 120 o 240 fps.

  • Existen dos maneras de usarlo.

  • Primero, codifica con la misma velocidad de fotogramas y, luego, crea un video con una velocidad de fotogramas alta real.

  • La otra opción es codificarlo a 30 fps para crear un video en cámara lenta listo para reproducir.

  • Por ejemplo, un segundo grabado a 120 fps tarda cuatro segundos en reproducirse a 30 fps.

  • Por lo tanto, este estiramiento del tiempo crea un efecto de cámara lenta.

  • Para implementar la grabación de alta velocidad, crea un HighSpeedVideoSessionConfig con tu videoCapture.

    val recorder = Recorder.Builder().build()
    val videoCapture = VideoCapture.withOutput(recorder)
    
    val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
           HighSpeedVideoSessionConfig(videoCapture, preview)).first()
    
    val sessionConfig = HighSpeedVideoSessionConfig(
        videoCapture,
        preview,
        frameRateRange = frameRateRange,
        isSlowMotionEnabled = true
    )
    
    cameraProvider.bindToLifecycle(
         lifecycleOwner, cameraSelector, sessionConfig)
    
    // Start recording slow motion videos.
    val recording = recorder.prepareRecording(context, outputOption)
          .start(executor, {})
  • Luego, establece isSlowMotionEnabled en verdadero para codificarlo a 30 fps y crear un video en cámara lenta, o establécelo en falso para crear un video con una velocidad de fotogramas alta.

  • Y cuando se habilita la cámara lenta, Google Fotos la identifica automáticamente y agrega una insignia de cámara lenta a la miniatura.

  • Veamos cómo funciona en Google Fotos.

  • Aquí, la cámara lenta le da un efecto dramático al gato.

  • Mi gato nunca quiere que lo toque.

  • Lo siento.

  • Por último, veamos algunas mejoras en nuestra API de Concurrent Camera.

  • La API de Concurrent permite que tu app transmita desde la cámara frontal y la posterior de forma simultánea.

  • Este año, presentamos algunas mejoras en la API de Concurrent Camera.

  • Puedes actualizar de forma dinámica la composición de la cámara después de vincularla al ciclo de vida y abrirla.

  • Esto incluye cambiar la posición, el tamaño y el orden de renderización.

  • También puedes agregar estilos, como esquinas redondeadas, o el ancho y el color del borde.

  • Y aquí te mostramos cómo hacerlo: solo usa CompositionSettings para configurar el diseño, como el redondeo de las esquinas, el ancho del borde y el color, y usa setZOrder para ajustar el orden de renderización.

    val concurrentCamera =
            cameraProvider.bindToLifecycle(list(config1, config2))
    // CameraX 1.7 features: rounded corner, border width & color
    val pipSettings = CompositionSettings.Builder()
        .setOffset(0.2f, 0.2f)
        .setScale(0.35f, 0.35f)
        .setRoundedCornerRatio(0.15f)
        .setBorderWidthRatio(0.02f)
        .setBorderColor(Color.WHITE)
        .setZOrder(1)
        .build()
    // CameraX 1.7 features: dynamical composition update
    concurrentCamera.setCompositionSettings(
    listOf(mainSettings, pipSettings))
  • Y, luego, solo debes invocar el setCompositionSettings en la cámara simultánea activa para actualizar la composición de forma dinámica.

  • Eso es todo.

  • Ya puedes actualizar la composición de forma dinámica.

  • Bien.

  • Ya revisamos todas las funciones.

  • Tenemos un aviso importante aquí, por lo que te recomendamos que actualices a CameraX 1.6 y todas las versiones posteriores.

  • Esto puede evitar fallas en los próximos dispositivos que admitan el nuevo formato, Eclipsa Video, y también garantizar que tu extensión de CameraX siga funcionando en la futura actualización de funciones de Pixel.

  • Con esto, finalizamos nuestra primera sesión.

  • Ya vimos las funciones de CameraX que te ayudan a crear una experiencia de cámara de nivel profesional. Ahora, me gustaría invitar a Aman para que hable sobre la IA integrada en el dispositivo en nuestra próxima sesión.

Aprendizaje automático en el dispositivo

  • Gracias, Scott, por explicarnos las funciones de CameraX.

  • Ahora, veamos cómo puedes aprovechar el aprendizaje automático integrado en el dispositivo para crear experiencias de cámara inteligentes.

  • El aprendizaje automático integrado en el dispositivo habilita una amplia variedad de casos de uso de la cámara en dos áreas principales.

  • Primero, está la captura previa.

  • Aquí, puedes analizar la vista previa de la cámara en tiempo real para realizar acciones como la detección de objetos o mejorar la vista previa en sí.

  • La segunda es la poscaptura.

  • Una vez que hayas capturado la imagen, puedes mejorar su calidad o realizar acciones como generar una descripción para ella.

  • Ahora, analicemos algunas de las soluciones de aprendizaje automático integrado en el dispositivo que tenemos para tus apps de cámara.

  • La primera biblioteca de AA integrado en el dispositivo de la que hablaremos es el Kit de AA.

  • ML Kit se creó principalmente para resolver problemas comunes en los dominios de visión y lenguaje natural, como el reconocimiento de texto, la detección de rostros y la traducción de idiomas.

  • El año pasado, presentamos el ML Kit.

  • Estos son algunos ejemplos de las APIs de Vision de ML Kit, como la detección de malla facial, la segmentación de sujetos y la detección de objetos en tiempo real.

  • Sí.

  • El año pasado, presentamos las APIs de IA generativa.

  • Estas APIs aprovechan la potencia de Gemini Nano para habilitar funciones como el resumen de texto, la descripción de imágenes y mucho más.

  • Este es un ejemplo de descripción de imagen con la API de Prompt.

  • Pasamos el mapa de bits de la imagen con la instrucción de texto al modelo, y la descripción del resultado se lee como un pequeño robot de Android verde con un diseño similar al de un cactus que se encuentra sobre una superficie negra.

  • Y todo esto sucede en el dispositivo.

  • Bastante genial, ¿no?

  • Adobe Photoshop usa la API de Prompt para mejorar la experiencia del usuario en su app.

  • Cuando editan recursos, los usuarios suelen tener que administrar varias capas en la app de Photoshop.

  • Con la API de Prompt, ahora la app puede cambiar automáticamente el nombre de las capas desorganizadas.

  • La app también usa la API de Prompt para generar leyendas y hashtags para las imágenes, lo que es muy útil para compartir en redes sociales.

  • Veamos el código para la descripción de imágenes con la API de Prompt.

    // 1. Initialize the on-device GenerativeModel client
    val generativeModel = Generation.getClient()
    
    // 2. Check whether Gemini Nano is available on the device
    val status = generativeModel.checkStatus()
    when (status) {
        FeatureStatus.UNAVAILABLE -> {}
        FeatureStatus.DOWNLOADABLE -> {}
        FeatureStatus.DOWNLOADING -> {}
        FeatureStatus.AVAILABLE -> {}
    }
  • Primero, necesitas un objeto cliente generativeModel y, luego, verificar si Gemini Nano está disponible o se puede descargar en tu dispositivo.

  • Luego, genera una solicitud de contenido combinando la imagen y la instrucción de texto y, por último, proporciona esta solicitud a la llamada a generateContent para ejecutar la inferencia en Gemini Nano y obtener la descripción de la imagen.

    // 3. Build a multimodal request with the image and text prompt
    val request = generateContentRequest(
        ImagePart(bitmap),
        TextPart("Describe this image in detail.")
    ) {}
    
    // 4. Run inference on-device and get the description
    val response = generativeModel.generateContent(request)
    val description = response.candidates.first().text
  • Sí, la siguiente biblioteca de AA integrado en el dispositivo que tenemos es MediaPipe.

  • MediaPipe ofrece una amplia variedad de soluciones de aprendizaje automático para tus aplicaciones.

  • Puedes visitar rápidamente el sitio web de MediaPipe para probar diferentes muestras, como detección de rostros, detección de objetos, segmentación de imágenes y mucho más.

  • Todos estos casos de uso suenan muy similares a ML Kit, por lo que, ¿qué biblioteca deberías usar?

  • Si bien MediaPipe y el ML Kit resuelven problemas similares, existen algunas diferencias clave.

  • Primero, la compatibilidad con plataformas.

  • MediaPipe funciona en Android, iOS, la Web y Python, mientras que ML Kit se enfoca solo en Android y iOS.

  • Además, MediaPipe ofrece personalización de modelos, mientras que el ML Kit proporciona APIs listas para usar.

  • Por último, mientras que MediaPipe es de código abierto, el ML Kit es un SDK de Google.

  • Este es un ejemplo de reconocimiento de gestos con las APIs de MediaPipe Vision.

  • Esta API detecta los gestos con la mano en tiempo real a partir de la entrada de cámara.

  • Esto se podría usar, por ejemplo, para iniciar o detener la grabación de la cámara con gestos simples con la mano mientras estás a una distancia del teléfono.

  • Veamos el código.

  • Por lo tanto, primero debes compilar el objeto GestureRecognizerOptions, proporcionando la ruta de acceso del modelo, el modo de transmisión en vivo y un objeto de escucha de resultados.

    // 1. Build GestureRecognizerOptions
    val options = GestureRecognizerOptions.builder().setBaseOptions(
        BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
        .setRunningMode(RunningMode.LIVE_STREAM)
        .setResultListener { result, image -> /* Handle detected gesture */ }
        .build()
    
    // 2. Create the GestureRecognizer
    val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)
    
    // 3. Convert CameraX ImageProxy to MediaPipe MPImage
    val bitmap = rotateBitmap(imageProxy.toBitmap(),
    imageProxy.imageInfo.rotationDegrees)
    val mpImage = BitmapImageBuilder(bitmap).build()
    
    // 4. Run gesture recognition on the image
    gestureRecognizer.recognizeAsync(mpImage, frameTime)
  • Luego, crea un objeto de reconocimiento de gestos a partir de las opciones.

  • Luego, obtén el ImageProxy de CameraX, conviértelo en un mapa de bits y úsalo para generar un objeto mpImage de MediaPipe.

  • Por último, debes proporcionar el objeto mpImage de MediaPipe a la llamada a recognizeAsync para detectar gestos en tiempo real.

Mejora de imágenes y videos

  • A continuación, tenemos la mejora de imágenes y videos integrada en el dispositivo, disponible a través de la API de los Servicios de Google Play.

  • Este modelo proporciona funciones como asignación de tonos, eliminación de desenfoque y superresolución en la mayoría de los dispositivos premium.

  • Este es un ejemplo de una imagen con asignación de tono y mejora de resolución con este modelo.

  • Puedes ver claramente los colores más intensos en el cielo y el mar, y una mayor nitidez en la imagen mejorada.

  • Veamos el código para la mejora de imágenes.

    // Step 1: Verify device support & install module
    val enhancementClient = Enhancement.getClient(context)
    
    suspend fun prepare(): Boolean {
        if (!enhancementClient.isDeviceSupported().await()) return false
        if (!enhancementClient.isModuleInstalled().await()) {
            check(enhancementClient.installModule().await()) {
                "Enhancement module install failed"
            }
        }
        return true
    }
  • Primero, debes consultar si el dispositivo subyacente lo admite o no y, si es necesario, debes instalar el módulo de mejora en tu dispositivo.

  • Luego, crea el objeto EnhancementOptions especificando el mapa de bits y las mejoras que deseas habilitar.

    // Step 2: Select Enhancement options & create session
    val options = EnhancementOptions(
        width = bitmap.width,
        height = bitmap.height,
        enhancementMode = EnhancementMode.BITMAP,
        isTonemappingEnabled = true,
        isDeblurAndDenoisePhotoEnabled = true,
        isDeblurAndDenoiseVideoEnabled = false,
        isUpscalePhotoEnabled = false,
        isUpscaleVideoEnabled = false
    )
    
    val enhancementSession = enhancementClient
        .createSessionAsync(options, enhancementExecutor)
  • Luego, crea una sesión de cliente con la API de los Servicios de Google Play, define una devolución de llamada para controlar el mapa de bits resultante y, por último, llama a la función de procesamiento con el mapa de bits y las opciones para mejorar la imagen.

    // Step 3: Process Enhancement
    val enhancedBitmap = suspendCancellableCoroutine { continuation ->
        val enhancementCallback = object : EnhancementCallback {
            override fun onBitmapProcessed(result: Bitmap) {
                if (continuation.isActive) continuation.resume(result)
            }
            override fun onError(statusCode: Int) {
                if (continuation.isActive) {
                    continuation.resumeWithException(
                        Exception("Bitmap processing failed: $statusCode")
                    )
                }
            }
            override fun onSurfaceProcessed(timestamp: Long) {}
        }
    
        enhancementSession.process(bitmap, options, enhancementCallback)
    }
  • Recientemente, integramos las APIs de mejora de video en Jetpack Media3.

  • Puedes consultar nuestra charla sobre contenido multimedia para obtener más información sobre las APIs de mejora de video e imágenes.

  • La última función de AA integrado en el dispositivo que abordaremos es la Mejora con poca luz.

  • Capturar imágenes con poca luz es un problema común para los usuarios de smartphones.

  • Piensa en los usuarios que capturan imágenes o realizan videollamadas en entornos con poca luz.

  • La función de Iluminación con poca luz resuelve este problema, ya que aumenta el brillo de la vista previa de la cámara en tiempo real.

  • Hay dos variantes de la función Optimización con poca luz: de hardware y de software.

  • La versión de hardware usa el hardware del OEM para mejorar los fotogramas con poca luz sin usar el aprendizaje automático.

  • En cambio, la versión de software usa un modelo de AA integrado en el dispositivo para mejorar los fotogramas.

  • Veamos rápidamente cómo podemos integrar CameraX con la función de mejora con poca luz de software, que también se conoce como mejora con poca luz de Google.

  • La función de Google para mejorar la iluminación requiere dos superficies clave: primero, necesitas una superficie de entrada para los fotogramas sin procesar de la cámara y una superficie de destino de salida para los fotogramas de salida mejorados.

  • Para integrarlo con CameraX, simplemente une la canalización de Google Low Light Boost dentro de un CameraEffect.

  • CameraX controla automáticamente el ciclo de vida de la superficie y el enrutamiento sin problemas.

  • Por último, lanzaremos el efecto de mejora con poca luz en CameraX en el cuarto trimestre de 2026.

    // Integrates SW Low Light Boost
    val lowLightBoostEffect =
        LowLightBoostEffect(
            CameraEffect.PREVIEW or
            CameraEffect.VIDEO_CAPTURE or
            CameraEffect.IMAGE_CAPTURE
        )
    
    val sessionConfig = SessionConfig(
        useCases = listOf(preview, imageCapture),
        effects = lowLightBoostEffect
    )
    
    cameraProvider.bindToLifecycle(.. sessionConfig)
  • Con esto, podrías crear un objeto de Low Light Boost y agregarlo a tu configuración de sesión, luego vincular la configuración de sesión al ciclo de vida de la cámara y funcionará en los dispositivos compatibles.

Resumen y migración

  • Con esto, llegamos al final de ambas secciones.

  • Resumamos rápidamente todo lo que vimos hoy.

  • Hoy hablamos sobre cómo puedes mejorar tus experiencias con la cámara en todos los dispositivos con CameraX y el aprendizaje automático integrado en el dispositivo.

  • Primero, con CameraXViewfinder, puedes habilitar las vistas previas de la cámara para que se adapten a diferentes factores de forma.

  • Luego, vimos que CameraX también te permite capturar contenido en dispositivos de RE.

  • Luego, la API de Feature Group te ayuda a encontrar la combinación óptima de funciones de hardware.

  • También exploramos las actualizaciones de la Captura de imágenes y aprendimos sobre la grabación de video de alta velocidad para videos en cámara lenta.

  • Por último, aprendimos a mejorar la vista previa de la cámara simultánea de forma dinámica.

  • Luego, analizamos algunas de las soluciones de aprendizaje automático integradas en el dispositivo, como ML Kit y MediaPipe, para crear experiencias de visión inteligentes.

  • Por último, vimos la mejora de imágenes y videos integrada en el dispositivo, junto con la función de mejora con poca luz para iluminar la vista previa de la cámara en tiempo real.

  • Como se mencionó anteriormente, planeamos inhabilitar Camera1, por lo que te recomendamos que migres a CameraX.

  • Para respaldar esta migración, recientemente lanzamos la skill de CameraX.

  • También tenemos una guía de migración y el repositorio de muestras de la cámara para guiarte.

  • Puedes visitar este vínculo para acceder a más recursos sobre las funciones que abordamos hoy.

  • Incluye vínculos a la documentación, el blog y los recursos de migración, todo en un solo lugar.

  • Me tomaré un segundo, haré una pausa.

  • Bien.

  • Gracias a todos.

  • Con esto, llegamos al final de nuestra charla.

Código

Fragmentos de código

Fragmento de Kotlin en el minuto 04:18 de "Funciones del visor adaptable"
var surfaceRequest by remember {
    mutableStateOf<SurfaceRequest?>(null)
}

LaunchedEffect(...) {
    preview.setSurfaceProvider { surfaceRequest = it }
    cameraProvider.bindToLifecycle(..., preview)
}

surfaceRequest?.let { request ->
    CameraXViewfinder(
        surfaceRequest = request,
        contentScale = ContentScale.Fit
    )
}
Fragmento de Kotlin en el minuto 05:14 de "Funciones del visor adaptable"
CameraXViewfinder(
    surfaceRequest = viewModel.surfaceRequest,
    contentScale = ContentScale.Fit,
    isTapToFocusEnabled = true,
    isPinchToZoomEnabled = true,
    onScreenFlashReady =
        {
           imageCapture.screenFlash = it
           imageCapture.flashMode = FLASH_MODE_SCREEN
        }
)
Fragmento de Kotlin en el minuto 07:30 de "Captura de cámara con Android XR"
// In a coroutine
val imageCapture = ImageCapture.Builder().build()

ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
    .collectLatest { isConnected ->
        if (isConnected) {
            val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
            val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
                .setRepeatingStreamForced(false)
                .build()
            val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
            cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
        }
    }
// Shutter button clicked
imageCapture.takePicture(...)
Fragmento de Kotlin en el minuto 09:42 de "Funciones y APIs de captura"
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    cameraSelector,
    SessionConfig(
        useCases = listOf(preview, videoCapture),
        // HDR_HLG10 > FPS_60 > Preview Stabilization.
        preferredFeatureGroup =
           listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
    )
)
Fragmento de Kotlin en el minuto 9:58 de "Funciones y APIs de captura"
// CameraX 1.6+ supports:
// 4K, video stabilization & Effect
val sessionConfig = SessionConfig(
   useCases = listOf(preview, videoCapture),
   requiredFeatureGroup =
     setOf(UHD_RECORDING, VIDEO_STABILIZATION),
   effects = listOf(overlayEffect)
)
Fragmento de Kotlin en el minuto 11:20 de "Funciones y APIs de captura"
val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
        ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))

val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)

val imageCapture = ImageCapture.Builder().apply {
       if (imageCapabilities.supportedOutputFormats
                .contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
           setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
       }
     }.build()

val extensionSessionConfig = ExtensionSessionConfig(
    ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
)

cameraProvider.bindToLifecycle(...,  extensionSessionConfig)
Fragmento de Kotlin en el minuto 12:11 de "Funciones y APIs de captura"
val imageCapture = ImageCapture.Builder()
        .setMotionPhotoMode(
             ImageCapture.MOTION_PHOTO_MODE_ON,
             customConfig
         )
        .build()

cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)
Fragmento de Kotlin en el minuto 13:06 de "Funciones y APIs de captura"
val recorder = Recorder.Builder().build()
val videoCapture = VideoCapture.withOutput(recorder)

val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
       HighSpeedVideoSessionConfig(videoCapture, preview)).first()

val sessionConfig = HighSpeedVideoSessionConfig(
    videoCapture,
    preview,
    frameRateRange = frameRateRange,
    isSlowMotionEnabled = true
)

cameraProvider.bindToLifecycle(
     lifecycleOwner, cameraSelector, sessionConfig)

// Start recording slow motion videos.
val recording = recorder.prepareRecording(context, outputOption)
      .start(executor, {})
Fragmento de Kotlin en el minuto 14:18 de "Funciones y APIs de captura"
val concurrentCamera =
        cameraProvider.bindToLifecycle(list(config1, config2))
// CameraX 1.7 features: rounded corner, border width & color
val pipSettings = CompositionSettings.Builder()
    .setOffset(0.2f, 0.2f)
    .setScale(0.35f, 0.35f)
    .setRoundedCornerRatio(0.15f)
    .setBorderWidthRatio(0.02f)
    .setBorderColor(Color.WHITE)
    .setZOrder(1)
    .build()
// CameraX 1.7 features: dynamical composition update
concurrentCamera.setCompositionSettings(
listOf(mainSettings, pipSettings))
Fragmento de Kotlin en el minuto 17:51 de "aprendizaje automático integrado en el dispositivo"
// 1. Initialize the on-device GenerativeModel client
val generativeModel = Generation.getClient()

// 2. Check whether Gemini Nano is available on the device
val status = generativeModel.checkStatus()
when (status) {
    FeatureStatus.UNAVAILABLE -> {}
    FeatureStatus.DOWNLOADABLE -> {}
    FeatureStatus.DOWNLOADING -> {}
    FeatureStatus.AVAILABLE -> {}
}
Fragmento de Kotlin en el minuto 18:06 de "Aprendizaje automático integrado en el dispositivo"
// 3. Build a multimodal request with the image and text prompt
val request = generateContentRequest(
    ImagePart(bitmap),
    TextPart("Describe this image in detail.")
) {}

// 4. Run inference on-device and get the description
val response = generativeModel.generateContent(request)
val description = response.candidates.first().text
Fragmento de Kotlin en el minuto 19:38 de "aprendizaje automático integrado en el dispositivo"
// 1. Build GestureRecognizerOptions
val options = GestureRecognizerOptions.builder().setBaseOptions(
    BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setResultListener { result, image -> /* Handle detected gesture */ }
    .build()

// 2. Create the GestureRecognizer
val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)

// 3. Convert CameraX ImageProxy to MediaPipe MPImage
val bitmap = rotateBitmap(imageProxy.toBitmap(),
imageProxy.imageInfo.rotationDegrees)
val mpImage = BitmapImageBuilder(bitmap).build()

// 4. Run gesture recognition on the image
gestureRecognizer.recognizeAsync(mpImage, frameTime)
Fragmento de Kotlin en el minuto 20:44 de "Mejora de imágenes y videos"
// Step 1: Verify device support & install module
val enhancementClient = Enhancement.getClient(context)

suspend fun prepare(): Boolean {
    if (!enhancementClient.isDeviceSupported().await()) return false
    if (!enhancementClient.isModuleInstalled().await()) {
        check(enhancementClient.installModule().await()) {
            "Enhancement module install failed"
        }
    }
    return true
}
Fragmento de Kotlin en el minuto 20:57 de "Mejora de imágenes y videos"
// Step 2: Select Enhancement options & create session
val options = EnhancementOptions(
    width = bitmap.width,
    height = bitmap.height,
    enhancementMode = EnhancementMode.BITMAP,
    isTonemappingEnabled = true,
    isDeblurAndDenoisePhotoEnabled = true,
    isDeblurAndDenoiseVideoEnabled = false,
    isUpscalePhotoEnabled = false,
    isUpscaleVideoEnabled = false
)

val enhancementSession = enhancementClient
    .createSessionAsync(options, enhancementExecutor)
Fragmento de Kotlin en el minuto 21:05 de "Mejora de imágenes y videos"
// Step 3: Process Enhancement
val enhancedBitmap = suspendCancellableCoroutine { continuation ->
    val enhancementCallback = object : EnhancementCallback {
        override fun onBitmapProcessed(result: Bitmap) {
            if (continuation.isActive) continuation.resume(result)
        }
        override fun onError(statusCode: Int) {
            if (continuation.isActive) {
                continuation.resumeWithException(
                    Exception("Bitmap processing failed: $statusCode")
                )
            }
        }
        override fun onSurfaceProcessed(timestamp: Long) {}
    }

    enhancementSession.process(bitmap, options, enhancementCallback)
}
Fragmento de Kotlin en el minuto 22:51 de "Mejora de imágenes y videos"
// Integrates SW Low Light Boost
val lowLightBoostEffect =
    LowLightBoostEffect(
        CameraEffect.PREVIEW or
        CameraEffect.VIDEO_CAPTURE or
        CameraEffect.IMAGE_CAPTURE
    )

val sessionConfig = SessionConfig(
    useCases = listOf(preview, imageCapture),
    effects = lowLightBoostEffect
)

cameraProvider.bindToLifecycle(.. sessionConfig)