Netra / work.md
chikentikka's picture
plate recog by TrOCR and frontend and some other things updated
f09c2db
|
Raw
History Blame Contribute Delete
16.6 kB

πŸ› οΈ Work Log β€” TrafficGuard AI

Running log of what's built. Newest entries at the bottom of each phase.


Phase 1 β€” Foundation & Setup

βœ… Backend skeleton (FastAPI)

Project structure scaffolded and byte-compiles clean.

backend/
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ weights/                  # YOLO weights (gitignored)
└── app/
    β”œβ”€β”€ main.py               # FastAPI app, CORS, routers, static evidence mount
    β”œβ”€β”€ config.py             # pydantic-settings + storage dir bootstrap
    β”œβ”€β”€ schemas.py            # API response models (ViolationOut, AnalysisResult)
    β”œβ”€β”€ routes/
    β”‚   β”œβ”€β”€ upload.py         # POST /api/upload β€” full analysis pipeline
    β”‚   β”œβ”€β”€ violations.py     # GET /api/violations[, /{id}] with filters
    β”‚   └── analytics.py      # GET /api/analytics/summary, /by-type
    β”œβ”€β”€ models/
    β”‚   β”œβ”€β”€ preprocessor.py   # CLAHE enhance + Laplacian quality score
    β”‚   β”œβ”€β”€ detector.py       # YOLOv8 wrapper (lazy-loaded), COCOβ†’category map
    β”‚   β”œβ”€β”€ violation.py      # rule engine β€” triple-riding (IoU) shipped
    β”‚   └── ocr.py            # EasyOCR plate reader + Indian-format regex
    β”œβ”€β”€ database/
    β”‚   β”œβ”€β”€ db.py             # SQLAlchemy engine/session, init_db
    β”‚   └── models.py         # Violation table
    └── utils/
        β”œβ”€β”€ annotator.py      # draw detection/violation boxes
        └── evidence.py       # save uploads + annotated evidence

Design notes

  • Heavy ML deps (ultralytics, easyocr, cv2) are lazy-loaded so the API boots without weights present.
  • Pipeline: upload β†’ preprocess (CLAHE + conditional denoise) β†’ YOLO detect β†’ rule-based violation analysis β†’ annotate β†’ persist to SQLite.
  • Phase 1 ships triple-riding detection only; helmet/seatbelt/red-light land in Phase 4.

βœ… Repo scaffold

  • .gitignore (Python, node, weights, data artifacts, secrets)
  • data/{uploads,evidence,sample_images,annotations}, notebooks/, docs/

βœ… Frontend skeleton (Vite + React)

Builds clean (npm run build). Editorial light theme β€” off-white surfaces, Fraunces serif headings, single teal accent, generous whitespace. No neon.

frontend/src/
β”œβ”€β”€ main.jsx                 # BrowserRouter bootstrap
β”œβ”€β”€ App.jsx                  # routes
β”œβ”€β”€ api.js                   # axios client (VITE_API_URL)
β”œβ”€β”€ index.css                # design tokens (palette, fonts, radius)
β”œβ”€β”€ App.css                  # component styles
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ Layout.jsx           # sidebar nav + content outlet
β”‚   β”œβ”€β”€ StatCard.jsx
β”‚   β”œβ”€β”€ ViolationCard.jsx
β”‚   └── UploadPanel.jsx      # drag-and-drop upload
└── pages/
    β”œβ”€β”€ Dashboard.jsx        # stat cards + recent activity
    β”œβ”€β”€ Analyze.jsx          # upload β†’ evidence + violations
    β”œβ”€β”€ Violations.jsx       # filterable register table
    └── Analytics.jsx        # recharts bar (by type)

βœ… Environment + end-to-end smoke test

  • Root .venv (Python 3.13). Installed backend/requirements.txt β€” torch 2.12, ultralytics 8.3, easyocr, opencv 4.10, fastapi 0.115 all import clean.
  • Wired yolo_weights to backend/weights/ (config.py); weights auto-download there on first inference (yolov8n.pt, 6.2 MB βœ“).
  • Booted uvicorn app.main:app and verified the full path:
    • GET /health β†’ {"status":"ok"}
    • POST /api/upload (blank test frame) β†’ valid AnalysisResult (quality 0, 0 detections, evidence image generated)
    • GET /evidence/<id>.jpg β†’ 200
    • GET /api/analytics/summary β†’ {"total":0,...}
    • SQLite trafficguard.db created at repo root with violations table
  • No errors/warnings in server log.

