简介:一份面向毕业设计、课程设计与期末大作业场景的Python人脸识别系统项目,基于Django搭建Web后端,整合OpenCV、face_recognition与Keras等库,实现了人脸检测、特征提取、比对识别与后台管理等功能,覆盖从数据处理、模型调用到页面展示的完整开发链路。压缩包内共158个文件,包括17个Python源码文件、22个JavaScript脚本、7个HTML页面、6个CSS样式,以及用于界面演示的GIF动图、训练好的HDF5模型权重和项目操作说明文档,整体大小约14.87MB,目录划分清晰,便于定位前端、后端与模型资源。项目附带详细操作说明,可直接运行与二次开发,既能支撑高校人脸识别课程设计、毕业设计答辩,也可作为计算机视觉与模式识别方向的学习案例,帮助理解Django与深度学习模型的实际结合方式,目录中还包含MD说明、配置文件等辅助内容。目前该资源已有1926人学习浏览。
1. 课程设计整体思路与选型逻辑
这份基于 Python + OpenCV + Django 的人脸识别系统源码包,是我整理的一个比较完整的课程设计项目。它不只是一堆零散脚本的堆砌,而是把“人脸采集 → 模型训练 → 实时识别 → Web端管理”这条完整链路都打通了。如果你正在做计算机、软件工程、人工智能方向的课程设计或毕业设计,这个项目源码加上配套的项目说明文档,基本可以直接拿来复现、二次开发,或者作为答辩时的技术支撑材料。
先花点篇幅聊聊技术栈选型的问题。很多人一提到人脸识别,第一反应就是上深度学习的模型,比如 FaceNet、ArcFace 这类。但在课程设计这个场景下,我建议你先冷静一下。课程设计的关键是什么?是逻辑自洽、流程完整、代码可解释,同时在自己机器上能跑得起来。深度学习模型虽然在复杂场景下精度更高,但依赖环境复杂、训练耗时,而且在答辩时如果被问到原理,一时半会解释不清楚反而容易丢分。
那 OpenCV 自带的 Haar 级联分类器 + LBPH(局部二值模式直方图)算法就非常适合这类项目。它有几个实打实的优势:一是环境依赖简单,pip install opencv-python就搞定;二是算法原理相对容易讲清楚,LBPH 是通过提取人脸局部的纹理特征,构建直方图来做比对的,答辩时画几张图就能说明白;三是计算量小,CPU 上就能实时跑,不需要 CUDA 这样的硬件门槛。
Django 的角色则是把底层的识别能力“包装”成用户能操作的 Web 服务。课程设计最怕什么?最怕功能做了一堆,但展示不出来。Django 的 MTV 架构天然适合搭一个带前端页面、数据库后台和路由控制的完整应用。你可以在网页上注册用户、上传照片,再通过摄像头实时调取识别结果,这种“前后端交互完整闭环”的体验感,对于课程设计的评分来说是很加分的。
我从这个项目里整理出来的源码结构大概是这样的:核心的采集脚本负责用摄像头抓取人脸样本,训练脚本负责生成模型文件,识别模块实时比对画面中的人脸,然后 Django 作为 Web 框架把识别结果、用户管理、考勤记录(或门禁记录)全部串起来。整套东西麻雀虽小,但五脏俱全。
2. 环境准备与依赖安装
2.1 开发环境版本配置
这个项目对环境的要求并不苛刻,但版本坑还是有一些的。我在实际搭建过程中踩了不少坑,这里直接给你一套验证过可以正常跑的版本组合:
| 依赖项 | 验证版本 | 说明 |
|---|---|---|
| Python | 3.8 ~ 3.10 | 建议 3.8 或 3.9,3.10+ 个别库有兼容问题 |
| OpenCV-Python | 4.5.x ~ 4.8.x | 不要装 4.9 以上开发版,API 变动较大 |
| Django | 3.2 LTS 或 4.1 LTS | LTS 版本稳定,资料多 |
| NumPy | 1.21 ~ 1.24 | 注意与 Python 版本匹配 |
| dlib(可选) | 19.22.0 | 用于人脸检测,当前项目非必需,可选装 |
有一个点需要特别提醒:OpenCV 的cv2.face模块(LBPH 人脸识别器所在的位置)需要额外安装opencv-contrib-python包。很多人只装了opencv-python,结果一import cv2.face就报错“module 'cv2' has no attribute 'face'”,卡在这一步半天都过不去。
2.2 安装过程实操记录
我建议你用一个干净的虚拟环境来装,避免把系统全局 Python 环境搞乱。用virtualenv或 Python 自带的venv都行:
# 创建虚拟环境 python -m venv face_env # 激活环境(Windows) face_env\Scripts\activate # 激活环境(Linux / macOS) source face_env/bin/activate # 升级 pip 并安装依赖 pip install --upgrade pip pip install opencv-python opencv-contrib-python pip install django==4.1.7 numpy==1.24.3装完之后可以快速验证一下:
import cv2 import django import numpy as np print(cv2.__version__) # 4.8.0 等 print(django.get_version()) print(cv2.face.LBPHFaceRecognizer_create()) # 验证 face 模块可用如果第三条不报错,说明opencv-contrib-python装对了。这块是整个环境准备中最容易翻车的地方,能跑通基本就稳了一半。
3. 人脸识别核心模块的实现拆解
3.1 人脸数据采集流程
人脸采集是整个系统的数据源头,这一步做不好,后面训练出来的模型精度肯定会受影响。核心思路是:用摄像头逐帧捕捉画面,通过 OpenCV 的 Haarcascade 检测器框出脸部区域,然后裁剪成固定尺寸的灰度图,保存到以“用户编号”命名的文件夹下。
import cv2 import os # 加载 Haar 级联人脸检测器 face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) # 打开摄像头 capture = cv2.VideoCapture(0) # 用户编号作为文件夹名,例如 1、2、3... user_id = input("请输入用户编号(如 1、2、3):") save_dir = f"./dataset/{user_id}" os.makedirs(save_dir, exist_ok=True) count = 0 while True: ret, frame = capture.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(100, 100) ) for (x, y, w, h) in faces: # 画出人脸框,方便使用者调整角度和位置 cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) face_roi = gray[y:y + h, x:x + w] face_resized = cv2.resize(face_roi, (200, 200)) cv2.imwrite(f"{save_dir}/{count}.jpg", face_resized) count += 1 print(f"采集进度: {count}") cv2.imshow("Face Capture", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break if count >= 100: # 每个人默认采集100张样本 break capture.release() cv2.destroyAllWindows()实操经验:采集样本时尽量多换几个角度和表情——抬头、低头、左右侧转、微笑、严肃,这样训练出来的模型对不同姿态的鲁棒性会更理想。我自己试过只在一个固定角度采集的样本,实际识别时稍微换个角度,识别率就明显掉下来了。另外注意采集环境的光线,最好在自然光充足且均匀的室内环境,避免顶光直射造成脸部阴影过重。
3.2 LBPH 模型训练原理与参数调优
说到训练,我需要花点篇幅把 LBPH 的原理讲透。LBPH 全称是 Local Binary Pattern Histogram,核心思想分三步:
第一步,把图像划分为若干小单元格(例如 8x8 的网格)。第二步,对每个像素点,与其周围像素比较灰度值大小,大于邻域中心记作 1,小于记作 0,这样就会得到一个二进制序列,转换成十进制就是该像素的 LBP 值。第三步,对每个网格内的所有 LBP 值统计直方图,把所有网格的直方图拼接起来,就形成了一张人脸的特征描述子。
这么做的妙处在于:LBP 值只关注像素之间的相对大小关系,而不是绝对灰度值,所以它对光照变化有一定的不敏感性。当然,它的短板也很明显——对大幅度姿态变化和遮挡比较敏感,这也是为什么前面强调采集样本时要多角度采集。
训练代码很简洁,OpenCV 把底层的训练和预测都封装好了:
import cv2 import os import numpy as np recognizer = cv2.face.LBPHFaceRecognizer_create() faces, labels = [], [] for user_id in os.listdir("./dataset"): user_path = f"./dataset/{user_id}" if not os.path.isdir(user_path): continue for img_name in os.listdir(user_path): img_path = f"{user_path}/{img_name}" img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) faces.append(img) labels.append(int(user_id)) # 训练模型并保存 recognizer.train(faces, np.array(labels)) recognizer.save("./models/trainer.yml") print("模型训练完成")这里有几个可以直接抄作业的经验:
第一,图片统一尺寸。我上面采集时 resize 到 200x200,训练时保持同样尺寸,否则 OpenCV 内部会强行缩放,但效果不如统一尺寸来得稳定。
第二,识别置信度的阈值设置。predict()方法返回两个值——标签和置信度。置信度越低,代表与训练样本的匹配度越高。我实测下来的参考范围是:小于 50 属于匹配度非常高,50~80 属于可接受范围,大于 80 基本就完全不匹配了。你可以按这个标准设置阈值来控制识别的严格程度。
第三,数据均衡。每个用户尽量采集相同数量的样本,万一某个用户只采集了 10 张而其他人都是 100 张,模型会被“带偏”,训练出来的分类边界会明显偏向样本多的那一方。
3.3 实时识别模块的实现细节
实时识别就是把训练好的模型加载起来,对摄像头每一帧画面做检测和比对。这个流程在逻辑上并不复杂,但有两个细节要特别注意:
一是视频帧要转成灰度图后再传给识别器,这和采集、训练阶段的预处理保持一致;二是要有一个“锁定机制”来避免同一张脸在连续的几十帧里被反复识别、记录多次。比如设定一个冷却时间,或者记录上一次识别的时间戳,间隔 5 秒之后再允许新的识别记录写入数据库。
import cv2 import time face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read("./models/trainer.yml") user_names = {1: "张三", 2: "李四", 3: "王五"} last_recognized_time = 0 capture = cv2.VideoCapture(0) while True: ret, frame = capture.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(100, 100)) for (x, y, w, h) in faces: face_roi = gray[y:y + h, x:x + w] face_resized = cv2.resize(face_roi, (200, 200)) label, confidence = recognizer.predict(face_resized) # 置信度越低,匹配度越高;我这里设定 80 为阈值 if confidence < 80: name = user_names.get(label, "Unknown") draw_color = (0, 255, 0) now = time.time() # 冷却时间 5 秒,防重复识别 if now - last_recognized_time > 5: print(f"识别结果: {name}, 置信度: {confidence}") last_recognized_time = now else: name = "Unknown" draw_color = (0, 0, 255) cv2.rectangle(frame, (x, y), (x + w, y + h), draw_color, 2) cv2.putText(frame, name, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, draw_color, 2) cv2.imshow("Face Recognition", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break capture.release() cv2.destroyAllWindows()4. Django Web 集成与业务逻辑闭环
4.1 Django 项目结构与数据模型设计
光有本地摄像头识别还不够,课程设计要想拿高分,一定要体现“系统化”和“业务闭环”。我这里的做法是用 Django 把整个识别流程包装成一个带用户管理、识别记录查询和后台管理的 Web 系统。简单来说,前端页面负责录入用户、上传照片;Django 后台负责把新用户的照片写入数据集文件夹并重新训练模型;识别页面调用摄像头实时识别,再把结果写入数据库;最后提供一个记录查询页面,展示每一天的识别历史。
先看数据模型,这是整个 Web 系统的地基:
from django.db import models class User(models.Model): user_id = models.IntegerField(unique=True, verbose_name="用户编号") name = models.CharField(max_length=50, verbose_name="姓名") photo_count = models.IntegerField(default=0, verbose_name="照片数量") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") class Meta: verbose_name = "用户信息" verbose_name_plural = verbose_name def __str__(self): return self.name class RecognitionLog(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户") image = models.ImageField(upload_to="logs/", null=True, blank=True, verbose_name="抓拍照片") confidence = models.FloatField(verbose_name="置信度") recognized_at = models.DateTimeField(auto_now_add=True, verbose_name="识别时间") class Meta: verbose_name = "识别记录" verbose_name_plural = verbose_name def __str__(self): return f"{self.user.name} - {self.recognized_at}"这里的RecognitionLog表是把识别结果落库的关键,后面所有“考勤记录”、“访问记录”之类的扩展功能都可以基于这张表来做。我用外键把每条记录关联到具体的用户上,这样查询会非常方便。
4.2 视图函数与识别流程串联
Django 的视图层是整个业务的调度中心。用户在前端页面上传照片后,视图函数要做的事包括:把照片保存到临时目录 → 用 OpenCV 检测人脸并裁剪 → 更新采集数据集 → 删除旧的模型文件 → 重新训练 → 返回结果给前端。
import os import cv2 from django.shortcuts import render, redirect from django.conf import settings from .models import User, RecognitionLog BASE_DIR = settings.BASE_DIR DATASET_DIR = os.path.join(BASE_DIR, "dataset") MODEL_PATH = os.path.join(BASE_DIR, "models", "trainer.yml") def register_user(request): if request.method == "POST": user_id = request.POST.get("user_id") name = request.POST.get("name") photo = request.FILES.get("photo") # 省略部分参数校验逻辑... # 保存上传的照片,并用 OpenCV 检测人脸 temp_path = os.path.join(BASE_DIR, "temp", photo.name) with open(temp_path, "wb") as f: for chunk in photo.chunks(): f.write(chunk) face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) img = cv2.imread(temp_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(100, 100)) user_dir = os.path.join(DATASET_DIR, str(user_id)) os.makedirs(user_dir, exist_ok=True) for i, (x, y, w, h) in enumerate(faces): face_roi = gray[y:y + h, x:x + w] face_resized = cv2.resize(face_roi, (200, 200)) cv2.imwrite(f"{user_dir}/{i}.jpg", face_resized) # 更新用户表并重新训练模型 user_obj, created = User.objects.update_or_create( user_id=user_id, defaults={"name": name, "photo_count": len(faces)} ) retrain_model() # 这个函数内部会遍历 dataset 并调用 recognizer.train return redirect("recognition_list") return render(request, "register.html")这里有个容易被忽视的坑:Django 的update_or_create返回的是(对象, 是否新建的布尔值),新手经常会漏掉这个解包操作,导致数据写入失败。另外,训练模型是 CPU 密集型操作,如果一次上传的人脸照片太多,HTTP 请求可能会超时,建议在视图函数中先返回“正在处理”的提示,使用异步任务(比如 Celery)来做模型重训练,但在课程设计阶段可以先用同步方式凑合,毕竟数据量不大。
另外一个很实用的小技巧是:RecognitionLog里我预留了image字段,用来保存识别时的抓拍截图。这可以让你的系统在识别记录页面展示“谁在什么时候出现过”的照片证据,这个功能在答辩演示时非常抓眼球,比单纯显示文字记录要有说服力得多。
4.3 前端页面与模板渲染
Django 模板这一层,我不建议做太复杂的页面,因为课程设计的重点在识别逻辑而不是前端炫技。但页面至少要包含三个模块:用户注册页(采集/上传照片)、实时识别页(可以嵌入视频流)、记录查询页。
<!-- recognition.html 核心片段 --> <div class="container"> <h3>实时人脸识别</h3> <video id="video" autoplay muted playsinline></video> <canvas id="canvas" style="display:none;"></canvas> <button id="captureBtn">开始识别</button> <div id="result"></div> </div> <script> const video = document.getElementById('video'); const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // 需要 HTTPS 或 localhost 下浏览器才允许调用摄像头 navigator.mediaDevices.getUserMedia({ video: true, audio: false }) .then(stream => { video.srcObject = stream; }) .catch(err => { console.error("摄像头调用失败: ", err); }); document.getElementById('captureBtn').addEventListener('click', () => { canvas.width = video.videoWidth; canvas.height = video.videoHeight; ctx.drawImage(video, 0, 0, canvas.width, canvas.height); const base64Data = canvas.toDataURL('image/jpeg'); // 通过 Ajax 发送给 Django 后端进行识别 fetch('/api/recognize/', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ image: base64Data }) }) .then(res => res.json()) .then(data => { document.getElementById('result').innerText = data.name ? `识别结果: ${data.name}` : '未识别到人脸'; }); }); </script>这个设计其实是在做“自动化考试”:把浏览器的视频帧通过 Ajax 传到 Django 后端,由后端的 OpenCV 识别器返回结果,再渲染到页面上。有人认为 OpenCV 在 280x360 的灰度图上做 LBPH 识别只要几十毫秒,这样的链路完全可以在 CPU 上跑通,不会有什么性能压力。
5. 部署疑难杂症与排错速查
5.1 摄像头调用的常见问题
这里整理了我在实际开发和部署中遇到的几个高频问题,直接贴出排查思路和解决方案。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
cv2.VideoCapture(0)返回 False | 摄像头被其他程序占用,或驱动未安装 | 先关闭可能占用摄像头的软件(如微信、钉钉、OBS),再运行;检测设备管理器确认驱动正常 |
| 浏览器摄像头黑屏 | 浏览器权限未放行,或域名不是 HTTPS/localhost | 在浏览器地址栏左侧点击“网站设置”,允许摄像头权限;本地调试务必用 localhost 访问 |
module 'cv2' has no attribute 'face' | 只安装了 opencv-python,face 模块在 contrib 包里 | pip install opencv-contrib-python后重启内核 |
LBPHFaceRecognizer_create报 AttributeError | OpenCV 版本过旧或过新 | 回退到 4.5~4.8 版本,API 最稳定 |
| Django 部署后静态文件 404 | 未执行 collectstatic | 修改 settings.py 的 STATIC_ROOT 后运行python manage.py collectstatic |
5.2 识别准确率不理想时的调优方向
如果你发现识别率总是差强人意,先不要急着换算法,按下面这个优先级去排查:
第一优先,检查训练样本质量。样本数量太少(比如每个人只有 10 张)、光线差异过大、照片角度过于单一,都会严重影响 LBPH 的表现。先把样本补足到 100 张以上,保证多角度、多表情、光线均匀。
第二优先,检查人脸检测框的准确性。Haar 级联分类器在侧面脸、低头场景下经常检测不到或框选不准。可以调整detectMultiScale的参数,比如把scaleFactor从 1.1 调整到 1.05(更精密的缩放扫描,检测更准但更慢),把minNeighbors从 5 降低到 4(允许更多的候选框通过),观察效果变化。
第三优先,检查预处理。在送入识别器之前,除了灰度化,还可以做直方图均衡化(cv2.equalizeHist)来增强对比度,减少环境光照的影响。这个操作对 LBPH 的提升非常明显,而且代价极小:
face_roi = gray[y:y + h, x:x + w] face_roi = cv2.equalizeHist(face_roi) # 直方图均衡化 face_resized = cv2.resize(face_roi, (200, 200))第四优先,检查置信度阈值是否合理。如果系统经常把不是用户A的人识别成用户A,说明阈值设得太宽松了,调低一些(比如从 80 降到 60);如果经常识别出“Unknown”,说明阈值设得太严格,稍微放宽一点。
5.3 Django 运行时的问题
Django 在开发环境跑得很顺畅,但一部署到服务器上就各种问题。这里说两个最常见的:
一个是ALLOWED_HOSTS。Django 4.x 默认是不运行非 localhost 域名访问的,如果你用服务器 IP 访问页面,需要在settings.py里把 IP 加进去:
ALLOWED_HOSTS = ["*"] # 课程设计阶段直接放行所有,生产环境千万别这样写另一个是数据库迁移。每次修改了models.py之后,必须依次执行两条命令:
python manage.py makemigrations python manage.py migrate经常有人改了模型不迁移就直接运行,结果报no such table的错误,其实数据库里根本没建好表。
6. 课程设计答辩的展示思路
最后聊点题外话,关于课程设计答辩时的展示角度。代码能跑起来只是基础分,要让老师觉得你的项目有深度,展示时应该强调三个点:
第一,系统架构的完整性。从数据采集、模型训练、实时识别到 Web 端的业务记录,形成了完整的闭环,不是一个孤立的算法 demo。可以画一张简单的系统架构图,说明每一层的作用和接口关系。
第二,工程化思维。比如在实时识别中加入 5 秒冷却时间防止记录重复、在注册用户后自动重新训练模型、在数据库中用外键维护用户和识别记录的关联关系——这些细节虽然小,但能证明你不只是会调库,而是在认真考虑系统的健壮性。
第三,明确的改进方向。答辩老师经常问“你还能怎么优化”,千万不要说“不知道”。你可以主动提出:如果换成深度学习模型(如 FaceNet)可以提升复杂场景下的识别率,但代价是更高的算力需求;如果加入更多的样本增强(旋转、缩放、亮度变换)可以提高模型的泛化能力;如果使用 Redis 做缓存可以提升并发场景下的响应速度。哪怕没有实际做过这些优化,提出合理的技术路线也能证明你真的思考过。
我自己在这个项目上写得最多的一段代码,反而是采集脚本里那句print(f"采集进度: {count}")。为什么?因为在实际操作中,没有进度提示的时候,你根本不知道摄像头对不对准了、有没有在正常保存图片。很多细节看着不起眼,但只有真正动手跑过一遍,才会知道哪一步最容易卡住。这个项目的源码里保留了这些“实战痕迹”,拿来做课程设计的底子,我觉得比直接抄一段只有核心算法的 demo 要实在得多。
本文还有配套的精品资源,点击获取