Alura > Cursos de Inteligência Artificial > Cursos de IA para Mobile > Conteúdos de IA para Mobile > Primeiras aulas do curso Android com IA: usando a câmera para detectar objetos com Machine Learning

Android com IA: usando a câmera para detectar objetos com Machine Learning

Conhecendo e instalando a lib de detecção e rastreio - Apresentação

Quer saber como criar aplicativos que utilizam a câmera do Android para analisar o que está acontecendo e fornecer informações relevantes com Inteligência Artificial e Machine Learning? Vamos mostrar como!

Boas-vindas a mais um conteúdo da Alura. Meu nome é Junior e serei seu instrutor.

Audiodescrição: Junior se identifica como um homem de pele clara, cabelos loiros e escuros. No rosto, utiliza um óculos com lentes grandes e armação na cor preta. No corpo, usa uma camiseta preta. Ao fundo, há uma parede lisa com uma luz degradê que vai do azul até o rosa.

O que Aprenderemos?

Neste curso, vamos utilizar o aplicativo AI Found, que permite listar uma série de produtos.

Interface do aplicativo AI Found, nas cores vermelho, rosa claro e branco, exibindo uma lista de produtos com suas imagens, nomes e preços em reais. No topo, há um cabeçalho vermelho com o texto 'AI Found' centralizado e no canto direito, um ícone de carrinho de compras. A lista inclui, de cima para baixo: um refrigerante de coca com o preço de R$ 5,99; batata chips sabor limão da marca Lay's por R$ 8,43; ketchup Heinz por R$ 11,48; uma bebida energética Monster por R$ 7,45; e macarrão instantâneo sabor curry por R$ 5,49. Cada item tem uma breve descrição à direita da imagem correspondente. Acima da tela do aplicativo, a barra de status do celular aparece na mesma cor vermelha.

Quando clicamos em um dos itens na lista, vemos uma tela que traz informações sobre esse produto, como a descrição e a quantidade. Podemos clicar nos botões de mais e de menos para adicionar algumas quantidades ao carrinho.

Tela de detalhes do item. Ela exibe um refrigerante em lata com rótulo da marca Coca-Cola sobre um fundo branco. Abaixo da imagem do item, o preço total de R$ 5,99 à esquerda e botões de mais e menos para selecionar a quantidade desejada à direita. Abaixo destes, a descrição do item, e abaixo dela, um botão vermelho para adicionar o item ao carrinho.

O que realmente queremos fazer com esse aplicativo? Quando clicarmos na opção de pesquisa avançada, no canto inferior direito da tela de lista de itens, ele abrirá a câmera e conseguiremos visualizar informações do que está na foto.

Ele identificará que existe um produto, trará para nós o nome dele, exibindo uma caixa de destaque em volta e uma aba com o nome do produto na parte inferior da tela.

ALT: Tela de escaneamento do aplicativo, mostrando a imagem da mão de uma pessoa segurando um pote de Nutella, contornada por um retângulo branco. Abaixo deste, um retângulo branco com a descrição 'Nutella de Pimenta'. Na parte inferior da tela, uma aba rosa-claro com o mesmo nome de produto e o preço R$ 56,56.

Ao clicar nessa aba, ele traz aquela mesma tela de descrição, só que para o produto que ele está visualizando. Além disso, se movimentarmos a câmera do dispositivo para outro produto, ele trocará o que identificou e trará para nós a informação do novo item.

Para entender como tudo isso vai funcionar, vamos utilizar uma biblioteca do ML Kit para rastreio e detecção de objetos. Para isso, vamos entender como essa biblioteca fornece informações relevantes, como rótulos, número de objetos que são detectados e os IDs de cada um.

Vamos entender como essas informações podem ser utilizadas para desenhar as caixas delimitadoras em volta do produto na câmera e fazer a pesquisa do que queremos dentro do aplicativo. Vamos entender como esse padrão de design aparece, permitindo que facilmente alternemos entre um produto que está sendo exibido agora e qualquer outro.

