Apps foundation

Melhorando as experiências de câmera em vários dispositivos com o CameraX e a inteligência no dispositivo

Alto-falantes

Sobre

Sobre a palestra

Os usuários esperam que os apps de terceiros capturem fotos e vídeos com a mesma alta fidelidade da câmera nativa do dispositivo, independente das condições de iluminação ou do nível de hardware. Oferecer essa consistência no ecossistema Android em expansão envolve considerações importantes sobre a arquitetura. Esta sessão demonstra como criar pipelines de captura robustos e profissionais que maximizam os recursos do dispositivo e permanecem confiáveis em todos os formatos.

Vamos explorar os avanços mais recentes da CameraX, mostrando como integrar recursos de ponta, como fotos com movimento e ajustes com IA no dispositivo, usando o Google Play Services, além de experiências espaciais com o SDK do Jetpack para XR. Também vamos analisar nossa habilidade do CameraX, que permite que o agente de programação de sua escolha implemente automaticamente os conceitos desta palestra e muito mais.

Resumo

  • O Jetpack CameraX é a biblioteca de câmera recomendada para apps Android, já que o Camera1 foi descontinuado. As restrições da política do Google Play vão começar a desativar a Camera1 no próximo ano no Android 18.
  • O CameraXViewfinder adapta as prévias da câmera em diferentes formatos, como dispositivos dobráveis. Na versão 1.7, ela adiciona controles por gestos e suporte ao flash da tela.
  • Os óculos de áudio e tela executam apps em um smartphone host enquanto expõem as câmeras nos dois dispositivos. Transmitir ProjectedDeviceContext permite que o CameraX capture imagens diretamente dos óculos.
  • A API Feature Group ajuda os apps a selecionar combinações compatíveis de recursos de câmera. A partir da versão 1.6, a CameraX oferece suporte à combinação de gravação em 4K com estabilização de vídeo.
  • O machine learning no dispositivo analisa as visualizações da câmera e as mídias capturadas usando o Kit de ML e o MediaPipe. Os desenvolvedores podem executar tarefas que vão desde a detecção de gestos em tempo real até descrições de imagens generativas.

Recursos

Gemini Notebook

Tópicos

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

Transcrição

Introdução e visão geral da CameraX

  • Olá!

  • A sessão já vai começar!

  • Meu nome é Aman, e este é o Scott.

  • Hoje, vamos falar sobre como você pode melhorar as experiências de câmera em vários dispositivos com o CameraX e a inteligência no dispositivo.

  • Vamos começar!

  • Pense em como você usa seus smartphones.

  • Muitas vezes, usamos a câmera do celular para registrar momentos e compartilhar com as pessoas que amamos.

  • A câmera se tornou central na experiência com o smartphone.

  • Na verdade, uma boa câmera costuma ser um fator decisivo na hora de comprar um smartphone.

  • Hoje, vamos abordar dois temas amplos para criar experiências de câmera premium.

  • Primeiro, vamos conhecer alguns recursos do CameraX e, em seguida, algumas técnicas de machine learning no dispositivo.

  • Vamos começar com a CameraX.

  • O Jetpack CameraX é responsável pela experiência da câmera no Android.

  • Além do CameraX, também temos o Camera1 e o Camera2, que fazem parte do framework da plataforma Android.

  • Então, qual biblioteca você deve usar?

  • A Camera1 foi a API de câmera original do Android.

  • Ela foi descontinuada, então os novos apps precisam usar a Camera2 ou a CameraX.

  • A Camera2 sucedeu a Camera1.

  • Portanto, se você precisa de controle de câmera de baixo nível para casos de uso complexos, a Camera2 é uma boa opção.

  • No entanto, é necessário lidar com as configurações específicas do dispositivo por conta própria.

  • Por fim, o Jetpack CameraX é baseado na Camera2.

  • Ela fornece uma API consistente e processa automaticamente as configurações específicas do dispositivo.

  • É a opção recomendada para a maioria dos desenvolvedores.

  • Temos um aviso muito importante.

  • Planejamos desativar a Camera1, e isso vai começar com as restrições da política do Google Play no próximo ano, no Android 18.

  • Se você ainda usa a API Camera1 legada, migre para o CameraX ou o Camera2.

  • Então, por que usar a CameraX?

  • Primeiro, ele é compatível com o Android 7 e versões mais recentes, o que o torna compatível com quase 99% dos dispositivos Android ativos.

  • Em segundo lugar, ele é fácil de usar e oferece APIs simples para casos de uso comuns, como visualização, captura e gravação com menos código.

  • Em terceiro lugar, ela oferece uma experiência consistente em todos os dispositivos, reduzindo problemas específicos, como o alongamento da visualização, que é bastante comum em dobráveis e tablets.

  • Por fim, ele simplifica a implementação de recursos avançados de OEM, como o modo noturno e o modo retrato.

  • Agora, gostaria de convidar o Scott para falar sobre alguns dos recursos do CameraX.

  • Agora é com você, Scott.

  • Obrigado, Aman.

  • Olá!

  • Meu nome é Scott.

  • Hoje, vamos falar sobre vários recursos da CameraX para que você possa criar uma experiência de câmera de nível profissional.

