Team Ai
Apppublic

Lilann/face-access-control

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
App README

Face Access Control

即時人臉辨識的辦公室門禁與考勤系統。Webcam 串流進 WebSocket, 三道防線過濾後寫進 Postgres + pgvector,所有 audit 都留紀錄。

🌐 Live Deployment

LayerURLStatus
Frontendhttps://frontend-omega-ten-95.vercel.app🟢 Vercel free
Backendhttps://lilann-face-access-control.hf.space🟢 HF Space (Docker, CPU basic)
DatabaseSupabase project fcgkbjzbfjbxgmcrwrst (Tokyo)🟢 Postgres + pgvector + HNSW

健康檢查:`/healthz`

為什麼做這個

中小企業 / 共享空間需要「無接觸進出 + 自動考勤」的場景越來越多 (疫情後、 人臉支付興起)。市面上現成方案要嘛綁定特定硬體 (海康、商湯)、要嘛收人頭費 (Kisi、Verkada)。

這個專案展示一條 「全開源、可自架、用免費雲端 tier 就能跑」 的可行路線:

  • —後端 InsightFace + FastAPI + Supabase pgvector,跑在 HuggingFace Space (Docker, CPU)
  • —前端 Next.js 14 + TypeScript,跑在 Vercel free
  • —真實生產級別考量:品質檢查 / 活體檢測 / cooldown / 多 embedding / threshold ROC 校準
  • —個資法 / GDPR 合規:硬刪、不存原圖、audit log
Note (2026-05-08):兩個已知限制計劃在後續迭代修: 1. Supabase servicerole 仍用 legacy HS256 JWT(supabase-py 2.9.1 不支援 `sbsecret*` opaque keys)。等 supabase-py 升級後改用新 secret + disable legacy。 2. CORS 仍是 `ALLOWEDORIGINS=*`:實測 HF Space reverse proxy 會把 request Origin echo 回 ACAO header,即使後端 starlette CORSMiddleware 設了 specific origins 也擋不住外部 origin。等改部署到 Render / Fly 才能真正鎖 origin。

系統架構

┌────────────────────┐  WebSocket   ┌─────────────────────────┐  RPC   ┌───────────────────────┐
│  Browser (Vercel)  │ ───────────► │  FastAPI (HF Docker)    │ ─────► │  Supabase Postgres    │
│                    │  {frame: b64}│                         │        │  + pgvector + HNSW    │
│  Next.js 14 + TS   │              │  _do_recognize():       │        │                       │
│  webcam getUserMedia│ ◄─────────── │   A. 品質評估           │        │  users                │
│  canvas resize 640 │ RecognizeRes │   B. Liveness          │        │  face_embeddings      │
│  JPEG 0.85 / 500ms │              │   C. 1:N match         │        │  attendance_logs      │
│  Top-K bar / bbox  │              │   cooldown 30s         │        │  recognition_events   │
│  backpressure      │              │  InsightFace buffalo_l │        │  match_face() RPC     │
└────────────────────┘              └─────────────────────────┘        └───────────────────────┘

詳細圖在 /about 頁。

關鍵設計決策

1. SQLite → pgvector 為什麼換

上一版 (face_recognition_system) 用 SQLite + 應用層線性掃 cosine。N=1000 時 50-150ms,prod 不可接受。pgvector + HNSW index 在 N=10k 時 top-K <1ms。 schema/migration/auth 跟業務 DB 同一條,部署簡單。

為什麼不是 Pinecone/Qdrant?專業向量 DB 要付費或自架, portfolio + 中小企業 demo 階段 pgvector + Supabase free tier 已綽綽有餘。

2. 多 embedding 註冊為什麼魯棒

單一張 embedding 對「不同光照 / 戴眼鏡 / 微側臉」很脆弱。每個 user 註冊 3-5 張 不同角度照片 → 5 個獨立 embedding 進 HNSW,比對時取「最近的 K 個 embedding」 而不是「最近的 K 個 user」,自然提升命中率與 confidence。

3. Threshold 怎麼用 ROC 決定的(不是拍腦袋)

scripts/evaluate_threshold.py 在自家 dataset 上跑:

  • —算 genuine pairs (同人不同張) vs impostor pairs (跨人) 的 cosine similarity
  • —出 ROC + DET curve + 分布圖
  • —表列 FAR=1e-1 / 1e-2 / 1e-3 / 1e-4 對應的 threshold 與 FRR
  • —生產級門禁建議 FAR=1e-3 (千分之一冒名),腳本印推薦值

預設 RECOGNITION_THRESHOLD=0.42 是 LFW-style 經驗起點,請自家照片重跑校準。

4. 三道防線 (defense in depth)

每張幀依序:

順序防線檢查失敗 event_type
A品質det_score≥0.65, 臉框最小邊≥80px, yaw≤40°low_quality
BLiveness拉普拉斯方差 + HSV 飽和度方差 → ≥0.85 視為真人spoof_detected
C1:N matchSupabase match_face RPC 取 top-5,最高 ≥ 0.42 視為命中unknown / check_in

