Embora muitas ferramentas de aálise facial ofereçam interfaces visuais para identificar rostos e marcar pontos chave, a verdadeira potência se revela quando se necessita acessar os dados subjacentes de forma programática. Se seu objetivo é ir além da visualização, obtendo, por exemplo, as 106 coordenadas 2D dos pontos faciais como um array NumPy para tarefas como estimativa de pose, animação 3D ou reconstrução, a maioria das interfaces gráficas é insuficiente.
A interface web de análise facial baseada em Gradio, por outro lado, expõe as capacidades do modelo InsightFace buffalo_l. Ela não apenas indica a presença de uma face, mas também disponibiliza a saída estruturada original — incluindo coordenadas 2D/3D de alta precisão, ângulos de pose e confiança — pronta para ser consumida via código. Este sistema serve como um pipeline de dados faciais pronto para uso. Para processamento em lote, integração em fluxos de trabalho Python ou conexão com outras ferramentas como OpenCV ou Blender, invocar sua API é significativamente mais robusto do que capturar telas e tentar extrair informações visuais.
Este guia demonstrará como interagir com essa interface Gradio usando scripts Python, sem depender de cliques no navegador, para obter as coordenadas dos pontos chave no formato NumPy: limpas, computáveis e reutilizáveis.
- Preparação do Ambiente e Verificação do Serviço
Antes de invocar a API, certifique-se de que o serviço Gradio esteja ativo e acessível. Verifique o status da aplicação:
1.1 Confirmação do Serviço Ativo
Execute um dos comandos abaixo em seu terminal para verificar se a porta 7860 está em uso pelo serviço:
netstat -tuln | grep 7860
# Ou (para sistemas baseados em Linux/macOS)
sudo lsof -i :7860
Se observar um processo Python associado à porta 7860, o serviço está pronto. Caso contrário, inicie-o conforme as instruções de sua configuração (ex: bash /root/build/start.sh) e aguarde a mensagem indicando que está "Running on public URL: http://localhost:7860".
1.2 Instalação de Dependências do Cliente (apenas requests)
Para interagir com a API Gradio, basta instalar a biblioteca requests:
pip install requests
Não é necessário instalar PyTorch, InsightFace ou Gradio no ambiente do cliente, pois a comunicação é puramente via HTTP.
1.3 Entendimento do Mecanismo da API Gradio
A interface Gradio expõe uma API HTTP POST padrão no caminho /api/predict/. Ela opera com um ponto de entrada unificado que aceita requisições JSON e retorna respostas JSON estruturadas. Os pontos importantes são:
- O corpo da requisição é um JSON que contém os valores dos componentes de entrada (ex: imagem em base64, opções de switches).
- O corpo da resposta também é um JSON, onde o campo
dataretorna os resultados dos componentes de saída na ordem em que foram definidos. - As coordenadas dos pontos chave estarão localizadas em um índice específico dentro do array
datada resposta, dependendo da configuração da interface.
- Processo Principal: Obtendo Coordenadas NumPy dos Pontos Chave
Agora, vamos criar um script Python para:
- Ler uma imagem local.
- Enviar uma requisição à API.
- Extrair as coordenadas 2D (106 pontos) como um array
np.ndarray(formato(106, 2)).
2.1 Preparação da Imagem e Estrutura da Requisição
Crie um arquivo Python, por exemplo, obter_pontos.py:
import base64
import numpy as np
import requests
import cv2
from pathlib import Path
# Função auxiliar para carregar e codificar a imagem
def carregar_e_codificar_imagem(caminho_imagem: Path) -> str:
"""Lê um arquivo de imagem e o codifica para uma string base64."""
with open(caminho_imagem, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
# Configurações da API e do arquivo de teste
URL_API = "http://localhost:7860/api/predict/"
IMAGEM_TESTE_PATH = Path("rosto_exemplo.jpg") # Substitua pelo caminho da sua imagem de teste
# Verificação se a imagem de teste existe
if not IMAGEM_TESTE_PATH.exists():
print(f"Erro: Imagem de teste não encontrada em '{IMAGEM_TESTE_PATH}'.")
print("Por favor, certifique-se de que 'rosto_exemplo.jpg' exista no diretório ou atualize o caminho.")
exit()
# 1. Codificar a imagem para base64
imagem_codificada_b64 = carregar_e_codificar_imagem(IMAGEM_TESTE_PATH)
# 2. Construir o payload da requisição
# A lista 'data' corresponde diretamente aos componentes de entrada da UI Gradio.
# A ordem é crucial e deve espelhar a UI (ex: [upload_imagem, toggle_bbox, toggle_keypoints, ...])
payload_api = {
"data": [
{"image": imagem_codificada_b64}, # Entrada da imagem (string base64)
False, # Desativar exibição da caixa delimitadora
True, # Ativar exibição dos pontos chave (fundamental para a saída)
False, # Desativar exibição da idade
False, # Desativar exibição do gênero
False, # Desativar exibição da pose
]
}
2.2 Envio da Requisição e Análise dos Pontos Chave
Continue no mesmo script, adicionando a lógica para a requisição e extração:
# 3. Enviar a requisição POST para a API
try:
resposta_api = requests.post(URL_API, json=payload_api)
resposta_api.raise_for_status() # Lança um HTTPError para respostas de status 4xx/5xx
dados_resposta = resposta_api.json()
# 4. Extrair os pontos chave do array 'data' da resposta
# Conforme a configuração padrão do WebUI, os pontos chave 2D são o terceiro item (índice 2).
if len(dados_resposta.get("data", [])) > 2 and dados_resposta["data"][2]:
pontos_2d_lista = dados_resposta["data"][2] # Formato: [[x1,y1], [x2,y2], ..., [x106,y106]]
# Converter para array NumPy para manipulação mais fácil
pontos_2d_numpy = np.array(pontos_2d_lista, dtype=np.float32)
print(f"✅ Pontos chave obtidos com sucesso. Formato: {pontos_2d_numpy.shape}")
print(f"Primeiros 3 pontos (X, Y):\n{pontos_2d_numpy[:3]}")
else:
print("⚠️ Atenção: Nenhuma face detectada ou pontos chave não retornados na posição esperada.")
print(f"Resposta completa da API: {dados_resposta}")
except requests.exceptions.RequestException as e:
print(f"❌ Erro na comunicação com a API Gradio: {e}")
except Exception as e:
print(f"❌ Ocorreu um erro inesperado: {e}")
2.3 Verificação Visual com OpenCV
Para confirmar a exatidão dos dados, visualize os pontos extraídos na imagem original:
# 5. (Opcional) Desenhar os pontos chave na imagem original usando OpenCV
if 'pontos_2d_numpy' in locals() and pontos_2d_numpy.size > 0:
imagem_viz = cv2.imread(str(IMAGEM_TESTE_PATH))
if imagem_viz is not None:
for x, y in pontos_2d_numpy:
cv2.circle(imagem_viz, (int(x), int(y)), 2, (0, 255, 0), -1) # Círculo verde, tamanho 2px
CAMINHO_SAIDA_IMAGEM = IMAGEM_TESTE_PATH.parent / f"{IMAGEM_TESTE_PATH.stem}_com_pontos.jpg"
cv2.imwrite(str(CAMINHO_SAIDA_IMAGEM), imagem_viz)
print(f"🖼️ Imagem com pontos chave salva em '{CAMINHO_SAIDA_IMAGEM}'")
else:
print(f"Erro: Não foi possível carregar a imagem '{IMAGEM_TESTE_PATH}' para visualização.")
Após executar o script, você terá um arquivo de imagem com 106 pontos verdes sobre as feições do rosto, validando a extração das coordenadas.
- Técnicas Avançadas: Lote, Normalização e Pontos 3D
Com a base esatbelecida, explore funcionalidades mais avançadas para cenários de uso reais:
3.1 Processamento em Lote de Múltiplas Imagens
Encapsule a lógica de extração em uma função para processar diretórios inteiros:
import os # Adicione 'os' se ainda não tiver importado
def processar_diretorio_imagens(diretorio_entrada: Path, diretorio_saida: Path):
"""
Processa todas as imagens JPG em um diretório, extraindo pontos chave e salvando como .npy.
"""
diretorio_saida.mkdir(parents=True, exist_ok=True)
for caminho_imagem_atual in diretorio_entrada.glob("*.jpg"):
print(f"Processando: {caminho_imagem_atual.name}...")
try:
imagem_b64_lote = carregar_e_codificar_imagem(caminho_imagem_atual)
payload_api["data"][0]["image"] = imagem_b64_lote # Atualiza a imagem no payload
resposta_lote = requests.post(URL_API, json=payload_api)
resposta_lote.raise_for_status()
dados_lote = resposta_lote.json()
if len(dados_lote.get("data", [])) > 2 and dados_lote["data"][2]:
pontos_lote = np.array(dados_lote["data"][2], dtype=np.float32)
nome_arquivo_saida = diretorio_saida / f"{caminho_imagem_atual.stem}.npy"
np.save(nome_arquivo_saida, pontos_lote)
print(f" 💾 Pontos chave salvos para {caminho_imagem_atual.name}")
else:
print(f" ⚠️ Nenhuma face ou pontos chave detectados em {caminho_imagem_atual.name}.")
except requests.exceptions.RequestException as e:
print(f" ❌ Erro de requisição para {caminho_imagem_atual.name}: {e}")
except Exception as e:
print(f" ❌ Falha ao processar {caminho_imagem_atual.name}: {e}")
# Exemplo de uso:
# DIRETORIO_FACES = Path("/caminho/para/suas/fotos")
# DIRETORIO_RESULTADOS = Path("./resultados_npy")
# processar_diretorio_imagens(DIRETORIO_FACES, DIRETORIO_RESULTADOS)
3.2 Obtenção de Coordenadas Normalizadas
As coordenadas retornadas são em pixels absolutos. Para compatibilidade entre diferentes resoluções, normalize-as para o intervalo [0, 1]:
# Suponha que a imagem original tinha dimensões (largura, altura)
largura_img, altura_img = 1920, 1080 # Use as dimensões reais da sua imagem
if 'pontos_2d_numpy' in locals() and pontos_2d_numpy.size > 0:
pontos_normalizados = pontos_2d_numpy / np.array([largura_img, altura_img])
print(f"Pontos chave normalizados (primeiros 3):\n{pontos_normalizados[:3]}")
3.3 Extração de 68 Pontos Chave 3D
O sistema também suporta a extração de 68 pontos chave 3D, que incluem um eixo Z para profundidade. Ajuste o payload para ativar esta opção:
# Payload modificado para requisição de pontos chave 3D
payload_3d_keypoints = {
"data": [
{"image": imagem_codificada_b64},
False, # Bounding box
False, # 2D Keypoints (desativar para não haver confusão)
False, # Idade
False, # Gênero
True, # ATIVAR 3D Keypoints (este é o switch que você precisa habilitar)
]
}
try:
resposta_3d = requests.post(URL_API, json=payload_3d_keypoints)
resposta_3d.raise_for_status()
dados_3d = resposta_3d.json()
# O índice do output 3D Keypoints pode variar, mas frequentemente é o último
# se for o último switch ativado no payload. Verifique a UI ou a resposta real.
if len(dados_3d.get("data", [])) > 5 and dados_3d["data"][5]: # Ex: Índice 5 se 6 switches no total
pontos_3d_numpy = np.array(dados_3d["data"][5], dtype=np.float32)
print(f"✅ Pontos chave 3D obtidos. Formato: {pontos_3d_numpy.shape}")
print(f"Primeiros 3 pontos 3D (X, Y, Z):\n{pontos_3d_numpy[:3]}")
else:
print("⚠️ Nenhuma face detectada ou pontos chave 3D não encontrados na resposta esperada.")
print(f"Resposta completa da API para 3D: {dados_3d}")
except requests.exceptions.RequestException as e:
print(f"❌ Erro ao solicitar pontos chave 3D: {e}")
- Solução de Problemas Comuns
Aqui estão alguns problemas frequentes e como resolvê-los:
4.1 Erro "Connection refused"
- Causa: O serviço Gradio não está em execução ou a porta
7860está ocupada por outra aplicação. - Solução:
- Inicie o serviço (ex:
bash /root/build/start.sh). - Verifique o uso da porta com
netstat -tuln | grep 7860. Se estiver ocupada, considere alterar a porta no scriptapp.pydo Gradio (ex:launch(port=7861)) e ajuste aURL_APIem seu script Python.
- Inicie o serviço (ex:
4.2 Número incorreto de pontos chave (menos de 106/68)
- Causa: Nenhuma face detectada na imagem, ou a face está muito obscura, angulada ou obstruída.
- Solução:
- Teste a mesma imagem na interface web do Gradio para verificar se ele consegue detectar a face e os pontos chave.
- Verifique o primeiro item da resposta
result["data"][0](ou[1], dependendo da configuração da UI) que geralmente contém informações de detecção ou caixas delimitadoras; se for uma lista vazia, nenhuma face foi detectada. - Use uma imagem de teste clara e frontal.
4.3 Valores de coordenadas anormais (todos zero ou muito grandes)
- Causa: A API retornou uma mensagem de erro ou uma estrutura de dados inesperada, mas o script não tratou essa situação.
- Solução: Implemente verificações robustas na resposta da API:
if resposta_api.status_code != 200:
raise Exception(f"Requisição à API falhou com status: {resposta_api.status_code}")
if "error" in dados_resposta:
raise Exception(f"Erro interno da API: {dados_resposta['error']}")
# Verifique também se 'data' existe e não está vazio, e se o índice esperado existe.
if not dados_resposta.get("data") or len(dados_resposta["data"]) <= 2: # Exemplo para índice 2
raise ValueError("Resposta da API não contém os dados esperados para pontos chave.")
4.4 Como determinar o índice exato de cada saída?
A ordem dos itens no array data da resposta JSON da API Gradio é determinada pela ordem dos componentes de saída definidos no gr.Interface(..., outputs=[...]) do script app.py do servidor Gradio. A maneira mais precisa de verificar é:
- Abra a interface Gradio no navegador (ex:
http://localhost:7860). - Abra as Ferramentas do Desenvolvedor (F12) e vá para a aba "Rede" (Network).
- Interaja com a UI (clique em "Analisar" ou similar) e observe as requisições XHR/Fetch.
- Encontre a requisição com o nome
predicte examine o corpo de sua resposta. A estruturaresult["data"]mostrará a ordem exata de todos os componentes de saída.
Isso garante que você esteja usando o índice correto, independentemente de futuras alterações na UI ou na implementação do servidor.