Recursos do Visor Adaptável

  • O primeiro é o Visor Adaptável.

  • Ele foi projetado para eliminar completamente as dificuldades de lidar com a visualização em diferentes formatos e tamanhos de tela.

  • Os usuários agora têm uma variedade de dispositivos, incluindo dobráveis, smartphones, laptops e dispositivos XR.

  • Por isso, é mais importante do que nunca criar uma experiência de câmera adaptada aos novos formatos.

  • E a visualização da câmera é notoriamente difícil de acertar, especialmente em dispositivos dobráveis widescreen.

  • Nesses dispositivos, quando abertos, a câmera interna é montada horizontalmente, enquanto a traseira é montada verticalmente.

  • Portanto, se você presumir que todos os sensores de detecção da câmera são montados verticalmente, isso vai levar a uma incompatibilidade espacial e causar distorção ou inclinação.

  • E o CameraXViewfinder foi projetado para lidar completamente com a complexidade para você, garantindo que a visualização da câmera funcione perfeitamente em todos os formatos.

  • Vamos ver como usar o elemento combinável CameraXViewfinder.

    var surfaceRequest by remember {
        mutableStateOf<SurfaceRequest?>(null)
    }
    
    LaunchedEffect(...) {
        preview.setSurfaceProvider { surfaceRequest = it }
        cameraProvider.bindToLifecycle(..., preview)
    }
    
    surfaceRequest?.let { request ->
        CameraXViewfinder(
            surfaceRequest = request,
            contentScale = ContentScale.Fit
        )
    }
  • Primeiro, declare a variável de conteúdo surfaceRequest para armazenar a solicitação de superfície.

  • Entendi.

  • Em segundo lugar, em LaunchedEffect, você define o provedor de superfície para capturar um objeto de solicitação de superfície.

  • Em seguida, vincule ao ciclo de vida e inicie a câmera.

  • Por fim, sempre que o estado for atualizado com uma solicitação de superfície válida e acionar a recomposição, defina a solicitação diretamente para o CameraXViewfinder.

  • E aqui, você pode definir o contentScale para ajustar ou cortar, dependendo da sua preferência.

  • Pronto, agora está tudo certo.

  • Na CameraX 1.7, adicionamos vários recursos novos ao CameraXViewfinder.

  • Isso inclui novos gestos, como tocar para focar ou fazer gesto de pinça para dar zoom.

  • Ele também é compatível com o flash da tela, que permite usar uma tela para iluminar sua selfie.

  • É fácil implementar esse recurso.

    CameraXViewfinder(
        surfaceRequest = viewModel.surfaceRequest,
        contentScale = ContentScale.Fit,
        isTapToFocusEnabled = true,
        isPinchToZoomEnabled = true,
        onScreenFlashReady =
            {
               imageCapture.screenFlash = it
               imageCapture.flashMode = FLASH_MODE_SCREEN
            }
    )
  • Basta ativar o toque para focar e o gesto de pinça para dar zoom.

  • Para o flash da tela, você recebe a instância do flash da tela do callback onScreenFlashReady, transmite para o ImageCapture e define um modo de flash como FLASH_MODE_SCREEN.

  • Pronto.

