FacePass 是一个课程大作业项目,目标是在本地完成图片上传、人脸检测、身份识别与结果可视化。当前版本完成了模型端、后端、前端三层分离的最小闭环,并加入边界错误处理、日志和重试机制。前端为纯 HTML + JS 页面(早期的 Gradio 界面已移除),由后端 GET / 直接返回,整套服务单进程即可跑起来。
当前模型层支持两种实现:
insightface:默认运行实现,使用 InsightFace 完成人脸检测与特征提取。fake:测试替身实现,不下载权重,用于在 CI / 本地测试中验证后端与接口解耦。
src/
|-- common/ # 日志、重试、安全图片解码、通用异常
|-- face_model/ # 模型端:FaceModel 抽象、DetectedFace、InsightFace / Fake 实现
|-- backend/ # 后端:Gallery、Recognizer、FastAPI API、配置、外部数据集导入
| `-- dataset_import.py # 解析上传的 test.zip / 本机数据集目录并跑外部评测
|-- eval/ # 评测内核:CelebA / 单脸 / 端到端数据集、指标与出图,被脚本和后端复用
`-- frontend/
`-- static/
`-- index.html # Web 前端(纯 HTML + JS),由后端 GET / 直接返回
run.bat # Windows 双击启动入口(瘦壳:找到一个 python 去跑 launcher.py)
scripts/
|-- launcher.py # 启动向导:检测 uv/python/venv、查依赖、选 CPU/GPU、写 config、启动
|-- run_dev.py # 一键启动脚本(只启后端,前端由后端内置返回)
|-- check_runtime.py # 打印 onnxruntime provider 可用性,确认是否吃到 GPU
|-- preannotate_test.py # 为 dataset/test/images 生成预标注草稿
|-- summarize_test_annotations.py # 统计 test 标注人数并检查漏标/多余标注
|-- eval_self.py # 单脸裁剪口径评测脚本
|-- analyze_threshold.py # 注册集相似度分布与阈值分析
|-- eval_end2end.py # 多脸端到端评测与出图
|-- eval_celeba.py # CelebA 100 类裁剪脸 top-1 评测与出图
`-- check_celeba_leakage.py # 检查 CelebA register/test 是否存在相同文件(数据泄漏)
dataset/
|-- identities.csv # 身份 ID 到显示名映射,已纳入版本控制
|-- registered/ # 真实注册集:p01..p20 注册照目录,已纳入版本控制
`-- test/ # 自采测试图与标注目录,测试图已纳入版本控制
celeba_100_identities_3reg_3test/ # CelebA 子集:register/ 与 test/,各身份 3 注册 3 测试,已纳入版本控制
data/
|-- registered/ # 测试 / 脚本 fixture
|-- test/ # 测试 / 脚本 fixture
`-- tmp_* # 临时样本,不是正式数据集根目录
models/ # gallery.pkl 等本地产物,不进 git
reports/ # 评测 JSON / PNG 输出,本地产物,不进 git
tests/ # 接口、边界和错误路径测试
requirements.txt # pip 依赖列表(约束与 pyproject.toml 一致)
config.example.toml # 模型路径与阈值的示例配置
普通使用者无需手动执行本节命令。 直接双击仓库根的
run.bat,启动向导会自动检测并安装依赖、选择 CPU/GPU 并启动(见 运行 → 方式一)。本节以及「方式二:命令行启动」中的命令,面向 CI、自动化脚本,或需要手动控制环境、进行调试的进阶用户。
本项目使用 uv 管理依赖。默认依赖固定为 CPU 版 onnxruntime==1.22.1,这样 CPU-only 机器、测试环境和 CI 都能直接安装运行。较新的 1.24.x 在本项目的 CPython 3.10 Windows 环境没有可用 wheel。
uv sync如果你本机没有装 uv,可以改用 pip + 仓库根的 requirements.txt,它的依赖约束与 pyproject.toml 保持一致:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt之后本文档里所有 uv run python ... / uv run uvicorn ... / uv run pytest ... 命令,把开头的 uv run 去掉、直接用激活环境里的 python 即可,例如:
# uv run python scripts/run_dev.py
python scripts/run_dev.py
# uv run uvicorn src.backend.api:app --port 8000
python -m uvicorn src.backend.api:app --port 8000
# uv run pytest tests -q
python -m pytest tests -qGPU 切换、uv pip install 等小节同样适用:没有 uv 时把 uv pip install 换成普通 pip install 即可。run.bat 也会自动处理这一点——找不到 uv 时会退回到系统 python(见下文“运行”)。
如果你本机已经配好了 NVIDIA 驱动,可以只在本地当前虚拟环境里把 ORT 切到 GPU 版,而不用改仓库默认依赖。Windows 上建议把 ORT 和它依赖的 CUDA/cuDNN Python 运行库一起装进 .venv:
uv pip uninstall onnxruntime
uv pip install "onnxruntime-gpu[cuda,cudnn]"这一步是可选本地增强,不应直接改成项目默认依赖,否则会降低其他同学和 CI 的可移植性。不同 Python / Windows 组合下可用的 GPU wheel 版本可能不同,安装后请立刻用下面的诊断脚本确认 provider 是否真的起来。
后端默认配置在 src/backend/config.py,当前 model_name="insightface"。如果只想跑不依赖权重的后端集成测试,可以在测试里把配置切到 fake。
运行 insightface 模型前,需要你自己准备本地 buffalo_l 目录。项目不会再自动下载或托管模型文件。当前适配器按“buffalo_l 模型目录本身”加载,不是传 models_cache 根目录。
准备工作:
- 准备本地
buffalo_l模型目录,例如F:\InsightFace\models_cache\models\buffalo_l。 - 在项目根的
config.toml里写好[model].path(见下文),或在启动时用--model-path显式传入。 - 准备注册集:将
p01..p20的注册照放到dataset/registered/p01/等目录。
这是推荐的启动方式,全程无需在命令行手动执行任何 uv / python 命令。 直接双击仓库根的 run.bat 即可启动。若机器装了 Windows Terminal(wt),双击会自动在 Windows Terminal 里重新打开,
并优先用 PowerShell(pwsh > powershell,都没有才退回 cmd),而不是旧的 cmd 窗口
(已在终端里运行时不会重开;想关掉这个行为设环境变量 FACEPASS_NO_WT=1)。run.bat 只是个瘦壳——找到一台机器上任意可用的 python
(优先 .venv,否则系统 python,再否则 uv run --no-project python)去运行
scripts/launcher.py,由后者完成首次启动向导:
- 检测当前机器上的
uv、python与项目虚拟环境.venv。 - 选择运行环境:优先复用
uv管理的.venv;尚未初始化时可选择执行uv sync;无uv时改用项目.venv或全局python,均不可用则退出。 - 校验运行依赖是否齐备,缺失时列出并可一键安装(
uv sync或pip install -r requirements.txt)。 onnxruntime 在确认 CPU/GPU 后再单独处理。 - 选择 CPU 或 GPU:
- CPU:在所选环境中确保 CPU 版 onnxruntime 就绪后启动。
- GPU:使用独立的
.venv-gpu,与项目.venv隔离,不受uv回同步影响。首次会创建该环境、 安装onnxruntime-gpu[cuda,cudnn]并验证 CUDA,此后复用、不再重装;即便期间以 CPU 模式运行过也不受影响。 首次选择 GPU 会额外下载 CUDA / cuDNN 等运行库,体积约 2.4 GB(.venv-gpu总大小约 2.8 GB, 远大于 CPU 版.venv的约 0.5 GB);之后复用、不再重复下载。磁盘紧张时可只用 CPU,或参见 清理 GPU 环境。 若无法创建.venv-gpu(缺少 uv,或 python 不含 venv/pip 等),会说明原因并提供三种处理方式: 装入当前环境、回退 CPU、退出。
- 将选择写入
config.toml的[runtime]表(device与launcher),此后双击直接据此启动; 若该环境失效(缺少虚拟环境、依赖或 CUDA)会自动回退并重新运行向导。如需强制重选,执行run.bat --reconfigure。
注:模型路径是否存在、模型校验等仍由后端启动时完成(不变)。进程异常退出时窗口会停下来显示 错误码,不会一闪而过。启动后浏览器打开
http://127.0.0.1:8000即是 Web 界面。
普通使用者用方式一双击
run.bat即可,无需手动执行uv run/python。下列命令面向 CI、自动化脚本,或需要手动控制环境、进行调试的进阶用户。
# 一键启动(只启后端,Web 前端由后端 GET / 直接返回)
uv run python scripts/run_dev.py
# 需要临时指定模型目录时
uv run python scripts/run_dev.py --model-path F:\InsightFace\models_cache\models\buffalo_l
# 或手动起后端
$env:FACEPASS_MODEL_PATH = "F:\InsightFace\models_cache\models\buffalo_l"
uv run uvicorn src.backend.api:app --port 8000run_dev.py 只启动 FastAPI 后端,Web 前端由 GET http://127.0.0.1:8000/ 直接返回,单进程即可。早期的 Gradio 前端(src/frontend/app.py)已移除。
如果路径校验通过,显式传入的模型目录会被写入项目根的 config.toml。TOML 里建议使用正斜杠:
[model]
path = "F:/InsightFace/models_cache/models/buffalo_l"
[recognition]
threshold = 0.30也可以参考并复制仓库里的 config.example.toml:
[model]
# Fill this with the buffalo_l model directory itself, not the models cache root.
path = "models/buffalo_l"
[recognition]
# Placeholder default. Replace this with a threshold chosen from real evaluation reports.
threshold = 0.30后续未显式传 --model-path 时,后端会按 CLI > GUI > config.toml 的优先级解析模型路径。
当前 InsightFaceModel 默认会优先尝试 CUDAExecutionProvider,不可用时自动回退到 CPUExecutionProvider。因此装好 GPU 版 onnxruntime 和匹配的 CUDA 环境后可以直接吃到 GPU;未配置时仍会按 CPU 跑通。
可以用下面的脚本检查当前环境是否真的暴露了 CUDA provider;如果同时传 --model-path,脚本还会把已加载 ONNX session 的 provider 打出来:
uv run python scripts/check_runtime.py
uv run python scripts/check_runtime.py --model-path F:\InsightFace\models_cache\models\buffalo_l如果输出里的 runtime.available_providers 和 session_providers 都包含 CUDAExecutionProvider,说明当前 FacePass 进程已经实际具备 GPU 推理能力。
如果你已经按上面的方式把 .venv 切到了 GPU 版 ORT,就不要再直接用会自动按锁文件回同步的默认 uv run 工作流;请改用当前虚拟环境里的解释器:
.\.venv\Scripts\python.exe scripts/check_runtime.py --model-path F:\InsightFace\models_cache\models\buffalo_l
.\.venv\Scripts\python.exe scripts/run_dev.py --model-path F:\InsightFace\models_cache\models\buffalo_lInsightFaceModel 在选择 CUDAExecutionProvider 时会先调用 onnxruntime.preload_dlls(directory=""),优先从 Python site-packages 里预加载 NVIDIA DLL,再创建 ONNX session;这样可以减少手工改系统 PATH 的需求,同时保持 CPU-only 默认安装不受影响。
健康检查:
curl http://127.0.0.1:8000/healthGPU 模式使用的独立 .venv-gpu 体积较大(约 2.8 GB,主要是 CUDA / cuDNN 运行库)。如果你之前用过
GPU、之后打算只用 CPU,直接删掉这个目录即可回收空间——它与项目 .venv 完全隔离,删除不会影响 CPU 运行:
Remove-Item -Recurse -Force .venv-gpu删除后请把 config.toml 里 [runtime] 的 device 改回 CPU,最稳妥的做法是重跑一次向导让它重新写入:
.\run.bat --reconfigure说明:
run.bat --reconfigure会忽略已保存的[runtime]配置、重新询问环境与 CPU/GPU 并覆盖写回。 即使忘了改config.toml,下次启动时向导也会发现.venv-gpu已不存在、自动回退重新选择,不会报错。 之后若又想用 GPU,再次选择 GPU 会重新创建.venv-gpu(需重新下载约 2.4 GB)。
所有接口返回 JSON,错误时格式为 {"message": "..."} 。
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/ |
返回 Web 前端页面(index.html) |
GET |
/health |
服务存活检查,返回 {"status":"ok"} |
GET |
/identities |
列出底库中所有已注册身份 |
POST |
/recognize |
上传图片,检测人脸并返回识别结果 |
POST |
/register |
上传单张图片,注册人脸到指定身份 |
POST |
/register/batch |
批量上传图片,注册到同一身份 |
POST |
/dataset-eval/inspect |
检查上传的 test.zip 或本机数据集目录布局是否合法 |
POST |
/dataset-eval/run |
对 test.zip 或本机数据集目录跑端到端评测,返回指标与内联图 |
所有响应均带 Pydantic
response_model,结构稳定,前端不需要猜字段。
GET http://127.0.0.1:8000/health
→ {"status": "ok"}
返回底库中全部身份信息:
{
"identities": [
{
"identity_id": "p01",
"name": "成龙",
"count": 1,
"prototype_count": 1,
"valid_image_count": 3
}
]
}字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
identity_id |
string |
身份 ID |
name |
string | null |
显示名(来自 identities.csv) |
count |
int |
兼容字段,等于 prototype_count |
prototype_count |
int |
原型向量数 |
valid_image_count |
int |
该身份的有效注册图数量 |
上传一张图片,检测其中所有人脸并返回识别结果。
请求:multipart/form-data
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
file |
file |
是 | 图片文件(JPG/PNG/WEBP,≤10MB) |
成功响应 (200):
[
{
"bbox": [120, 80, 200, 240],
"identity_id": "p01",
"name": "成龙",
"similarity": 0.873,
"is_unknown": false
},
{
"bbox": [400, 60, 180, 220],
"identity_id": "unknown",
"name": null,
"similarity": 0.215,
"is_unknown": true
}
]字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
bbox |
[int,int,int,int] |
人脸框 [x, y, width, height] |
identity_id |
string |
匹配到的身份 ID,未匹配时为 "unknown" |
name |
string | null |
匹配身份的显示名,unknown 时为 null |
similarity |
float |
与底库最佳匹配的余弦相似度 |
is_unknown |
bool |
是否低于阈值被判定为 unknown |
错误:
400— 图片格式无效或未检测到人脸413— 图片超过 10MB
上传单张图片,注册到指定身份。注册成功后自动重建底库,无需重启。
请求:multipart/form-data
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
file |
file |
是 | 人脸照片 |
identity_id |
string |
是 | 身份 ID,例如 p21(仅允许字母、数字、下划线、连字符,不能为空) |
name |
string |
否 | 显示名,会写入 identities.csv |
成功响应 (200):
{
"identity_id": "p21",
"name": "张三"
}错误:
400— 图片无效 / 未检测到人脸 /identity_id非法413— 图片过大503— 识别器未初始化
批量上传多张图片,全部注册到同一个身份。适合注册集整理时批量导入。
请求:multipart/form-data
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
files |
file[] |
是 | 多张人脸照片 |
identity_id |
string |
是 | 目标身份 ID(命名约束同上) |
name |
string |
否 | 显示名 |
成功响应 (200):
{
"identity_id": "p21",
"name": "张三",
"saved": 3
}字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
identity_id |
string |
身份 ID |
name |
string |
显示名 |
saved |
int |
成功录入的图片数量 |
错误:
400— 没有一张图片成功录入(全部没人脸或格式错误)/identity_id非法413— 有图片超过 10MB503— 识别器未初始化
对一份外部数据集(上传的 test.zip,或本机数据集目录绝对路径 dataset_dir,二选一)做端到端评测。
inspect:仅检查布局是否合法,返回{"has_registered": bool},表示该数据集是否自带注册集。run:gallery_choice表单字段选择用local(仓库注册集)还是数据集自带注册集建库;返回评测指标、混淆对、漏检/误检列表,以及内联(base64 data URL)的混淆矩阵、检测、准确率三张图。
HTML 前端的「评估」标签页已接入这两个接口:支持上传 test.zip,或填入/用系统对话框选择本机数据集目录绝对路径(超大数据集免上传)。如需脚本化批量评测,也可直接走 scripts/eval_end2end.py(见下文“数据与评测”)。
启动服务后打开 http://127.0.0.1:8000,界面有「识别 / 录入 / 底库」三个标签页。切到「录入」:
- 拖入或选择一人多张照片(支持批量)
- 填写身份 ID 和姓名
- 点击「录入底库」,完成后可在「底库」页看到更新
可以用 Python 脚本调用 /register/batch 接口,适合整理大量身份时使用:
import requests
from pathlib import Path
API = "http://127.0.0.1:8000"
# 为 identity p21 注册 3 张照片
identity_dir = Path("dataset/registered/p21")
files = [
("files", (p.name, p.read_bytes(), "image/jpeg"))
for p in identity_dir.glob("*.jpg")
]
resp = requests.post(
f"{API}/register/batch",
data={"identity_id": "p21", "name": "张三"},
files=files,
)
print(resp.json()) # → {"identity_id": "p21", "name": "张三", "saved": 3}dataset/
|-- identities.csv # 身份映射表(注册脚本自动更新)
`-- registered/
|-- p01/
| |-- p01_r01.jpg
| |-- p01_r02.jpg
| `-- p01_r03.jpg
|-- p02/
| `-- ...
`-- p21/ # 新身份
|-- p21_r01.jpg
`-- p21_r02.jpg
identities.csv 格式为 identity_id,name,domain,每注册一个新身份时后端自动 upsert 一行,并保留原有的 domain 列不被清空。
- 启动服务 → 双击
run.bat或uv run python scripts/run_dev.py - 注册数据 → 通过
/register/batch导入所有注册照 - 遍历测试图 → 逐张调
/recognize获取检测结果 - 对比标注 → 拿 API 返回的
bbox/identity_id/similarity与annotation.json做配对 - 计算指标 → 统计 top-1 准确率、检测召回率、unknown 准确率等
import requests, json
from pathlib import Path
API = "http://127.0.0.1:8000"
results = {}
for img_path in Path("dataset/test/images").glob("*.jpg"):
with open(img_path, "rb") as f:
resp = requests.post(f"{API}/recognize", files={"file": f})
results[img_path.name] = resp.json()
with open("results.json", "w") as f:
json.dump(results, f, indent=2, ensure_ascii=False)health = requests.get(f"{API}/health").json()
print(health) # → {"status": "ok"}
# 确认底库中已有多少身份
identities = requests.get(f"{API}/identities").json()
print(len(identities["identities"])) # → 已注册身份数不想自己写配对逻辑时,直接用现成脚本
scripts/eval_end2end.py即可得到完整指标与混淆矩阵(见下文)。
当前仓库内 dataset/test 的正式标注文件是 dataset/test/annotation.json,格式是“按图片名分组”的 JSON 对象:
{
"group_01.jpg": [
{"bbox": [12, 34, 80, 80], "identity": "p01", "score": 0.71},
{"bbox": [120, 40, 76, 76], "identity": "unknown", "score": 0.08}
],
"p01_t01.jpg": [
{"bbox": [220, 65, 140, 180], "identity": "p01", "score": 0.93}
]
}说明:
- 顶层 key 必须等于图片文件名,例如
group_01.jpg、p01_t01.jpg。 - 每个 value 是该图里所有人脸的列表。
bbox固定为[x, y, w, h]。identity只能写p01..p20或unknown。score是预标注阶段保留的相似度参考值;评测会忽略它,但人工核对时应该保留。- 现有加载器同时兼容
.json和旧.jsonl,但当前仓库标准是annotation.json,后续不要再把dataset/test主标注写回 JSONL。
这是当前最容易出错的地方:往 dataset/test/images 增加图片时,必须同步维护 dataset/test/annotation.json。只加图片、不加标注,后面任何人都不知道这张图是谁,也没法直接评测。
推荐流程:
- 把图片放进 dataset/test/images。
- 单人照命名为
pXX_tNN.jpg,例如p06_t02.jpg。 - 合照命名为
group_NN.jpg。 - 不要提交自己都看不懂含义的文件名。
- 单人照命名为
- 同一个提交里更新 dataset/test/annotation.json。
- 顶层 key 必须和图片文件名完全一致。
- 单人照也必须显式写 bbox 和 identity,不要靠文件名猜。
- 不确定身份就先写
unknown,不要为了“看起来完整”硬写某个pXX。
- 如果是新加了一批图片,先跑一次预标注,再人工核对。
uv run python scripts/preannotate_test.py `
--images-dir dataset/test/images `
--out dataset/test/annotation.json `
--registered-root dataset/registered `
--overwrite- 预标注只是草稿,必须人工检查后再提交。
- 重点看低分项、单人照多脸、多人照漏脸。
- 当前规则里,低于
0.25的草稿身份会直接写成unknown。
- 提交前一定跑下面这个检查。
uv run python scripts/summarize_test_annotations.py如果输出里“缺少标注的图片”或“多余标注项”不是 0,先修 annotation,再提交图片。
目录约定需要特别说明:
dataset/:真实实验数据目录,当前已纳入版本控制;其中dataset/registered已准备完毕,dataset/test当前主要包含测试图。data/:仓库内测试与脚本 fixture 目录,主要放临时合成样本和.gitkeep,不是正式数据集根目录。
外部数据集(/dataset-eval/* 接口)支持两种输入:
ZIP:上传较小的test.zip。文件夹:直接传本机绝对路径,例如F:\datasets\my_eval。纯浏览器无法可靠暴露任意本机绝对目录,因此目录模式只把路径字符串发给后端,再由后端从本机磁盘读取。
文件夹模式只接受两种明确布局,不再递归猜目录:
- 选中的目录本身就是测试目录:目录下直接有
images/与一个标注文件。 - 选中的是数据集根目录:目录下直接有
test/images/与对应标注文件。
仓库内相关脚本入口:
scripts/preannotate_test.py:对dataset/test/images跑预标注,生成待人工核对的annotation.json草稿。scripts/summarize_test_annotations.py:统计当前 test 标注里每个pXX的出现次数,并检查图片与标注是否一一对应。scripts/eval_self.py:复用标注框裁剪后的单脸口径,适合先看识别本身是否区分开。scripts/eval_end2end.py:复用真实Recognizer.recognize_image()整图链路,做“检测框 ↔ 标注框 IoU 贪心配对 + 端到端 top-1”评测。scripts/analyze_threshold.py:只看注册集内部相似度分布,用来给 unknown 阈值找候选值。
当前 dataset/test/annotation.json 统计如下:
- 标注图片数:
66 - 标注人脸总数:
165 - 其中
unknown:97 - 图片目录:
dataset/test/images - 缺少标注的图片:
0 - 多余标注项:
0
可用下面的命令重新生成本节:
uv run python scripts/summarize_test_annotations.py --write-readme如果上面两项不是 0,先修复 dataset/test/annotation.json,再继续评测或提交图片。
| 身份 | 标注人数 |
|---|---|
| p01 | 4 |
| p02 | 3 |
| p03 | 3 |
| p04 | 4 |
| p05 | 3 |
| p06 | 4 |
| p07 | 4 |
| p08 | 3 |
| p09 | 3 |
| p10 | 3 |
| p11 | 3 |
| p12 | 3 |
| p13 | 3 |
| p14 | 3 |
| p15 | 4 |
| p16 | 5 |
| p17 | 3 |
| p18 | 3 |
| p19 | 3 |
| p20 | 4 |
| unknown | 97 |
单脸评测:
uv run python scripts/eval_self.py `
--annotations-path dataset/test/annotation.json `
--test-root dataset/test `
--registered-root dataset/registered `
--model-name insightface `
--threshold 0.30阈值分析:
uv run python scripts/analyze_threshold.py `
--registered-root dataset/registered `
--model-name insightface `
--histogram-path reports/threshold_hist.png多脸端到端评测:
uv run python scripts/eval_end2end.py `
--annotations-path dataset/test/annotation.json `
--test-root dataset/test `
--registered-root dataset/registered `
--model-name insightface `
--threshold 0.30eval_end2end.py 会输出:
reports/end2end_eval.jsonreports/end2end_confusion_matrix.pngreports/end2end_detection.pngreports/end2end_accuracy.png
端到端评测的指标口径:
- 检测层:
detection_recall、detection_precision、false_positives - 识别层:严格
strict_top1_accuracy与宽松matched_top1_accuracy unknown:标注为 unknown 的检出准确率,以及系统判成 unknown 的精确率
如果本机还没有准备 dataset/test 标注或 dataset/registered 注册集,eval_end2end.py 会打印提示并直接退出,不会抛异常。
启动建库时的几个常见 warning 目前属于预期行为:
- 某张注册图检测到多张人脸时,系统会记录 warning,并使用面积最大的一张做人脸注册。
- 图片像素总数超过
25_000_000时,safe_load_image会拒绝加载并跳过该图,日志里会显示“图片尺寸过大”。
仓库内 celeba_100_identities_3reg_3test/ 是一个 100 身份的 CelebA 子集,目录结构为 register/identity_XXXXX/ 与 test/identity_XXXXX/,每个身份各 3 张注册图、3 张测试图,图片本身已是裁剪好的人脸。用它做纯识别(不含检测)的 top-1 评测:
uv run python scripts/eval_celeba.py `
--data-dir celeba_100_identities_3reg_3test `
--model-name insightfaceeval_celeba.py 会输出:
reports/celeba_eval.json:top-1 准确率、逐类准确率、若干成功/失败样本。reports/celeba_top1_accuracy.pngreports/celeba_per_class_accuracy.png
提交前可以先确认 register/test 之间没有相同图片造成数据泄漏:
uv run python scripts/check_celeba_leakage.py --data-dir celeba_100_identities_3reg_3test该脚本按文件哈希比对 register 与 test。退出码:0 无重叠,2 发现跨 register/test 的相同文件,1 目录或身份不一致等错误。
错误按三档处理:
- 致命错误:后端启动阶段模型加载失败、身份库不存在且无法构建、身份库为空,会记录清晰错误并以非零状态退出(
run.bat会捕获并停留显示)。 - 瞬时错误:IO/网络操作使用有限次数指数退避重试;耗尽后返回友好提示,不让进程直接崩掉。
- 可恢复输入错误:坏图片、空文件、非图片、单张注册图读取失败不重试;API 返回 400/413,建库时跳过坏图并继续。
仓库不提交任何超过 50MB 的权重、构建好的身份库或大数组文件。.gitignore 已排除:
models/data/*.onnx*.pkl*.npy.venv/- Python 缓存和日志
当前约定是:模型目录由使用者自行下载并放在本地任意位置,再通过 --model-path、FACEPASS_MODEL_PATH 或 config.toml 指定。项目本身不负责下载、不依赖 Hugging Face 托管,也不会把模型目录纳入 git。
dataset/ 与上述模型产物不同:当前仓库已经提交了 dataset/identities.csv、dataset/registered/、dataset/test/images/ 和 dataset/test/annotation.json。CelebA 子集 celeba_100_identities_3reg_3test/(裁剪脸,单图较小)也已纳入版本控制。
uv run pytest tests -q当前测试覆盖:
- Gallery 注册、保存、加载、最大相似度匹配。
- Gallery 空库匹配返回空结果,unknown 语义由 Recognizer 统一持有。
- Recognizer 阈值判定和 unknown 输出。
- API 健康检查、身份列表、坏图片 400、正常图片 200、注册接口。
load_identities支持注入路径,不再绑定定义时默认配置。safe_load_image坏图/空文件处理。with_retry只重试瞬时异常,不重试ValueError。FakeFaceModel可在不下载权重的情况下完整跑通建库和/recognizeHTTP 流程。- 多脸端到端评测的 IoU 配对、严格/宽松 top-1、unknown 指标、漏检/误检日志与 bench 脚本产图。
- CelebA 子集数据集加载、单脸 top-1 评测与
eval_celeba.py脚本,泄漏检查脚本。 - 外部数据集导入(
test.zip/ 本机目录)的布局解析、/dataset-eval/inspect与/dataset-eval/run接口。 run_dev.py启动后端、check_runtime.pyprovider 诊断脚本。- 后端不导入具体模型实现,前端不导入任何
src.*内部模块。
- 给新的 HTML 前端补上“数据集评测”页,接入已就绪的
/dataset-eval/*接口。 - 持续维护
dataset/test/annotation.json,确保新增图片和标注始终同步提交。 - 基于注册集内部相似度分布确定最终 unknown 阈值,不能用测试集调参。
- 最终提交前按课程要求补充打包脚本和报告。
| 姓名 | 学号 | 分工 |
|---|---|---|