资讯动态

基于Python+OpenCV+Django实现人脸识别系统:从特征提取到Web部署

发布时间:2026/10/1 21:08:14 来源:尧图企业网站定制
简介基于Python、OpenCV、Django与人脸识别库实现的人脸识别系统课程设计源码包面向高校计算机相关专业学生适用于毕业设计、课程大作业或人脸识别项目入门。项目已获导师指导并取得97分高分代码完整且可直接运行覆盖人脸图像采集、特征提取、模型训练、实时识别与人脸库管理的完整流程。资源共130个文件包含19个Python源码、41个pyc编译文件、65张jpeg/png人脸样本图片、sqlite3数据库以及用于模型存储的pb和data、index等TensorFlow文件压缩包整体22.62MB目录层级清晰便于按模块阅读与复用系统中内置训练好的模型加载后即可进行识别测试sqlite3库可保存人员信息与特征数据方便管理多个注册人脸。目前已有883人学习下载。通过该项目可掌握Django Web框架与OpenCV、人脸识别库的整合方法理解从图像输入到特征匹配的技术实现还可在已有模型基础上扩展自己的识别场景是快速上手实战项目的优质参考。1. 课程设计里的人脸识别系统到底在做什么先说说这类课程设计最常见的尴尬算法原理写了一大堆却拿不出一套能当场演示的系统。而基于PythonOpenCVDjango人脸识别库实现的人脸识别系统这个标题正是把能演示当成了最高优先级——用OpenCV负责图像采集和处理用人脸识别库给出特征向量用Django把识别能力包成一个Web页面。你不用自己去训练卷积神经网络也不需要了解太多人脸识别底层理论只要把几个成熟组件串起来就能做出一个打开浏览器、上传照片、返回姓名的闭环。这篇文章写给正在做人脸识别课程设计、毕业设计或者刚入门想知道这几个组件怎么串起来的人。全文按选型→采集→特征→集成→排错→调优推进每一步都有可复现的命令和参数说明你可以照着改也可以把它当成答辩前的检查清单。2. 技术栈拆解为什么是 OpenCV Django 人脸识别库而不是全端深度学习很多课程设计的题目只写了人脸识别但到底用什么算法、什么库完全由你自己定。这里的核心权衡是老师要看到的是一个能打开的Web系统不是一个训练日志。所以选型的第一原则是成熟、能跑、好调试。2.1 人脸识别库的选择face_recognition 还是 dlib 还是 OpenCV 自带识别器人脸识别库这几个字在不同源码里指代的东西不一样。常见的有三个OpenCV自带的LBPH人脸识别器、dlib的人脸检测与特征提取、face_recognition——它是dlib的Python封装把检测、特征提取、距离比对都收拢成了几行API。我建议课程设计直接选face_recognition。一是API足够简单face_recognition.face_encodings(image)直接返回128维特征向量比对用face_distance就能拿到距离新生不需要从头理解ResNet原理。二是识别效果稳定它底层用的是dlib的ResNet模型在教室、宿舍这类场景里比LBPH可靠得多。三是它能一次性检测多张脸张合影里可以返回多组特征课程设计答辩时这是个加分项。不是说LBPH不能用。如果你做的是静态人脸的简单识别LBPH也能跑但它本质上是在灰度直方图上做统计遇到光照变化、表情变化准确率掉得很快。我见过同学答辩现场戴个眼镜LBPH就认不出来了这种翻车不止一次。方案检测方式特征维度上手难度课程设计推荐度OpenCV LBPHHaar直方图可变低一般dlib 原生HOG/CNN128中一般face_recognitiondlib封装128低推荐你可能会问为什么不直接用深度学习框架自己训练一个分类器因为课程设计的时间成本不划算。自己训练CNN需要标注数据、调整超参、设计网络最后效果还不一定比预训练模型好。更实际的做法是人脸检测用dlib或OpenCV特征提取用预训练模型距离判断用欧氏距离这套组合已经覆盖了人脸识别的完整链路。2.2 Django 在系统里的职责边界只做 Web 外壳不做算法计算第二个常见错误是认为Django人脸识别系统需要把算法写进Django模型里。实际上Django只负责Web层接收HTTP请求、解析上传文件、调用识别逻辑、返回JSON。识别计算是独立的Python函数不属于Django的ORM、模板或管理后台。为什么必须分开因为人脸特征提取和比对是CPU密集操作如果放在Django视图里同步执行一个请求进来服务器就会卡住其它访问全部排队。课程设计演示时浏览器一直转圈老师会觉得系统很卡。我常用的结构是Django项目里建一个recognition应用应用下放两个模块views.py处理请求service.py封装识别函数。views.py不直接import face_recognition而是调用service.recognize。这样以后想换成TensorFlow或ONNX模型只需要改service.py。数据流是这样的前端上传图片 - urls.py路由到views.recognize_upload - request.FILES取文件 - service.recognize(image_bytes) - 返回人脸位置、名字、距离 - JsonResponse。整个过程不碰数据库因为人脸特征库是pickle文件识别是内存操作不需要持久化。课程设计里要不要建数据库这取决于题目要求。如果老师要求用户注册考勤记录那就需要Django自带的sqlite加一张表存日志。如果只是认人用文件特征库就够了还能避免ORM序列化向量这种麻烦事。2.3 最小环境安装从 python 安装到 opencv 安装一次跑通环境是第一个大坑。很多同学照着网上的python安装教程装完Python转头import cv2报错或者装face_recognition时dlib编译失败。我一般建议用Python 3.8到3.11之间的版本别追最新因为有些预编译轮子还没跟上。安装时记得勾选Add Python to PATH否则后面pip都不认。然后创建虚拟环境。Windows下是python -m venv venv激活后命令行前面会出现(venv)。这一步很重要避免把包装到系统Python里造成版本冲突。python -m venv venv # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate pip install --upgrade pip pip install django opencv-python face_recognition numpy参数说明opencv-python是最常用的发行包它包含cv2模块安装体积较大。不要再装opencv-contrib-python两个包一起装容易出现二进制冲突这也是我踩过的坑。face_recognition在Windows上安装时如果报CMake is required或Microsoft Visual C 14.0 is required需要先安装CMake和Visual Studio Build Tools因为dlib要从源码编译。如果你不想折腾编译可以换一条路用Anaconda环境conda install -c conda-forge dlib python-face-recognitionconda会直接提供编译好的包。但在时间紧张时我建议直接装face_recognition通常一条pip命令就能好。装完之后用一行命令验证环境python -c import cv2, django, face_recognition; print(cv2.__version__, django.get_version())如果输出版本号说明基础环境已经通了。后面所有翻车都从这之后开始。3. 数据准备用 OpenCV 采集人脸样本预处理成识别库要的格式识别系统的地基是数据。很多课程设计失败在于只给每个人拍了一张正脸照然后抱怨识别不准。我的习惯是每人采集20到50张样本覆盖正面、左右偏转、抬头低头、室内光、靠窗光。这样特征库里有足够变化阈值才有意义。3.1 人脸采集脚本摄像头连续抓帧、自动检测并裁剪对齐采集阶段不需要face_recognition直接用OpenCV就够了。采集只要求能检测到人脸并保存OpenCV的Haar级联检测器在CPU上跑得很快不依赖外部模型不容易翻车。以下是一个完整的小脚本。import cv2 import os person_name zhangsan # 建议用英文/拼音避开中文路径 save_dir os.path.join(dataset, person_name) os.makedirs(save_dir, exist_okTrue) cap cv2.VideoCapture(0) if not cap.isOpened(): raise IOError(无法打开摄像头请检查驱动或换 index) # 用 OpenCV 自带的 Haar 人脸检测器 face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) count 0 max_samples 30 while count max_samples: ret, frame cap.read() if not ret: continue gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale( gray, scaleFactor1.1, minNeighbors5, minSize(120, 120) ) for (x, y, w, h) in faces: # 检测框向外扩 20%避免只截到脸中部 x0 max(0, x - int(0.2 * w)) y0 max(0, y - int(0.2 * h)) x1 min(frame.shape[1], x int(1.2 * w)) y1 min(frame.shape[0], y int(1.2 * h)) face frame[y0:y1, x0:x1] # 统一缩放到 160x160保证后续特征提取输入稳定 face cv2.resize(face, (160, 160)) cv2.imwrite(os.path.join(save_dir, f{count:03d}.jpg), face) count 1 cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.imshow(capture, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() print(f已保存 {count} 张样本到 {save_dir})逻辑说明detectMultiScale直接返回人脸矩形坐标。我用max(0, ...)防止裁剪越界用外扩比例让上额和下巴都保留下来。resize到160x160是所有样本统一尺寸这样不管摄像头是1080P还是720P特征提取的输入都是一致的。参数说明scaleFactor1.1表示每次搜索窗口缩小10%这个值越小检测越精确但越慢课程设计里1.1到1.2都行minNeighbors5表示一个候选框被连续5次检测才算数等于5时误检少但可能漏检如果你发现框不住人脸可以降到3。minSize(120,120)过滤掉太远的、像素不足的人脸。3.2 图像预处理灰度、直方图均衡化、归一化的真实作用采集脚本里我只做了灰度化和resize没做更多预处理。为什么因为face_recognition内部有自己的人脸对齐它的模型期望输入是RGB图你如果提前转成灰度反而少了一个通道的信息。这里有一个课程设计里常见的误区把OpenCV图像处理里学的灰度化、均衡化、高斯模糊一股脑用在人脸识别前置效果未必好。但这不代表预处理没用。OpenCV图像处理在人脸识别里的正确用途是图像质量修正当光线暗、对比度低时可以用cv2.equalizeHist对灰度图做直方图均衡化增加暗部细节。下面这行可以放在保存前# 采集时把原始图和均衡化图各存一套后面用识别率决定用哪套 gray_eq cv2.equalizeHist(gray) # 保存均衡化后的灰度图文件名加 _eq 后缀注意如果用均衡化后的灰度图做特征提取识别时也要走同样的预处理链路否则训练识别不一致。我遇到过同学训练时用均衡化识别时直接传原图准确率明显下降。归一化的作用则体现在后期。无论你用什么识别库输入图像的像素范围最好一致。OpenCV读出来是BGR、0到255face_recognition.load_image_file读出来是RGB、0到255底层模型自己有归一化所以外部不需要除以255。你只需要保证图像尺寸接近模型训练时的尺寸这就是我统一resize到160x160的原因。3.3 数据目录组织与样本清洗直接决定特征库质量采集完的数据目录要清晰推荐结构dataset/ zhangsan/ 000.jpg, 001.jpg, ... lisi/ 000.jpg, 001.jpg, ...这样后面遍历时os.listdir(dataset)拿到的人名就是标签。比把标签写在文件名里更安全因为文件名可能含特殊字符。样本清洗是很多人忽略的一步。采集过程中会混入模糊帧、重复帧、甚至拍到别人脸但保存到当前人目录下的坏数据。我的习惯是采集完先人工扫一遍把模糊的、闭眼的、拍到侧脸的删掉如果某个人样本少了重新采集补上。实际经验是10张高质量样本比50张低质量样本更好。4. 特征提取与 Django 集成从 128 维向量到 Web 注册/识别接口采集完图片后下一步是把每张图转成一个128维向量。这是人脸识别的核心不是比较图片像素而是比较向量距离。face_recognition封装了dlib的模型一行face_encodings就能完成检测、对齐和特征提取。4.1 用 face_recognition 批量提取特征保存到本地特征库写一个独立的生成特征库脚本不要把它放Django视图里否则每次启动都要重新提取。下面这个脚本遍历dataset目录下每个人名文件夹提取所有样本的特征保存成pickle文件。import os import pickle import face_recognition dataset_dir dataset known_encodings [] known_names [] for name in os.listdir(dataset_dir): person_dir os.path.join(dataset_dir, name) if not os.path.isdir(person_dir): continue for img_name in os.listdir(person_dir): img_path os.path.join(person_dir, img_name) image face_recognition.load_image_file(img_path) encodings face_recognition.face_encodings(image) if len(encodings) 0: known_encodings.append(encodings[0]) known_names.append(name) else: print(f跳过无脸图片: {img_path}) with open(face_db.pkl, wb) as f: pickle.dump({ encodings: known_encodings, names: known_names, }, f) print(f特征库保存完毕共 {len(known_names)} 条记录)逻辑说明遍历dataset目录下每个人名文件夹逐张提取特征。如果图片中检测不到人脸face_encodings返回空列表这一条直接跳过避免把脏数据塞进特征库。最后用pickle把所有人和特征的对应关系存下来下次启动秒级加载。参数说明known_encodings里每个元素是一个numpy数组形状是(128,)。如果一个文件夹里有20张图就生成20条向量names列表里对应的name重复20次。比对时一张待识别脸会和全部向量算欧氏距离然后取最近的那个。这里有个提速技巧如果样本已经裁剪好可以用face_recognition.face_encodings(image, known_face_locations[(0, h, w, 0)])手动传入人脸框跳过检测阶段。但前提是你确认每张图只有一个脸且已经对齐否则不要用。4.2 创建 Django 项目与应用从项目结构到识别核心视图Django部分是课程设计的门面。首先创建项目和app。常见做法是项目名叫face_project应用叫recognition。django-admin startproject face_project cd face_project python manage.py startapp recognition这两条命令完成之后去face_project/settings.py的INSTALLED_APPS里加上recognition。这是django创建app后必做的一步漏掉的话后面很多功能会不生效。然后编辑recognition/views.py写识别接口。为了演示方便我用csrf_exempt装饰器关闭POST校验但注意这只是课程设计里的简化做法真正生产项目不能用csrf_exempt。# recognition/views.py import pickle import numpy as np import face_recognition from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt # 模块加载时读一次特征库避免每次请求都读文件 with open(face_db.pkl, rb) as f: db pickle.load(f) known_encodings np.array(db[encodings]) known_names db[names] csrf_exempt def recognize_upload(request): if request.method ! POST: return JsonResponse({error: 请用 POST 提交}, status405) upload request.FILES.get(image) if not upload: return JsonResponse({error: 缺少 image 字段}, status400) image face_recognition.load_image_file(upload) face_locations face_recognition.face_locations(image) face_encodings face_recognition.face_encodings(image, face_locations) results [] for i, encoding in enumerate(face_encodings): # 用欧氏距离找最近的人脸 distances face_recognition.face_distance(known_encodings, encoding) best_idx int(np.argmin(distances)) name known_names[best_idx] distance float(distances[best_idx]) location face_locations[i] if i len(face_locations) else None results.append({ name: name, distance: distance, location: [int(v) for v in location] if location else None, }) return JsonResponse({faces: results})逻辑说明face_locations返回图中每个人脸的位置face_encodings再根据位置提取特征。这样就支持一张照片里有多个人。每个待识别向量与所有已知特征算距离取最小距离作为预测结果至于这个最小距离到底算不算同一个人由阈值决定后面章节细讲。参数说明known_encodings转成np.array后face_distance可以批量计算速度比Python循环快很多。如果只有一个人脸distance小于0.45一般可以认为是本人但如果你的数据集只有5个人这个值可以放松到0.5因为类间距离比较近。4.3 路由与测试接口用 curl 验证识别接口通不通写完视图还要配路由。在recognition/urls.py里写from django.urls import path from . import views urlpatterns [ path(recognize/, views.recognize_upload, namerecognize_upload), ]然后在face_project/urls.py里include一下。这一步很多新手会漏结果访问404。配置完后先不用写前端页面用curl或Postman测试接口更省事。下面用curl测试上传一张测试图片curl -X POST -F imagetest_zhangsan.jpg http://127.0.0.1:8000/recognize/如果识别成功返回JSON里会有name字段如果返回error多半是文件字段名不对或者特征库路径没有放在manage.py所在目录。这里有个坑pickle文件的路径是相对于当前工作目录的你用python manage.py runserver启动时工作目录是项目根目录所以face_db.pkl要放在项目根目录否则就会报FileNotFoundError。5. 避坑人脸识别系统最常见的 5 个翻车现场这部分不是理论几乎每条都是我在实际调试里遇到过的。按现象→原因→解决写方便你对着排错。5.1 OpenCV 打不开摄像头index0 不是万能的现象cv2.VideoCapture(0)成功实例化但read()一直返回False窗口黑屏或者直接报cant open camera by index。原因笔记本有多个摄像头时系统分配的不一定是0如果摄像头被微信、浏览器占用也会独占设备在虚拟机上摄像头设备默认没被桥接进来。解决写一个小脚本循环尝试index0到3看哪个能读到帧。如果本地实在没有摄像头就不要用实时采集改成上传图片演示。在云服务器上做课程设计时我一般直接用上传图片这个路线省掉摄像头的变量。5.2 中文路径导致 Dlib 读取图片失败现象数据集目录叫张三特征提取时dlib报错或返回的encodings为空。OpenCV的imread遇到中文路径也会返回None但不会报错导致后面一片混乱。原因dlib和部分OpenCV版本在Windows下使用C接口读取文件系统默认编码不是UTF-8中文路径被截断。解决最省事的方法是把所有目录和图片文件名都改成英文、拼音或数字比如zhangsan_001.jpg。如果你的图片已经是中文名可以用np.fromfile加cv2.imdecode来读但我不建议在课程设计里额外引入这个链路改文件名最稳。Django上传的文件名同样处理否则会存成乱码。5.3 face_recognition 安装时 dlib 编译失败CMake 和 VS Build Tools 才是根源现象pip install face_recognition时终端滚动一大段后报错Failed building wheel for dlib、CMake is required或Microsoft Visual C 14.0 is required。原因dlib没有对应你Python版本的预编译wheel需要本地编译。编译依赖C编译器、CMake和Python开发头文件缺一个就失败。解决先安装Visual Studio Build Tools安装时勾选使用C的桌面开发再安装CMake安装时勾选Add CMake to system PATH然后重试pip install face_recognition。如果你用的是Anaconda更省事的方式是conda install -c conda-forge dlib python-face-recognition由conda直接提供编译好的包。这个细节能救很多人。5.4 Django debug 模式下的内存泄漏与特征库重新加载现象识别接口第一次调用正常第二次开始越来越慢内存占用缓慢上涨最后服务器卡死。原因把特征库加载写在了视图函数里每个请求都重新读取pickle、重新创建dlib对象旧对象没有被及时释放。Django开发服务器又是多线程累积起来就拖垮了进程。解决把特征库加载放到模块顶层即views.py的import之后、视图函数之外。模块在进程启动时加载一次所有请求共享同一个列表。还要注意如果特征是numpy数组加载后尽量用np.array包一层避免列表转数组的隐式转换重复发生。课程设计用runserver演示没问题但别拿runserver当生产服务器它默认是单进程、不适合并发。5.5 识别率忽高忽低光线、角度、模糊才是幕后黑手现象同一个人在白天能识别傍晚识别失败正脸能识别侧脸30度就失败照片稍微模糊就认错。原因人脸识别模型虽然由CNN训练出来的但它对图像质量非常敏感。录入的照片如果是顺光、正面识别时一旦换个光照特征分布就会偏移。模糊图像经过dlib检测后人脸特征提取出的向量也可能偏离正常范围。解决采集样本时要覆盖场景变化。比如白天窗边拍10张晚上开灯拍10张戴不戴眼镜各拍几张。识别端可以加一个清晰度判断用cv2.Laplacian计算方差方差小于某个阈值就返回图片模糊请重拍。这是一个很实用的OpenCV图像处理技巧能挡住一大半误识别。6. 把课程设计做成能演示的系统阈值调优与性能进阶6.1 tolerance 阈值怎么调查表法找最优face_recognition的compare_faces默认threshold0.6这个值偏宽松可能把两个不同的人判成同一个人。课程设计演示时如果只是上传张三返回张三0.6没问题但如果还要上传李四不能识别成张三就必须收紧阈值。我一般准备10张确实匹配的图和10张不匹配的图算距离后遍历0.3到0.7之间的阈值选误报最少、漏报最少的点。这个流程不需要机器学习框架用Python算术就能跑。import numpy as np # 假设 test_distances 是真实匹配的距离false_distances 是不匹配的距离 best_t 0.5 best_score 0.0 for t in np.arange(0.3, 0.7, 0.01): true_pos np.mean([d t for d in test_distances]) false_pos np.mean([d t for d in false_distances]) score true_pos - false_pos if score best_score: best_score score best_t t print(最优阈值:, best_t)这段代码的逻辑是把两个距离数组分别比较选一个让该认出的都认出、不该认出的都不认的t。课程设计不要求严谨ROC只要这个分数最大就能用。参数说明t的扫描范围0.3到0.7是人脸识别常见的经验区间。小于0.3太严容易漏检大于0.7太松几乎来谁都是熟人。如果你的特征库只有一个人threshold可以设到0.5以内。6.2 实时视频识别 vs 上传图片识别两种演示场景的取舍答辩时老师可能要看实时摄像头也可能只看上传图片。我建议主演示用上传图片因为可控。实时视频一旦现场光线不好或人离镜头远很容易翻车。实时识别的性能参数是隔帧处理和缩放。face_recognition做一次特征提取在CPU上要几十到一百毫秒全帧处理会卡。常见做法是每3帧取一帧并把画面缩小一半速度能提升4倍。# 实时识别主循环中的关键片段 frame_idx 0 while True: ret, frame cap.read() if not ret: break frame_idx 1 if frame_idx % 3 ! 0: # 每隔2帧才处理一帧 continue small cv2.resize(frame, (0, 0), fx0.5, fy0.5) rgb cv2.cvtColor(small, cv2.COLOR_BGR2RGB) locs face_recognition.face_locations(rgb) encs face_recognition.face_encodings(rgb, locs) # 下面再遍历 encs 和 known_encodings 做比对这里把frame缩到一半处理像素变成原来的四分之一dlib检测时间会大幅下降。但代价是人脸区域变小如果人脸本身在画面里只有100像素缩小后就不到50像素可能检测不到。所以实时演示时要保证人脸贴近镜头或者不要缩得太狠fx0.75也可以。6.3 性能进阶特征库常驻内存与更快的加载方式最后一个能明显提升体验的技巧是让特征库真正常驻内存。前面views.py里已经放在模块顶层了但Django的runserver默认使用自动重载每次修改代码都会重启整个进程特征库也会重新加载。这本身不是问题问题是你可能没意识到重启一次要等好几秒。更稳的方案是写一个独立的service模块初始化一次之后不管怎么改视图代码都不会重建。另外pickle加载速度很快但如果特征库非常大比如几千条加载耗时也会增加。这时可以把已知特征转成numpy数组用np.load/np.save存成.npy格式加载更快。# 保存时也可以用 np.savez 同时存向量和名字 np.savez(face_db.npz, encodingsnp.array(known_encodings), namesknown_names) # 加载时只需一行 data np.load(face_db.npz)这个npz格式比pickle略小一点课程设计里不值得大书特书但能给老师一种你考虑了性能的印象。我做这套系统时最深的教训是不要一开始就追求准确率先把拍脸→存特征→上传→比对→显示这条链路跑通再回头调阈值和性能。人脸识别本身不难难的是数据质量和环境一致性。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