Architecture
Layers that know as little as possible about each other
Detectors and trackers know nothing about scenes. The spatial engine knows only geometry, the rule engine assigns meaning, and analytics only read what was stored. That separation is what lets a run be analysed again, a detector be swapped, and the licensed modules plug in without the core ever importing them.
The pipeline
- 1
Camera or video
A video file, USB webcam, or RTSP or HTTP stream.
- 2
Detection
A detector finds people, vehicles and other objects in each frame.
- 3
Tracking
Detections become anonymous tracks that restart with every run.
- 4
Your scene
Lines, gates, zones and routes turn movement into interactions.
- 5
Rules
Crossings, visits, dwell, routes and sequences are decided per track.
- 6
Events
One stored record per observation, with its track, time and rule.
- 7
Analysis
Summaries, video review, evaluation, exports and the API.
Worker process, one per running camera
-
Frame source
vision/sources/Files, USB cameras and RTSP or HTTP streams behind a FrameSource interface, with reconnection.
Knows nothing about detection.
-
Preprocessing
vision/preprocessing.pyRotation, crop, preview resizing and JPEG encoding for the browser.
-
Detector
vision/detectors/Ultralytics, ONNX Runtime or torchvision provider, chosen with the hardware probe and a recorded fallback chain (vision/inference/).
Knows nothing about lines, zones or routes.
-
Tracker
vision/trackers/ByteTrack or BoT-SORT: anonymous, session-scoped ids in tentative, tracked, lost and removed states.
-
Entity resolver Licensed
recognition/runtime.pyOnly with a licence: says whether a track is an enrolled person or a registered plate. The core sees an interface, never recognition code.
-
Spatial engine
spatial/engine.pyGeometry only: crossings with direction, zone presence and dwell, sampled ground points, calibrated distances.
Does not decide what an interaction means.
-
Rule engine
rules/engine.pyPer-track state machines for lines, zones, route groups, sequences and dwell rules. A track without complete evidence is never forced into a route.
-
Relationship engine Optional
relationships/engine.pyObservations, time-bounded relationships and correlated events, each with a confidence and its evidence.
Imports no detector, tracker or recognition model.
API process
-
Supervisor and storage
storage/writer.pyDrains the worker queues and writes events, track summaries and trajectories in batches to SQLite or PostgreSQL; restarts crashed live workers.
-
Cross-camera correlation Optional
crosscam/Joins identity sightings on different cameras into scored transitions, on a background thread.
-
Analytics and interfaces
analytics/Analytics only read stored rows. The REST API, WebSockets, webhooks and the cvscope command line sit on top.
Never re-runs detection to answer a question.
Paths are relative to backend/pathscope/ and link to the synced commit. A GPU or decoder crash stays inside its worker process; the API keeps running.
Processes and messages
Each running camera is its own process, supervised by the API. Live previews, run events and finished tracks travel on queues; commands travel back.
Technology decisions
From docs/architecture.md, with the reason for each choice.
| Concern | Decision | Reason |
|---|---|---|
| Backend | Python 3.11+, FastAPI, Pydantic v2, SQLAlchemy 2, Alembic | Mature, typed, well documented, and the vision ecosystem is Python. |
| Database | SQLite (default, WAL mode) or PostgreSQL via PATHSCOPE_DATABASE_URL | Local-first single-machine installs need zero setup; the ORM and migrations are dialect-neutral so server deployments can use PostgreSQL. |
| Detection | Provider interface with three implementations: Ultralytics (YOLO11 / YOLOv8 / RT-DETR), ONNX Runtime (exported detectors), torchvision (SSDLite, Faster R-CNN) | Ultralytics is the most practical maintained YOLO ecosystem but is AGPL-3.0, so it is an optional extra and its licence is shown in the model catalog. ONNX Runtime and torchvision give permissively licensed paths. |
| Tracking | In-house ByteTrack (default) and BoT-SORT (camera motion compensation, optional appearance matching) behind a Tracker interface | The ByteTrack algorithm is simple, MIT-licensed in its reference form, robust for people and vehicles, and an in-house implementation exposes the track states and statistics the research UI needs. Other trackers can be registered. |
| Inference runtime | Provider probe (PyTorch CUDA / MPS / CPU, ONNX Runtime CPU / CUDA / TensorRT execution providers) with AUTO selection and graceful fallback | Hardware-aware recommendation is a core requirement; no single runtime is available everywhere. |
| Video I/O | OpenCV VideoCapture for files, USB and RTSP/HTTP | Cross-platform and mature. Sources implement a small FrameSource interface so more can be added. |
| Workers | One multiprocessing process per running camera, supervised by the API process | A GPU or decoder crash must not take the API down; separate processes also avoid the GIL for decoding and inference. |
| Frontend | React 19, TypeScript 5.9, Vite 7, react-router 7, TanStack Query 5, Zustand 5, Konva 10 / react-konva, Recharts 3 | Mature, maintained, permissively licensed. Konva is a proper canvas scene graph with hit testing, which the editor needs. |
| Styling | Hand-written CSS with design tokens, system font stack, no component framework | Restrained industrial/scientific look, information density, no external requests at runtime. |
| Packaging | pyproject.toml, setup scripts, Dockerfile (CPU and CUDA), docker-compose with optional PostgreSQL | Reproducible installs without cloud services. |
Extension points
Small interfaces, each documented, each with a single place to register an implementation.
-
Detector
vision/detectors/base.pyLoad a model and return detections with canonical class names. Register the provider and add catalogue entries with honest requirement and licence data.
How to add one -
Tracker
vision/trackers/base.pyAnonymous, session-scoped ids with tentative, tracked, lost and removed states; register it with its user-facing settings.
How to add one -
FrameSource
vision/sources/base.pyopen, read, close and an optional seek. Add it to the source factory to accept a new kind of camera.
How to add one -
EntityResolver
domain/entities.pyWhat a track resolves to. The core only sees this interface, which is how the licensed modules stay out of it.
How to add one -
GraphBackend
relationships/graph.pyThe contract the relationship API queries. Relational tables serve it today; a graph database can be added behind it.
How to add one -
Vision language models
anomaly/llm/One provider at a time for the Anomaly Assistant: local servers, OpenAI-compatible APIs, Gemini or Claude.
How to add one
Repository layout
backend/
pathscope/
api/ FastAPI routers and request/response schemas
db/ SQLAlchemy models, session management
domain/ Pydantic domain models (scene config, rules, geometry)
hardware/ Hardware probe and recommendation engine
models/ Model catalog, installation, benchmarking
vision/
detectors/ Detector providers (ultralytics, onnxruntime, torchvision)
trackers/ Tracker interface, ByteTrack, BoT-SORT, camera motion, appearance encoders
sources/ Frame sources (file, usb, rtsp/http)
inference/ Runtime/device probing and selection
pipeline.py Frame -> detector -> tracker -> spatial -> rules
spatial/ Geometry, spatial engine, calibration
rules/ Rule engine, route state machine, actions
analytics/ Aggregation, behaviour metrics, export
workers/ Camera worker process, supervisor, message types
storage/ Event writer and retention
recognition/ Licensed modules (locked without a licence): common/, licensing/,
face/ (detector, alignment, quality, embeddings, matcher, enrollment),
plate/ (detector, preprocessing, ocr, parser, temporal), registry/,
events/, api/, runtime.py, service.py, catalog.py
relationships/ Relationship & Event Correlation Engine: entities, observations,
relation registry, spatial, temporal, confidence, rules, correlation,
engine, replay, graph, visual (interactive graph queries), patterns,
summary, access, service, api
location/ Location Engine: site hierarchy, layouts, camera topology, zone mapping,
plausibility and travel time, cross-camera settings, /api/locations
crosscam/ Cross-camera correlation: sightings, transitions, journeys, topology deviations
alembic/ Database migrations
tests/ Unit and integration tests
frontend/
src/
api/ Typed API client
components/ Reusable UI primitives
pages/ Projects, Live, Experiments, Data, Analysis, Cameras, Models, Hardware, Settings, Setup
scene-builder/ Canvas editor, tools, inspector, editor store (undo/redo)
rule-builder/ Structured rule editor (subject and HAS RELATIONSHIP clauses)
relationships/ Relationship explorer, timeline, search, rule editor, settings
locations/ Interactive graph (canvas, model, workbench), journeys, site plan,
topology editor, site view
docs/ Architecture, installation, models, data model, privacy, developer guide
samples/ Sample scene and experiment configurations
scripts/ Setup and development scripts Quality gates
Every pull request must pass four checks: a repository guard (no binaries, weights, secrets, invisible Unicode or media outside docs/), the backend tests and linter, the frontend type check, tests and build, and a dependency review. Then a code owner reviews it.
The conventions are strict about truthfulness: never present an estimate as a measurement, and label anything unfinished in the interface.