Face Filter API 使用指南 — Studio 團隊參考¶
版本: v1.0
日期: 2026-08-09
狀態: 已實作
概述¶
Face Filter API 提供臉部過濾功能,讓 Studio 前端可以: 1. 查詢臉部列表(支持來源過濾) 2. 獲取單一臉部過濾報告 3. 套用過濾規則 4. 獲取過濾統計 5. 管理過濾規則
API 端點¶
1. 獲取臉部列表¶
端點: GET /api/v1/file/{file_uuid}/faces
功能: 獲取檔案的臉部列表,支持分頁和來源過濾
查詢參數:
{
"source": "all", // "all" | "apple_vision" | "mediapipe"
"page": 1, // 頁碼(從 1 開始)
"page_size": 50 // 每頁數量
}
回應範例:
{
"success": true,
"data": {
"faces": [
{
"face_id": "face_001",
"frame": 671,
"primary_source": "apple_vision",
"primary_confidence": 0.84,
"detection_sources": [
{"source_type": "apple_vision", "confidence": 0.84},
{"source_type": "mediapipe", "confidence": 0.92}
],
"filter_features": {
"face_id": "face_001",
"file_uuid": "0489e104d7d1e11e613da01f7aa130ca",
"frame": 671,
"bbox_size": 336, // 新增:min(width, height)
"eye_ratio": 0.33,
"nose_ratio": 0.02,
"pose_score": 0.08,
"mediapipe_detected": true,
"mediapipe_iou": 0.85,
"embedding_similarity": 0.82,
"consecutive_frames": 15,
"total_frames_detected": 15,
"apple_vision_confidence": 0.84,
"mediapipe_confidence": 0.92,
"primary_source": "apple_vision"
}
}
],
"pagination": {
"page": 1,
"page_size": 50,
"total": 392,
"total_pages": 8
},
"source_summary": {
"apple_vision": 392,
"mediapipe": 5,
"merged_unique": 392
}
}
}
Studio 使用範例:
// 獲取第一頁臉部
const response = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/faces', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source: 'all',
page: 1,
page_size: 50
})
});
const data = await response.json();
console.log(`Total faces: ${data.data.pagination.total}`);
console.log(`First face: ${data.data.faces[0].face_id}`);
2. 獲取單一臉部過濾報告¶
端點: GET /api/v1/file/{file_uuid}/face/{face_id}/filter-report
功能: 獲取單一臉部的詳細過濾報告
回應範例:
{
"face_id": "face_001",
"frame": 671,
"overall_confidence": 0.45,
"recommendation": "reject",
"rule_results": {
"landmark_analysis": {
"enabled": true,
"score": 0.75,
"passed": true,
"reason": "nose_ratio too low (0.020)"
},
"mediapipe_verification": {
"enabled": true,
"score": 1.0,
"passed": true,
"reason": null
},
"embedding_similarity": {
"enabled": true,
"score": 0.21,
"passed": false,
"reason": "Low embedding similarity (0.150)"
},
"temporal_consistency": {
"enabled": true,
"score": 0.33,
"passed": false,
"reason": "Single frame detection (1)"
}
},
"reasons": [
"nose_ratio too low (0.020)",
"Low embedding similarity (0.150)",
"Single frame detection (1)"
]
}
Studio 使用範例:
// 獲取臉部過濾報告
const response = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/face/face_001/filter-report');
const report = await response.json();
console.log(`Recommendation: ${report.recommendation}`);
console.log(`Confidence: ${report.overall_confidence}`);
console.log(`Reasons: ${report.reasons.join(', ')}`);
// 顯示每個規則的結果
Object.entries(report.rule_results).forEach(([rule, result]) => {
console.log(`${rule}: ${result.passed ? '✅' : '❌'} (score: ${result.score})`);
});
3. 套用過濾規則¶
端點: POST /api/v1/file/{file_uuid}/faces/filter
功能: 套用過濾規則到所有臉部,返回過濾結果
請求範例:
{
"rules": {
"version": "1.0",
"rules": {
"landmark_analysis": {
"enabled": true,
"weight": 0.3,
"thresholds": {
"eye_ratio_min": 0.25,
"eye_ratio_max": 0.55,
"nose_ratio_min": 0.08,
"pose_score_max": 0.8
}
},
"mediapipe_verification": {
"enabled": true,
"weight": 0.2,
"thresholds": {
"min_iou": 0.3
}
},
"embedding_similarity": {
"enabled": true,
"weight": 0.3,
"thresholds": {
"min_similarity": 0.70
}
},
"temporal_consistency": {
"enabled": true,
"weight": 0.2,
"thresholds": {
"min_consecutive_frames": 3
}
}
},
"overall_thresholds": {
"auto_accept": 0.7,
"auto_reject": 0.3,
"review_range": [0.3, 0.7]
}
}
}
回應範例:
{
"total_faces": 392,
"passed": 385,
"filtered": 7,
"filtered_faces": [
{
"face_id": "face_001",
"frame": 671,
"reasons": [
"nose_ratio too low (0.020)",
"Low embedding similarity (0.150)",
"Single frame detection (1)"
]
}
],
"filter_stats": {
"landmark": 3,
"mediapipe": 1,
"embedding": 2,
"temporal": 1
}
}
Studio 使用範例:
// 套用過濾規則
const filterConfig = {
rules: {
landmark_analysis: {
enabled: true,
weight: 0.3,
thresholds: {
eye_ratio_min: 0.25,
eye_ratio_max: 0.55,
nose_ratio_min: 0.08,
pose_score_max: 0.8
}
},
// ... 其他規則
},
overall_thresholds: {
auto_accept: 0.7,
auto_reject: 0.3,
review_range: [0.3, 0.7]
}
};
const response = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/faces/filter', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ rules: filterConfig })
});
const result = await response.json();
console.log(`Passed: ${result.passed}, Filtered: ${result.filtered}`);
// 顯示被過濾的臉部
result.filtered_faces.forEach(face => {
console.log(`Face ${face.face_id} (frame ${face.frame}): ${face.reasons.join(', ')}`);
});
4. 獲取過濾統計¶
端點: GET /api/v1/file/{file_uuid}/filter-stats
功能: 獲取臉部的過濾統計資訊
回應範例:
{
"total_faces": 392,
"by_category": {
"total_faces": 392,
"by_source": {
"apple_vision": 392,
"mediapipe": 5
},
"by_quality": {
"avg_eye_ratio": 0.37,
"avg_nose_ratio": 0.12,
"avg_pose_score": 0.15,
"mediapipe_verified": 5,
"avg_embedding_similarity": 0.78,
"avg_consecutive_frames": 12.5
}
}
}
Studio 使用範例:
// 獲取過濾統計
const response = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/filter-stats');
const stats = await response.json();
console.log(`Total faces: ${stats.total_faces}`);
console.log(`Apple Vision faces: ${stats.by_category.by_source.apple_vision}`);
console.log(`MediaPipe faces: ${stats.by_category.by_source.mediapipe}`);
console.log(`Avg embedding similarity: ${stats.by_category.by_quality.avg_embedding_similarity}`);
5. 管理過濾規則¶
5.1 獲取過濾規則¶
端點: GET /api/v1/file/{file_uuid}/filter-rules
回應範例:
{
"rules": [
{
"id": 1,
"rule_name": "default_filter",
"rules": {
"version": "1.0",
"rules": {
"landmark_analysis": { ... },
"mediapipe_verification": { ... },
"embedding_similarity": { ... },
"temporal_consistency": { ... }
},
"overall_thresholds": { ... }
},
"is_active": true,
"created_at": "2026-08-09T12:00:00Z",
"updated_at": "2026-08-09T12:00:00Z"
}
]
}
5.2 儲存過濾規則¶
端點: POST /api/v1/file/{file_uuid}/filter-rules
請求範例:
{
"rule_name": "strict_filter",
"rules": {
"version": "1.0",
"rules": {
"landmark_analysis": {
"enabled": true,
"weight": 0.3,
"thresholds": {
"eye_ratio_min": 0.30,
"eye_ratio_max": 0.50,
"nose_ratio_min": 0.10,
"pose_score_max": 0.5
}
},
// ... 其他規則
},
"overall_thresholds": {
"auto_accept": 0.8,
"auto_reject": 0.2,
"review_range": [0.2, 0.8]
}
},
"is_active": true
}
Studio 使用範例:
// 獲取現有規則
const getResponse = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/filter-rules');
const rulesData = await getResponse.json();
// 儲存新規則
const strictFilter = {
rule_name: "strict_filter",
rules: {
// ... 規則配置
},
is_active: true
};
const saveResponse = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/filter-rules', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(strictFilter)
});
const savedRule = await saveResponse.json();
console.log(`Saved rule: ${savedRule.rule_name} (id: ${savedRule.id})`);
過濾規則說明¶
規則類型¶
1. Landmark 分析 (landmark_analysis)¶
功能: 檢查臉部 landmark 幾何特徵是否正常
閾值:
- eye_ratio_min: 眼睛間距比例最小值(預設 0.25)
- eye_ratio_max: 眼睛間距比例最大值(預設 0.55)
- nose_ratio_min: 眼睛到鼻子比例最小值(預設 0.08)
- pose_score_max: Pose 角度評分最大值(預設 0.8)
權重: 0.3
2. MediaPipe 驗證 (mediapipe_verification)¶
功能: 驗證 MediaPipe 是否也檢測到該臉部
值:
- min_iou: 最小 IoU 值(預設 0.3)
權重: 0.2
3. Embedding 相似度 (embedding_similarity)¶
功能: 檢查臉部 embedding 與已知人臉的相似度
閾值:
- min_similarity: 最小相似度(預設 0.70)
權重: 0.3
4. 時間一致性 (temporal_consistency)¶
功能: 檢查臉部是否在連續幀中出現
閾值:
- min_consecutive_frames: 最小連續數(預設 3)
權重: 0.2
總體閾值¶
overall_thresholds:
- auto_accept: 自動接受閾值(預設 0.7)
- auto_reject: 自動拒絕閾值(預設 0.3)
- review_range: 審查範圍(預設 [0.3, 0.7])
建議:
- overall_confidence >= 0.7: 接受
- overall_confidence <= 0.3: 拒絕
- 0.3 < overall_confidence < 0.7: 審查
Bbox Size 說明¶
欄位: bbox_size
計算方式: min(width, height)
說明: - 預先計算並儲存於後端 - 用於前端即時過濾 - 維持當前 Swift 實作的計算方式
前端過濾範例:
// 前端即時過濾
const quickFilter = (faces, rules) => {
return faces.filter(face => {
// Bbox size 過濾
if (rules.min_bbox_size && face.filter_features.bbox_size < rules.min_bbox_size) {
return false;
}
return true;
});
};
// 使用範例
const filtered = quickFilter(allFaces, {
min_bbox_size: 80 // 最小 80 像素
});
建議閾值: - 保守: 80 像素 - 平衡: 64 像素(當前預設) - 寬鬆: 48 像素
未來擴展:
- 可考慮增加 max(bbox.width, bbox.height) 選項
- 可考慮增加 bbox.width * bbox.height (面積) 選項
預設配置¶
API 提供預設過濾規則,可直接使用:
{
"version": "1.0",
"rules": {
"landmark_analysis": {
"enabled": true,
"weight": 0.3,
"thresholds": {
"eye_ratio_min": 0.25,
"eye_ratio_max": 0.55,
"nose_ratio_min": 0.08,
"pose_score_max": 0.8
}
},
"mediapipe_verification": {
"enabled": true,
"weight": 0.2,
"thresholds": {
"min_iou": 0.3
}
},
"embedding_similarity": {
"enabled": true,
"weight": 0.3,
"thresholds": {
"min_similarity": 0.70
}
},
"temporal_consistency": {
"enabled": true,
"weight": 0.2,
"thresholds": {
"min_consecutive_frames": 3
}
}
},
"overall_thresholds": {
"auto_accept": 0.7,
"auto_reject": 0.3,
"review_range": [0.3, 0.7]
}
}
完整使用範例¶
場景 1: 過濾低品質臉部¶
// 1. 獲取臉部列表
const facesResponse = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/faces', {
method: 'POST',
body: JSON.stringify({ source: 'all', page: 1, page_size: 100 })
});
const facesData = await facesResponse.json();
// 2. 套用過濾規則
const filterResponse = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/faces/filter', {
method: 'POST',
body: JSON.stringify({ rules: null }) // 使用預設規則
});
const filterResult = await filterResponse.json();
console.log(`Total: ${filterResult.total_faces}`);
console.log(`Passed: ${filterResult.passed}`);
console.log(`Filtered: ${filterResult.filtered}`);
// 3. 顯示被過濾的臉部
filterResult.filtered_faces.forEach(face => {
console.log(`❌ Face ${face.face_id} (frame ${face.frame})`);
face.reasons.forEach(reason => {
console.log(` - ${reason}`);
});
});
場景 2: 自訂嚴格過濾¶
// 自訂嚴格過濾規則
const strictRules = {
version: "1.0",
rules: {
landmark_analysis: {
enabled: true,
weight: 0.3,
thresholds: {
eye_ratio_min: 0.30, // 更嚴格
eye_ratio_max: 0.50, // 更嚴格
nose_ratio_min: 0.10, // 更嚴格
pose_score_max: 0.5 // 更嚴格
}
},
mediapipe_verification: {
enabled: true,
weight: 0.2,
thresholds: {
min_iou: 0.5 // 更嚴格
}
},
embedding_similarity: {
enabled: true,
weight: 0.3,
thresholds: {
min_similarity: 0.80 // 更嚴格
}
},
temporal_consistency: {
enabled: true,
weight: 0.2,
thresholds: {
min_consecutive_frames: 5 // 更嚴格
}
}
},
overall_thresholds: {
auto_accept: 0.8, // 更嚴格
auto_reject: 0.2, // 更嚴格
review_range: [0.2, 0.8]
}
};
// 儲存規則
await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/filter-rules', {
method: 'POST',
body: JSON.stringify({
rule_name: "strict_filter",
rules: strictRules,
is_active: true
})
});
// 套用嚴格規則
const result = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/faces/filter', {
method: 'POST',
body: JSON.stringify({ rules: strictRules })
});
錯誤處理¶
常見錯誤¶
404 Not Found:
原因: 臉部 ID 不存在500 Internal Server Error:
原因: 資料庫查詢失敗錯誤處理範例¶
try {
const response = await fetch('/api/v1/file/0489e104d7d1e11e613da01f7aa130ca/face/face_999/filter-report');
if (!response.ok) {
if (response.status === 404) {
console.error('Face not found');
} else {
console.error('Server error');
}
return;
}
const report = await response.json();
console.log(report);
} catch (error) {
console.error('Request failed:', error);
}
最佳實踐¶
1. 使用預設規則開始¶
建議先使用預設規則,再根據實際需求調整。
2. 逐步調整閾值¶
不要一次性調整所有值,逐步調整並觀察結果。
3. 監控過濾統計¶
定期檢查過濾統計,確保過濾規則合理。
4. 保存多個規則配置¶
為不同場景保存不同的規則配置(如:嚴格、平衡、寬鬆)。
5. 使用者自訂¶
允許使用者自訂過濾規則,提供預設模板。
支援與反饋¶
如有問題或建議,請聯繫 Core Team。
文件版本: v1.0
最後更新: 2026-08-09