Durante nossos vídeos, além de utilizar o próprio emulador, vamos utilizar um dispositivo físico, permitindo, por exemplo, entrarmos na câmera do celular com nosso aplicativo aberto e apontar para um produto real que temos em mãos. Assim, ele fará a identificação do que estamos buscando, permitindo fazer a adição dele no nosso carrinho. Vamos ver isso em tempo real.

Para Quem é este Curso?

Este é um conteúdo importante para quem quer adicionar a funcionalidade da câmera do seu dispositivo, trazer informações mais relevantes do que ela está vendo, utilizando o poder de processamento do próprio dispositivo, sem precisar enviar nada para fora dele, utilizando o Machine Learning e a biblioteca de detecção e rastreio de objetos da própria Google.

Pré-requisitos

Se tiver alguma dúvida ou quiser compartilhar algo conosco, convidamos você a utilizar nossos canais do Discord ou mesmo no fórum do curso.

Esperamos que você tenha se animado para começar. Vamos lá!

Conhecendo e instalando a lib de detecção e rastreio - Review do projeto

Antes de começar a implementar nossas funcionalidades, é crucial conhecer a estrutura do projeto, o AI Found.

Conhecendo o Projeto

Após fazer o download e instalar, vamos abri-lo no Android Studio e revisar as pastas que temos.

A ideia com esse aplicativo é adicionar uma função que agrega valor à pesquisa de produtos. No mundo real, vários aplicativos semelhantes têm adicionado a possibilidade de apontar a câmera do celular e pesquisar o que está ali, trazendo informações como preço, descrição e produtos similares. Alguns aplicativos estão começando a adicionar essa funcionalidade.

Vamos ver, em primeira mão, como fazer isso dentro de um aplicativo real.

A estrutura desse projeto está quase toda feita — toda a parte de lógica e visual já está pronta. Nós o recebemos somente para implementar a nova função.

Estrutura de pastas do aplicativo:

Dentro da pasta "data", temos os modelos Product e ProductObject que fazem o aplicativo funcionar, como o de produto, que tem o nome, o ID, a quantidade, a descrição, a imagem desse produto, que é um @DrawableRes, porque está direto no projeto.

Dentro de "extensions", temos o arquivo ComposeExtensions.kt com extensões do próprio Composable, feitas para facilitar implementações futuras. Já o arquivo StringExtensions.kt tem duas extensões de string que utilizamos no arquivo ProductSample.kt, da pasta "sampledata".

No interior desse arquivo, temos o código de uma API falsa. Apesar de não a ter disponível por completo, temos uma ideia de como pegar todos os produtos, o formato que eles serão retornados e como fazer a requisição para um único produto.

Isso é muito útil em casos reais, onde a API ainda está sendo construída por outro time, mas já temos que começar a implementar essa funcionalidade. No ProductSample.kt temos, portanto, uma simulação de como fazer essas buscas.

Nesse arquivo também temos uma lista com alguns produtos de exemplo, como refrigerantes, batata, ketchup. Eles possuem o nome, o preço, e sua imagem, permitindo fazer os primeiros testes.

Dentro da pasta "ui", temos toda a parte visual do app, com a parte do carrinho, componentes do Jetpack Compose, a pasta de detalhes, de home. Toda a parte de navegação também está nessa pasta.

Dentro da pasta "utils", por sua vez, temos o arquivo PermissionUtils com parte de permissões, além da MainActivity e da classe de AppApplication para injeção de dependências.

Caso haja interesse em aprender sobre permissões e armazenamento de arquivos, temos conteúdos disponíveis sobre esse assunto na plataforma da Alura, como o curso Jetpack Compose: lidando com armazenamento de arquivos no Android.

Descendo um pouco mais, dentro da pasta "assets" temos dois arquivos de modelos customizados, model_a.tflite e model_products.tflite. No curso anterior de Ml Kit com imagens, entendemos o que esses modelos são, como funcionam, e até como criar os nossos. Neste curso, também faremos uso deles.

Dentro da pasta "res", temos a pasta "drawable" com duas imagens principais para testar o aplicativo: amostra1_varios_produtos.png, com várias fotos de itens que vamos utilizar, e amostra2_dois_produtos.png, que possui outros produtos.