⚠️ B 目前用啟發式,生產請替換 [Silent-Face-Anti-Spoofing](https://github.com/minivision-ai/Silent-Face-Anti-Spoofing) 的 MiniFASNet。

5. Cooldown 機制

實機 webcam 500ms 一幀 → 同個人在門口站 5 秒會打卡 10 次。in-memory last_check_in[user_id] dict 30 秒內不重複 insert attendance_logs,但仍 回傳「比對成功」讓前端顯示綠色狀態。

隱私與合規

  • —不存原始照片:註冊時抽完 embedding 立刻丟掉 binary,DB 只留 vector(512)
  • —DELETE /users/{id} 為硬刪(CASCADE 連帶 faceembeddings + attendancelogs),符合個資法可被遺忘權
  • —所有 frame 經 WSS / HTTPS,server in-memory 處理完即釋放
  • —audit log (recognition_events) 不含 image,只留 metadata

效能指標 (實測待填)

指標值備註
後端冷啟動 (lifespan)_____ sRender free / Docker
單張辨識延遲 (CPU)_____ msWS 端到端,含 base64 + RPC round-trip
HNSW top-K @ N=1000< 5mspgvector M=16, ef_construction=64
Cooldown 命中率_____%同人 5s 內重複比例
Threshold @ FAR=1e-3_____跑 evaluate_threshold.py 後填

技術棧

前端:Next.js 14 App Router · TypeScript · Tailwind CSS · recharts 後端:FastAPI · WebSocket (asyncio) · InsightFace buffalo_l · ONNX Runtime · OpenCV · Pillow · Pydantic v2 · Loguru 資料庫:Supabase Postgres + pgvector + HNSW 部署:Vercel (前端) · Render / Fly.io (後端 Docker)

本地啟動

bash
# 1. Clone + 進專案
git clone <this-repo> && cd face-access-control

# 2. Supabase: 在 SQL Editor 整段貼 backend/schema.sql 跑
#    Settings → API 拿 URL + service_role key

# 3. Backend
cd backend
python -m venv venv
venv/bin/activate                      # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env                   # 填 SUPABASE_URL / SUPABASE_SERVICE_KEY
uvicorn main:app --reload              # http://localhost:8000

# 4. Frontend (另開 terminal)
cd ../frontend
npm install
cp .env.local.example .env.local       # NEXT_PUBLIC_API_URL=http://localhost:8000
npm run dev                            # http://localhost:3000

第一次跑後端時 InsightFace 會下載 buffalo_l (~280MB)。Dockerfile 會在 build 時 預載,所以 docker build 後容器啟動即可用。

部署到雲端

詳見最末「下一步行動清單」。簡述:

  1. 1.Supabase:開新 project (free tier 500MB),跑 schema.sql
  2. 2.Render:連 GitHub repo,選 backend/Dockerfile,設環境變數 SUPABASEURL / SUPABASESERVICEKEY (+ ALLOWEDORIGINS=https://your.vercel.app)
  3. 3.Vercel:連同一個 repo,root 設 frontend,環境變數 NEXT_PUBLIC_API_URL=https://your-backend.onrender.com

跑 threshold 評估

bash
mkdir -p data
# 把每個人的照片放進對應子資料夾:
# data/Alice/img1.jpg img2.jpg img3.jpg
# data/Bob/img1.jpg ...
# 至少 2 個人,每人至少 2 張

python scripts/evaluate_threshold.py --data ./data --output ./reports
# 終端會印推薦的 threshold 值,貼回 backend/.env

產出 reports/:

  • —roc_curve.png — ROC (FAR x-log scale)
  • —det_curve.png — DET (兩軸 log)
  • —score_distribution.png — genuine vs impostor 分布
  • —threshold_table.csv — FAR=1e-1..1e-4 對應 threshold + FRR

後續路線

  • —[ ] Liveness 升級:替換成 Silent-Face-Anti-Spoofing MiniFASNet(成熟、輕量、開源)
  • —[ ] 戴口罩支援:替換成 InsightFace antelopev2 模型(注意 license)
  • —[ ] 多攝影機:每攝影機獨立 WS room,server 維護 stream → user_id 對應
  • —[ ] Edge 部署:Jetson Nano + ONNX Runtime ARM build,門口本機跑不依賴雲端
  • —[ ] Redis Cooldown:取代 in-memory dict,server 重啟也保留狀態
  • —[ ] GDPR 自服務:用戶自己上 portal 申請刪除,無需找 admin

License

MIT — 程式碼。 InsightFace buffalo_l 權重為 non-commercial research only(modelzoo 明文)。 商用部署請:(a) 跟 InsightFace 商業授權、(b) 用 VGGFace2 retrain、或 (c) 換 facerecognition (dlib)。