diff --git a/docs_v1.0/API_V1.0.0/TRACE/TRACE_HEATMAP_SPEC_V1.0.0.md b/docs_v1.0/API_V1.0.0/TRACE/TRACE_HEATMAP_SPEC_V1.0.0.md new file mode 100644 index 0000000..593f496 --- /dev/null +++ b/docs_v1.0/API_V1.0.0/TRACE/TRACE_HEATMAP_SPEC_V1.0.0.md @@ -0,0 +1,183 @@ +# Trace Heatmap Specification v1.0.0 + +## Concept + +將臉部追蹤資料標準化為熱力圖格式,包含空間(畫面位置)和時間(影片進度)兩個維度。 + +## Data Model + +### 1. Spatial Heatmap — 臉在哪裡? + +將畫面划分為 grid,計算每個 cell 的 face 活動量: + +``` +frame (1920×1080) +┌──────────────────────┐ +│ ░░ ░░ ░░░░ │ y +│ ░░░░░░░░░░░░░░ │ +│ ░░░░░░░░░░░░ │ +│ ░░░░░░░░ │ ← 活動集中在畫面中央 +│ ░░ │ +└──────────────────────┘ + x → +``` + +### 2. Temporal Heatmap — 什麼時候有臉? + +``` +face_density + │ ██ ████ ██ + │ ██ ██ ████ ██ ██ + │ ██ ██ ██████ ██ ██ + └──────────────────────────→ time + 0s 3000s 6000s +``` + +## API Design + +### GET /api/v1/file/:file_uuid/face_trace/heatmap + +回傳標準化熱力圖資料。 + +#### Query Parameters + +| Param | Type | Default | Description | +|-------|------|---------|-------------| +| `type` | enum | `spatial` | `spatial` \| `temporal` \| `combined` | +| `grid_x` | int | 20 | Spatial: X axis grid cells | +| `grid_y` | int | 12 | Spatial: Y axis grid cells | +| `bin_sec` | int | 60 | Temporal: time bucket in seconds | +| `time_start` | float | 0 | Time range start (seconds) | +| `time_end` | float | — | Time range end (default: video duration) | +| `min_confidence` | float | 0.5 | Minimum detection confidence | +| `trace_ids` | string | — | Comma-separated trace_id filter (optional) | + +#### Response: Spatial + +```json +{ + "type": "spatial", + "frame_width": 1920, + "frame_height": 1080, + "grid_x": 20, + "grid_y": 12, + "cell_w": 96, + "cell_h": 90, + "data": [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 3, 5, 8, 12, 14, 10, 6, 3, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 4, 8, 15, 25, 35, 40, 32, 20, 10, 4, 1, 0, 0, 0, 0, 0], + ... + ], + "max_value": 40, + "total_detections": 108204, + "total_traces": 6892 +} +``` + +#### Response: Temporal + +```json +{ + "type": "temporal", + "bin_sec": 60, + "duration": 5954, + "bins": [ + {"t_start": 0, "t_end": 60, "face_count": 45, "trace_count": 12, "avg_confidence": 0.82}, + {"t_start": 60, "t_end": 120, "face_count": 120, "trace_count": 28, "avg_confidence": 0.79}, + ... + ] +} +``` + +#### Response: Combined + +```json +{ + "type": "combined", + "bin_sec": 60, + "grid_x": 20, + "grid_y": 12, + "duration": 5954, + "data": [ + { + "t_start": 0, + "t_end": 60, + "heatmap": [[...], [...]], + "max_cell": 8, + "face_count": 45 + }, + ... + ] +} +``` + +## Standardization Rules + +1. **Normalization**: All values normalized to 0.0–1.0 range with `max_value` for rendering +2. **Resolution independence**: Grid size configurable, always returns `cell_w`/`cell_h` +3. **Temporal bucketing**: Default 60s bins, adjustable +4. **Filterable**: By time range, confidence, specific traces +5. **Cachable**: Response includes `file_uuid` + query hash for CDN/Redis cache + +## Frontend Rendering + +### Spatial Heatmap (Canvas) + +```javascript +function renderHeatmap(canvas, data) { + const cellW = canvas.width / data.grid_x; + const cellH = canvas.height / data.grid_y; + for (let y = 0; y < data.grid_y; y++) { + for (let x = 0; x < data.grid_x; x++) { + const intensity = data.data[y][x] / data.max_value; + ctx.fillStyle = `rgba(255, 0, 0, ${intensity})`; + ctx.fillRect(x * cellW, y * cellH, cellW, cellH); + } + } +} +``` + +### Temporal Heatmap (SVG) + +```svg + + +``` + +## Implementation + +### Backend (src/api/trace_agent_api.rs) + +Add new route: +```rust +.route("/api/v1/file/:file_uuid/face_trace/heatmap", get(list_trace_heatmap)) +``` + +### SQL (spatial) + +```sql +SELECT + FLOOR(x / $grid_w) AS cell_x, + FLOOR(y / $grid_h) AS cell_y, + COUNT(*) AS intensity +FROM dev.face_detections +WHERE file_uuid = $1 AND confidence >= $2 +GROUP BY cell_x, cell_y +ORDER BY cell_x, cell_y; +``` + +### SQL (temporal) + +```sql +SELECT + FLOOR(frame_number / ($fps * $bin_sec)) AS bin, + COUNT(*) AS face_count, + COUNT(DISTINCT trace_id) AS trace_count, + AVG(confidence) AS avg_confidence +FROM dev.face_detections +WHERE file_uuid = $1 AND confidence >= $2 +GROUP BY bin +ORDER BY bin; +```