# 👁️ Camada de Visão por IA ## Objetivo Usar IA de visão computacional para **identificar automaticamente** o que está na foto tirada pelo utilizador, sugerindo: - Nome do item - Categoria - Tags relevantes Isto reduz o esforço do utilizador ao mínimo. --- ## Serviço escolhido: Google Vision API ### Porquê Google Vision? - Fácil integração com Firebase (mesmo ecosistema Google) - Excelente deteção de objetos do quotidiano - Labels em múltiplos idiomas - Tier gratuito generoso (1.000 unidades/mês) - Documentação extensa ### Funcionalidades usadas no MVP | Feature | Uso | |---------|-----| | `LABEL_DETECTION` | Identificar o objeto (ex: "T-shirt", "Laptop") | | `OBJECT_LOCALIZATION` | Confirmar que existe um objeto na foto | > No MVP **não** usamos OCR, Safe Search ou outras features avançadas. --- ## Integração técnica ### Chamada à API (exemplo React Native) ```javascript // services/visionApi.js const VISION_API_KEY = process.env.GOOGLE_VISION_API_KEY; const VISION_API_URL = `https://vision.googleapis.com/v1/images:annotate?key=${VISION_API_KEY}`; export async function analyzeImage(base64Image) { const requestBody = { requests: [ { image: { content: base64Image }, features: [ { type: 'LABEL_DETECTION', maxResults: 10 }, { type: 'OBJECT_LOCALIZATION', maxResults: 5 } ] } ] }; const response = await fetch(VISION_API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); const data = await response.json(); return parseVisionResponse(data); } ``` ### Parsing da resposta ```javascript // services/visionApi.js (continuação) export function parseVisionResponse(data) { const labels = data.responses[0]?.labelAnnotations || []; const objects = data.responses[0]?.localizedObjectAnnotations || []; // Extrair labels com score > 0.7 const highConfidenceLabels = labels .filter(l => l.score > 0.7) .map(l => l.description.toLowerCase()); // Mapear para categoria da app const category = mapLabelsToCategory(highConfidenceLabels); // Sugerir nome baseado no objeto mais confiante const suggestedName = objects[0]?.name || labels[0]?.description || 'Item'; return { suggestedName, category, tags: highConfidenceLabels.slice(0, 5), rawLabels: highConfidenceLabels }; } ``` --- ## Mapeamento de Labels → Categorias ```javascript // constants/categoryMapping.js export const LABEL_TO_CATEGORY = { // Roupa 't-shirt': 'clothing', 'shirt': 'clothing', 'dress': 'clothing', 'jacket': 'clothing', 'coat': 'clothing', 'jeans': 'clothing', 'trousers': 'clothing', 'sweater': 'clothing', 'clothing': 'clothing', 'fashion': 'clothing', // Eletrónica 'laptop': 'electronics', 'computer': 'electronics', 'smartphone': 'electronics', 'tablet': 'electronics', 'headphones': 'electronics', 'camera': 'electronics', 'charger': 'electronics', 'cable': 'electronics', 'electronics': 'electronics', // Calçado 'shoe': 'footwear', 'boot': 'footwear', 'sneaker': 'footwear', 'sandal': 'footwear', 'footwear': 'footwear', // Acessórios 'bag': 'accessories', 'backpack': 'accessories', 'watch': 'accessories', 'sunglasses': 'accessories', 'hat': 'accessories', 'belt': 'accessories', // Documentos 'passport': 'documents', 'document': 'documents', 'book': 'documents', 'notebook': 'documents', }; export function mapLabelsToCategory(labels) { for (const label of labels) { if (LABEL_TO_CATEGORY[label]) { return LABEL_TO_CATEGORY[label]; } } return 'other'; // fallback } ``` --- ## Fluxo de UX ao adicionar item ``` 1. Utilizador tira foto 2. App mostra loading ("A analisar item...") 3. Google Vision responde com labels 4. App mostra: - Nome sugerido (editável) - Categoria sugerida (editável) - Tags sugeridas (checkboxes, editáveis) 5. Utilizador confirma ou ajusta 6. Item guardado ``` > O utilizador tem **sempre** controlo final. A IA apenas sugere. --- ## Tratamento de erros | Erro | Comportamento | |------|---------------| | API indisponível | Mostrar formulário manual, sem sugestões | | Foto sem objeto reconhecível | Mostrar sugestão "Outro" e pedir nome manual | | Score muito baixo (< 0.5) | Ignorar label, não sugerir | | Timeout | Retry 1x, depois formulário manual | --- ## Evolução futura desta camada - Fase 2: Treinar modelo custom com os dados dos utilizadores - Fase 3: Identificar marcas e modelos específicos - Fase 3: Reconhecimento de cor dominante para filtros de outfit --- ## Notas para o agente IA - A chave da API (`GOOGLE_VISION_API_KEY`) nunca deve aparecer em código client-side em produção — usar Firebase Cloud Functions como proxy - No MVP, a chamada pode ser feita diretamente do cliente para simplicidade - Nunca guardar `rawLabels` como `name` sem confirmação do utilizador - O mapeamento de labels é iterativo — quando encontrar labels não mapeadas, adicionar ao ficheiro `categoryMapping.js`