Run: cd backend && ../.venv/bin/python -m uvicorn app.main:app --reload (must run from backend/ for the app.* imports to resolve)

βœ… Cross-platform support (Mac + Windows team)

  • run.sh (macOS/Linux) + run.bat (Windows) β€” launch the backend via the project .venv regardless of the shell's default python/uvicorn. Fixes the anaconda-PATH clash that caused ModuleNotFoundError: ultralytics.
  • config.py SQLite URL now uses .as_posix() so it's valid on Windows paths.
  • .gitattributes normalizes line endings (.sh β†’ LF, .bat β†’ CRLF, weights/ images marked binary).
  • README setup rewritten with macOS/Linux and Windows (PowerShell) steps; venv lives at repo root.

Note: run the backend with the .venv, not anaconda. Either use the launcher (./run.sh / run.bat) or activate the venv first.

βœ… Frontend redesign β€” editorial polish

Full visual pass on the dashboard. Builds + runs clean (Vite HMR on :5173).

  • Design system (index.css): warm paper palette, layered soft shadows, Fraunces display + Inter body + JetBrains Mono for plates, motion tokens, rise entrance animation, custom scrollbar, subtle radial background.
  • Layout: refined sidebar (gradient brand mark, active-route accent bar, nav heading) + sticky blurred topbar with date + "New analysis" CTA + a live backend status dot (polls /health every 15s).
  • Dashboard: icon stat cards (hover-lift, accent variant, trend chips), a donut "violation mix" with center total + legend, a gradient "get started" panel, and a recent-activity grid.
  • Analyze: image-preview on drop, before/after compare (original ↔ annotated), result metric chips, loader state, feature hint chips.
  • Records: live search + type filter + count pill, polished sticky-header table with severity badges and mono plate/IDs.
  • Analytics: donut + bar charts side by side.
  • New shared components: PageHeader, EmptyState, StatCard, Donut, refined ViolationCard + UploadPanel.
  • Responsive: collapses to single column under 940px.

Note: couldn't auto-screenshot from here β€” verified via npm run build (clean) and a dev-server boot (HTTP 200, no errors). View at http://localhost:5173.


Phase 2 β€” Image Preprocessing Pipeline

βœ… Adaptive preprocessor (preprocessor.py)

Assess the frame, then apply only the corrections it needs.

  • Quality assessment β†’ QualityReport (0-100): overall score plus sharpness (Laplacian var), brightness (mean), contrast (std).
  • CLAHE low-light enhancement (L channel) β€” fires on low brightness / flat contrast.
  • Denoise (fastNlMeansDenoisingColored) β€” low-light / low overall score.
  • Dehaze β€” per-channel percentile contrast stretch (washed-out frames).
  • Deblur β€” unsharp mask when Laplacian variance < 100 (motion blur).
  • normalize() β€” aspect-preserving 640Γ—640 letterbox utility (YOLO does its own resize, so kept off the main path).

preprocess() returns (image, QualityReport) and records which fixes ran.

βœ… Wired through the stack

  • schemas.py: new QualityReport; AnalysisResult.quality_score β†’ quality.
  • upload.py: passes asdict(report) into the response.
  • Frontend Analyze.jsx: quality score chip + "Auto-corrected:" chips listing applied fixes (great demo moment) β€” styled in App.css.

βœ… Verified

  • Unit test on synthetic dark / hazy / blurry / flat frames β€” correct corrections fire for each (e.g. dark β†’ Denoise + CLAHE; blurry β†’ +Sharpen).
  • normalize() β†’ (640, 640, 3) uint8.
  • Full serialization path (pipeline β†’ asdict β†’ AnalysisResult.model_dump_json) produces the nested quality object.
  • Frontend build clean.

Phase 3 β€” Vehicle & Person Detection