Captura de câmera com o Android XR

  • Vimos como o CameraX permite uma prévia da câmera que funciona perfeitamente em todos os formatos.

  • Agora vamos dar um passo adiante com uma captura de câmera no Android XR.

  • Existem três categorias de dispositivos XR: primeiro, os headsets XR.

  • São headsets totalmente fechados e imersivos que usam uma câmera para capturar o mundo físico e exibir dentro do headset.

  • E, em segundo lugar, os óculos XR com fio.

  • Anunciamos o XREAL Aura na palestra de hoje nessa categoria.

  • Como você pode ver nas palestras, esses são os óculos transparentes conectados ao disco de computação que permitem visualizar o conteúdo virtual sobreposto ao mundo real.

  • E o terceiro, óculos com áudio e tela virtual.

  • Estes são os óculos leves.

  • Eles podem combinar áudio, câmera e tela para experiências viva-voz e de fácil visualização.

  • As duas primeiras categorias, headset XR e óculos XR com fio, também são compatíveis com Camera2 e CameraX, assim como um smartphone comum.

  • Por isso, hoje vamos focar apenas nos óculos com áudio e com tela virtual.

  • Nesses óculos, os apps são executados no smartphone Android host, em vez de nos óculos, mas aqui há acesso à câmera do smartphone e à câmera dos óculos.

  • A questão principal é: como saber com qual câmera você está falando?

  • É simples.

  • Com o Context padrão, o CameraX acessa a câmera do smartphone.

  • E com o ProjectedDeviceContext, o CameraX acessa a câmera dos óculos.

  • Portanto, contextos diferentes levam a câmeras de destino diferentes.

  • E lembre-se de que as permissões da câmera e dos óculos são separadas.

  • As permissões do smartphone e dos óculos são separadas, então você precisa solicitar a permissão dos óculos separadamente.

  • A outra opção é usar a atividade projetada, que também é executada nos dispositivos Android host, mas a interface é projetada nos óculos.

  • E aqui a regra muda.

  • Se você estiver usando o Contexto padrão, vai acessar a câmera dos óculos.

  • Se você quiser acessar a câmera do smartphone, use o HostContext.

  • Vamos analisar o código e ver como capturar usando a câmera dos óculos.

    // 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(...)
  • Primeiro, invoque isProjectedDeviceConnected para conferir se os óculos estão conectados ou não.

  • Em seguida, chame createProjectedDeviceContext para receber um projectedContext.

  • Em segundo lugar, aqui você usa o LifecycleCameraProvider para inicializar o CameraX, em vez do ProcessCameraProvider.

  • O ProcessCameraProvider é um singleton, então não é possível mudar o contexto depois da inicialização.

  • Portanto, o uso do LifecycleCameraProvider permite que você mude o contexto quando quiser.

  • Em seguida, desative o fluxo de repetição adicional no CameraX.

  • Isso é necessário porque os óculos atuais só aceitam um único stream.

  • No momento, a API é interna, mas será disponibilizada ao público em breve.

  • Pronto.

  • A partir daqui, é o CameraX padrão. Basta vincular o ImageCapture e você já estará capturando pelos óculos.

