Python+Django人脸识别门禁系统:源码部署与二次开发实战
简介一个基于PythonDjango的人脸识别门禁管理系统完整源码包面向毕业设计、课程设计或希望结合Web开发与人脸识别技术的初学者。系统以Django搭建后端覆盖用户注册登录、人脸特征提取与比对、门禁权限控制等核心模块涉及OpenCV图像预处理、数据库ORM操作、RESTful API接口设计等关键知识点可帮助读者理解从HTTP请求处理到人脸识别结果返回的完整链路。压缩包共9358个文件约410.73MB包含2571个Python源码、2505个编译pyc、1196个mo/1146个po多语言资源、274个HTML页面、235个JavaScript脚本以及大量依赖库与配置数据目录结构完整便于按模块查阅。目前已有342人学习下载适合作为毕业设计参考模板或二次开发基础尤其能直观学习ORM模型设计、RESTful API实现及人脸识别算法在真实项目中的落地方式并可结合Django自带后台完成数据管理。1. 人脸识别门禁系统这套PythonDjango源码能直接落地吗如果毕业设计或者公司小项目里需要一个能演示、能联网管理、还能二次开发的门禁系统基于PythonDjango的人脸识别门禁管理系统源码是投入产出比最高的起点。这套源码把Django的业务管理能力和人脸识别算法结合到一起前台做到刷脸开门、通行记录后台做到人员录入、部门管理、权限分配识别部分用本地人脸识别算法完成不调云API也不依赖第三方服务。适合正在找Python Django毕业设计方向的学生、准备在公司内部做智能门禁demo的后端开发以及想低成本做校园或园区门禁原型的人。下面我从技术选型开始一路拆到部署步骤、核心代码、踩坑记录和进阶改造把“拿下来能不能跑、改起来从哪里下手”一次讲清楚。2. 技术选型Django负责业务、face_recognition负责认脸授权与识别彻底分开门禁系统的本质是“认人”“放行”“留痕”三件事。认人由人脸识别算法负责放行由门禁控制逻辑负责留痕由数据库和日志负责。这套源码里三件事分别由face_recognition库、Django视图层和Django ORM完成人员授权信息和特征向量分开存储这是它和一体机方案最大的区别。2.1 Django框架在门禁场景里的三个不可替代性先回答一个常见疑问为什么是Django而不是Flask、Spring Boot或者直接嵌入式C做毕业设计和内部项目时Django框架有三个不可替代的点。第一Django的MTV架构天然把“页面展示”“业务处理”“数据库访问”分层。门禁系统要同时处理前台识别页面、后台管理页面、识别接口和硬件联动日志用Django的urls、views、models三层结构不用自己再设计一套框架直接按目录约定往里填代码即可对django项目实战新手尤其友好。第二Django自带的Admin后台能直接管理人员、部门、通行记录这些基础数据创建管理员时顺便拿到一个完整后台完全不用额外写管理端页面毕业设计演示时这个内置后台很加分。第三Django的ORM对SQLite、MySQL、PostgreSQL都做了适配演示阶段用SQLite零配置部署阶段切MySQL只改settings.py和驱动二次开发阻力最小的路径就是它。ORM还有一个隐性好处就是数据模型的关联关系在代码里直接可读。下面这段模型设计是这类门禁系统的基础拿过来能直接改# models.py 核心模型设计 class Department(models.Model): name models.CharField(max_length64, verbose_name部门名称) class Personnel(models.Model): # 工号、姓名、部门face_encoding 存 128 维特征向量逗号分隔 work_no models.CharField(max_length32, uniqueTrue, verbose_name工号) name models.CharField(max_length32, verbose_name姓名) department models.ForeignKey(Department, on_deletemodels.SET_NULL, nullTrue) face_encoding models.TextField(blankTrue, nullTrue, verbose_name128维特征向量) photo models.ImageField(upload_tofaces/, blankTrue, verbose_name注册照片) created_at models.DateTimeField(auto_now_addTrue) class AccessRecord(models.Model): personnel models.ForeignKey(Personnel, on_deletemodels.SET_NULL, nullTrue) result models.BooleanField(defaultFalse, verbose_name是否放行) snapshot models.ImageField(upload_tosnapshots/, blankTrue, verbose_name抓拍图) created_at models.DateTimeField(auto_now_addTrue)这里把门禁系统的核心表结构写出来了Department是组织维度Personnel是人员与特征维度AccessRecord是通行日志。face_encoding用TextField而不是BinaryField的原因很实际SQLite对二进制字段的兼容性不如文本把特征转成逗号分隔字符串便于在Admin后台直接查看和排错识别时再用numpy转回数组即可字符串形式也方便跨库迁移数据。2.2 本地人脸识别方案对比face_recognition、OpenCV LBPH、云API、商用SDK人脸识别算法选型是这类系统容易翻车的地方方案选错要么卡在编译要么卡在费用。把这四类主流方案放在一起对比一次看明白方案运行位置精度速度成本典型场景face_recognitiondlib本地较高CPU单张几百毫秒依赖dlib编译无授权费毕业设计、小范围门禁OpenCV LBPH本地较低快无入门演示、纯学习云API云端高快但有网络延迟按次计费生产级、多门店场景商用SDK本地最高快授权费高高端楼宇、园区这套源码采用face_recognition方案理由很直接它把识别门槛降到最低。底层由dlib完成人脸检测和128维特征提取上层只暴露face_locations、face_encodings、compare_faces几个函数开发者在几天内就能把识别链路跑通而用OpenCV原生LBPH训练模型会涉及样本采集、灰度归一化、模型保存等额外工作对门禁这种需要动态增删人员的场景反而不方便。云API虽然精度高但每次识别都产生费用和延迟门禁系统里刷脸频率高长期用成本不可控且多数校园门禁、企业门禁对网络依赖有要求断网时纯云方案直接失效。这里有一个“硬件无关”的关键点人脸识别算法负责的是“这张脸是谁”Django负责的是“这个人能不能进”两者通过work_no这样的人员ID关联。人脸特征向量的精度上限取决于dlib模型本身而我们能调的是后续的相似度阈值这是后文避坑章节的重点。2.3 为什么SQLite做演示够用、上生产再切MySQL有人会质疑商用门禁都用MySQL或PostgreSQL毕业设计项目用SQLite是不是太业余这类判断要分场景。SQLite的写入性能在单机、低并发场景下完全够用而且零配置、无独立服务进程一个文件就能把整个数据库打包带走。人脸识别门禁系统在演示阶段并发量极低通行记录表即便一天产生几千条SQLite也能轻松支撑。算一笔具体的账一个人脸特征向量是128个float大约占256字节录入一千名员工也就250KB量级通行记录即使每天一千条一年不到40万行。这种数据量在SQLite里完全没压力。真正导致性能瓶颈的不是数据库选型而是识别前的全表特征比对。源码里若直接遍历Personnel表逐个算距离在千人规模下CPU仍可接受但到几千人时就要考虑向量索引或按班组缩小比对范围这才是要提前设计的部分。切换MySQL时只需把settings.py里DATABASES的ENGINE改成django.db.backends.mysql并装pymysql即可ORM层代码不用动这是Django带给二次开发的底气。3. 部署复现从空环境到刷脸开门踩通完整流程这一章按“装依赖→建库建表→建管理员→跑服务→注册人脸”的顺序来每一步对应一条命令和必要说明。网上的python安装教程很多但真正卡人的往往不是Python本体而是dlib编译和摄像头索引这两处会在后续避坑章节重点展开。3.1 环境搭建与依赖安装含dlib编译注意事项推荐Python 3.8到3.10之间的版本避开Python 3.12在dlib兼容性上的坑。新建虚拟环境后先装依赖python -m venv venv # Windows 激活虚拟环境 venv\Scripts\activate # Linux / macOS 激活虚拟环境 source venv/bin/activate执行激活后pip安装前先确认pip和setuptools是最新版python -m pip install --upgrade pip setuptools wheel pip install -r requirements.txt一份可用的requirements.txt按下面的版本约束来既能跑通识别链路又避免最新版Django带来的兼容性问题django3.2,4.2 opencv-python4.5.5.62 dlib19.22.0 face-recognition1.3.0 numpy1.21.0 Pillow9.0.0 cmake3.18这里的版本号有讲究。opencv-python锁在4.5.x是因为新版对部分USB摄像头的V4L2后端有兼容变化dlib在Windows上需要CMake和Visual Studio Build Tools才能编译动手前先确认这两个工具已安装否则pip install dlib会当场报错。如果你的机器没有编译条件可以退一步只装opencv-python和numpy把识别部分改成OpenCV自带的LBPH逻辑框架不变只是精度略低。安装完成后用一段极短代码验证环境是否正常避免后续启动服务时才发现底层库没装好# check_env.py import face_recognition import django import cv2 print(face_recognition:, face_recognition.__version__) print(django:, django.get_version()) print(opencv:, cv2.__version__)print输出三项版本号即为正常。经常有人在这里种下隐患sklearn、scipy等间接依赖被pip自动升到了不兼容版本导致运行期报错所以不推荐用pip install -U把整个环境全部升级只装requirements.txt里列出的版本。3.2 数据库迁移与管理员创建依赖装好后进入项目根目录先执行数据库迁移。Django用迁移文件的方式追踪模型变化首次跑项目的固定动作是makemigrations和migrate两步python manage.py makemigrations python manage.py migratemakemigrations把模型改动翻译成迁移文件migrate把这些迁移真正落库。执行成功后会看到类似Applying faces.0001_initial... OK的输出说明department、personnel、accessrecord这些表已在SQLite中创建。这一步如果报错九成是models.py里有语法错误或字段约束冲突按报错行号回去检查即可。接着创建超级管理员用于登录内置后台python manage.py createsuperuser按提示输入用户名、邮箱、密码即可。Django的Admin后台是全套系统里管理端的核心入口人员录入、通行记录查询、部门维护都可以在这个界面完成这就是前面说的“白嫖后台”的落地动作。3.3 启动服务、注册第一张人脸管理端创建完成后先启动开发服务器验证页面可达python manage.py runserver 0.0.0.0:80000.0.0.0表示监听所有网卡这样同一局域网内的识别终端、手机都能访问局域网调试时不用改代码。浏览器打开http://127.0.0.1:8000/admin进入后台先在部门管理里建一个“研发部”再到人员管理里新建一条人员记录上传一张清晰的正面照并保存这一步成功后就完成了数据的第一次写入。接下来打开前台识别页面一般地址是http://127.0.0.1:8000/页面上会看到摄像头画面和“开始识别”按钮对着摄像头拍一张。如果刚才已注册过照片系统应该能返回人员姓名和放行结果出现“未识别”或“检测不到人脸”则多半是阈值或光线问题留到避坑章节处理。至此空环境到刷脸开门的最小闭环已经走通剩下的是读懂内部逻辑并做二次开发。4. 核心模块拆解人脸录入、特征比对与门禁控制是怎么串起来的理解了整体流程后把源码里最关键的三个模块单独拿出来拆人脸录入要怎么检测和入库识别时特征比对怎么算距离门禁控制怎么从软件逻辑映射到真实硬件。这三段代码是二次开发绕不过去的部分也是面试和答辩时最容易被追问的细节。4.1 人脸录入链路前端拍照上传、服务端检测、特征入库录入的核心逻辑是“前端摄像头拍摄照片→base64编码上传→服务端检测人脸位置→提取特征向量→存库”。前端部分通常用getUserMedia打开摄像头canvas截取当前帧后转成base64的JPEG数据再通过fetch或axios POST到接口。服务端对接代码如下# views.py 人脸注册接口 import base64 import io import json import face_recognition import numpy as np from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from .models import Personnel csrf_exempt def register_face(request): if request.method ! POST: return JsonResponse({code: 400, msg: 仅支持POST请求}) data json.loads(request.body) # 前端把摄像头帧转成 base64 后随 JSON 一起提交 image_bytes base64.b64decode(data[image]) image face_recognition.load_image_file(io.BytesIO(image_bytes)) # 第一步检测图片中的人脸位置 face_locations face_recognition.face_locations(image) if len(face_locations) ! 1: return JsonResponse({code: 400, msg: 画面中必须且只能有一个人脸}) # 第二步对人脸区域提取 128 维特征向量 face_encodings face_recognition.face_encodings(image, face_locations) encoding_text ,.join([str(v) for v in face_encodings[0]]) personnel Personnel.objects.create( work_nodata[work_no], namedata[name], department_iddata.get(department_id), face_encodingencoding_text, ) return JsonResponse({code: 200, msg: 注册成功, person_id: personnel.id})逻辑说明face_locations返回的是图像中所有人脸的位置框限制“只能一个人脸”是为了避免误注册这条规则在门禁场景里是安全底线。face_encodings返回二维数组每个元素是128个浮点数组成的特征向量把向量转成逗号分隔字符串存入TextField是为了在Admin后台可直接查看和二次修改实测这个格式在SQLite里读写效率也不错。参数说明work_no是工号或学号是整个系统的唯一业务键department_id关联到部门表可为空。坑点在于face_recognition.load_image_file接受的是文件路径或文件对象不能直接接受bytes所以这里用io.BytesIO包一层。很多新人在这里直接传bytes导致报错这个细节值得记下来。4.2 识别核心特征向量比对与阈值的“玄学”识别接口的逻辑和注册相反收到摄像头画面后检测人脸并提取特征再和库里已有的特征逐个算欧氏距离距离小于阈值就判定为同一个人。核心代码# views.py 人脸识别接口 csrf_exempt def verify_face(request): if request.method ! POST: return JsonResponse({code: 400, msg: 仅支持POST请求}) data json.loads(request.body) image_bytes base64.b64decode(data[image]) image face_recognition.load_image_file(io.BytesIO(image_bytes)) face_locations face_recognition.face_locations(image) if not face_locations: return JsonResponse({code: 400, msg: 未检测到人脸}) # 当前画面里可能有多个人这里只取面积最大的人脸做比对 target_encoding face_recognition.face_encodings(image, face_locations)[0] candidates Personnel.objects.exclude(face_encoding) for person in candidates: known_enc np.fromstring(person.face_encoding, sep,) # face_distance 返回欧氏距离越小越相似 distance face_recognition.face_distance([known_enc], target_encoding)[0] if distance 0.45: AccessRecord.objects.create( personnelperson, resultTrue, snapshot ) return JsonResponse({code: 200, msg: 识别成功, name: person.name, distance: round(float(distance), 4)}) return JsonResponse({code: 401, msg: 未授权人员})参数说明threshold阈值设为0.45是权衡结果。face_recognition官方文档里0.6是“比较宽松”的阈值适合人数少、光线好的场景0.4适合要求严格的门禁。推荐先从0.45起步识别失败再把阈值放宽到0.5出现陌生人误识别就收紧到0.4。这个值直接影响安全级别是门禁系统里最需要反复调试的参数。这段代码有个可以优化的点每来一个识别请求就把全表特征向量拉出来逐个算距离属于O(n)复杂度在千人规模下尚可接受上万人就可能出现几百毫秒延迟。进阶做法是按班组或部门过滤候选集或者用faiss这类向量检索库建索引第6章会提到。另外np.fromstring在numpy较新版本里可能提示DeprecationWarning换成np.fromstring(person.face_encoding, dtypefloat, sep,)即可消除。4.3 门禁控制与通行记录从软件逻辑到硬件联动识别成功后真正“开门”的动作在源码里有两个分支。纯演示模式只记录日志并返回前端提示接真实硬件时通过串口或GPIO控制电锁。下面是常见的继电器联动方式# utils/lock_control.py 门禁控制 import serial def control_lock(action: str) - bool: action: open 开锁 / close 关锁 通过 USB 转串口控制继电器进而控电磁锁 try: # COM3 是 Windows 下的串口号Linux 通常是 /dev/ttyUSB0 ser serial.Serial(COM3, 9600, timeout1) if action open: ser.write(bOPEN) else: ser.write(bCLOSE) ser.close() return True except serial.SerialException as exc: print(f串口控制失败: {exc}) return False在真实项目中电磁锁通常通过继电器接到工控机或树莓派的GPIO上Django服务运行在工控机里识别成功后调用一次open动作锁舌吸合2秒后自动闭合。这里串口协议是自定义的实际硬件不同命令字也不一样常见做法是把open和close的指令定义在配置文件中便于根据继电器固件调整。百元级继电器和电磁锁即可完成原型搭建成本远低于一体式人脸识别门禁机。识别结果与硬件动作要放到同一事务里处理先写AccessRecord再调用control_lock开锁避免出现“锁开了但日志没记”的尴尬情况。在Django里可以用transaction.atomic()包住这两步保证数据一致性。4.4 Django Admin后台毕业设计最省事的管理端整个系统的管理端几乎不需要额外开发Django Admin自带列表页、搜索、筛选、分页功能只需在admin.py里注册模型并配置展示字段# admin.py 注册后台管理 from django.contrib import admin from .models import Department, Personnel, AccessRecord class PersonnelAdmin(admin.ModelAdmin): list_display [work_no, name, department, created_at] search_fields [work_no, name] list_filter [department] class AccessRecordAdmin(admin.ModelAdmin): list_display [personnel, result, created_at] date_hierarchy created_at admin.site.register(Department) admin.site.register(Personnel, PersonnelAdmin) admin.site.register(AccessRecord, AccessRecordAdmin)这里list_display决定列表页展示哪些列search_fields开启搜索框date_hierarchy在通行记录页生成日期钻取导航。这些配置都是声明式的不用写一行HTML就能得到一个可用的管理后台对智能校园门禁管理系统这类需要展示完整管理链路的设计来说Django Admin是最省时间的选择。5. 避坑指南部署和二次开发最常见的五个坑做完几次实际部署后能明显感觉到这个项目上的坑非常集中来来回回就是那几个。排查思路比具体命令更重要把现象和原因对上解决方案自然就出来了。5.1 dlib安装失败卡在编译环节现象pip install dlib或pip install face-recognition时日志卡在Building wheel for dlib随后报错提示CMake must be installed或Microsoft Visual C 14.0 is required。原因dlib是C库pip安装时需要在本地编译。Windows上必须提前安装CMake和Visual Studio Build Tools否则无法编译Linux上缺的是g和cmake。解决Windows先装Visual Studio Build Tools勾选“使用C的桌面开发”组件再单独装CMake并勾选加入PATHLinux执行sudo apt install build-essential cmake后重试。如果你只需要做演示可以放弃face_recognition改用OpenCV的LBPH识别器依赖会轻很多。5.2 摄像头打不开黑屏或一直转圈现象页面正常加载但摄像头画面黑屏浏览器控制台报NotAllowedError或摄像头设备找不到。服务端日志里常见的是OpenCV无法打开摄像头并输出[ WARN:] Cannot open camera。原因一是浏览器权限问题非localhost环境下getUserMedia要求HTTPS或显式授权二是OpenCV的cv2.VideoCapture(0)里的参数0在部分电脑上是错误的索引0对应内置摄像头还是USB摄像头因设备而异。解决浏览器地址栏使用localhost或127.0.0.1访问可绕过HTTPS限制如果用的是USB摄像头把VideoCapture(0)改成VideoCapture(1)试试。打印出cv2.getBuildInformation()确认OpenCV编译时是否带V4L2或MSMF后端某些精简版opencv-python不带摄像头后端换成opencv-python-headless的同时要手动装opencv-contrib-python才能用摄像头。5.3 识别率低张三识别成李四或压根认不出现象注册时用的照片能识别现场环境里经常失败或者陌生人多次被误认为已授权人员。调整阈值0.45变成0.5后误识别率上升收紧到0.4又出现大量“未授权”。原因阈值是原因之一但更常见的原因是注册照片太单一。门禁场景中光线角度变化很大如果只注册一张正脸照特征向量只代表“某个光照条件下的一张脸”识别泛化能力自然差。解决注册时采集两张以上不同角度、不同光照的照片取多张特征向量的平均值入库。识别端如果摄像头安装位置偏高会拍到更多俯仰角度的人脸注册照片里最好也包含同一角度的样本。这是提升识别率投入产出比最高的手段比调阈值更有效。5.4 千人以上规模识别变慢页面卡顿现象人员录入到几百人后每次识别都要2到3秒才能返回结果明显不满足门禁体验。服务端CPU占用率高。原因识别接口把全表特征向量拉出来逐个计算欧氏距离属于线性扫描。人数增多后计算量线性上涨加上每次都要从数据库读取几百条Text字段并反序列化成numpy数组IO开销也不小。解决常见做法有两级。第一级按department_id过滤候选集把一次全表扫描缩到部门内扫描第二级用vector字段加HNSW索引比如引入pgvector或faiss做向量检索把比较次数从O(n)降到接近O(log n)。做毕业设计时用第一级足够演示效果上不会有明显差距。5.5 静态文件404Admin后台样式全丢现象登录Django Admin后台页面没有CSS和JS样式控制台大量404头像图片也加载不出来。原因Django在开发模式下不自动托管静态文件需要手动配置STATIC_URL和STATICFILES_DIRS并用django.contrib.staticfiles的runserver辅助路由。很多项目模板忘了把admin静态文件目录指出来。解决确认settings.py里包含INSTALLED_APPS里的django.contrib.staticfiles且STATIC_URL /static/如果STATIC_ROOT已配置先执行python manage.py collectstatic。开发模式下用runserver访问后台时静态文件由staticfiles app自动处理这一步配置好基本不会再出问题。6. 进阶玩法把单机演示改造成WebSocket实时推送的在线门禁系统前端识别页面目前是“请求→响应”模式识别结果要等HTTP返回后才能刷新页面。真要放到园区或校园门禁场景里需要把识别结果实时推送到门卫大屏、管理后台或微信通知上这时就该引入Django Channels用WebSocket做实时推送。改造思路很清晰识别成功后在Django内部发送一个channel layer消息前端大屏通过WebSocket订阅并即时展示抓拍和放行结果。Channels的核心配置是先装channels包然后在settings.py的INSTALLED_APPS里加入channels再设置ASGI_APPLICATION指向项目的asgi.py。WebSocket消费者接收前端连接请求把连接加入一个门禁事件分组# consumers.py 门禁事件消费者 from channels.generic.websocket import AsyncWebsocketConsumer class AccessConsumer(AsyncWebsocketConsumer): async def connect(self): # 所有大屏客户端加入 access_events 分组 await self.channel_layer.group_add(access_events, self.channel_name) await self.accept() async def disconnect(self, close_code): await self.channel_layer.group_discard(access_events, self.channel_name) async def access_notify(self, event): # 收到门禁事件后推送给前端 await self.send(text_datajson.dumps({ name: event[name], result: event[result], time: event[time], }))识别接口里调用channel_layer的group_send即可完成推送。配合阈值调优和考勤统计这套源码的完成度会明显上一个台阶。从那以后我每次部署人脸识别门禁系统都会强制走一遍“摄像头权限确认、dlib编译验证、注册双样本、阈值下限测试”四个检查项把这四步走完现场翻车的概率能降掉一大半。这套源码本身是很好的起点但要变成能稳定运行的方案始终绕不过对阈值和样本质量的精细调校顺着这条路走下去收获比直接买一套成品更大希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →