# Face Pipeline: Detection → Clustering → Trace **Date**: 2026-05-16 --- ## 流程 ``` Video Frames │ ▼ ┌─────────────────────────────┐ │ 0. Cut Detection │ PySceneDetect │ scene boundaries │ → chunk (chunk_type='cut') └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ 1. Face Detection │ 每幀偵測人臉 │ confidence ≥ 0.5 │ → face_detections (cut_id 對應所屬 cut) └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ 2. Face Clustering │ embedding + IoU + distance │ trace_id assignment │ 同一人 + 同 cut → 同一 trace_id │ per-file sequential │ trace_id 跨 cut 持續給號(不歸零) └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ 3. Face Trace │ 跨影格連續追蹤 │ per-file sequential │ trace_id = 0, 1, 2, ... │ scoped by cut │ 每個 trace 完全落在一個 cut 內 └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ 4. Identity Binding │ embedding 比對 │ identity_id assignment │ → known person / stranger └─────────────────────────────┘ ``` ## scope ```sql trace_id → per-file sequential (file_uuid, trace_id) 唯一 cut_id → chunk.id WHERE chunk_type='cut' 輔助 scope,不影響唯一性 identity_id → global FK 跨 cut / file 關聯同一人 ``` ## 約束 | 約束 | 說明 | |------|------| | 唯一 | `(file_uuid, trace_id)` | | 單一 cut | 每個 trace 完全落在一個 cut 內(`0` 個跨 cut trace) | | 獨立 | `trace_id` ≠ `identity_id`。前者是物體軌跡,後者是身份分別 | ## 各階段資料量 ``` Stage | 量 | Key ------------------------|-------------|---------------------- Raw faces | 262,021 | face_detections rows After clustering | 6,892 | distinct trace_id With identity | 147,602 | identity_id NOT NULL (2,035 identities) Stranger (unbound) | 114,419 | identity_id IS NULL ``` ## Trace 大小分布 | Faces per trace | Trace count | 說明 | |:---------------:|:-----------:|------| | 1 | 610 | 一閃而過 | | 2-5 | 969 | 短暫出現 | | 6-20 | 1,541 | 片段 | | 21-100 | 2,218 | 一般 | | 101+ | 1,554 | 主要角色 | ## Clustering 方式 Face Tracker (`scripts/face_tracker.py`) 使用三種方法決定同一人: 1. **IoU (Intersection over Union)** — 前後影格框重疊率 2. **Cosine distance** — face embedding 相似度 3. **Euclidean distance** — bbox 中心距離 三者加權決策:iou > 0.5 || (cosine < 0.3 && distance < 100px) ## Trace 結構 ```json { "trace_id": 2, // per-file sequential "faces": [ // face_detections GROUP BY trace_id {"face_id": "4587_0", "frame": 4587, "confidence": 0.92}, {"face_id": "4588_0", "frame": 4588, "confidence": 0.91}, ... ], "start_frame": 4587, "end_frame": 4722, "face_count": 46, "identity_id": 101 // NULL = stranger } ``` ## API 查詢 ```bash # Trace 列表(含 face_count、區間) POST /api/v1/file/:uuid/face_trace/sortby # Trace 內 faces(逐幀 + 可選 interpolation) GET /api/v1/file/:uuid/trace/:trace_id/faces # Trace 綁定身份 POST /api/v1/identity/:uuid/bind ```