docs: trace heatmap spec v1.0.0 — spatial + temporal + combined

This commit is contained in:
Warren
2026-05-08 13:33:57 +08:00
parent f469197ce6
commit 2058599e63

View File

@@ -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.01.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
<rect x="0" y="0" width="10" height="100" fill="rgba(255,0,0,0.1)" />
<rect x="10" y="0" width="10" height="100" fill="rgba(255,0,0,0.3)" />
```
## 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;
```