Conhecemos várias pastas do aplicativo, mas não falamos da câmera. A seguir, vamos comentar um pouco melhor sobre como essa questão funciona no Jetpack Compose. Vamos lá!

Conhecendo e instalando a lib de detecção e rastreio - Lidando com a câmera no Jetpack Compose

Para entender como funciona o componente de câmera no projeto, especificamente com o Jetpack Compose, localizaremos a pasta "camera" dentro do diretório "ui".

Em seu interior, vamos encontrar arquivos que precisamos analisar com mais atenção.

Entendendo o Código da Câmera

Começaremos acessando o cameraAnalyzer. Ele implementa o ImageAnalysis.Analyzer, cuja função entenderemos em breve. Antes, é importante saber que no CameraContainer.kt, o segundo arquivo nesta pasta, preparamos todas as permissões de câmera necessárias para a câmera e para o preview funcionarem.

Em seguida, entramos no principal arquivo que precisamos analisar com mais atenção, que é o CameraScreen.kt. Este é um código quase todo feito em Jetpack Compose. Com este projeto aberto, podemos fazer o seguinte teste:

Primeiro, removeremos todas as linhas abaixo, que abrangem por volta da linha 54 até a 59. Esse trecho é uma parte de análise da câmera.

CameraScreen.kt:

val cameraAnalyzer = remember {
    CameraAnalyzer { imageProxy ->
        Log.d("CameraAnalyzer", "Image received: ${state.imageWidth}x${state.imageHeight}")
        imageProxy.close()
    }
}

Depois, vamos remover todo o código dos dois set abaixo, localizados por volta da linha 58 até a linha 62.

setEnabledUseCases(CameraController.IMAGE_ANALYSIS)
setImageAnalysisAnalyzer(
    ContextCompat.getMainExecutor(context),
    cameraAnalyzer
)

Com isso, percebemos que nenhum erro vai aparecer. Se rodarmos o projeto, ele ainda vai funcionar normalmente.

Queremos deixar esse código dessa maneira para explicar que, para que a câmera apareça em tela junto com o preview, precisamos do componente cameraController que controla a câmera e está por volta da linha 56, e de um preview para mostrar o que a câmera está capturando. Para isso, temos o CameraPreview, por volta da linha 62.

CameraPreview(cameraController = cameraController)

Vamos clicar nele e pressionar "Ctrl+B" para acessar o seu código, por volta da linha 86 no mesmo arquivo.

private fun CameraPreview(
    modifier: Modifier = Modifier,
    cameraController: LifecycleCameraController,
) {
    val lifecycleOwner = LocalLifecycleOwner.current
    
    AndroidView(
        factory = { context ->
            PreviewView(context).aplicativoly {
                this.controller = cameraController
                cameraController.bindToLifecycle(lifecycleOwner)
            }
        },
        modifier = modifier.fillMaxSize()
    )
}

No momento em que estamos gravando este conteúdo, o Jetpack Compose não tem uma maneira nativa de exibir o preview da câmera. Portanto, precisamos utilizar a opção de interoperabilidade dele com o sistema de Views, chamando o AndroidView e passando informações para ele.

Apesar de estarmos utilizando a interoperabilidade, esse é um recurso que funciona muito bem. Declaramos com o próprio modifier do Jetpack Compose que esse componente deve fazer parte da tela inteira e preencher todo o seu conteúdo.

Dentro desse AndroidView, passamos o cameraController, obtido via Jetpack Compose, e um ciclo de vida também obtido com o Jetpack Compose para fazer o controle de quando, por exemplo, minimizamos o aplicativo, trocamos ou recebemos algo que possa fazer com que o ciclo de vida mude, seja interrompido ou finalizado.

Feito isso, temos dois passos únicos para adicionar a câmera e adicionar o preview. O aplicativo vai funcionar. Porém, como este é um curso que visa trabalhar com a análise da câmera, vamos devolver os dois trechos de código que recortamos pressionando "Ctrl+Z" várias vezes.

Entre eles, o trecho de análise da câmera possui o cameraAnalyzer já implementado, que vai fazer uso da classe cameraAnalyzer que já deixamos pronta na mesma pasta "camera".

