Files
dayMaker_lp/documentação/03_AI_VISION_LAYER.md
2026-05-13 15:59:02 +01:00

205 lines
5.0 KiB
Markdown

# 👁️ 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`