Video Analytics Guide¶
Comprehensive guide to the AI-powered video analytics features in Home Security Intelligence.
Overview¶
Home Security Intelligence provides a multi-model AI pipeline that transforms raw camera footage into actionable security insights. The video analytics system processes images through multiple specialized models to detect, classify, track, and assess security risks in real-time.
Key Capabilities¶
| Feature | Description | Models Used |
|---|---|---|
| Object Detection | Detect people, vehicles, animals, and objects | YOLO26 |
| Scene Understanding | Generate captions and descriptions | Florence-2-base |
| Anomaly Detection | Compare against learned baselines | SigLIP 2 Base (as clip) |
| Threat Detection | Identify weapons and dangerous items | Threat-Detection-YOLOv8n |
| Person Analysis | Pose, demographics, clothing, re-identification | Multiple models |
| Vehicle Analysis | Vehicle type, damage, license plates | Multiple models |
| Risk Assessment | LLM-based contextual risk analysis | Nemotron-3-Nano-30B |
Architecture¶
Detection Pipeline¶
Camera Upload -> File Watcher -> Object Detection -> Batch Aggregator -> Enrichment -> Risk Analysis -> Event
(1) (2) (3) (4) (5) (6) (7)
%%{init: {
'theme': 'dark',
'themeVariables': {
'primaryColor': '#3B82F6',
'primaryTextColor': '#FFFFFF',
'primaryBorderColor': '#60A5FA',
'secondaryColor': '#A855F7',
'tertiaryColor': '#009688',
'background': '#121212',
'mainBkg': '#1a1a2e',
'lineColor': '#666666'
}
}}%%
flowchart LR
subgraph Input["1. Image Input"]
CAM[Camera]
FTP[FTP Server<br/>/export/foscam/]
FW[FileWatcher<br/>inotify + debounce]
end
subgraph Detection["2. Object Detection"]
DQ[(detection_queue)]
YOLO[YOLO26<br/>ai-gateway /yolo26<br/>port 8090]
end
subgraph Batching["3. Batch Aggregation"]
BA[BatchAggregator<br/>90s window / 30s idle]
AQ[(analysis_queue)]
end
subgraph Enrichment["4. Context Enrichment"]
FLOR[Florence-2-base<br/>Scene captions<br/>ai-gateway /florence]
CLIP[SigLIP 2 Base<br/>Anomaly detection<br/>ai-gateway /clip]
MZ[Model Zoo<br/>On-demand models<br/>ai-gateway /enrichment]
end
subgraph Analysis["5. Risk Assessment"]
NEM[Nemotron-3-Nano-30B<br/>LLM risk scoring]
end
subgraph Output["6. Event Output"]
DB[(PostgreSQL<br/>Event storage)]
WS[WebSocket<br/>Real-time broadcast]
UI[React Dashboard]
end
CAM -->|FTP upload| FTP
FTP -->|inotify| FW
FW -->|queue job| DQ
DQ --> YOLO
YOLO -->|detections| BA
BA -->|batch ready| AQ
AQ --> FLOR
AQ --> CLIP
AQ --> MZ
FLOR -->|captions| NEM
CLIP -->|embeddings| NEM
MZ -->|enrichment| NEM
NEM -->|risk score| DB
NEM -->|event| WS
WS --> UI - Camera Upload: Cameras upload images via FTP to
/export/foscam/{camera_name}/ - File Watcher: Monitors directories for new images with deduplication
- Object Detection: YOLO26 identifies objects and their bounding boxes
- Batch Aggregator: Groups detections into 90-second time windows
- Enrichment: Model Zoo extracts additional context (clothing, pose, etc.)
- Risk Analysis: Nemotron LLM evaluates the complete context
- Event Creation: Security events are created and broadcast via WebSocket
Where the Models Run¶
All detection, vision-language, embedding and enrichment models run inside one container, ai-gateway. A FastAPI front on port 8090 translates HTTP to gRPC for a Triton Inference Server that runs in the same container on the GPU. The backend reaches each model through a router prefix:
| Router | Models served |
|---|---|
/yolo26 | YOLO26 object detection |
/florence | Florence-2-base |
/clip | SigLIP 2 Base (registered as clip / clip_text) |
/enrichment | vehicle, fashion-clip, demographics, action (heavy models) |
/enrich-lt | pose, threat, reid, pet, depth (light models) |
Triton loads its whole model list when the container starts; the compose healthcheck allows three minutes for it. The list lives in ai/gateway/main.py (ALL_MODELS), and models.yml decides whether each model lands on the GPU or the CPU.
Models Used Per Analysis Step¶
Per-model VRAM from models.yml:
| Model | Purpose | VRAM (MB) | Served under |
|---|---|---|---|
| threat-detection-yolov8n | Weapon detection | 300 | /enrich-lt |
| yolov8n-pose | Body posture analysis | 200 | /enrich-lt |
| osnet-ain-x1-0 | Person re-identification | 100 | /enrich-lt |
| pet-classifier | Cat/dog detection | 200 | /enrich-lt |
| depth-anything-v2-tiny | Distance estimation | 100 | /enrich-lt |
| vit-age-classifier | Age estimation | 200 | /enrichment |
| vit-gender-classifier | Gender estimation | 200 | /enrichment |
| fashion-clip | Clothing analysis | 500 | /enrichment |
| vehicle-segment-classification | Vehicle type | 1500 | /enrichment |
| stgcn-plus-plus | Action recognition | 20 (CPU) | /enrichment |
Set ENRICHMENT_<TASK>_SERVICE in .env to move a task between the heavy and light endpoints; the defaults are in .env.example.
Object Detection¶
YOLO26 Detection¶
The primary object detector uses YOLO26 for fast, accurate detection. Run the requests against the AI gateway, not a standalone detector container:
# Check detector health
curl http://localhost:8090/yolo26/health
# Single-image detection (multipart upload)
curl -F "file=@frame.jpg" http://localhost:8090/yolo26/detect
# Batch detection
curl -F "files=@a.jpg" -F "files=@b.jpg" http://localhost:8090/yolo26/detect/batch
Detected Object Classes:
- People: person
- Vehicles: car, truck, bus, motorcycle, bicycle
- Animals: dog, cat, bird
- Objects: backpack, handbag, suitcase, umbrella
- And 80+ COCO classes
Detection Response:
{
"detections": [
{
"class": "person",
"confidence": 0.9214,
"bbox": { "x": 120, "y": 80, "width": 160, "height": 370 }
}
],
"image_width": 1920,
"image_height": 1080,
"inference_time_ms": 5.76
}
Detection Filtering¶
Detections are filtered by:
- Confidence threshold: Minimum confidence to keep a detection.
.env.exampleshipsDETECTION_CONFIDENCE_THRESHOLD=0.5; classes listed inDETECTION_CLASS_THRESHOLDSuse their own value instead (for exampleperson0.40,car0.50,bus0.55) - Object classes: Filter to security-relevant objects
- Zone filtering: Only process detections in defined zones
Scene Understanding¶
Florence-2 Captioning¶
Florence-2-base provides rich scene descriptions. The gateway exposes it as a task-token prompt API, not a per-task endpoint — you post an image and a Florence task token:
# Health check
curl http://localhost:8090/florence/health
# Caption a frame
curl -X POST http://localhost:8090/florence/extract \
-H 'Content-Type: application/json' \
-d '{"image": "<base64>", "prompt": "<MORE_DETAILED_CAPTION>"}'
Task tokens the backend sends to /extract:
| Token | Description |
|---|---|
<CAPTION> | Brief scene description |
<DETAILED_CAPTION> | Comprehensive scene analysis |
<MORE_DETAILED_CAPTION> | Extended detailed description |
Other Florence endpoints on the same router:
| Endpoint | Description |
|---|---|
/florence/ocr | Text recognition |
/florence/ocr-with-regions | Text with bounding boxes |
/florence/detect | Bounding box detection |
/florence/dense-caption | Per-region descriptions |
/florence/describe-region | Caption one region |
/florence/phrase-grounding | Ground a phrase to boxes |
/florence/detect_security_objects | Fixed security vocabulary |
/florence/batch-extract | Many images in one request |
Response Example:
{
"result": "A person in a blue jacket approaches the front door carrying a package",
"prompt_used": "<MORE_DETAILED_CAPTION>",
"inference_time_ms": 145.2
}
Anomaly Detection¶
Zone Baseline Comparison¶
The system learns normal activity patterns per zone and flags deviations from them (backend/services/zone_anomaly_service.py):
- Detections accumulate into per-zone baselines
- Each new detection is scored against the baseline in standard deviations
- A deviation past the threshold raises an anomaly (severity: >= 3.0 std is WARNING, >= 4.0 std is CRITICAL)
Baseline Metrics:
| Metric | Description |
|---|---|
hourly_pattern | Expected activity by hour (24 buckets) |
day_of_week_pattern | Expected activity by day (7 buckets) |
typical_dwell_time | Average time objects stay in view |
typical_crossing_rate | Expected zone crossings per hour |
Anomaly Checks (all three compare in standard deviations against the baseline; default trigger is 2.0 std):
- Unusual time: detection at an hour the zone is normally quiet
- Unusual frequency: a spike or drop in recent detections for the zone
- Unusual dwell: an object in view far longer than the zone typical
New people and vehicles are handled separately, by household matching and face recognition — see Face Recognition Guide.
Baseline Visualization¶
The dashboard provides four visualization components for understanding learned activity patterns:
24-Hour Activity Pattern (HourlyPatternChart)¶
A line chart showing average detections for each hour of the day (0-23):
- Green line: Average detections per hour
- Shaded band: Confidence interval (+/- 1 standard deviation)
- Orange dot: Peak activity hour
- Point opacity: Data quality indicator (more samples = more opaque)
Interpreting the chart:
- Full opacity points have 20+ samples (high confidence)
- Faded points have fewer samples (still learning)
- Hover over points to see exact values and sample counts
Weekly Activity Pattern (DailyPatternChart)¶
A bar chart showing activity levels for each day of the week:
- Bar height: Average detections for that day
- Bar color intensity: Activity level relative to the busiest day
- Orange dot on bar: Peak hour for that day
- Weekend bars: Highlighted in blue
Interpreting the chart:
- Hover over bars to see average detections, peak hour, and total samples
- "Busiest day" and "Quietest day" badges identify patterns
Current Deviation Status (BaselineDeviationCard)¶
A color-coded card showing how current activity compares to baseline:
| Color | Interpretation | Deviation Range |
|---|---|---|
| Blue | Far below / Below normal | < -1.0 std dev |
| Green | Normal | -1.0 to +1.0 std dev |
| Yellow | Slightly above normal | +1.0 to +2.0 std dev |
| Orange | Above normal | +2.0 to +3.0 std dev |
| Red | Far above normal | >= +3.0 std dev |
The "far below" and "below normal" bands split again at -2.0 std; both render in blue.
The card displays:
- Deviation score: Number of standard deviations from baseline
- Contributing factors: What's causing the deviation (e.g., "high_person_count")
- Last updated: When the deviation was calculated
Object Baseline Chart (ObjectBaselineChart)¶
Per-class detection statistics showing frequency patterns by object type:
- Grouped bars per class: Average hourly rate, peak hour, total detections
- Color-coded by class: Person (blue), Vehicle (green), Animal (orange), etc.
- Sort options: By frequency, total detections, peak hour, or alphabetically
Baseline Tuning¶
For per-camera baseline configuration, see the Baseline Tuning Panel in the camera settings:
- Sensitivity threshold: Adjusts how many standard deviations trigger an anomaly (0.5-5.0)
- Minimum samples: Sets how many data points are needed before detection is reliable
- Reset baseline: Clears all learned data to start fresh
API endpoints for programmatic control are documented in Baseline Configuration API.
Person Analysis¶
Pose Estimation¶
POST http://localhost:8090/enrich-lt/pose-analyze runs yolov8n-pose (17 COCO keypoints) and derives a posture from the keypoint geometry:
{
"keypoints": [
{ "name": "nose", "x": 0.45, "y": 0.12, "confidence": 0.95 },
{ "name": "left_shoulder", "x": 0.42, "y": 0.25, "confidence": 0.92 }
],
"posture": "standing",
"alerts": [],
"num_people": 1,
"inference_time_ms": 12.4
}
Posture values the gateway emits: standing, crouching, lying, bending, unknown. A crouching or lying posture adds a text alert to the alerts list.
Demographics¶
POST http://localhost:8090/enrichment/demographics estimates age and gender from a face crop:
{
"age_range": "21-30",
"age_confidence": 0.81,
"gender": "male",
"gender_confidence": 0.93,
"inference_time_ms": 22.1
}
Age Ranges: 0-10, 11-20, 21-30, 31-40, 41-50, 51-60, 61-70, 71+ — the labels the gateway maps demographics_age class indices onto in ai/gateway/adapters/enrichment.py. The DemographicsResults.age_range check constraint in backend/models/enrichment.py accepts those plus 71-80, 81+ and unknown.
Clothing Analysis¶
POST http://localhost:8090/enrichment/clothing-classify runs the Marqo FashionSigLIP zero-shot clothing classifier:
{
"clothing_type": "jacket",
"color": "blue",
"style": "casual",
"confidence": 0.74,
"top_category": "outerwear",
"description": "Blue jacket, dark trousers",
"is_suspicious": false,
"is_service_uniform": false,
"inference_time_ms": 31.0
}
Person Re-Identification¶
POST http://localhost:8090/enrich-lt/person-reid runs OSNet-AIN x1.0 and returns a normalized 512-dimensional embedding for tracking across cameras:
Matching and match thresholds are applied in the backend, not by the gateway.
Use Cases:
- Track individuals across multiple cameras
- Identify repeat visitors
- Link detections to known household members
Vehicle Analysis¶
Vehicle Classification¶
POST http://localhost:8090/enrichment/vehicle-classify runs a ResNet-50 trained on MIO-TCD. It scores eleven labels, drops background and pedestrian, and returns the best remaining one:
{
"vehicle_type": "car",
"display_name": "car/sedan",
"confidence": 0.91,
"is_commercial": false,
"all_scores": { "car": 0.91, "pickup_truck": 0.05 },
"inference_time_ms": 18.3
}
Vehicle Classes: articulated_truck, bicycle, bus, car, motorcycle, non_motorized_vehicle, pickup_truck, single_unit_truck, work_van
License Plate Detection¶
The enrichment pipeline prefers FastALPR (end-to-end detection plus OCR, backend/services/fast_alpr_loader.py). When FastALPR is unavailable it falls back to yolo11-license-plate detection followed by PaddleOCR. Either path produces plate text plus a box for the pipeline's license_plates list:
{
"license_plates": [
{
"text": "ABC 1234",
"ocr_confidence": 0.88,
"bbox": { "x": 100, "y": 200, "width": 100, "height": 40 }
}
]
}
Threat Detection¶
Weapon Detection¶
The enrichment pipeline routes threat checks to POST http://localhost:8090/enrich-lt/threat-detect, which runs threat-detection-yolov8n:
{
"threats_detected": [
{
"class": "knife",
"confidence": 0.85,
"bbox": { "x": 150, "y": 200, "width": 30, "height": 80 }
}
],
"is_threat": true,
"max_confidence": 0.85,
"inference_time_ms": 9.1
}
Detection classes the gateway post-processes: knife, pistol, rifle, threat_object. The backend applies severity separately — see ai/enrichment/models/threat_detector.py, which maps gun/rifle/pistol to critical, knife and sword-type items to high, and bat/crowbar/hammer to medium.
Risk Assessment¶
Nemotron LLM Analysis¶
The Nemotron-3-Nano-30B model provides contextual risk assessment. It runs in its own container, ai-llm, on llama.cpp at port 8091 (NEMOTRON_URL); the optional vllm compose profile swaps in ai-llm-vllm instead. Inspect a finished event's prompt and response through the backend at /api/llm-reasoning/events/{event_id}.
Input Context:
- All detections in the batch
- Florence captions and descriptions
- Zone information and types
- Historical baseline comparison
- Household member matching
- Time of day and patterns
Output:
{
"risk_score": 45,
"risk_level": "medium",
"summary": "Unknown person approached front door at unusual hour",
"reasoning": "Activity at 2:14 AM when no family members are expected...",
"recommended_actions": ["Review footage", "Check if visitor expected"]
}
Risk Score Mapping:
| Score | Level | Color | Action |
|---|---|---|---|
| 0-29 | Low | Green | Informational only |
| 30-59 | Medium | Yellow | Review when convenient |
| 60-84 | High | Orange | Prompt review |
| 85-100 | Critical | Red | Immediate attention |
API Reference¶
Analytics Endpoints¶
| Endpoint | Method | Description |
|---|---|---|
/api/analytics/detection-trends | GET | Daily detection counts |
/api/analytics/risk-history | GET | Risk level distribution over time |
/api/analytics/camera-uptime | GET | Uptime percentage per camera |
/api/analytics/object-distribution | GET | Detection counts by object type |
/api/analytics/risk-score-distribution | GET | Risk score histogram |
/api/analytics/risk-score-trends | GET | Average risk score over time |
Query Parameters¶
All analytics endpoints accept:
| Parameter | Type | Description |
|---|---|---|
start_date | Date | Start date (ISO format, required) |
end_date | Date | End date (ISO format, required) |
camera_id | String | Filter by camera (optional) |
Example Request¶
curl "http://localhost:8000/api/analytics/detection-trends?start_date=2026-01-01&end_date=2026-01-26"
Response Format¶
{
"data_points": [
{ "date": "2026-01-01", "count": 156 },
{ "date": "2026-01-02", "count": 203 }
],
"total_detections": 4521,
"start_date": "2026-01-01",
"end_date": "2026-01-26"
}
Model Status API¶
Model status and loading go through the backend under /api/system/models (backend/api/routes/model_management.py). Do not call the enrichment service ports directly.
# Every model in the registry, with runtime state
curl http://localhost:8000/api/system/models
# One model
curl http://localhost:8000/api/system/models/pet-classifier/status
# Combined VRAM totals
curl http://localhost:8000/api/system/models/vram-summary
# Load / unload / reload
curl -X POST http://localhost:8000/api/system/models/pet-classifier/load
curl -X POST http://localhost:8000/api/system/models/pet-classifier/unload
curl -X POST http://localhost:8000/api/system/models/pet-classifier/reload
GET /api/system/models returns one entry per registry model:
{
"models": [
{
"name": "pet-classifier",
"category": "classification",
"estimated_vram_mb": 200,
"enabled": true,
"service": "ai-enrichment-light",
"gpu_id": 1,
"runtime": {
"loaded": true,
"actual_vram_mb": 187,
"last_used": "2026-09-22T10:30:00Z",
"load_count": 5
}
}
],
"service_status": {
"ai-enrichment": "healthy",
"ai-enrichment-light": "healthy"
}
}
backend/api/routes/system.py exposes a second, simpler pair: GET /api/system/models for names and GET /api/system/models/{model_name} for one model. model_management.py is registered first, so it answers the bare /api/system/models request.
Best Practices¶
Optimizing Detection Quality¶
- Camera Placement: Ensure cameras have clear views of entry points
- Lighting: Good lighting improves detection accuracy
- Resolution: Higher resolution enables better detail extraction
- Zone Configuration: Focus analysis on important areas
Managing VRAM¶
- Priority Models: Keep critical models (threat detection) always ready
- Preloading: Preload expected models before high-activity periods
- Monitoring: Watch VRAM utilization via
/models/status
Reducing False Positives¶
- Zone Configuration: Exclude high-motion areas (trees, roads)
- Household Registration: Add known people and vehicles
- Baseline Learning: Allow system to learn normal patterns
- Feedback: Use the feedback system to improve calibration
Troubleshooting¶
No Detections¶
- Check camera is uploading to correct directory
- Verify file watcher is running:
curl http://localhost:8000/api/system/pipeline - Check the AI gateway:
curl http://localhost:8090/yolo26/health - Review detection queue depth in system telemetry
Slow Analysis¶
- Check GPU utilization:
curl http://localhost:8000/api/system/gpu - Review pipeline latency:
curl http://localhost:8000/api/system/pipeline-latency - Consider adjusting batch window settings
- Check for VRAM pressure in model status
Inaccurate Risk Scores¶
- Review recent events for patterns
- Check zone configuration is accurate
- Register household members to reduce false positives
- Allow baseline learning time (7+ days recommended)
Related Documentation¶
- Zone Configuration Guide - Configure detection zones
- Face Recognition Guide - Person identification
- Analytics Endpoints - API reference
- AI Performance - Model monitoring