Recursos e APIs de captura

  • Ok.

  • Em seguida, vamos ver como o CameraX ajuda você a encontrar a combinação de recursos que funcionam juntos e aproveitar ao máximo o dispositivo.

  • Os apps de câmera modernos são definidos por recursos avançados, e os usuários esperam que a câmera ofereça suporte a recursos como HDR, 60 FPS e estabilização de visualização, tudo ao mesmo tempo.

  • Mas, como desenvolvedores, sabemos que combinar esses recursos nem sempre é simples.

  • Verificar se cada recurso é compatível não garante que eles vão funcionar juntos.

  • Essa incerteza força os desenvolvedores a serem conservadores e limita o que os usuários podem experimentar em dispositivos compatíveis.

  • Para resolver isso, introduzimos a API Feature Group, que usa o framework de consulta de combinação de recursos da Camera2.

  • Há duas maneiras de usar: primeiro, use requiredFeatureGroup para verificar e ativar a combinação de recursos quando ela for compatível.

  • Em segundo lugar, você pode fornecer sua prioridade de recurso por preferredFeatureGroup, e a CameraX vai escolher a melhor combinação compatível para você.

  • E aqui está o exemplo.

  • À direita, à esquerda, desculpe, sem nada ativado, apenas um smartphone comum, gravação comum.

  • À esquerda, ativamos o HDR, 60 QPS e a Estabilização de Prévia ao mesmo tempo.

  • E você pode ver que a qualidade melhorou significativamente com o vídeo mais brilhante, suave e sem trepidação.

  • Vamos analisar o 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),
        )
    )
  • Aqui, usamos o preferredFeatureGroup para fornecer nossa lista de prioridades de recursos, que é HDR, 60 QPS e, em seguida, estabilização de prévia.

  • O CameraX vai verificar a combinação e selecionar as melhores opções compatíveis com o dispositivo.

  • Com o CameraX 1.6, adicionamos a gravação em 4K e a estabilização de vídeo diretamente à API Groups.

    // CameraX 1.6+ supports:
    // 4K, video stabilization & Effect
    val sessionConfig = SessionConfig(
       useCases = listOf(preview, videoCapture),
       requiredFeatureGroup =
         setOf(UHD_RECORDING, VIDEO_STABILIZATION),
       effects = listOf(overlayEffect)
    )
  • A combinação de 4K e estabilização de vídeo é compatível com muitos dispositivos premium.

  • Agora, com a API Feature Group, é possível ativar os dois juntos com confiança.

  • Além disso, agora você tem flexibilidade para aplicar o efeito em tempo real com a API Feature Group, o que não era possível antes.

  • Agora vamos falar sobre como fazer capturas de imagens de alta qualidade na CameraX.

  • A CameraX agora oferece suporte à captura de RAW DNG.

  • Ele dá acesso à imagem RAW não processada e não compactada diretamente do sensor da câmera para edição profissional.

  • Em seguida, melhoramos nossa extensão do Modo Noturno.

  • Essa extensão permite capturar fotos claras e nítidas em condições de pouca luz sem usar o flash.

  • Além disso, lançamos uma nova API de indicador de modo noturno.

  • Ele vai monitorar quando o ambiente atual estiver escuro e for adequado para o Modo Noturno, e você poderá usá-lo para mostrar o indicador do Modo Noturno.

  • Além disso, o Ultra HDR agora é compatível com as extensões da CameraX.

  • Isso traz um intervalo dinâmico mais amplo e cores vibrantes para o Modo Noturno, que, com o Ultra HDR, é compatível com muitos dispositivos premium recentes.

  • Aqui você pode ver a diferença entre os modos normal, noturno e Modo Noturno com Ultra HDR.

  • Vamos ver como implementar isso.

    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)
  • Primeiro, crie um ExtensionSessionConfig para o Modo noturno e use-o para acessar a instância da CameraInfo.

  • Em seguida, use o CameraInfo para acessar o recurso de captura de imagens e consulte se o Ultra HDR é compatível.

  • Em seguida, defina o formato de saída como Ultra HDR.

  • Por fim, crie o ExtensionSessionConfig novamente com os casos de uso e vincule ao ciclo de vida.

  • Agora você já pode usar o Modo Noturno com fotos em Ultra HDR.

  • Também estamos trazendo o suporte para fotos com movimento para o CameraX.

  • As fotos com movimento capturam o som e o movimento ao redor da foto, preservando o momento, não apenas uma imagem estática.

  • Padronizamos isso em diferentes smartphones e dispositivos Android, facilitando o suporte às fotos com movimento.

  • As fotos com movimento vão chegar à CameraX no primeiro trimestre de 2027.

  • Quando estiver disponível, basta definir o modo de fotos com movimento em ImageCapture.Builder para começar a capturar as fotos com movimento.

    val imageCapture = ImageCapture.Builder()
            .setMotionPhotoMode(
                 ImageCapture.MOTION_PHOTO_MODE_ON,
                 customConfig
             )
            .build()
    
    cameraProvider.bindToLifecycle(.. imageCapture)
    imageCapture.takePicture(...)
  • Em seguida, vamos analisar como a CameraX permite a gravação de vídeos em alta velocidade.

  • Registrar ações rápidas com nitidez é um recurso essencial dos apps de câmera modernos.

  • É especialmente útil para cenários de ritmo acelerado, como esportes.

  • Isso é feito com uma captura de alta velocidade a 120 ou 240 qps.

  • Há duas maneiras de usá-lo.

  • Primeiro, codifique com o mesmo frame rate e, em seguida, crie um vídeo com frame rate alto.

  • A outra opção é codificar a 30 qps para criar um vídeo em câmera lenta pronto para reprodução.

  • Por exemplo, um segundo gravado a 120 QPS leva quatro segundos para ser reproduzido a 30 QPS.

  • Assim, esse período cria um efeito de câmera lenta.

  • Para implementar a gravação em alta velocidade, crie um HighSpeedVideoSessionConfig com seu 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, {})
  • Em seguida, defina isSlowMotionEnabled como verdadeiro para codificar a 30 qps e criar um vídeo em câmera lenta ou defina como falso para criar um vídeo com frame rate de alta velocidade.

  • Quando a câmera lenta está ativada, o Google Fotos identifica isso automaticamente e adiciona um selo de câmera lenta à miniatura.

  • Vamos ver como isso funciona no Google Fotos.

  • Aqui, a câmera lenta dá um efeito dramático ao gato.

  • Meu gato nunca deixa eu tocar nele.

  • Sinto muito.

  • Por fim, vamos conferir algumas melhorias na nossa API de câmera simultânea.

  • A API simultânea permite que o app faça streaming das câmeras frontal e traseira ao mesmo tempo.

  • Este ano, fizemos algumas melhorias na API Concurrent Camera.

  • É possível atualizar dinamicamente a composição da câmera depois de vincular ao ciclo de vida e abrir uma câmera.

  • Isso inclui mudar a posição, o tamanho e a ordem de renderização.

  • Também é possível adicionar estilos, como cantos arredondados, largura da borda e cor da borda.

  • Para fazer isso, basta usar o CompositionSettings para configurar a estilização, como arredondamento de cantos, largura da borda e cor, e usar um setZOrder para ajustar a ordem de renderização.

    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))
  • Em seguida, basta invocar o setCompositionSettings na câmera simultânea ativa para atualizar a composição de forma dinâmica.

  • Pronto.

  • Agora você pode atualizar a composição dinamicamente.

  • Ok.

  • Agora que já analisamos todos os recursos,

  • Temos um aviso importante: recomendamos que você faça upgrade para o CameraX 1.6 e versões mais recentes.

  • Isso pode evitar falhas em dispositivos futuros que oferecem suporte ao novo formato, o Eclipsa Video, e também garantir que a extensão do CameraX continue funcionando na atualização futura do Pixel.

  • Com isso, encerramos nossa primeira sessão.

  • Abordamos os recursos da CameraX que ajudam a criar uma experiência de câmera de nível profissional. Agora, gostaria de convidar Aman para falar sobre nossa próxima sessão, que é sobre IA no dispositivo.

