Série de IA no Dispositivo (Parte 3): MediaPipe Tasks
ML Kit oferece os modelos da Google. MediaPipe fornece a pipeline.
Na Parte 1, optamos por ML Kit porque é um pacote completo: adicione uma dependência, passe uma InputImage e obtenha um resultado estruturado. Isso cobre uma grande fatia da IA móvel cotidiana. Mas toda equipe eventualmente atinge um limite que o ML Kit não pode ultrapassar — você precisa de um modelo TFLite específico ou precisa encadear várias tarefas em uma funcionalidade que nenhuma API do ML Kit expõe.
É exatamente aí que o MediaPipe Tasks vive. É a camada diretamente abaixo do ML Kit: ainda totalmente no dispositivo, ainda uma dependência Gradle limpa, mas você fornece o modelo e possui a pipeline. Ele abrange visão, texto, áudio e IA gerativa — e neste artigo, primeiro mapearemos todo esse conjunto de ferramentas, então caminharemos por três características concretas do Android que colocam as tarefas de visão em prática: toque para segmentar, embedding de imagem e uma ferramenta “encontre essa pessoa em várias fotos” que une três tarefas. Fecharemos avaliando o MediaPipe com o mesmo quadro usado na Série 1 e 2.
O Conjunto de Ferramentas: O Que o MediaPipe Tasks Realmente Pode Fazer?
O MediaPipe Tasks é um conjunto de bibliotecas para implantar soluções de ML no dispositivo com código mínimo. Cada tarefa envolve um modelo por trás de uma pipeline otimizada que se baseia na aceleração de hardware (CPU, GPU e TPU), executa em tempo real e mantém os dados privados ao nunca deixar o dispositivo. O catálogo é amplo, então, em vez de listar todas as tarefas, aqui estão os domínios que ele abrange:
- Visão: a família mais extensa — detecção de objetos, classificação e segmentação de imagens, segmentação interativa, detecção facial e marcas faciais 3D, rastreamento de mãos e postura, reconhecimento de gestos e embelezamento de imagem para similaridade visual.
- Texto e Linguagem: classificação de texto (por exemplo, sentimento), detecção de linguagem e embelezamento de texto para similaridade semântica.
- Áudio: classificação de áudio para reconhecer eventos sonoros de um conjunto de categorias treinadas.
- IA Gerativa: inferência de LLM no dispositivo, geração de imagem, chamada de função e RAG. (A Google está direcionando essa área para a nova pilha LiteRT-LM — um tópico para uma entrada futura nesta série — então mantemos nossa atenção aqui nas tarefas clássicas).
Dois atributos importam em todos eles. Primeiro, você escolhe o modelo — aponte uma tarefa para um arquivo .tflite/.task embalado ou personalizado e troque-o sem tocar no código acima dele, e use MediaPipe Model Maker para retreinar a tarefa em seus próprios dados. Segundo, as tarefas são cross-platform (Android, Web/JS e Python hoje, com iOS planejado). Essa liberdade no nível do modelo é o único fator que separa o MediaPipe dos modelos fechados da ML Kit apenas para uso interno da Google.
Para os exemplos práticos abaixo, ficaremos na área de Visão, já que é onde se concentram os casos de uso no dispositivo com câmera e fotos — mas os padrões que você verá (aponte para um modelo, envolva sua entrada, leia um resultado estruturado) são idênticos a todos as tarefas acima.
Exemplos de Implementação Prática
Todos os exemplos abaixo são extraídos do nosso aplicativo de referência. Cada um é um padrão autocontido que você pode incorporar em seu próprio projeto.
Toque para Segmentar: Segmentação Interativa
A configuração é a forma padrão do MediaPipe — aponte BaseOptions para um modelo em sua pasta assets/, então construa o cliente da tarefa. Aqui, magic_touch.tflite é o modelo de segmentação
private val segmentador: InteractiveSegmenter? = try {
val opcoesBase = BaseOptions.builder()
.setModelAssetPath("seu_modelo.tflite") // seu modelo, em assets/
.build()
val opcoes = InteractiveSegmenterOptions.builder()
.setBaseOptions(opcoesBase)
.setOutputCategoryMask(true)
.setOutputConfidenceMasks(false)
.build()
InteractiveSegmenter.createFromOptions(context, opcoes)
} catch (e: Exception) {
... // degrade-se graciosamente se o recurso do modelo não estiver presente
}A parte interessante é como um gesto do usuário se torna entrada para o modelo. O segmentador não aceita uma caixa delimitadora - ele aceita um único ponto de Interesse da Região. Então, um toque Compose em coordenadas normalizadas [0, 1] mapeia diretamente para o pixel que o modelo deve segmentar ao redor:
// Do manipulador de toque - toque normalizado → ROI → máscara
val roi = InteractiveSegmenter.RegionOfInterest.create(
NormalizedKeypoint.create(normX * bitmap.width, normY * bitmap.height)
)
val mpImage = BitmapImageBuilder(bitmap).build()
val mascara = segmentador.segment(mpImage, roi).categoryMask().get()A partir daí, caminhando pelo buffer de bytes da máscara, podemos fazer as coisas divertidas - pintar uma sobreposição neon ou fisicamente separar “o objeto” de “o fundo” em dois bitmaps.
Inserção de Imagem e Similaridade do Cosseno
A inserção é onde o MediaPipe se torna silenciosamente um mecanismo de pesquisa. Você configura o inseridor com normalização L2 ativada e cada imagem se reduz a uma FloatArray que você pode comparar:
val opcoesBase = BaseOptions.builder()
.setModelAssetPath("....tflite")
.build()
val opcoes = ImageEmbedderOptions.builder()
.setBaseOptions(opcoesBase)
.setL2Normalize(true) // faz a similaridade do cosseno comportar-se
.setQuantize(false)
.build()
suspend fun inserir(bitmap: Bitmap): FloatArray = withContext(Dispatchers.IO) {
val mpImage = BitmapImageBuilder(bitmap).build()
getInserter().inserir(mpImage)
.embeddingResult().embeddings()[0].floatEmbedding()
}O MediaPipe fornece um ImageEmbedder.cosineSimilarity(...) embutido para seus próprios objetos de Inserção, mas uma vez que você está segurando arrays de floats brutos (digamos, cacheados de um run anterior), um cosseno manual mantém as coisas simples:
fun compararInsercoes(a: FloatArray, b: FloatArray): Double {
var dot = 0.0; var normA = 0.0; var normB = 0.0
for (i in a.indices) {
dot += a[i] * b[i]
normA += a[i] * a[i]
normB += b[i] * b[i]
}
return dot / (sqrt(normA) * sqrt(normB))
}Uma similaridade acima de 0,75 significa “esses parecem a mesma coisa.”
The Showcase — Face Finder
Antes de começar a criar qualquer coisa, é bom saber que o MediaPipe tem seu próprio detector facial — e ele é minúsculo. O modelo BlazeFace para curto alcance (`face_detection_short_range.tflite`) pesa apenas 224 KB e retorna, para cada rosto, uma caixa delimitadora, um score de confiança e seis pontos-chave: olho esquerdo, olho direito, ponta do nariz, boca e as duas orelhas. A configuração é a mesma forma em três camadas como todas as outras tarefas:
private val faceDetector: FaceDetector? = try {
val options = FaceDetector.FaceDetectorOptions.builder()
.setBaseOptions(
BaseOptions.builder()
.setModelAssetPath("face_detection_short_range.tflite")
.build()
)
.setMinDetectionConfidence(0.5f)
.setRunningMode(RunningMode.IMAGE)
.build()
FaceDetector.createFromOptions(context, options)
} catch (e: Exception) {
null // degrade-se graciosamente se o modelo de ativo não estiver presente
}
fun detect(bitmap: Bitmap): FaceDetectorResult? {
val detector = faceDetector ?: return null
return detector.detect(BitmapImageBuilder(bitmap).build())
}Um detalhe que te pegará quando desenhar os resultados: a caixa delimitadora retorna em coordenadas de pixel da imagem, mas os seis pontos-chave são normalizados (0..1). Misturar isso coloca sua caixa no lugar errado enquanto os pontos ficam perfeitamente posicionados — escala a caixa por `canvasSize / bitmapSize` e multiplique os pontos-chave diretamente pelo tamanho do canvas.
Por que usar MediaPipe (quando o ML Kit já existe)?
O ML Kit ainda é a primeira escolha certa — comece lá. Você chega além dele por três razões concretas, todas as quais apareceram nos exemplos acima:
- Traga seu próprio modelo. ML Kit liga você aos modelos pré-treinados do Google. A função
setModelAssetPath(...)da MediaPipe significa que você pode enviar um classificador ou embbedder específico ao domínio e o código envolvente nunca muda. - Componha tarefas em recursos. Face Finder não é uma API — é três tarefas coladas com nossa própria lógica de fusão. A MediaPipe expõe os resultados intermediários (marcos, máscaras, embeddings brutos) para que você possa construir sobre eles.
- Preencha os espaços deixados pelo ML Kit. Existe um motivo real e não glamuroso para nosso aplicativo usar o MediaPipe Face Landmarker em vez do
face-mesh-detectiondo ML Kit: essa biblioteca (16.0.0-beta1) tem uma incompatibilidade binária commlkit:common ≥ 18.x, que outras dependências do ML Kit no projeto já puxam. O landmarker da MediaPipe contornou toda a disputa e nos deu 478 pontos 3D extras.
Além da flexibilidade, você mantém todos os benefícios do dispositivo: sem viagens pela rede, sem custo de nuvem por chamada e imagens dos usuários que nunca deixam o telefone.
Por Trás das Cenas: A API de Tarefas e Entrega de Modelos
Cada tarefa da MediaPipe segue o mesmo contrato de três camadas, por isso as quatro características acima parecem tão semelhantes em código:
Alguns detalhes práticos que valem a pena saber:
- Modelos vivem em
assets/. Embalamos diretamentemagic_touch.tflite,mobilenet_v3_large.tflite,efficientnet_lite0.tfliteeface_landmarker.task. Esse é o custo da flexibilidade — esses arquivos adicionam peso real ao APK, então para modelos maiores você desejará fazer download sob demanda em vez de embalar. - Tudo fala
MPImage. Você envolve umBitmapcomBitmapImageBuilder(bitmap).build()ao entrar e desembrulha máscaras comByteBufferExtractorao sair. É uma fronteira fina e previsível. - É uma API moldada em Java no mundo Kotlin. Construtores,
Optional.get(), sem coroutines nativas. Nosso caso de uso envolve cada chamada emwithContext(Dispatchers.IO)e expõe funçõessuspend— uma pequena quantidade de cola que você escreve uma vez por tarefa. - Aceleração de hardware está embutida. Tarefas delegam para CPU ou GPU por trás dos panos, então segmentação e embelezamento permanecem em tempo real no dispositivo sem você tocar NDK ou configuração de delegate.
Alternativas à vista
- ML Kit: O ponto de partida chave — modelos da Google, código mínimo, ótimo para os 80% comuns (Parte 1). Apenas alcance além dele quando precisar de um modelo personalizado ou composição de tarefas.
- LiteRT (anteriormente TensorFlow Lite): A camada abaixo do MediaPipe — execução de modelo bruta com controle total sobre tensores, delegates e pré/post-processamento. Escolha-o quando até mesmo as wrappers de tarefa do MediaPipe forem muito opinativas.
- Cloud Vision APIs: Para trabalhos pesados demais para o silício móvel, ao custo de latência, dependência da rede e envio de imagens do usuário fora do dispositivo.
Uma regra útil de dedo — a mesma da Série 1 — é começar no nível mais alto de abstração que atenda às suas necessidades. Tente ML Kit primeiro. Desça para MediaPipe Vision Tasks quando precisar do seu próprio modelo ou precisar fundir várias tarefas. Desça apenas para LiteRT quando precisar de controle em nível de tensor. Alcance a nuvem apenas quando o modelo realmente não pode rodar no dispositivo.
Avaliando MediaPipe Vision Tasks: Quão amigável para desenvolvedores é?
Usando o mesmo Android On-Device AI Rater do Framework da Série 1 e 2 — oito critérios ponderados, de 0 a 10 cada — aqui está como MediaPipe Vision Tasks pontua:
A Nota Final: 8.4 / 10 (Excelente ✅)
- Integração com Android (9/10): Dependência padrão do Maven Central, sem NDK/CMake. A única dificuldade é gerenciar os ativos de modelos por conta própria.
- Simplicidade da API (7.5/10): O padrão de três camadas é consistente e fácil de aprender, mas você faz mais manualmente do que com o ML Kit — construindo
MPImage, percorrendo buffers de bytes de máscaras e criando sua própria matemática de similaridade. - Design Kotlin-First (7/10): Construtores estilo Java,
Optional.get(), e sem suporte nativo para coroutines/Flow. Você envolve chamadas emwithContextesuspend. - Compatibilidade de Modelos (9.5/10): A principal força — traga qualquer modelo compatível
.tflite/.task. Isso é exatamente o que o ML Kit renuncia. - Desempenho e Aceleração de HW (9/10): Inferência em tempo real no dispositivo com delegação automática entre CPU/GPU; segmentação e incorporação parecem instantâneas.
- Documentação e Comunidade (8/10): Guias sólidos de tarefas e aplicativos de amostra oficiais, embora algumas páginas de tarefas e a mudança de versão possam deixá-lo em dúvida.
- Capacidade Offline (10/10): Totalmente no dispositivo por design — nenhum caminho de rede em toda a linha de montagem.
- Manutenção e Estabilidade (7.5/10): Mantido pela Google e usado em produção, mas o ecossistema se move (as APIs mudam entre as versões e as tarefas GenAI foram depreciadas), então fixe suas versões.
As notas contam uma história clara que reflete as partes 1 e 2. Assim como o ML Kit troca a flexibilidade do modelo pela facilidade de uso, o MediaPipe troca um pouco da facilidade de uso pela flexibilidade do modelo e composição de tarefas. As duas menores pontuações — simplicidade da API e ergonomia Kotlin — são o preço para controlar toda a linha de montagem. Para equipes que precisam de um modelo personalizado ou uma funcionalidade que nenhuma única API oferece, é um preço bem valido.
Conclusão
A ferramenta de linha de montagem no dispositivo é uma escada, não um único degrau. A parte 1 abordou o ML Kit para recursos Google-model turnkey. A parte 2 abordou a Firebase AI Logic para capacidade Gemini com suporte em nuvem. Este artigo cobriu as MediaPipe Vision Tasks para o meio-termo: quando você ainda está totalmente no dispositivo e offline, mas precisa de seu próprio modelo ou sua própria linha de montagem.
O sinal de que você superou o ML Kit é simples — você se encontra querendo um modelo específico, ou querendo conectar dois resultados AI em uma única decisão. Face Finder é exatamente isso: três tarefas, um veredicto ponderado, sem nuvem e sem motor personalizado. O MediaPipe tornou isso uma funcionalidade de fim de semana em vez de um projeto de pesquisa.
Comece alto na escada. Desça para o MediaPipe no momento em que os modelos fechados do ML Kit ou as APIs de propósito único começarem a lhe atrapalhar — e desfrute do fato de que tudo ainda funciona, privadamente, direto no dispositivo.

