Senast uppdaterad: 2026-02-09
Det här dokumentet är den gemensamma planen för projektet i den här repot. Äldre/idé-dokument (t.ex. "stora arkitekturvisioner") hör hemma i separata filer.
Bygga ett CPU-first system för bildinferens (YOLO via ONNX Runtime) med:
- En Runner (FastAPI) som kan inferera uppladdade bilder och filer i en input-mapp.
- En enkel UI (Next.js) som använder Runnerns endpoints.
- En portabel “model bundle”-kontrakt (modell + labels + meta) som går att flytta till Raspberry Pi senare.
- Docker Compose-flöde som gör att projektet fungerar “direkt” på en ny maskin.
- Core: CPU Runner (FastAPI + ONNX Runtime) med stöd för robust bild-decode och HEIC.
- Architecture: Docker Compose fullstack (Runner + UI), Model Bundle-format (ONNX+Meta), och one-shot model prep.
- Workflow: Server-side upload persistens, demo-läge, och "Watch folder" för automatisk inferens.
- UI: Modern Next.js frontend med PWA-förberedelser, dark mode, och bounding box visualisering.
- Video: Video-inferens (MP4/AVI/MOV/MKV/WebM) med annoterad export, interpolering och H.264-preview.
- Integration: OPC UA Server (40100-1), MQTT Client, och Webhook-stöd — även för video.
- Ops: Health checks, loggning, "Model Registry" API, samt förbättrat build-flöde i
vision.bat. - Training: Dataset management API, tränings-UI, annoteringsverktyg och YOLO-export.
- Privacy: Ansikts-anonymisering (ULFD, MIT-licens) med blur/pixelate, API-status, frontend-integration och i18n.
- CPU först: Vi prioriterar stabilt API + filflöde + model bundle-kontrakt.
- Builder vs Runner: Träning/export hör hemma på devmaskin (x86), Runner ska kunna köras på Pi.
- Säkerhet runt radering: all destruktiv rensning är avstängd som default.
Goal: Stödja integritetskrav (GDPR) via automatisk anonymisering.
- Research: Utvärdera
Ultra-Light-Fast-Generic-Face-Detector-1MBvs YOLOv8n-face.- ULFD vald (MIT-licens, kommersiellt fri). YOLOv8-face = AGPL-3.0, SCRFD = icke-kommersiell.
- Implementation:
-
PrivacyEngineklass för sekundär ONNX-inferens. - Pillow-baserad
GaussianBlurellerPixelate. - Config:
VISION_PRIVACY_FACE_BLUR=1. - Buggfixar: prior-avkodning, preprocessing
(pixel-127)/128, default min_score=0.15 för ULFD.
-
- Integration:
- Inject
anonymize_faces()före huvud-inferensen iroutes.py. -
privacy_appliedochprivacy_facesi API-respons (InferResponse). -
/api/v1/privacyendpoint för status. - Frontend: Privacy-sektion i Inställningar (status, modell, läge).
- Frontend: Privacy-badge i inferensresultat ("🔒 X ansikten anonymiserade").
- i18n: Översättningar för alla 7 språk (sv, en, nl, sk, zh, fr, es).
- Stöd för att sudda registreringsskyltar (License Plates) i framtiden.
- Inject
PI-mål (2 veckor) Leverera ansikts-anonymisering som kan slås på via env-flagga, med fallback-modell, loggning och enkel QA.
Spår & Stories
- Research & Validering ✅
- Benchmarka face-detectors (ULFD-1MB vs YOLOv8n-face) på CPU.
- Dokumentera precision, latency och modellstorlek.
- Välj standardmodell + fallback.
- Resultat: ULFD (MIT) vald. 240×320 input, ~1MB, CPU-vänlig. YOLOv8-face (AGPL), SCRFD (icke-kommersiell) ej lämpliga.
- Core Implementation ✅
- Skapa
PrivacyEnginemed ONNX-session, pre/post-process. - Implementera
anonymize_faces()medGaussianBlur/Pixelate. - Lägg till
VISION_PRIVACY_FACE_BLUR=1(default off). - Säkerställ no-op när ingen modell finns.
- Fix: Prior-avkodning kördes ej (output "boxes" tolkades felaktigt som avkodade koordinater).
- Fix: Preprocessing använde
/255istället för(pixel-127)/128som ULFD kräver. - Fix: Default min_score sänkt till 0.15 för ULFD (producerar lägre scores).
- Integration ✅
- Hook i
routes.pyföre huvud-inferens. - Flagga i API-respons att anonymisering körts.
- Logga latency & antal ansikten.
- Frontend-integration (Settings-sida + resultat-badge).
- QA & Docs ✅
- Testbilder + smoke test (1 detected, 1 anonymized — korrekt).
- Uppdatera README/plan.md med usage.
- Kolla Docker-build med modellen.
Definition of Done
- Env-flagga aktiverar anonymisering.
- Inga krascher när privacy-modell saknas.
- Latency loggas.
- Dokumentation uppdaterad.
- Coral TPU (Edge TPU) integration.
- Hailo-8 integration (via ONNX EP).
Goal: Stödja vanliga videoformat (MP4, AVI, MOV, MKV, WebM) utöver enstaka bilder.
- Backend:
- Video-ingest via OpenCV (
cv2.VideoCapture) — extrahera frames. - Nytt endpoint
POST /api/v1/infer/videosom tar emot videofil. - Frame sampling-strategi: alla frames, var N:te, eller FPS-baserat (konfigurerbart).
- Batch-inferens per frame med bounding box-resultat.
- Valfritt: returnera annoterad video (bounding boxes inritade) som nedladdning.
- Privacy-stöd: anonymisera ansikten per frame innan detektion.
- Config:
VISION_VIDEO_FRAME_INTERVAL,VISION_VIDEO_MAX_FRAMES.
- Video-ingest via OpenCV (
- API-respons:
-
VideoInferResponsemed per-frame detektioner + sammanfattning. - Streaming-progress (SSE eller polling) under lång videobearbetning.
-
- Frontend:
- Video-uppladdning (drag-and-drop, filväljare med video-MIME).
- Progress-bar under bearbetning.
- Frame-navigator: bläddra mellan frames och se detektioner per frame.
- Video-preview med annoterade bounding boxes.
- Watch Folder:
- Stöd för videofiler i watch-mappen (auto-process).
- JSON-output per video (sammanfattning + per-frame).
PI-mål (3 veckor) Stödja vanliga videoformat i hela flödet: upload, inferens, preview, watch folder.
Spår & Stories
- Video Ingest & Frame Extraction (3–4 dagar)
- Lägg till
opencv-python-headlessi requirements. - Hjälparklass
VideoFrameExtractor(öppna, sampla frames, stäng). - Konfigurerbart: var N:te frame, max antal frames, FPS-target.
- Backend API (3–4 dagar)
-
POST /api/v1/infer/videoendpoint. -
VideoInferResponseschema (frames, detektioner per frame, total summary). - Streaming progress via SSE (
/api/v1/infer/video/status/{job_id}). - Bakgrundsjobb (async task) för längre videor.
- Frontend (4–5 dagar)
- Utöka uppladdning till att acceptera video.
- Progress-indikator under bearbetning.
- Frame-navigering (slider/timeline) med detektionsresultat per frame.
- Video-export: nedladdning av annoterad video.
- Watch Folder & Integration (2 dagar)
- Watcher känner igen videofiler.
- JSON-output med per-frame-resultat.
- MQTT/Webhook-notifiering vid klar video.
Definition of Done
- MP4/AVI/MOV kan laddas upp och infereras.
- Per-frame-resultat visas i UI.
- Privacy fungerar även på video (blur per frame).
- Watch folder hanterar videofiler.
Goal: Låta användaren välja vilken typ av detektion som ska utföras — t.ex. person, fordon, registreringsskylt, ansikte — istället för att alltid köra alla klasser.
- Funktionsväljare i UI:
- Dropdown/chip-selector på huvudsidan för att välja aktiva detektionsklasser.
- Spara senaste val i
localStorage. - Snabbval-profiler: "Alla", "Personer", "Fordon", "Anpassat".
- Backend:
- Utöka filter-API med predefined profiler (person, vehicle, all).
- Ny query-param
?classes=person,bus,carpå infer-endpoints. - Stöd för att filtrera redan vid inferens (NMS-nivå) eller post-filter.
- Multi-modell:
- Stöd för att byta modell beroende på uppgift (t.ex. YOLO-general vs face-only vs LPR).
- Task-baserad modellväxling: "person detection" → yolov8n, "face" → ULFD, "license plate" → LPR-modell.
- UI: Välj uppgift → system laddar rätt modell automatiskt.
- Kombinerade pipelines:
- Kör flera modeller i sekvens (t.ex. YOLO + Privacy + LPR).
- Sammanslagna resultat i en
InferResponse.
- Frontend:
- Funktionsväljare (task picker) med ikoner.
- Visa vilka klasser som är aktiva i resultat-panelen.
- i18n för alla funktionsnamn.
PI-mål (2 veckor) Ge användaren kontroll över vilken detektion som körs, med snabbval-profiler och framtidssäkrad multi-modell-arkitektur.
Spår & Stories
- Klass-filter i UI (2–3 dagar) ✅
- Chip-selector komponent med tillgängliga klasser (hämtade från aktiv modells
labels.txt). - Snabbval-knappar: "Alla", "Personer", "Fordon".
- Spara val i
localStorage, skicka som query-param.
- Backend Profiler (2–3 dagar) ✅
- Predefined profiler i
filters.json:all,persons,vehicles. - Endpoint
GET /api/v1/tasks— lista tillgängliga uppgifter. - Infer-endpoint accepterar
?classes=...för inline-filtrering.
- Multi-modell Arkitektur (3–4 dagar)
-
TaskRegistrysom mappar uppgift → modell. - Auto-switch modell vid task-byte.
- Config:
VISION_TASKSJSON-definition.
- Frontend Task Picker (2–3 dagar) ✅
- Task-picker komponent med ikoner (🧑 Person, 🚌 Fordon, 🔒 Ansikte, 🔤 Skylt).
- Visa aktiv task + klasser i resultat.
- i18n: sv, en + övriga språk.
Definition of Done
- Användaren kan välja detektionstyp i UI.
- Rätt modell/filter tillämpas automatiskt.
- Profiler möjliga att konfigurera.
- Kombinerade pipelines fungerar (detektion + privacy).
Goal: Låta användaren ladda ner bearbetade resultat — annoterade bilder (med bounding boxes), anonymiserade bilder, och annoterade/anonymiserade videor.
- Bild-export:
- Endpoint
GET /api/v1/export/image— returnera bild med inritade bounding boxes. - Valfritt: med eller utan privacy-blur (query-param
?privacy=1). - Valfritt: bara anonymiserad bild (utan bounding boxes) via
?mode=privacy_only. - Konfigurerbar box-stil: färg, tjocklek, labels on/off.
- Nedladdningsknapp i UI bredvid preview.
- Endpoint
- Video-export (kräver P13):
- Endpoint
POST /api/v1/infer/video/export/{job_id}— rendera annoterad video. - Bounding boxes inritade per frame med smooth interpolering.
- Privacy-blur per frame (om aktiverat).
- Format: MP4 (H.264) via ffmpeg, browser-kompatibelt.
- Asynkron bearbetning med progress (polling).
- Preview-endpoint
GET /api/v1/infer/video/preview/{job_id}. - Download-endpoint
GET /api/v1/infer/video/export/{job_id}.
- Endpoint
- Batch-export:
- Exportera alla bilder i en mapp som ZIP med annoterade versioner.
- Endpoint
POST /api/v1/export/batch— ta emot lista med filnamn. - Watch folder: valfri output av annoterade bilder (inte bara JSON).
- Config:
VISION_EXPORT_ANNOTATED=1,VISION_EXPORT_FORMAT=jpg|png.
- Frontend:
- Nedladdningsknapp (⬇️) på preview-bilden efter inferens.
- Export-meny: "Original", "Med detektioner", "Anonymiserad", "Anonymiserad + detektioner".
- Video: progress-bar + nedladdningslänk när klar.
- Batch-export: markera flera filer → "Exportera alla".
- i18n: exportrelaterade strängar för alla språk.
PI-mål (2 veckor) Ge användaren möjlighet att ladda ner bearbetade bilder och videor direkt från UI:t.
Spår & Stories
- Bild-export Backend (2–3 dagar) ✅
-
ImageAnnotator-klass: rita bounding boxes + labels på PIL-bild. - Endpoint
GET /api/v1/export/image?name=...&boxes=1&privacy=1. -
POST /api/v1/export/imageför uppladdade bilder. - Returnera JPEG/PNG som
StreamingResponse.
- Video-export Backend (3–4 dagar) ✅
- OpenCV-baserad video-skrivare (
cv2.VideoWriter). - Rita bounding boxes + valfri privacy per frame.
- Bakgrundsjobb med progress-tracking.
- Endpoint
POST /api/v1/export/video→ jobb-ID →GET /api/v1/export/video/{id}.
- Batch-export (2–3 dagar) ✅
- ZIP-generator med annoterade bilder.
- Watch folder output-mode:
VISION_WATCH_MODE=annotated. - Endpoint
POST /api/v1/export/batch.
- Frontend (3–4 dagar) ✅
- Nedladdningsknapp-komponent med dropdown-meny.
- Export-alternativ: original, annoterad, anonymiserad, båda.
- Progress-indikator för video + batch.
- i18n: sv, en + övriga språk.
Definition of Done
- Annoterade bilder kan laddas ner med ett klick.
- Anonymiserade bilder kan exporteras separat.
- Video-export fungerar med bounding boxes + privacy.
- Batch-export av hela mappar som ZIP.
- Watch folder kan skriva annoterade bilder.
Skapad: 2026-02-09 Baserad på analys av befintlig kodbas och gap mot P14/P15 specifikationerna.
| Område | Status | Kvarvarande arbete |
|---|---|---|
| P14 – Klass-filter i UI | Klart ✅ | — |
| P14 – TaskRegistry & multi-modell | Partiell (~50%) | Auto-switch modell vid task-byte, kombinerade pipelines |
| P14 – Task picker i UI | Klart ✅ | — |
| P15 – Video-export | Klart ✅ | — |
| P15 – Bild-export | Klart ✅ | — |
| P15 – Batch-export | Klart ✅ | Batch-export UI (filvälj + "Exportera alla") kvar |
| P15 – Watch folder annoterad output | Klart ✅ | — |
- Filtersystem (från P8):
FilterSelector.tsx,filters.json,_load_filters()/_save_filters()/_apply_filter()iroutes.py, CRUD-endpointsGET/POST/DELETE /api/v1/filters. - Labels-endpoint:
GET /api/v1/models/labels(routes.py L1480) hämtar klasser från aktiv modellslabels.txt. - Video-rendering:
_draw_boxes_on_frame()ivideo_render.pyL201 — kan återanvändas som bas förImageAnnotator. - Privacy-anonymisering:
POST /api/v1/privacy/anonymizereturnerar anonymiserad bild (utan bboxar).
Steg 1: ImageAnnotator-klass (backend/app/inference/image_export.py)
- Ny klass som tar en PIL-bild + lista av detektioner → ritar bounding boxes + labels.
- Konfigurerbar box-stil: färg (per klass), tjocklek, labels on/off.
- Återanvänd färglogik från
_draw_boxes_on_frame()ivideo_render.py. - Stöd för privacy-overlay (anropa
PrivacyEngine.anonymize_faces()omprivacy=1). - Returnera PIL Image (JPEG/PNG-ready).
Steg 2: Export-endpoints (backend/app/api/routes.py)
GET /api/v1/export/image?name=<filnamn>&boxes=1&privacy=1&mode=annotated|privacy_only- Läser bild från
output/ellerinput/, applicerar annotation. - Returnerar
StreamingResponse(JPEG).
- Läser bild från
POST /api/v1/export/image— för uppladdad bild (skicka bild + detektioner i body).- Schema:
ImageExportRequest,ImageExportParamsischema.py.
Steg 3: Tester
- Smoke test: ladda upp bild → infera → exportera annoterad → verifiera att bild har bboxar.
Steg 4: Inline ?classes= parameter (backend/app/api/routes.py)
- Lägg till
classes: Optional[str] = Query(None)påPOST /api/v1/inferochPOST /api/v1/infer/filtered. - Post-filter: om
classesanges, filtrera detektioner efter klass-namn (case-insensitive). - Fungerar oberoende av sparade filter-profiler.
Steg 5: Snabbval-profiler (models/filters.json)
- Lägg till predefined profiler:
"all"(tomt include = alla),"persons"(person),"vehicles"(car, bus, truck, motorcycle, bicycle). - Markera dem som
"builtin": trueså de inte kan raderas via API. - Uppdatera
_load_filters()ochDELETE-endpoint att skydda inbyggda profiler.
Steg 6: Chip-selector i UI (frontend/src/components/FilterSelector.tsx)
- Refaktorera till chip/toggle-baserad selector som visar top-klasser inline.
- Snabbval-knappar: "Alla", "Personer", "Fordon" som pre-selekterar rätt profil.
- Spara senaste val i
localStoragemed keyvision_active_filter.
Steg 7: Nedladdningsknapp (frontend/src/app/page.tsx)
- Lägg till ⬇️-knapp bredvid preview-bilden efter inferens.
- Export-dropdown-meny:
- "Original" — ladda ner originalbild.
- "Med detektioner" —
GET /api/v1/export/image?name=...&boxes=1 - "Anonymiserad" —
GET /api/v1/export/image?name=...&privacy=1 - "Anonymiserad + detektioner" —
GET /api/v1/export/image?name=...&boxes=1&privacy=1
- Triggera browser-download via
<a download>ellerURL.createObjectURL.
Steg 8: i18n (frontend/src/i18n/translations.ts)
- Nya nycklar:
export.download,export.original,export.annotated,export.anonymized,export.both,export.batchExport,export.exportAll. - Alla 7 språk (sv, en, nl, sk, zh, fr, es).
Steg 9: TaskRegistry (backend/app/inference/task_registry.py)
- Ny klass som mappar task-namn → modell-bundle-path.
- Config via
VISION_TASKSenv-var (JSON) ellertasks.jsonimodels/. - Standardtasks:
"detection"→ aktiv YOLO-modell (default)."face"→models/privacy/ulfd/v1/(om tillgänglig).
- Metod:
get_model_for_task(task: str) → ModelBundle. - Metod:
list_tasks() → List[TaskInfo]med metadata (namn, beskrivning, ikon, tillgängliga klasser).
Steg 10: GET /api/v1/tasks endpoint (backend/app/api/routes.py)
- Returnerar lista av tillgängliga tasks med metadata.
- Schema:
TaskInfo(name, description, icon, model_name, classes),TaskListResponse.
Steg 11: Task-baserad modellväxling
- Utöka infer-endpoints:
?task=face→ laddar ULFD-modell automatiskt. OnnxYoloEnginebehöver stödja hot-swap eller hålla flera sessions.- Fallback: om task-modell saknas → returnera 404 med tydligt meddelande.
Steg 12: Kombinerade pipelines
- Utöka
?task=till att acceptera kommaseparerade tasks:?task=detection,face. - Kör modeller i sekvens, slå samman resultat i en
InferResponse. - Privacy-pipeline: om
faceingår → anonymisera + returneraprivacy_applied=true.
Steg 13: Task-picker komponent (frontend/src/components/TaskPicker.tsx)
- Hämta tasks från
GET /api/v1/tasks. - Visa som horisontell rad med ikoner: 🧑 Person, 🚌 Fordon, 🔒 Ansikte, 🔤 Skylt.
- Selektera task → skicka
?task=...vid inferens. - Visa aktiv task + tillgängliga klasser i resultat-panelen.
- Spara val i
localStorage.
Steg 14: i18n för tasks (frontend/src/i18n/translations.ts)
- Nycklar:
tasks.title,tasks.detection,tasks.face,tasks.licensePlate,tasks.all,tasks.selectTask. - Alla 7 språk.
Steg 15: Batch-export backend
- Endpoint
POST /api/v1/export/batch— tar emot{ files: ["img1.jpg", "img2.jpg"], boxes: true, privacy: false }. - Genererar annoterade bilder → packar i ZIP → returnerar som
StreamingResponse. - Bakgrundsjobb om > 10 filer (med progress-tracking).
- Schema:
BatchExportRequest,BatchExportStatus.
Steg 16: Watch folder annoterad output (backend/app/watcher.py)
- Ny mode:
VISION_WATCH_MODE=annotated(utöverjson,move,both). - Config:
VISION_EXPORT_ANNOTATED=1,VISION_EXPORT_FORMAT=jpg|png. - När aktiverat: spara annoterad bild i
output/-mappen bredvid JSON. - Återanvänd
ImageAnnotatorfrån steg 1.
Steg 17: Batch-export UI (frontend/src/app/page.tsx)
- Markera flera filer i resultat-listan (checkboxar).
- "Exportera alla"-knapp →
POST /api/v1/export/batch. - Progress-bar under generering.
- Nedladdning av ZIP-fil.
Steg 18: Slutpolering & QA
- Verifiera alla endpoints med curl/Postman.
- Kontrollera i18n i alla 7 språk.
- Uppdatera
plan.md— markera klara items. - Testa Docker-build.
| Risk | Påverkan | Mitigation |
|---|---|---|
| Multi-modell hot-swap kan ge minnespress | Hög | Lazy-load modeller, unload inaktiv session |
ImageAnnotator font-rendering i Docker |
Låg | Fallback till OpenCV putText om PIL font saknas |
| Batch-export av stora mappar (100+ bilder) | Medel | Bakgrundsjobb + fildelstorleksgräns |
| Task-pipeline latency (2+ modeller) | Medel | Parallellisera oberoende modeller, cacha resultat |
- Bild-export (P15) — störst användarvärde, enklast att implementera.
- Chip-selector + snabbval (P14) — UX-förbättring med befintlig backend.
?classes=inline-filter (P14) — liten ändring, stor flexibilitet.- Batch-export (P15) — bra komplement, men kräver ZIP-logik.
- TaskRegistry (P14) — arkitekturförändring, framtidssäkrar.
- Watch folder annoterad output (P15) — nisch-feature, låg prioritet.
- plan.md är källan för status + TODO.