# FaceRecAPI **Repository Path**: binghai228/FaceRecAPI ## Basic Information - **Project Name**: FaceRecAPI - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-06-23 - **Last Updated**: 2025-06-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README @[TOC](目录) # 一、概述 在当今的AI浪潮中,人脸识别技术已渗透到我们生活的方方面面。构建一个高效、可扩展的人脸识别服务是许多开发者和企业的需求。传统的Django框架虽然功能强大,但对于追求极致性能和现代API开发的场景,[**FastAPI**](https://fastapi.tiangolo.com/) 无疑是更优的选择。 **为什么选择 FastAPI?** * **极致性能**:基于 Starlette 和 Pydantic,FastAPI 的性能与 NodeJS 和 Go 不相上下,是最高性能的 Python Web 框架之一。 * **开发高效**:得益于 Python 的类型提示,代码自动补全、类型检查和错误排查能力大大增强,开发效率提升约 200%-300%。 * **现代易用**:自动生成交互式 API 文档 (Swagger UI / ReDoc),支持异步 `async/await`,并采用依赖注入系统,代码结构清晰,易于维护。 本文将带领读者,利用 **FastAPI** 作为后端服务框架,**SQLModel** 作为现代化的数据库ORM,并结合强大的 **Dlib** 和 **ArcFace (FastDeploy)** 深度学习模型,从零开始构建一个结构简洁、功能完整、具备商业级潜力的人脸识别系统。 --- # 二、项目搭建 ## 2.1 环境安装 首先,确保系统中已安装 Python 3.8 或更高版本(**`推荐选择3.10`**)。然后,安装所有必要的库: ```bash # Web框架和服务器 pip install fastapi "uvicorn[standard]" "python-multipart" jinja2 -i https://pypi.tuna.tsinghua.edu.cn/simple # 数据库ORM pip install sqlmodel -i https://pypi.tuna.tsinghua.edu.cn/simple # 深度学习与图像处理 pip install numpy==1.23 opencv-python dlib fastdeploy-python -i https://pypi.tuna.tsinghua.edu.cn/simple ``` 注意:dlib 库的安装需要编译,可能会花费几分钟时间,请耐心等待。另外,python3.11及以上版本需要自行参考[FastDeploy官网](https://gitee.com/paddlepaddle/FastDeploy/tree/release%2F1.0.7/)编译FastDeploy库(本文推荐使用Python3.10,无需手动编译)。 ## 2.2 核心模型下载 为了能够使用深度学习进行人脸识别,需要下载人脸检测和特征提取的深度学习模型。 1. **Dlib 人脸关键点检测模型**: 下载地址:[http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2](http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2) 2. **ArcFace 人脸特征提取模型**: ```bash wget https://bj.bcebos.com/paddlehub/fastdeploy/ms1mv3_arcface_r100.onnx ``` 下载并解压后,将 `shape_predictor_68_face_landmarks.dat` 和 `ms1mv3_arcface_r100.onnx` 这两个文件放在项目根目录下。 ## 2.3 项目结构 一个清晰的项目结构是高效开发的基石。本文采用如下简洁明了的结构: ``` /FaceRecAPI ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI应用主文件,包含API路由 │ ├── database.py # 数据库连接与会话管理 │ ├── models.py # SQLModel数据模型定义 │ ├── crud.py # 数据库增删改查(CRUD)操作 │ └── face_utils.py # 封装人脸检测与特征提取的核心函数 ├── static/ # 存放CSS, JS等静态文件 │ └── ... ├── templates/ # 存放HTML模板文件 │ └── ... ├── media/ # 存放上传的人物照片 │ └── person_photos/ ├── shape_predictor_68_face_landmarks.dat # Dlib模型 └── ms1mv3_arcface_r100.onnx # ArcFace模型 ``` # 三、后端核心功能开发 ## 3.1 数据库与模型定义 (app/models.py) **SQLModel** 结合了 Pydantic 和 SQLAlchemy 的优点,能用一个类同时定义数据模型、API数据结构和数据库表结构。 ```python from typing import Optional from sqlmodel import Field, SQLModel class PersonBase(SQLModel): """ 人物基础模型,用于API输入输出 """ chinese_name: str = Field(index=True) description: Optional[str] = None class Person(PersonBase, table=True): """ 人物数据库表模型 """ id: Optional[int] = Field(default=None, primary_key=True) photo_path: Optional[str] = None # 使用bytes存储numpy array转换后的二进制数据 embedding: Optional[bytes] = None class PersonRead(PersonBase): """ 用于读取人物的输出模型,包含ID和照片路径 """ id: int photo_path: Optional[str] ``` ## 3.2 数据库配置 (app/database.py) 下面使用轻量级的 SQLite 数据库进行实现,并配置数据库引擎和会话。 ```python from sqlmodel import create_engine, Session, SQLModel # 使用SQLite数据库文件 DATABASE_URL = "sqlite:///database.db" # connect_args 是SQLite特有的,确保单线程操作安全 engine = create_engine(DATABASE_URL, echo=True, connect_args={"check_same_thread": False}) def create_db_and_tables(): """ 创建数据库和表 """ SQLModel.metadata.create_all(engine) def get_session(): """ 依赖注入:获取数据库会话 """ with Session(engine) as session: yield session ``` ## 3.3 封装人脸处理工具 (app/face_utils.py) 将所有与深度学习模型相关的代码(人脸检测、特征提取)封装到一个单独的文件中,使主逻辑更清晰。 ```python from typing import Optional import dlib import cv2 import numpy as np import fastdeploy as fd from pathlib import Path # --- 模型初始化 --- # 获取项目根目录 BASE_DIR = Path(__file__).resolve().parent.parent # 1. Dlib 人脸检测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor(str(BASE_DIR / 'shape_predictor_68_face_landmarks.dat')) # 2. ArcFace 特征提取模型 option = fd.RuntimeOption() option.use_cpu() embedding_model = fd.vision.faceid.ArcFace(str(BASE_DIR / 'ms1mv3_arcface_r100.onnx'), runtime_option=option) def detect_and_extract_face(image: np.ndarray) -> Optional[np.ndarray]: """ 从图像中检测并裁剪出最大的人脸。 :param image: OpenCV格式的图像 (BGR) :return: 裁剪后的人脸图像,如果未检测到则返回None """ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) faces = detector(gray) if not faces: return None # 找到最大的人脸 face = max(faces, key=lambda rect: rect.width() * rect.height()) (x, y, w, h) = (face.left(), face.top(), face.width(), face.height()) # 稍微扩大裁剪区域,以包含整个头部 padding_h = int(0.4 * h) start_y = max(0, y - padding_h) face_image = image[start_y : y + h, x : x + w] return face_image def get_embedding(face_image: np.ndarray) -> np.ndarray: """ 提取人脸图像的512维特征向量并进行归一化。 :param face_image: 裁剪后的人脸图像 :return: 归一化后的特征向量 (numpy array) """ face_image = cv2.cvtColor(face_image, cv2.COLOR_BGR2GRAY) # 去除颜色 face_image = cv2.cvtColor(face_image, cv2.COLOR_GRAY2BGR) result = embedding_model.predict(face_image) embedding = result.embedding embedding = np.array(embedding, dtype=np.float32) # 使用float32以减小存储 # L2 归一化 embedding /= np.linalg.norm(embedding) return embedding ``` ## 3.4 数据库操作 (app/crud.py) 创建专门的函数来处理与数据库的交互,实现关注点分离。 ```python from sqlmodel import Session, select from app import models def get_person(db: Session, person_id: int): ''' 根据ID获取人物信息 :param db: 数据库会话 :param person_id: 人物ID :return: 人物信息 ''' return db.get(models.Person, person_id) def get_persons(db: Session, skip: int = 0, limit: int = 100): ''' 获取所有人物信息 :param db: 数据库会话 :param skip: 跳过的记录数 :param limit: 限制返回的记录数 :return: 人物信息列表 ''' return db.exec(select(models.Person).offset(skip).limit(limit)).all() def create_person(db: Session, person: models.Person): ''' 创建人物信息 :param db: 数据库会话 :param person: 人物信息 :return: 创建后的人物信息 ''' db.add(person) db.commit() db.refresh(person) return person def delete_person(db: Session, person_id: int): ''' 删除人物信息 :param db: 数据库会话 :param person_id: 人物ID :return: 删除的人物信息 ''' person = db.get(models.Person, person_id) if not person: return None db.delete(person) db.commit() return person ``` ## 3.5 创建API端点 (app/main.py) 这是本项目的主应用文件,它将所有部分串联起来,并定义API路由。 ```python import cv2 import numpy as np import uuid from pathlib import Path from typing import List from fastapi import FastAPI, Depends, HTTPException, UploadFile, File, Form from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles from sqlmodel import Session from app import crud, models, face_utils from app.database import get_session, create_db_and_tables # --- FastAPI 应用初始化 --- app = FastAPI(title="人脸识别API系统") # 挂载静态文件目录,用于存放上传的图片 BASE_DIR = Path(__file__).resolve().parent.parent app.mount("/media", StaticFiles(directory=BASE_DIR / "media"), name="media") # --- 应用启动时创建数据库表 --- @app.on_event("startup") def on_startup(): create_db_and_tables() # --- 人物管理API --- @app.post("/persons/", response_model=models.PersonRead) def create_person_api( chinese_name: str = Form(...), description: str = Form(None), photo: UploadFile = File(...), db: Session = Depends(get_session) ): # 1. 读取上传的图片 contents = photo.file.read() nparr = np.frombuffer(contents, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) if img is None: raise HTTPException(status_code=400, detail="照片读取异常,请检查源文件。") # 2. 人脸检测 face_image = face_utils.detect_and_extract_face(img) if face_image is None: raise HTTPException(status_code=400, detail="未检测到人脸,请上传有效照片。") # 3. 提取特征向量 embedding = face_utils.get_embedding(face_image) # 4. 保存裁剪后的人脸图片 media_dir = BASE_DIR / "media" / "person_photos" media_dir.mkdir(parents=True, exist_ok=True) filename = f"{uuid.uuid4().hex}.jpg" save_path = media_dir / filename cv2.imwrite(str(save_path), face_image) # 5. 创建数据库记录 person_data = models.Person( chinese_name=chinese_name, description=description, photo_path=f"/media/person_photos/{filename}", embedding=embedding.tobytes() # 将numpy array转为bytes存储 ) return crud.create_person(db, person_data) @app.get("/persons/", response_model=List[models.PersonRead]) def read_persons_api(skip: int = 0, limit: int = 100, db: Session = Depends(get_session)): persons = crud.get_persons(db, skip=skip, limit=limit) return persons @app.delete("/persons/{person_id}") def delete_person_api(person_id: int, db: Session = Depends(get_session)): deleted_person = crud.delete_person(db, person_id=person_id) if not deleted_person: raise HTTPException(status_code=404, detail="人物不存在") return {"message": f"人物 {deleted_person.chinese_name} 已删除"} # --- 在线人脸识别API --- @app.post("/recognize/") def recognize_face_api(photo: UploadFile = File(...), db: Session = Depends(get_session)): # 1. 读取和处理上传的图片 contents = photo.file.read() nparr = np.frombuffer(contents, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) if img is None: raise HTTPException(status_code=400, detail="照片读取异常,请检查源文件。") face_image = face_utils.detect_and_extract_face(img) if face_image is None: return JSONResponse(status_code=400, content={"message": "未检测到人脸"}) # 2. 提取待识别图片的特征 query_embedding = face_utils.get_embedding(face_image) # 3. 从数据库获取所有人脸特征 all_persons = crud.get_persons(db, limit=1000) # 假设人物库小于1000 if not all_persons: return JSONResponse(status_code=404, content={"message": "人物库为空"}) db_embeddings = [np.frombuffer(p.embedding, dtype=np.float32) for p in all_persons] # 4. 计算余弦相似度 similarities = [np.dot(query_embedding, db_emb) for db_emb in db_embeddings] # 5. 找到最相似的人 best_match_index = np.argmax(similarities) max_similarity = similarities[best_match_index] # 设定一个阈值,低于该阈值则认为不是同一个人 if max_similarity < 0.5: return {"name": "未找到匹配人物", "similarity": f"{max_similarity*100:.2f}%"} most_similar_person = all_persons[best_match_index] result = { 'name': most_similar_person.chinese_name, 'similarity': f"{max_similarity * 100:.2f}%", 'photo_url': most_similar_person.photo_path } return result ``` ## 3.6 启动应用 在项目根目录下,运行以下命令启动服务: ```bash uvicorn app.main:app --reload ``` 现在,打开浏览器访问 `http://127.0.0.1:8000/docs`,你将看到 FastAPI 自动生成的交互式 API 文档,可以在这里直接测试你的所有 API 接口! --- # 四、前端界面与集成 FastAPI 的核心是构建高效的 API,但一个完整的项目离不开用户交互的界面。在这一部分,本项目将利用经典的前端技术栈(HTML, CSS, JavaScript)和 Bootstrap 5,打造一个简洁、直观的单页面应用(SPA),并将其与 FastAPI 后端无缝集成。 ## 4.1 设计理念:简洁的单页面应用 (SPA) 相比于传统的多页面跳转,单页面应用将所有功能都承载在一个页面上,通过 JavaScript 动态更新内容,提供了更流畅的用户体验。本项目前端页面将包含两个核心功能区: 1. **在线识别区**:用户上传一张图片,系统立即返回识别结果。 2. **人物库管理区**:展示所有已录入的人物信息,并提供一个表单用于添加新的人物。 这种设计不仅结构清晰,也让前后端交互的逻辑更加集中和易于管理。 ## 4.2 配置 FastAPI 服务静态文件与模板 首先,需要让 FastAPI 应用能够“托管”HTML 页面和静态资源(如 CSS, JS 文件)。 1. **创建目录**:在项目根目录下,确保已经创建了 `templates` 和 `static/js` 文件夹。 2. **安装 Jinja2**:如果之前没装,请安装模板引擎。 ```bash pip install jinja2 ``` 3. **修改 `app/main.py`**:在 `app/main.py` 文件中,需要引入必要的模块,并配置模板和静态文件路径。 ```python # app/main.py # ... 其他 import ... from fastapi import Request from fastapi.templating import Jinja2Templates from fastapi.responses import HTMLResponse # --- FastAPI 应用初始化 --- app = FastAPI(title="人脸识别API系统") # 获取项目根目录 BASE_DIR = Path(__file__).resolve().parent.parent # 1. 挂载 static 文件夹,前端可以通过 /static/... 访问 app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") # 2. 挂载 media 文件夹,用于访问上传的图片 app.mount("/media", StaticFiles(directory=BASE_DIR / "media"), name="media") # 3. 配置 Jinja2 模板引擎 templates = Jinja2Templates(directory=BASE_DIR / "templates") # --- 应用启动事件 --- @app.on_event("startup") def on_startup(): create_db_and_tables() # --- 新增:服务前端页面的根路由 --- @app.get("/", response_class=HTMLResponse) async def read_root(request: Request): # 返回 index.html 模板 return templates.TemplateResponse("index.html", {"request": request}) # ... 其他 API 路由(/persons/, /recognize/ 等)保持不变 ... ``` ## 4.3 创建主 HTML 页面 (templates/index.html) 在 `templates` 文件夹中创建 `index.html`。这个文件是整个应用的“骨架”,它将定义页面的所有可见元素。 ```html
相似度: