hasari-api / ARCHITECTURE.md
erdoganpeker's picture
v0.3.0 — multimodal vehicle damage MVP
e327f0d
# Mimari — Hasarİ v2
Bu doküman ML pipeline'ın iç işleyişini, parça-merkezli çıktı reorganizasyonunu ve servisler arası kontratları açıklar.
## 1. ML Pipeline — Akıllı Hibrit Mimari
Pipeline iki YOLO26-seg modelini **paralel** koşturur, sonra **akıllı eşleme** ile her hasarı doğru parçaya atar.
```
Görüntü
├──► Quality check (blur, exposure, vehicle present)
├──► [Paralel] Hasar modeli (YOLO26-seg, 6 sınıf)
│ └─► damages: [{type, polygon, bbox, confidence}, ...]
├──► [Paralel] Parça modeli (YOLO26-seg, 23 sınıf)
│ └─► parts: [{name, polygon, bbox, confidence}, ...]
├──► Akıllı eşleme (IoU + intersection_ratio)
│ ├─ Hasar parçaya tam kaplı → tek parça
│ ├─ Hasar iki+ parçaya yayılıyor → multi-part flag + affected_parts
│ ├─ Düşük güven → is_low_confidence_match flag
│ └─ Parça bulunamadı → primary_part = "unknown"
├──► Şiddet sınıflandırma (ensemble)
│ ├─ Kural-tabanlı: maske alanı + hasar tipi + parça → hafif/orta/ağır
│ ├─ CNN classifier: hasar bölgesi crop → 3-sınıf
│ └─ Ensemble: ağırlıklı kombinasyon
├──► Maliyet motoru (kademeli lookup)
│ ├─ (parça, hasar_tipi, şiddet) → range
│ ├─ Aynı parçada çoklu hasar → en pahalı dominant
│ └─ Toplam aggregation
└──► Output formatter (parça-merkezli reorganizasyon)
└─► JSON: { parts: [...], summary, multi_part_damages, visualization_urls }
```
### Neden paralel + IoU eşleme?
**Alternatif 1 — Kaskad (parça → her parçada hasar):**
- ✅ Daha az false positive (sadece parça içi tarama)
- ❌ Yavaş (her parça için ayrı inference)
- ❌ İki parçaya yayılan hasarda problem (kapı↔çamurluk arası çizik)
- ❌ Parça kaçırılırsa üzerindeki hasar da kaçar
**Alternatif 2 — Naif paralel (her hasar en yakın parçaya):**
- ✅ Hızlı, tek geçişte
- ❌ Yanlış parça eşleme riski
**Seçim — Akıllı paralel:**
- Paralel hızını korur
- IoU + intersection_ratio ile *doğru* eşleme yapar
- Multi-part hasarı ayrı işaretler (bilgi kaybı yok)
- Parça kaçırılsa bile hasar yine tespit edilir ("unknown" fallback)
### IoU eşleme kuralları
```python
# pseudocode — services/ml/pipeline.py
for damage in damages:
candidates = []
for part in parts:
iou = iou(damage.mask, part.mask)
intersection_ratio = intersection_area(damage.mask, part.mask) / damage.area
if intersection_ratio >= 0.5: # hasar parçanın yarısından fazla içindeyse
candidates.append((part, iou, intersection_ratio))
if not candidates:
damage.primary_part = "unknown"
elif len(candidates) == 1:
damage.primary_part = candidates[0][0].name
else:
# Birden fazla parça örtüşüyor → multi-part
candidates.sort(key=lambda c: -c[2]) # intersection_ratio'ya göre
damage.primary_part = candidates[0][0].name
damage.secondary_parts = [{"part": c[0].name, "ratio": c[2]} for c in candidates[1:]]
damage.is_multi_part = True
# Düşük güven kontrolü
if candidates and candidates[0][1] < 0.05:
damage.is_low_confidence_match = True
```
`iou_threshold = 0.05` bilinçli olarak düşük — bilinmeyen parça > yanlış parça. Detaylar: `services/ml/pipeline.py`.
## 2. Parça-merkezli output reorganizasyonu
Pipeline iç düzeyde **hasar-listesi** tutar, ama API kullanıcısına **parça-merkezli** JSON döner. `services/ml/output_formatter.py` bu çeviriyi yapar.
İç (raw):
```python
damages = [{id, type, primary_part, severity, cost, ...}, ...]
parts = [{name, polygon, confidence}, ...]
```
Dış (kullanıcıya):
```python
parts = [
{
name, name_tr, status, # status: clean | minor | moderate | severe
damage_count,
damages: [...], # bu parçaya ait hasarlar
part_cost_min_tl, part_cost_max_tl,
cost_note, # "Tek parça değişimi diğer hasarları kapsar"
}
]
```
Kritik: **hasarsız parçalar da listede**, `status: "clean"` ile. Kullanıcı "kontrol edildi, hasar yok" güvencesi alıyor.
## 3. Şiddet ensemble
`services/ml/severity_classifier.py` üç katman:
1. **Kural-tabanlı** (`RuleBasedSeverity`):
- `area_ratio < 0.005` → hafif
- `0.005 ≤ area_ratio < 0.02` → orta
- `area_ratio ≥ 0.02` → ağır
- Hasar tipi çarpanları: glass_shatter → her zaman ≥ orta, scratch → genelde hafif
2. **CNN classifier** (`CNNSeverity`):
- EfficientNetV2-S, hasar crop'u → 3-sınıf softmax
- Roboflow severity setinde fine-tune edilir
3. **Ensemble** (`EnsembleSeverity`):
- Anlaşıyorlarsa → o seviye, yüksek güven
- Anlaşmıyorlarsa → ağırlıklı (kural %40, CNN %60), düşük güven flag
v1'de kural-tabanlı yeter (açıklanabilir, sigortacı için satılabilir). CNN v2 backlog.
## 4. Maliyet motoru — kademeli lookup
`services/ml/cost_engine.py` + `cost_table.yaml` (TR-specific).
**Lookup hiyerarşisi:** (parça, tip, şiddet) → (parça, tip default) → (global default) → hard default. Her seviye düştükçe `cost_confidence` düşer (high → medium → low).
**Çoklu hasar aggregation:** Aynı parçada birden fazla hasar varsa, naif toplam yanlış olur (parça değişimi diğer hasarları zaten kapsar). En pahalı %70'i aşıyorsa, sadece o alınır + `cost_note: "Tek parça değişimi diğer hasarları kapsar"`.
**v1 sınırlamaları:**
- Lookup tablosu manuel — TR pazar verileriyle güncellenmeli (otoyedek, yetkili servisler, TSB)
- Araç modeline duyarlı değil (Fiat Egea vs. BMW 5 farkı yok) — v2 backlog
- İşçilik vs. parça ayrı satır değil — agregate range
## 5. Servisler arası kontratlar
### Backend ↔ Frontend (TypeScript)
`packages/types/src/` altında Pydantic şemalarının birebir TS karşılığı. Web, desktop ve mobile aynı tipleri kullanır.
| Backend (Pydantic) | Frontend (TS) | Yer |
|---|---|---|
| `models.Damage` | `Damage` | `packages/types/src/damage.ts` |
| `models.Part` | `Part` | `packages/types/src/part.ts` |
| `models.Inspection` | `Inspection` | `packages/types/src/inspection.ts` |
| `models.InspectionSummary` | `InspectionSummary` | `packages/types/src/inspection.ts` |
| `models.HealthResponse` | `HealthResponse` | `packages/types/src/api.ts` |
Senkron tutmak için: `python services/backend/scripts/export_openapi.py` çalıştırıp `packages/types/openapi.json` üretilir, manuel karşılaştırma yapılır. (v0.2'de openapi-typescript ile otomatik gen düşünülebilir.)
### Frontend (UI paylaşımı)
`packages/ui` web ve desktop tarafından doğrudan import edilir. Mobile (RN) React DOM yerine RN component'leri kullandığından `packages/ui`'yi DOĞRUDAN kullanmaz — `apps/mobile/components/` altında benzer component'ler RN-native olarak yaşar. Hedef: aynı API yüzeyini koru, render farklı.
### ML ↔ Backend
ML pipeline (`services/ml/pipeline.py`) Python class olarak çağrılır:
```python
pipeline = DamagePipeline(damage_weights="...", parts_weights="...", ...)
result = pipeline.run(image_path) # dict, packages/types ile uyumlu
```
Backend `services/backend/ml_service.py` singleton wrapper'ı tutar (model yükleme tek sefer, request başına inference). Celery worker (`worker.py`) async job için bu singleton'ı yeniden kullanır.
## 6. Veri akışı — uçtan uca
```
Kullanıcı (Web/Desktop/Mobile)
│ multipart POST /api/v1/inspect
FastAPI
├─ S3/MinIO'ya orijinal görüntü yükle
├─ Postgres'e inspection kaydı (status=queued)
├─ Celery task'ı kuyruğa koy
└─ {inspection_id, status_url} döndür
Celery Worker (Redis broker)
├─ S3'ten görüntü çek
├─ ML Pipeline (singleton, warm)
│ ├─ paralel hasar + parça inference
│ ├─ IoU eşleme, severity, cost
│ └─ visualization.py: 3 PNG üret (annotated, parts, damages)
├─ Postgres'e sonuç + status=completed
├─ S3'e visualization PNG'leri
└─ Redis pub/sub: status push
Frontend
├─ Polling: GET /api/v1/inspect/{id} (2sn aralık)
│ veya WS /api/v1/inspect/{id}/stream
└─ status=completed → result JSON + visualization URL'leri
```
## 7. Performans hedefleri
| Senaryo | Hedef latency | Notlar |
|---|---|---|
| Sync tek görüntü (cloud GPU, T4) | < 2sn | YOLO26-m batch=1 |
| Async 5 görüntü | < 8sn | paralel inference |
| Async 30 görüntü (360° tarama) | < 30sn | Celery 4-worker |
| Mobile on-device QC | < 100ms | YOLO26-n TFLite |
| Cold start (model load) | < 8sn | singleton ile bir kere |
Yerel dev (RTX 5050 8GB): batch=8 m-model ~30ms/image. Production GPU (T4) benzer.
## 8. Gözlemlenebilirlik (v0.2)
- **Sentry** — frontend ve backend hataları
- **Prometheus** — request latency, inference time, queue depth, error rate
- **Grafana**`observability/` altında dashboard JSON
- **Structured logs** — backend loguru JSON, frontend pino (browser)
- **Trace ID** — her inspection için, headers ve log'larda
## 9. Güvenlik
- **Auth:** v1 API key (`X-API-Key`), v2 JWT
- **CORS:** `localhost:3000` (web dev), `tauri://localhost` (desktop prod), Expo emülatör. Production'da env'den whitelist.
- **CSP:** Tauri config'te sıkı, sadece localhost+https.
- **PII:** Plaka/VIN ham görüntüde — anonimleştirme v0.2.
- **Rate limit:** v0.2, Redis-based.
## 10. Bilinçli kısıtlamalar
- **CarDD academic non-commercial** — ticari pilot için ayrı izin gerekli, ya da kendi etiketli veri seti.
- **Türk araç modelleri zayıf temsil** — pilot'tan 500-1000 TR görüntüyü etiketleyip fine-tune (Hafta 11-12).
- **Aydınlatma/açı edge case'leri** — capture flow'da rehberlik, yine de %20-30 başarısız case kaçınılmaz.
- **Maliyet tablosu manuel** — TR pazar verisiyle güncellenmeli, otomatik scraping v2.