val cameraAnalyzer = remember {
    CameraAnalyzer { imageProxy ->
        Log.d("CameraAnalyzer", "Image received: ${state.imageWidth}x${state.imageHeight}")
        imageProxy.close()
    }
}

Vamos acessar a classe cameraAnalyzer no arquivo CameraAnalyzer.

CameraAnalyzer.kt:

class CameraAnalyzer(
    private val onImageProxy: (image: ImageProxy) -> Unit
) : ImageAnalysis.Analyzer {
    override fun analyze(image: ImageProxy) {
        onImageProxy(image)
    }
}

Você pode criar qualquer classe com o nome que seja, desde que ela implemente esse ImageAnalysis.Analyzer e sobrescreva o método analyze, que terá um ImageProxy — basicamente, cada frame (quadro) do preview que veremos na câmera.

Ou seja, quando apontamos a câmera do celular, ela vai transmitir em tempo real o que está passando na frente, como se fossem várias imagens uma atrás da outra. Conseguimos ter acesso aos quadros dessas imagens por meio do analyze que passamos para o cameraController no arquivo CameraScreen.kt, realizando a instância do cameraAnalyzer.

O código que implementamos como amostra do cameraAnalyzer no arquivo CameraScreen.kt pega cada um desses quadros dentro de um Log.d, mostrando o tamanho da sua altura e da sua largura. No final, precisamos ter ciência que cada imagem precisa ser fechada depois que trabalhamos com ela. Por isso, temos um imageProxy.Close no final desse bloco.

No segundo trecho que retornamos ao código, com os dois set, deixamos essa análise sendo passada pelo cameraController dentro de um LifecycleCaeraController(context).apply.

CameraScreen.kt:

val cameraController = remember {
    LifecycleCameraController(context).apply {
        setEnabledUseCases(CameraController.IMAGE_ANALYSIS)
        setImageAnalysisAnalyzer(
            ContextCompat.getMainExecutor(context),
            cameraAnalyzer
        )
    }
}

Feito isso, entendemos como o código da câmera funciona.

No final, por volta da linha 75, temos um pequeno overlay, que é um componente do Compose, o qual vamos utilizar para desenhar informações em cima da tela, como por exemplo, se existe um produto detectado ou não. Nesse caso, por enquanto, apenas mostramos um único texto.

// 3 Overlay
BoxWithConstraints(
    modifier = Modifier
        .fillMaxSize()
        .background(Color.Black.copy(alpha = 0.2f))
) {
    Text(
        text = state.textMessage ?: "Nenhum produto detectado",
        fontSize = 20.sp,
        color = Color.White,
        textAlign = TextAlign.Center,
        modifier = Modifier
            .padding(16.dp)
            .fillMaxWidth()
    )
    
    Log.d("CameraScreen", "Screen size: ${maxWidth.dpToPx()} x ${maxHeight.dpToPx()}")
}

O Caso da Adidas

Além desse projeto que recebemos e que já entendemos como funciona, também recebemos o link de um arquivo da Google sobre o caso da Adidas, comentando sobre essa uma marca muito famosa de roupas e calçados implementou uma funcionalidade no seu aplicativo utilizando a biblioteca de detecção e rastreio de objetos.

Isso permite que, quando alguém entre na loja da Adidas e aponte a câmera do celular para algum produto como um calçado, obtenha informações como quais são os calçados similares àquele, e se aquele calçado existir na loja, quais são os tamanhos, qual é o preço e quais são as cores disponíveis, por exemplo.

Essa é uma função que tem ficado cada vez mais comum nos aplicativos — apontamos a câmera e obtemos informações extras de um produto ou local. A biblioteca de detecção e rastreio utilizada pela Adidas permite fazer isso.

A seguir, começaremos a entender como essa biblioteca funciona e quais as diferenças dela para outras que já utilizamos antes. Vamos lá!

Sobre o curso Android com IA: usando a câmera para detectar objetos com Machine Learning

O curso Android com IA: usando a câmera para detectar objetos com Machine Learning possui 149 minutos de vídeos, em um total de 54 atividades. Gostou? Conheça nossos outros cursos de IA para Mobile em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda IA para Mobile acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas