Desenvolvendo um aplicativo para busca visual de produtos
Eu queria desenvolver isso há muito tempo. Desde a época em que trabalhei no Buscapé, eu via aplicativos como o Google Goggles e achava mágico. Recentemente, encontrei o projeto MediaPipe da Google e o seu guia para embedding de imagem, e soube que era por aí...
Criar a representação numérica de uma imagem em forma de vetor (embedding) é útil para comparar a semelhança entre imagens usando técnicas matemáticas que calculam a proximidade entre vetores, como a similaridade de cosseno.
Para fazer um protótipo de aplicativo de busca visual de produtos eu precisei de:
-
Um indexador: para gerar embeddings de todas as fotos dos meus produtos e armazená-los em um banco de dados vetorial ou biblioteca de índice. Escolhi fazer em Python com o índice IndexFlatIP da FAISS (uma biblioteca da Meta para busca de similaridade entre vetores).
-
Um endpoint: que vai receber o embedding da foto tirada pelo usuário e encontrar similares no índice (fiz em Flask).
-
O aplicativo: que deve usar o mesmo modelo e transformações na imagem que o indexador, gerar o embedding da foto tirada e enviá-lo para o endpoint (fiz o aplicativo em Kotlin).
Como o índice do tipo IndexFlatIP calcula o Produto Interno (IP), aplicar a normalização L2 (norma euclidiana) nos embeddings é essencial, pois transforma matematicamente o produto interno na própria similaridade de cosseno. O aplicativo, portanto, deve usar o mesmo modelo que o indexador, e ambos precisam aplicar a normalização L2 no embedding antes de qualquer comparação.
O modelo MobileNetV3 disponível no MediaPipe Image Embedder foi um bom começo, mas mudanças no fundo das imagens afetaram muito os resultados, tornando-os inutilizáveis. As imagens no meu catálogo têm o fundo branco ou cinza, mas as fotos de teste que tirei em uma loja física tem o fundo da prateleira e isso virou um problema.
Tentei remover o fundo das imagens utilizando modelos de segmentação focados em dispositivos móveis (como MobileSAM e EdgeSAM), mas, insatisfeito com a latência e a qualidade dos resultados, comecei a pesquisar outros modelos de embedding além do MobileNetV3.
Encontrei e implementei versões adequadas dos seguintes modelos para uso em dispositivos móveis (baixa latência, tamanho de modelo em megabytes):
-
MobileNetV4
Foi um trabalho difícil, pois, para cada versão, precisei implementar: 1. o indexador, 2. o endpoint e 3. o código no aplicativo.
Além disso, escrevi "validadores", scripts onde comparo o embedding gerado pelo indexador com o gerado pelo aplicativo em uma mesma imagem. As transformações na imagem e execução do modelo no aplicativo precisam ser extremamente similares ao que é feito no indexador, e vice-versa.
No indexador em Python, praticamente todos os modelos trabalham com PyTorch. Para o aplicativo em Kotlin, eu os exportei e usei via ONNX.
Nesse momento encontrei resultados incríveis (às vezes mágicos), especialmente com o MobileCLIP e o SigLIP2 — embora ainda tenha ressalvas quanto à capacidade de implantação real do aplicativo devido ao tamanho desses modelos (em megabytes) e à dificuldade em reduzir isso com as ferramentas atuais.
Como podemos notar, estamos trabalhando no ombro de gigantes com frameworks, modelos e muito código aberto da Google, Meta, Apple e outras grandes empresas e laboratórios de tecnologia. Especialmente por isso eu achei interessante compartilhar minha jornada até aqui.