Aprendizado de máquina no dispositivo

  • Obrigado, Scott, por nos explicar os recursos do CameraX.

  • Agora, vamos ver como você pode usar o machine learning no dispositivo para criar experiências de câmera inteligentes.

  • O machine learning no dispositivo permite uma ampla variedade de casos de uso de câmera em duas áreas principais.

  • A primeira é a pré-captura.

  • Aqui, é possível analisar a visualização da câmera em tempo real para fazer coisas como detecção de objetos ou melhorar a própria visualização.

  • A segunda é a pós-captura.

  • Depois de capturar a imagem, você pode melhorar a qualidade dela ou gerar uma descrição, por exemplo.

  • Agora, vamos falar sobre algumas das soluções de aprendizado de máquina no dispositivo que temos para seus apps de câmera.

  • A primeira biblioteca de ML no dispositivo que vamos abordar é o Kit de ML.

  • O Kit de ML foi criado principalmente para resolver problemas comuns nos domínios de visão e linguagem natural, como reconhecimento de texto, detecção facial e tradução de idiomas.

  • No ano passado, apresentamos o Kit de ML.

  • Confira alguns exemplos de APIs Vision do Kit de ML, como detecção de malha facial, segmentação de objetos e detecção de objetos em tempo real.

  • Sim.

  • No ano passado, lançamos as APIs de IA generativa.

  • Essas APIs usam o poder do Gemini Nano para oferecer recursos como resumo de texto, descrição de imagens e muito mais.

  • Confira um exemplo de descrição de imagem usando a API Prompt.

  • Passamos o bitmap da imagem com o comando de texto para o modelo, e a descrição do resultado é um pequeno robô Android verde com um design semelhante a um cacto em uma superfície preta.

  • E tudo isso acontece no dispositivo.

  • Não é incrível?

  • O Adobe Photoshop usa a API Prompt para melhorar a experiência do usuário no app.

  • Ao editar recursos, os usuários geralmente precisam gerenciar várias camadas no app Photoshop.

  • Usando a API Prompt, agora o app pode renomear automaticamente as camadas desorganizadas.

  • O app também usa a API Prompt para gerar legendas e hashtags para imagens, o que é muito útil para compartilhar nas redes sociais.

  • Vamos analisar o código para descrição de imagens usando a API 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 -> {}
    }
  • Primeiro, você precisa de um objeto de cliente generativeModel. Depois, verifique se o Gemini Nano está disponível ou pode ser baixado no seu dispositivo.

  • Em seguida, gere uma solicitação de conteúdo combinando a imagem e o comando de texto e, por fim, forneça essa solicitação à chamada generateContent para executar a inferência no Gemini Nano e receber a descrição da imagem.

    // 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
  • Sim, a próxima biblioteca de ML no dispositivo é o MediaPipe.

  • O MediaPipe oferece uma ampla variedade de soluções de machine learning para seus aplicativos.

  • Acesse o site do MediaPipe para testar diferentes exemplos, como detecção facial, detecção de objetos, segmentação de imagens e muito mais.

  • Todos esses casos de uso são muito semelhantes ao Kit de ML. Então, qual biblioteca você deve usar?

  • Embora o MediaPipe e o Kit de ML resolvam problemas semelhantes, há algumas diferenças importantes.

  • Primeiro, o suporte à plataforma.

  • O MediaPipe funciona no Android, iOS, na Web e no Python, enquanto o Kit de ML se concentra apenas no Android e no iOS.

  • Além disso, o MediaPipe oferece personalização de modelos, enquanto o Kit de ML fornece APIs prontas para uso.

  • Por fim, enquanto o MediaPipe é de código aberto, o Kit de ML é um SDK do Google.

  • Veja um exemplo de reconhecimento de gestos usando as APIs MediaPipe Vision.

  • Essa API detecta os gestos com as mãos em tempo real usando a entrada de câmera.

  • Isso pode ser usado, por exemplo, para iniciar ou interromper a gravação da câmera usando gestos com a mão simples enquanto você está longe do smartphone.

  • Vamos analisar o código.

  • Primeiro, você precisa criar o objeto GestureRecognizerOptions, fornecendo o caminho do modelo, o modo de transmissão ao vivo e um listener 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)
  • Em seguida, crie um objeto de reconhecimento de gestos com base nas opções.

  • Em seguida, pegue o ImageProxy do CameraX, converta-o em um bitmap e use o bitmap para gerar um objeto mpImage do MediaPipe.

  • Por fim, forneça o objeto mpImage do MediaPipe à chamada recognizeAsync para detectar gestos em tempo real.