βœ… Structured detector (detector.py)

  • COCO_MAP: COCO id β†’ (kind, label, category) for person, bicycle, car, motorcycle (β†’ Two-Wheeler), bus (β†’ Public Transport), truck (β†’ Heavy Vehicle).
  • Each detection now carries kind / label / category / bbox / confidence / occupants (conf filter + NMS handled by YOLO).
  • Occupant counting: _assign_occupants() attributes each person to the vehicle whose box best contains them (intersection Γ· person area β‰₯ 0.2). Containment (not IoU) is used since a rider is far smaller than the vehicle.
  • summarize(): road-user counts grouped by category.

βœ… Wired through the stack

  • violation.py: triple-riding now reads occupants >= 3 from detection (no more recomputing IoU).
  • annotator.py: switched class β†’ kind; shows xN occupant count on vehicles with β‰₯ 2 riders.
  • schemas.py + upload.py: response gains a road_users breakdown.
  • Frontend Analyze.jsx: "Road users:" chip row (count Γ— category).

βœ… Verified

  • Crafted scene (two-wheeler + 3 overlapping persons + a car) β†’ moto occupants = 3, car 0, triple-riding fires with correct description.
  • summarize() groups correctly; full AnalysisResult (with road_users) serializes; frontend build clean.

Phase 4 β€” Violation Detection Models ⭐ CORE

βœ… Extensible rule framework (models/rules/ + violation.py)

Each rule is a self-contained module exposing CODE / NAME / SEVERITY, status() and check(scene). violation.py registers them, runs them all via analyze(detections, image), and exposes catalog(). Add a violation type by dropping a module in rules/ and registering it β€” no other code changes.

Scene (rules/base.py) gives each rule vehicles / persons / signals.

Rule Status How it works
Triple riding active occupants >= 3 on a Two-Wheeler
Helmet non-compliance needs-weight auto-enables when weights/helmet.pt exists; runs a helmet YOLO on each rider crop
Red-light running needs-config HSV signal-colour classify + stop-line; opt-in via red_light_enforcement
Seatbelt planned needs a driver-region classifier
Wrong-side driving planned needs per-camera lane direction
Illegal parking planned needs no-parking zone polygons

βœ… Supporting changes

  • detector.py: traffic lights now detected as kind="signal" (COCO 9); summarize() excludes signals.
  • config.py: helmet_weights, red_light_enforcement, stop_line_frac.
  • GET /api/rules (+ RuleInfo schema) returns the catalogue with statuses.
  • Frontend Dashboard.jsx: "Detection coverage" panel β€” all 6 rules with Active / Needs weight / Needs config / Planned tags ("N of 6 active").

βœ… Verified

  • catalog() β†’ 6 rules with correct statuses.
  • Triple riding fires via the coordinator (analyze(dets, img)).
  • Red-light: with enforcement on + a synthetic red signal, only the vehicle past the stop line is flagged (the one above the line is not).
  • Upload route runs end-to-end in-process; /api/rules returns 6; FE build clean.

βœ… Helmet rule live with a real model

Wired a trained helmet weight (helmet.pt, classes Plate / WithHelmet / WithoutHelmet). Fixes made when it didn't fire:

  • Class-name matching generalised (WithoutHelmet, head, "no … helmet").
  • Runs on the full frame (heads sit above the bike box) and attributes each bare-head box to the nearest two-wheeler by IoU.
  • Runs at imgsz=960 (config helmet_imgsz; heads were missed at 640), conf floor 0.3; dedupes to one helmet violation per vehicle.
  • Verified on a real triple-riding photo β†’ TRIPLE_RIDING + HELMET both fire.
  • Note: the model also emits Plate boxes β€” reuse for Phase 5 OCR.

To enable the model-backed rules

  • Helmet: drop a helmet-detection YOLO at backend/weights/helmet.pt (e.g. from Roboflow Universe) β€” the rule flips to active automatically.
  • Red-light: set red_light_enforcement=true (and tune stop_line_frac) once the camera's stop line is known.

Phase 5 β€” License Plate Recognition

