feat: Phase 1 handover - schema migration, correction mechanism, API fixes
Schema changes: dev.chunks->dev.chunk, remove old_chunk_id/chunk_index Correction: asr-1.json format, generate/apply scripts API: 37/37 endpoints fixed and tested Docs: HANDOVER_V2.0.md for M4
This commit is contained in:
266
docs_v1.0/API_V1.0.0/TRACE/FACE_TRACE_MODEL_V1.0.0.md
Normal file
266
docs_v1.0/API_V1.0.0/TRACE/FACE_TRACE_MODEL_V1.0.0.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Face Trace Data Model v1.0.0
|
||||
|
||||
## 現狀問題
|
||||
|
||||
目前 trace 的資料模型是隱含的 — `face_detections` table 只有一個 `trace_id` 欄位,沒有獨立的 trace 實體:
|
||||
|
||||
```sql
|
||||
-- 現狀:trace 只是 face_detections 的一個 grouping column
|
||||
SELECT trace_id, COUNT(*) FROM face_detections GROUP BY trace_id;
|
||||
```
|
||||
|
||||
這導致:
|
||||
- Trace metadata(持續時間、平均信心度)需要 aggregation query 才能取得
|
||||
- Identity binding 只能在 detection 層級,無法對整個 trace 綁定
|
||||
- Interpolation 資料沒有標準儲存位置
|
||||
- 跨 file 的 trace 關聯(同一人 reappear)無法表達
|
||||
|
||||
## 提議模型
|
||||
|
||||
### 新增 `face_traces` table
|
||||
|
||||
```sql
|
||||
CREATE TABLE dev.face_traces (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
file_uuid VARCHAR(32) NOT NULL,
|
||||
trace_id INT NOT NULL, -- per-file trace number
|
||||
identity_id INT REFERENCES dev.identities(id),
|
||||
|
||||
-- 時間範圍 (frame-based)
|
||||
first_frame INT NOT NULL,
|
||||
last_frame INT NOT NULL,
|
||||
frame_count INT NOT NULL,
|
||||
|
||||
-- 時間範圍 (time-based)
|
||||
first_sec FLOAT NOT NULL,
|
||||
last_sec FLOAT NOT NULL,
|
||||
duration_sec FLOAT NOT NULL,
|
||||
|
||||
-- 信心度
|
||||
avg_confidence FLOAT NOT NULL,
|
||||
min_confidence FLOAT NOT NULL,
|
||||
max_confidence FLOAT NOT NULL,
|
||||
|
||||
-- 空間範圍
|
||||
bbox_union JSONB, -- {x, y, w, h} 包含所有 detection 的最小外框
|
||||
|
||||
-- 比對用 embedding (trace 級別的 face embedding,取質量最好的 detection)
|
||||
sample_face_id VARCHAR(64), -- 最高信心度的 detection ID
|
||||
embedding REAL[], -- 該 detection 的 embedding
|
||||
|
||||
-- 狀態
|
||||
status VARCHAR(20) DEFAULT 'active', -- active | merged | deleted
|
||||
merged_into INT, -- 如果被 merge,指向新的 trace_id
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
UNIQUE(file_uuid, trace_id)
|
||||
);
|
||||
```
|
||||
|
||||
### 與現有 `face_detections` 的關係
|
||||
|
||||
```
|
||||
face_traces (new) face_detections (existing)
|
||||
┌─────────────────────┐ ┌──────────────────────────┐
|
||||
│ id: 1 │ 1:N │ id: 12400 │
|
||||
│ trace_id: 3128 │────── │ trace_id: 3128 │
|
||||
│ file_uuid: 3abeee...│ │ file_uuid: 3abeee... │
|
||||
│ identity_id: 2102 │ │ frame_number: 68280 │
|
||||
│ first_frame: 68161 │ │ x: 371, y: 468 │
|
||||
│ last_frame: 69269 │ │ embedding: [...] │
|
||||
│ avg_confidence: 0.78│ └──────────────────────────┘
|
||||
│ sample_face_id: ....│
|
||||
│ embedding: [...] │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
### Migration
|
||||
|
||||
```sql
|
||||
-- 從現有 face_detections 資料建立 face_traces
|
||||
INSERT INTO dev.face_traces (
|
||||
file_uuid, trace_id,
|
||||
first_frame, last_frame, frame_count,
|
||||
first_sec, last_sec, duration_sec,
|
||||
avg_confidence, min_confidence, max_confidence
|
||||
)
|
||||
SELECT
|
||||
file_uuid,
|
||||
trace_id,
|
||||
MIN(frame_number) AS first_frame,
|
||||
MAX(frame_number) AS last_frame,
|
||||
COUNT(*) AS frame_count,
|
||||
MIN(frame_number)::float / 25.0 AS first_sec,
|
||||
MAX(frame_number)::float / 25.0 AS last_sec,
|
||||
(MAX(frame_number) - MIN(frame_number))::float / 25.0 AS duration_sec,
|
||||
AVG(confidence) AS avg_confidence,
|
||||
MIN(confidence) AS min_confidence,
|
||||
MAX(confidence) AS max_confidence
|
||||
FROM dev.face_detections
|
||||
WHERE file_uuid = '3abeee81...' AND trace_id IS NOT NULL
|
||||
GROUP BY file_uuid, trace_id;
|
||||
```
|
||||
|
||||
### 新增 API
|
||||
|
||||
#### GET /api/v1/file/:file_uuid/face_trace/:trace_id
|
||||
|
||||
回傳單一 trace 的完整 metadata(取代目前的 aggregation query)。
|
||||
|
||||
#### PATCH /api/v1/file/:file_uuid/face_trace/:trace_id
|
||||
|
||||
更新 trace 屬性(例如綁定 identity):
|
||||
|
||||
```json
|
||||
{"identity_id": 2102}
|
||||
```
|
||||
|
||||
#### POST /api/v1/file/:file_uuid/face_trace/merge
|
||||
|
||||
合併多個 trace(同一人 reappear 被切斷時的處理):
|
||||
|
||||
```json
|
||||
{
|
||||
"source_trace_ids": [3128, 3201, 3350],
|
||||
"target_trace_id": 3128
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/v1/file/:file_uuid/face_trace/:trace_id/interpolate
|
||||
|
||||
產生並儲存 interpolation 資料:
|
||||
|
||||
```json
|
||||
{
|
||||
"stride": 1,
|
||||
"store": true
|
||||
}
|
||||
```
|
||||
|
||||
## 3D 立體化
|
||||
|
||||
### Z 軸來源
|
||||
|
||||
目前 2D bbox 可以透過以下方式推估深度 (z):
|
||||
|
||||
| 方法 | 公式 | 精度 | 需求 |
|
||||
|------|------|:----:|------|
|
||||
| **Bbox 大小推估** | `z = focal_length * real_height / bbox_height` | 低 | 假設人臉大小固定 ~20cm |
|
||||
| **Bbox 面積** | `z ∝ 1 / sqrt(w * h)` | 低 | 無 |
|
||||
| **Stereo / 多視角** | 三角測量 | 高 | 需多個 camera |
|
||||
| **Depth model** | MiDaS / Depth Anything | 高 | 需 GPU inference |
|
||||
| **LiDAR** | 直接深度 | 最高 | 需 LiDAR 硬體 |
|
||||
|
||||
### Z from Bbox Size (最簡單)
|
||||
|
||||
人到鏡頭的距離 ≈ `臉部真實大小(20cm) × 焦距 / bbox_pixel_height`。
|
||||
|
||||
對於無 calibration 的影片,可以用相對深度:
|
||||
|
||||
```
|
||||
z_rel = 1.0 / sqrt(bbox_width × bbox_height)
|
||||
```
|
||||
|
||||
將 z_rel normalize 到 0.0 (最近) ~ 1.0 (最遠),即為相對深度。
|
||||
|
||||
### 3D Trace Schema 擴充
|
||||
|
||||
```sql
|
||||
-- 在 face_traces 加入 Z 軸統計
|
||||
ALTER TABLE dev.face_traces ADD COLUMN z_center FLOAT; -- 平均深度
|
||||
ALTER TABLE dev.face_traces ADD COLUMN z_min FLOAT; -- 最近
|
||||
ALTER TABLE dev.face_traces ADD COLUMN z_max FLOAT; -- 最遠
|
||||
ALTER TABLE dev.face_traces ADD COLUMN z_travel FLOAT; -- 深度總移動量
|
||||
|
||||
-- 在 face_detections 加入 Z
|
||||
ALTER TABLE dev.face_detections ADD COLUMN z_rel FLOAT; -- 單幀相對深度
|
||||
```
|
||||
|
||||
### 3D 軌跡資料格式
|
||||
|
||||
```json
|
||||
GET /api/v1/file/:file_uuid/trace/:trace_id/faces?dimension=3d
|
||||
|
||||
{
|
||||
"trace_id": 3128,
|
||||
"dimension": "3d",
|
||||
"faces": [
|
||||
{
|
||||
"frame": 68280, "t": 2731.2,
|
||||
"x": 371, "y": 468, "z": 0.45,
|
||||
"bbox": {"w": 338, "h": 338}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 從 2D bbox 計算 Z
|
||||
|
||||
```python
|
||||
def bbox_to_z_rel(w: float, h: float, frame_w: int, frame_h: int) -> float:
|
||||
"""
|
||||
將 bbox 大小轉換為相對深度
|
||||
- 傳回值 0.0 = 最近 (最大 bbox)
|
||||
- 傳回值 1.0 = 最遠 (最小 bbox)
|
||||
"""
|
||||
area_pct = (w * h) / (frame_w * frame_h)
|
||||
# 1% 面積 → z=0 (最近), 0.01% 面積 → z=1 (最遠)
|
||||
z = 1.0 - min(area_pct * 50, 1.0)
|
||||
return round(z, 4)
|
||||
```
|
||||
|
||||
### 3D Trace 的應用
|
||||
|
||||
| 應用 | 說明 |
|
||||
|------|------|
|
||||
| **Approach/Retreat** | 人物走近/遠離鏡頭,z 值變化 |
|
||||
| **Fill ratio** | bbox 面積佔畫面比例 = 鏡頭構圖 |
|
||||
| **MR Bridge** | (x, y, z, t) 直接餵給 AR/VR 引擎 |
|
||||
| **Cross-camera** | 同一人物在不同 camera 的 z 值可校準空間位置 |
|
||||
| **Heatmap Z-layer** | 熱力圖可依 z 值分層(前景 vs 背景) |
|
||||
|
||||
### Z 軸視覺化
|
||||
|
||||
```
|
||||
t (time)
|
||||
│ z (depth)
|
||||
│ ╱
|
||||
│ ●────●────●────●────● ← 人物從遠走到近
|
||||
│ ╲ ╱ (z: 0.8 → 0.3)
|
||||
│ ●────●──●
|
||||
│ z_travel = 0.5
|
||||
└──────────────────→ x, y
|
||||
```
|
||||
|
||||
Z 軸變化可視為獨立的時間序列:
|
||||
|
||||
```
|
||||
z_rel
|
||||
1.0 ┤ far
|
||||
│ ████
|
||||
0.8 ┤ ██ ██
|
||||
│ ██ ██
|
||||
0.6 ┤ ██ ██
|
||||
│ ██ ██
|
||||
0.4 ┤██ ██
|
||||
│ ██
|
||||
0.2 ┤ ██
|
||||
│ ██
|
||||
0.0 ┤ ██ near
|
||||
└────────────────────────→ time
|
||||
2707s 2770s
|
||||
|
||||
解讀:人物先逐漸走近 (z 0.5→0.2),最後稍微後退
|
||||
```
|
||||
|
||||
### 與現有系統的整合
|
||||
|
||||
| 元件 | 變更 |
|
||||
|------|------|
|
||||
| `face_trace/sortby` | 改從 `face_traces` 查詢(更快,不需 GROUP BY) |
|
||||
| `trace/:trace_id/faces` | 不變(仍從 `face_detections`) |
|
||||
| Qdrant sync | trace 層級的 embedding 寫入獨立 collection |
|
||||
| Video render | 從 `face_traces` 讀 metadata 決定 render 參數 |
|
||||
| Portal Timeline | 從 `face_traces` 讀取 identity 名稱顯示 |
|
||||
209
docs_v1.0/API_V1.0.0/TRACE/VIRTUAL_CHARACTER_MODEL_V1.0.0.md
Normal file
209
docs_v1.0/API_V1.0.0/TRACE/VIRTUAL_CHARACTER_MODEL_V1.0.0.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Virtual Character Model v1.0.0
|
||||
|
||||
從 face traces 重建虛擬人物。
|
||||
|
||||
## Concept
|
||||
|
||||
將影片中同一 identity 的所有 trace 合併為一個**虛擬人物模型**,包含:
|
||||
|
||||
```
|
||||
影片中的 Cary Grant
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ Virtual Character │
|
||||
│ ├── Identity: Cary │
|
||||
│ ├── 3D Paths │ ← 所有 trace 的 (x,y,z,t) 軌跡
|
||||
│ ├── Appearance: │ ← 臉部樣本、embedding
|
||||
│ ├── Voice: │ ← ASRX speaker embedding
|
||||
│ ├── Behavior: │ ← 移動速度、停留位置
|
||||
│ └── MR Data: │ ← 可直接餵給 AR/VR 的格式
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Model
|
||||
|
||||
### Characters Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE dev.characters (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
identity_id INT REFERENCES dev.identities(id),
|
||||
file_uuid VARCHAR(32), -- 來源影片 (可跨多片)
|
||||
|
||||
-- 3D 空間範圍
|
||||
world_bbox JSONB, -- 此角色在場景中的 3D 活動範圍
|
||||
total_travel FLOAT, -- 總移動距離 (m)
|
||||
|
||||
-- 外觀
|
||||
sample_image TEXT, -- 最佳臉部截圖路徑
|
||||
face_model REAL[], -- 平均 face embedding
|
||||
voice_model REAL[], -- 平均 voice embedding
|
||||
|
||||
-- 行為特徵
|
||||
avg_speed FLOAT, -- 平均移動速度
|
||||
height_avg FLOAT, -- 平均出現高度 (y%)
|
||||
hotspots JSONB, -- 經常停留的區域 [{x, y, z, duration}]
|
||||
|
||||
-- MR
|
||||
gltf_url TEXT, -- 3D 模型的 glTF 路徑(可選)
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### Character Paths Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE dev.character_paths (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
character_id INT REFERENCES dev.characters(id),
|
||||
trace_id INT, -- 來源 trace
|
||||
file_uuid VARCHAR(32),
|
||||
|
||||
-- 3D 軌跡 (簡化版 waypoints)
|
||||
waypoints JSONB NOT NULL, -- [{t, x, y, z}, ...]
|
||||
|
||||
-- 統計
|
||||
duration FLOAT,
|
||||
distance FLOAT, -- 移動距離
|
||||
speed_avg FLOAT,
|
||||
speed_max FLOAT,
|
||||
|
||||
start_time FLOAT,
|
||||
end_time FLOAT
|
||||
);
|
||||
```
|
||||
|
||||
## 虛擬人物建構流程
|
||||
|
||||
```
|
||||
1. Face Detection
|
||||
└→ 2D bbox (x, y, w, h) per frame
|
||||
|
||||
2. Face Tracking
|
||||
└→ trace_id 賦予
|
||||
|
||||
3. 3D 化
|
||||
└→ z = f(bbox_size) → 3D point (x, y, z, t)
|
||||
|
||||
4. Identity Binding
|
||||
└→ trace_id → identity_id
|
||||
|
||||
5. Character Assembly
|
||||
└→ 同一 identity 的所有 trace 合併
|
||||
│
|
||||
├── 路徑拼接:trace 中斷處用 interpolation 連接
|
||||
├── 速度曲線:計算各 segment 的速度
|
||||
├── 熱點分析:找出停留點
|
||||
└── 外觀模型:平均 face embedding
|
||||
|
||||
6. MR Export
|
||||
└→ glTF / USDZ / 自訂格式
|
||||
```
|
||||
|
||||
## 視覺化
|
||||
|
||||
### 角色路徑總覽
|
||||
|
||||
```
|
||||
Cary Grant 在 Charade 中的完整路徑:
|
||||
|
||||
Y%
|
||||
100% ┤
|
||||
│ ╔══╗
|
||||
│ ╔══╝ ╚══╗
|
||||
50% ┤ ╔═══╝ ╚══╗
|
||||
│ ╔═══╝ ╚══╗
|
||||
│ ╔══╝ ╚══╗
|
||||
0% ┤═╝ ╚════
|
||||
└────────────────────────────────────────→ X%
|
||||
0% 20% 40% 60% 80% 100%
|
||||
|
||||
點 → 每次出現的起始位置
|
||||
線 → 移動軌跡
|
||||
顏色 → 時間 (冷→暖)
|
||||
```
|
||||
|
||||
### 行為分析
|
||||
|
||||
```json
|
||||
{
|
||||
"character": "Cary Grant",
|
||||
"total_appearances": 47,
|
||||
"total_screen_time": 823.5,
|
||||
"avg_speed": 0.32,
|
||||
"hotspots": [
|
||||
{"x": 0.5, "y": 0.4, "duration": 45.2, "label": "沙發區"},
|
||||
{"x": 0.7, "y": 0.3, "duration": 28.1, "label": "門口"}
|
||||
],
|
||||
"speed_profile": {
|
||||
"still": 0.35,
|
||||
"walking": 0.55,
|
||||
"fast": 0.10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MR 輸出
|
||||
|
||||
```json
|
||||
{
|
||||
"format": "momentry_character",
|
||||
"version": "1.0",
|
||||
"character": {
|
||||
"name": "Cary Grant",
|
||||
"tmdb_id": 2102
|
||||
},
|
||||
"scene": {
|
||||
"file_uuid": "3abeee81...",
|
||||
"duration": 5954
|
||||
},
|
||||
"paths": [
|
||||
{
|
||||
"trace_id": 3128,
|
||||
"waypoints": [
|
||||
{"t": 2707, "x": 0.12, "y": 0.25, "z": 0.45},
|
||||
{"t": 2730, "x": 0.35, "y": 0.40, "z": 0.30},
|
||||
{"t": 2750, "x": 0.50, "y": 0.55, "z": 0.20}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### POST /api/v1/character/build
|
||||
|
||||
從 file 建立角色模型。
|
||||
|
||||
```json
|
||||
{
|
||||
"file_uuid": "3abeee81...",
|
||||
"identity_ids": [2102, 187],
|
||||
"include_mr_export": true
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/v1/character/:character_id
|
||||
|
||||
取得角色模型完整資料。
|
||||
|
||||
### GET /api/v1/character/:character_id/paths
|
||||
|
||||
取得角色 3D 路徑 for MR rendering。
|
||||
|
||||
## 與 Trace 的關係
|
||||
|
||||
```
|
||||
Trace (現有) Character (新增)
|
||||
┌────────────┐ ┌──────────────────┐
|
||||
│ trace_id │ 1:N │ character_id │
|
||||
│ file_uuid │────────────── │ identity_id │
|
||||
│ face_count │ 多個 trace │ world_bbox │
|
||||
│ duration │ 組成一個角色 │ total_travel │
|
||||
│ 2D bbox │ │ speed_profile │
|
||||
│ z from bbox│ │ mr_export │
|
||||
└────────────┘ └──────────────────┘
|
||||
```
|
||||
Reference in New Issue
Block a user