Melhoria de imagens e vídeos

  • Em seguida, temos o recurso de melhoria de imagens e vídeos no dispositivo, disponível pela API do Google Play Services.

  • Esse modelo oferece recursos como mapeamento de tons, remoção de desfoque e super resolução na maioria dos dispositivos premium.

  • Veja um exemplo de uma imagem com mapeamento de tons e upscaling usando esse modelo.

  • É possível ver claramente as cores mais ricas no céu e no mar e a nitidez aprimorada na imagem melhorada.

  • Vamos analisar o código para melhoria de imagem.

    // 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
    }
  • Primeiro, é preciso consultar se o dispositivo é compatível ou não e, se necessário, instalar o módulo de melhoria no dispositivo.

  • Em seguida, crie o objeto EnhancementOptions especificando o bitmap e as melhorias que você quer usar.

    // 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)
  • Em seguida, crie uma sessão de cliente com a API Google Play Services, defina um callback para processar o bitmap resultante e, por fim, chame a função de processamento com o bitmap e as opções para melhorar a imagem.

    // 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)
    }
  • Recentemente, integramos as APIs de melhoria de vídeo ao Jetpack Media3.

  • Confira nossa palestra sobre mídia para saber mais sobre as APIs Image and Video Enhancement.

  • O último recurso de ML no dispositivo que vamos abordar é o Modo de Pouca Luz.

  • Capturar imagens com pouca luz é um problema comum para usuários de smartphones.

  • Pense em usuários que capturam imagens ou fazem videochamadas em ambientes com pouca iluminação.

  • O recurso de aumento do modo pouca luz resolve esse problema, clareando a visualização da câmera em tempo real.

  • Há duas variantes da Otimização em Pouca Luz: hardware e software.

  • A versão do hardware usa o hardware do OEM para ajustar os frames com pouca iluminação sem usar aprendizado de máquina.

  • Já a versão do software usa um modelo de ML no dispositivo para melhorar os frames.

  • Vamos dar uma olhada rápida em como integrar a CameraX com o Software Low Light Boost, também conhecido como Google Low Light Boost.

  • O Google Low Light Boost requer duas superfícies principais: primeiro, você precisa de uma superfície de entrada para os frames brutos da câmera e uma superfície de destino de saída para os frames de saída aprimorados.

  • Para integrar com o CameraX, basta envolver o pipeline do Google Low Light Boost em um CameraEffect.

  • O CameraX processa automaticamente o ciclo de vida e o roteamento da superfície para você.

  • Por fim, vamos lançar o efeito de otimização em pouca luz no CameraX no quarto 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)
  • Com isso, você pode criar um objeto de otimização em pouca luz e adicioná-lo à configuração da sessão. Depois, vincule a configuração da sessão ao ciclo de vida da câmera, e ela vai funcionar nos dispositivos compatíveis.