βœ… Plate detection + OCR (plates.py, ocr.py)

  • Detection (PlateService): uses a dedicated plate.pt if present, else reuses the helmet model's Plate class (no extra weight needed). Config: plate_weights, plate_conf.
  • Preprocess (_prep): upscale small crops to ~96px height + CLAHE contrast.
  • OCR (PlateReader): EasyOCR with an A-Z0-9 allowlist, keeps only fragments with conf β‰₯ 0.3 (no more garbage), strips the embossed "IND" tag. Dropped the old blanket Oβ†’0/Bβ†’8 correction (it corrupted the letter section).
  • Matching: each plate is attached to the violation whose vehicle box best contains it β†’ Violation.license_plate is now populated and shown on cards / Records table.
  • Evidence: annotator draws plate boxes (teal) with the OCR text.

βœ… Verified

  • Synthetic legible plate β†’ reads MH12AB1234 exactly (OCR path correct).
  • Real wide-shot photo: plate region detected but only 02 legible at this resolution (54Γ—31px) β†’ correctly returns "No plate" instead of guessing.
  • False-positive plate on a shop sign is filtered (low conf + outside vehicles).
  • Full upload route runs end-to-end with plates wired in.

⚠️ Demo note: OCR accuracy depends on plate resolution. Use images where a plate is reasonably close/large to show real plate numbers. Distant plates in wide shots are too small to read (shown as "No plate", not wrong text).


Phase 6 β€” Evidence Generation & Storage

βœ… Evidence provenance + packaging

  • annotator.watermark(): burns a bottom bar onto evidence β€” TrafficGuard AI | <location> | <timestamp>.
  • evidence.save_metadata() / load_metadata(): a JSON evidence package sidecar per upload (evidence_id, timestamp, location, image names, quality, road users, violations).
  • upload.py: accepts a location form field, sets location + timestamp on each record, watermarks the image, writes the package JSON.

βœ… Review workflow (pending / confirmed / dismissed)

  • PATCH /api/violations/{id} (ViolationStatusUpdate) β€” confirm/dismiss.
  • GET /api/violations/{id}/evidence β€” returns the evidence package.
  • Frontend Records: status tag + βœ“/βœ— review buttons (dismissed rows dim), new Location column; Analyze has a camera location input that flows through to storage + watermark.

βœ… Verified

  • Upload with location="Dadar TT Junction" β†’ record stores location, evidence package JSON written with all 8 keys (2 violations inside).
  • PATCH flips status pending β†’ confirmed.
  • Backend compiles, full route + status + evidence endpoints run, FE build clean.

Phase 7 β€” Backend API (completed the gaps)

βœ… New endpoints

  • POST /api/batch-upload β€” runs the pipeline over many images; returns BatchResult {processed, total_violations, results[]}. Pipeline extracted into shared process_image() (used by both /upload and /batch-upload).
  • GET /api/analytics/trends β€” violations per day (func.date), oldest first.
  • GET /api/analytics/top-plates?limit= β€” top offenders by plate.

Full API surface now: upload, batch-upload, violations (list/get/patch/evidence), rules, analytics (summary/by-type/trends/top-plates), static /evidence.

βœ… Surfaced on the Analytics page

  • Trend line chart (violations/day) + Top offenders ranked list, alongside the existing donut + bar. api.js: getTrends, getTopPlates.

βœ… Verified

  • Batch of 2 β†’ processed 2, total_violations 4.
  • Trends groups by day correctly; top-plates returns [] here (test images have unreadable plates β€” expected). Backend compiles, FE build clean.

Frontend polish pass (judge-ready)

  • Animated count-up on all dashboard stat numbers (useCountUp hook + StatCard with numeric value + suffix).
  • Pipeline visualizer on Analyze (Pipeline.jsx): the 5 ML stages (Enhance β†’ Detect β†’ Classify β†’ Read plates β†’ Evidence) animate while processing, then show βœ“ + real per-stage stats (quality, users, flags, plates, evidence). Replaces the old static hint chips.
  • Verified visually via Playwright screenshots of all pages β€” dashboard, analyze (result), analytics, records all render cleanly and cohesively. (Installed playwright --no-save; not in package.json. Chromium cached in ~/Library, outside the repo.)

⏭️ Next (Phases 9-11)

  • Phase 9: test real sample images, edge cases, measure inference time
  • Phase 10: deploy (Vercel + Render/Railway)
  • Phase 11: pitch deck, demo video, README polish
  • Optional: evidence export/download, per-position OCR correction