Resumo e migração

  • Isso nos leva ao fim das duas seções.

  • Vamos resumir rapidamente o que abordamos hoje.

  • Hoje, falamos sobre como melhorar as experiências de câmera em vários dispositivos com o CameraX e o machine learning no dispositivo.

  • Primeiro, com o CameraXViewfinder, é possível ativar as prévias da câmera para se adaptarem a diferentes formatos.

  • Em seguida, vimos que o CameraX também permite capturar em dispositivos XR.

  • Em seguida, a API Groups ajuda a encontrar a combinação ideal de recursos de hardware.

  • Também exploramos as atualizações da Captura de Imagens e aprendemos sobre a gravação de vídeos em alta velocidade para vídeos em câmera lenta.

  • Por fim, aprendemos a melhorar a prévia da câmera simultânea de forma dinâmica.

  • Depois, analisamos algumas das soluções de machine learning no dispositivo, como o Kit de ML e o MediaPipe, para criar experiências de visão inteligente.

  • Por fim, vimos a melhoria de imagem e vídeo no dispositivo, além do aumento do modo pouca luz para clarear a prévia da câmera em tempo real.

  • Como mencionado anteriormente, planejamos desativar a Camera1. Por isso, recomendamos que você migre para a CameraX.

  • Para facilitar essa migração, lançamos recentemente a habilidade do CameraX.

  • Também temos um guia de migração e o repositório de exemplos de câmera para orientar você.

  • Acesse este link para conferir mais recursos sobre as funcionalidades que abordamos hoje.

  • Ele inclui links para a documentação, o blog e os recursos de migração, tudo em um só lugar.

  • Vou fazer uma pausa rápida.

  • Ok.

  • Agradeço a todos.

  • Isso encerra nossa conversa.

Código

Snippets de código

Snippet do Kotlin em 04:18 em "Recursos do Adaptive Viewfinder"
var surfaceRequest by remember {
    mutableStateOf<SurfaceRequest?>(null)
}

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

surfaceRequest?.let { request ->
    CameraXViewfinder(
        surfaceRequest = request,
        contentScale = ContentScale.Fit
    )
}
Snippet do Kotlin em 05:14 em "Recursos do Adaptive Viewfinder"
CameraXViewfinder(
    surfaceRequest = viewModel.surfaceRequest,
    contentScale = ContentScale.Fit,
    isTapToFocusEnabled = true,
    isPinchToZoomEnabled = true,
    onScreenFlashReady =
        {
           imageCapture.screenFlash = it
           imageCapture.flashMode = FLASH_MODE_SCREEN
        }
)
Snippet do Kotlin às 07:30 em "Captura de câmera com o 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(...)
Snippet do Kotlin em 09:42 em "Recursos e 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),
    )
)
Snippet do Kotlin em 09:58 em "Recursos e 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)
)
Snippet do Kotlin às 11:20 em "Capture features and APIs"
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)
Snippet do Kotlin às 12:11 em "Recursos e APIs de captura"
val imageCapture = ImageCapture.Builder()
        .setMotionPhotoMode(
             ImageCapture.MOTION_PHOTO_MODE_ON,
             customConfig
         )
        .build()

cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)
Snippet do Kotlin às 13:06 em "Recursos e 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, {})
Snippet do Kotlin em 14:18 em "Recursos e 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))
Snippet do Kotlin aos 17:51 em "Aprendizado de máquina no 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 -> {}
}
Snippet do Kotlin aos 18:06 em "Aprendizado de máquina no 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
Snippet do Kotlin em 19:38 em "Aprendizado de máquina no 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)
Snippet do Kotlin aos 20:44 em "Melhoria de imagem e vídeo"
// 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
}
Snippet do Kotlin aos 20:57 em "Melhoria de imagem e vídeo"
// 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)
Snippet do Kotlin às 21:05 em "Melhoria de imagem e vídeo"
// 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)
}
Snippet do Kotlin em 22:51 em "Melhoria de imagem e vídeo"
// 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)