简介:这是一套基于Python的人脸识别智能小区门禁管理系统源码,面向Python学习者、计算机专业学生及安防系统开发者,用于解决小区出入身份验证与门禁自动化管理问题。资源包共101个文件,大小约12.17MB,文件类型涵盖.py源码、.pyc编译文件、.ui界面文件、图片素材、.sql数据库脚本、说明文档及模型文件等,能覆盖从界面展示到底层数据存储的多个环节。系统包含数据采集、人脸识别、数据管理、用户界面和访问控制等模块,源码中实现了前端人脸采集与后端MySQL存储联动,以及人脸比对、放行判定和出入记录生成逻辑。目前已有36人学习或下载,配套文档和图片素材有助于理解整体架构,适合作为课程设计、毕业设计或人脸识别项目开发的参考,便于二次开发。
1. 人脸识别门禁系统源码:先看清这套Python项目能干什么
做小区门禁,老办法是刷卡、按密码、保安盘问,卡丢了补卡要钱,密码泄露了安全等于零。人脸识别的好处是“人即凭证”,不用带卡、不用记密码,走到摄像头前停一秒就开门,后台自动留记录。这套基于Python的人脸识别智能化小区门禁管理系统,正是把这条链路完整实现出来的参考项目——前端摄像头采集人脸,后端MySQL存用户信息和出入记录,识别通过就控制门禁开关,整个过程不需要人工介入。
它不是一个大而全的商业产品,而是一个值得拿来拆解、改造成自己课设或毕设项目的完整源码包。资源里附带需要文档、多张测试图片以及README,从环境配置到数据库建表再到识别逻辑,都能对得上。适合正在做Python课设、毕业设计,或者想了解OpenCV人脸识别工程化落地方式的开发者。接下来我按实际复现的顺序,带你把这套系统从头到尾过一遍,讲清楚原理、步骤和容易翻车的地方。
2. 系统架构与识别流程:从摄像头到数据库的完整链路
2.1 模块划分与数据流
整套系统在架构上并不复杂,核心就是“采集→处理→比对→控制→记录”五步。我第一次拆这个项目的时候,先不看代码,只把文件结构和需求文档翻了一遍,发现它在设计上是分了层的,每一层干一件事,耦合度比较低,这对我后期改代码非常友好。
- 数据采集层:调用摄像头实时捕获画面,或者读取本地图片,拿到原始的人脸图像。
- 图像预处理层:把采集到的图像转为灰度图、做直方图均衡化、裁剪出人脸区域,减少光照和背景干扰。
- 人脸识别层:用OpenCV的训练器或深度学习模型提取人脸特征,与MySQL里存的样本特征做比对,输出匹配结果和置信度。
- 业务逻辑层:根据识别结果决定是否开锁,同时把“谁在什么时间进出”写入MySQL的出入记录表。
- 用户交互层:提供登记人脸、删除用户、查看记录的操作入口,界面不需要多华丽,功能完整就行。
数据流的方向很明确:摄像头/图片 → 人脸检测 → 特征提取 → 数据库比对 → 门禁控制 → 写入记录。这套设计里最值得学习的一点是,识别和存储是分离的。识别模块只负责“认出这个人”,存储模块只负责“存住这些人和记录”,两者通过统一的接口对接。你要是想把它改成宿舍门禁、实验室门禁,只需要换数据采集源和开锁逻辑,其他模块基本不用大动。
2.2 人脸识别流水线:检测、对齐、特征提取与比对
人脸识别不是一步到位的,实际处理时是一条流水线。我以系统里用到的OpenCV方案为例拆开讲。
第一步是人脸检测,常见做法是用OpenCV自带的Haar级联分类器,加载haarcascade_frontalface_default.xml模型,对灰度图做多尺度扫描,返回人脸矩形坐标。检测到人脸之后,要把人脸区域裁出来,缩放到统一尺寸,再送进识别器。
import cv2 # 加载Haar级联分类器,OpenCV自带模型文件 face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml") # 无论从图片还是视频帧来,都先转灰度,降低计算量 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # detectMultiScale的参数直接影响检测效果 faces = face_cascade.detectMultiScale( gray, scaleFactor=1.1, # 每次缩放比例,越小检测越慢但越准 minNeighbors=5, # 附近至少5个框都认为是人脸才保留,过滤误检 minSize=(80, 80) # 人脸最小尺寸,小于这个尺寸直接忽略 ) for (x, y, w, h) in faces: # 裁出人脸区域并统一缩放到200x200,方便送入识别器 face_roi = gray[y:y+h, x:x+w] face_roi = cv2.resize(face_roi, (200, 200))这段代码是整个系统里最常被调用的部分。scaleFactor和minNeighbors是两个需要重点调试的参数,后面避坑章节我会详细讲。检测到的人脸区域,会传给识别器做进一步处理。
第二步是特征提取和比对,系统用的是OpenCV自带的LBPH(局部二值模式直方图)人脸识别器。LBPH的原理是把人脸图像划分成若干小区域,在每个区域内计算局部二值模式特征,再统计成直方图作为这张脸的“指纹”。比对时计算当前人脸直方图与数据库中人脸直方图的距离,距离越小说明越像。
import cv2 import os # 创建LBPH识别器,这是OpenCV内置的经典算法 recognizer = cv2.face.LBPHFaceRecognizer_create() # 训练数据格式:一组人脸灰度图 + 对应的用户ID faces = [] labels = [] for image_path, user_id in training_samples: img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (200, 200)) faces.append(img) labels.append(user_id) # 训练模型,把特征写入model.yml recognizer.train(faces, np.array(labels)) recognizer.save("model.yml")训练好模型之后,识别时调用predict方法,返回两个值:预测的用户ID和置信度。置信度越低表示匹配度越高,一般LBPH的置信度阈值设在50到80之间,超过这个范围就认为是陌生人。系统里门禁放行逻辑就是拿这个置信度和阈值比较。
# 加载训练好的模型 recognizer.read("model.yml") # 对当前采集到的人脸做预测 label, confidence = recognizer.predict(face_roi) # 置信度越低越可信,超过阈值判定为陌生人 if confidence < 60: print(f"识别成功:用户ID {label},置信度 {confidence:.2f}") # 这里触发开锁逻辑,比如给GPIO一个高电平 else: print("陌生人,不允许通行")这里有个容易被忽略的设计点:LBPH识别的是“人脸的相似程度”,不是绝对的身份判断。同一个人的脸在不同光照、不同角度下,置信度会波动很大,所以阈值不能拍脑袋定死。我在复现的时候是先采集了一批样本,统计同一人和不同人的置信度分布,再取中间的平衡点作为阈值,效果比默认值好很多。
2.3 访问控制逻辑与记录生成
识别完成之后,系统不会直接开锁,中间还夹着一个访问控制逻辑。这套系统的处理方式是:识别成功且置信度在阈值内,控制逻辑给门禁设备发送开锁信号,同时把用户ID、识别时间、识别结果写到MySQL的access_record表里;识别失败则只记录一条“未授权”的日志,不触发开锁。这样设计的好处是每一笔通行都有据可查,物业查监控的时候能精确到人。
从整个架构来看,这个项目的选型是中规中矩但非常务实的。没有用特别前沿的深度学习模型,而是选了OpenCV自带的人脸检测和LBPH识别器,理由是它依赖少、跑得快、普通笔记本就能在实时视频流上跑得动。如果你手头有GPU和大量标注数据,替换成FaceNet或者ArcFace会更准,但作为学习项目,先把LBPH这条路走通是性价比最高的选择。
3. 环境搭建与依赖配置:Python、OpenCV与MySQL的对接
3.1 依赖清单与安装步骤
这套系统对运行环境的要求并不苛刻,主要在“能用”和“能跑实时”之间取平衡。我建议使用64位Python 3.8到3.10之间的版本,搭配OpenCV-Python、OpenCV-contrib-Python、NumPy、PyMySQL这几个核心依赖。LBPH识别器在OpenCV的contrib包里,只装opencv-python是不够的,这个坑我踩过,不装contrib直接报module 'cv2' has no attribute 'face'。
# 建议先升级pip再装依赖 python -m pip install --upgrade pip # 核心依赖:opencv-python不带face模块,必须装contrib版本 pip install opencv-python pip install opencv-contrib-python # 科学计算和MySQL驱动 pip install numpy pip install pymysql # 如果你需要GUI界面,建议再加一个tkinter(Python自带,无需pip)装完后在Python交互环境里验证一下,确认cv2.face.LBPHFaceRecognizer_create能正常调用,别等代码写了一大半才发现环境不对。我一般会用一个最小化脚本做烟雾测试,能跑通再往下走。
import cv2 import numpy as np import pymysql print("OpenCV版本:", cv2.__version__) # 验证LBPH识别器是否可用 recognizer = cv2.face.LBPHFaceRecognizer_create() print("LBPH识别器初始化成功") # 验证MySQL连接,参数替换成你自己的 conn = pymysql.connect(host="localhost", user="root", password="123456", database="door_system") print("MySQL连接成功") conn.close()如果cv2.face这一行报错,99%的情况是opencv-contrib-python没装成功,或者被opencv-python覆盖了版本,卸载重装一遍就好。
3.2 数据库初始化与连接参数
MySQL部分我建议先手工建好库和表,再跑Python代码,别指望程序自动建库。这个项目的表结构围绕两个核心实体设计:用户表和出入记录表。用户表存人脸的标识信息和图片路径,出入记录表存每一次的通行日志。两张表通过用户ID关联。
-- 创建数据库,指定utf8mb4字符集,避免中文乱码 CREATE DATABASE IF NOT EXISTS door_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE door_system; -- 用户信息表 CREATE TABLE IF NOT EXISTS user_info ( id INT AUTO_INCREMENT PRIMARY KEY, user_name VARCHAR(50) NOT NULL, face_image_path VARCHAR(255) NOT NULL, register_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; -- 出入记录表 CREATE TABLE IF NOT EXISTS access_record ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, access_time DATETIME DEFAULT CURRENT_TIMESTAMP, access_result VARCHAR(20) NOT NULL, FOREIGN KEY (user_id) REFERENCES user_info(id) ) ENGINE=InnoDB;Python这边的MySQL连接参数有一个容易踩坑的地方:字符集没有显式指定时,即使数据库建表用了utf8mb4,插入中文姓名也有大概率出现乱码。所以连接时必须带上charset="utf8mb4"。
import pymysql # host、user、password按你的实际环境改 conn = pymysql.connect( host="localhost", user="root", password="123456", database="door_system", charset="utf8mb4", # 不指定这个,中文大概率乱码 cursorclass=pymysql.cursors.DictCursor # 返回字典,取字段名方便 ) cursor = conn.cursor()连接参数里还有一个容易被忽略的细节:cursorclass设为DictCursor之后,查询结果每行是一个字典,可以用row["user_name"]取值,比默认的元组下标方式可读性好很多。门禁系统里要频繁把用户ID和人脸特征对应起来,用字典游标能让代码干净不少。
3.3 人脸样本采集脚本
数据库建好了,下一步就是把用户的照片采集进来。这套系统的样本采集方式很直接,用摄像头拍用户的正面照,每按一次空格存一张,采集完自动把图片路径写入user_info表。注意,每个人的样本至少需要10张以上,而且要在不同角度、不同光照下采集,否则后面训练出来的模型是“偏科”的。
import cv2 import os import pymysql # 样本保存目录,按用户ID建子文件夹 save_dir = "face_samples" os.makedirs(save_dir, exist_ok=True) # 连接数据库 conn = pymysql.connect(host="localhost", user="root", password="123456", database="door_system", charset="utf8mb4") cursor = conn.cursor() # 创建用户记录,拿到自增ID user_name = input("请输入姓名: ") cursor.execute("INSERT INTO user_info (user_name) VALUES (%s)", (user_name,)) conn.commit() user_id = cursor.lastrowid # 打开摄像头 cap = cv2.VideoCapture(0) if not cap.isOpened(): print("摄像头打开失败,检查设备索引") exit() count = 0 while count < 20: ret, frame = cap.read() if not ret: break # 实时显示当前画面,方便用户调整位置 cv2.imshow("collect", frame) # 按空格键采集当前帧 key = cv2.waitKey(1) & 0xFF if key == ord(" "): img_path = os.path.join(save_dir, f"user_{user_id}_{count}.jpg") cv2.imwrite(img_path, frame) count += 1 print(f"已采集 {count}/20 张") elif key == 27: # ESC退出 break cap.release() cv2.destroyAllWindows()采集数量上我的建议是20张起步,快门按快一点,半分钟就能拍完。样本多样性比样本数量更重要——左脸、右脸、仰头一点、俯头一点、戴不戴眼镜各拍几张,比同一个姿势拍100张有用得多。
4. 数据库设计与核心代码实现:把门禁逻辑跑通的关键实现
4.1 从样本到模型:训练脚本与参数说明
样本采集完成后,要做一次模型训练,把每个人的脸部特征固化到model.yml文件里。训练脚本的任务是遍历样本目录,读取每一张图片、转灰度、检测人脸、缩放、提取特征并和用户ID关联。这一节是系统的“工厂车间”,门禁能不能认对人,取决于这一环做得好不好。
import cv2 import os import numpy as np # 样本根目录 sample_dir = "face_samples" faces = [] labels = [] # 遍历每个用户子目录 for user_folder in os.listdir(sample_dir): user_path = os.path.join(sample_dir, user_folder) # 文件夹名是 user_用户ID if not os.path.isdir(user_path): continue user_id = int(user_folder.split("_")[1]) for img_name in os.listdir(user_path): img_path = os.path.join(user_path, img_name) # 中英文路径统一处理,Windows下用imdecode避免中文乱码 img_array = np.fromfile(img_path, dtype=np.uint8) img = cv2.imdecode(img_array, cv2.IMREAD_GRAYSCALE) # 统一缩放,保持特征维度一致 img = cv2.resize(img, (200, 200)) faces.append(img) labels.append(user_id) # 创建并训练LBPH识别器 recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.train(faces, np.array(labels)) recognizer.save("model.yml") print(f"训练完成,共 {len(faces)} 张样本")训练完后,你会得到一个model.yml文件,这是一个文本格式的模型文件,里面保存了每个用户的LBPH直方图特征。识别时只需要加载这个文件,不需要再重新训练,启动速度很快。这里有个细节是样本图统一缩放成200x200,这个尺寸是精度和速度的折中,再大识别更稳但更慢,再小速度快但容易丢特征。
4.2 识别主循环:实时视频流与门禁控制
识别部分是整个系统的主程序,逻辑是:打开摄像头 → 逐帧检测人脸 → 用模型预测 → 根据置信度控制门禁 → 写入数据库。门禁控制在实际项目中一般通过串口或者GPIO控制继电器,但在源码包里用的是模拟方式——打印输出结果,方便在普通电脑上直接演示。
import cv2 import pymysql from datetime import datetime # 加载训练好的模型 recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read("model.yml") face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml") # 数据库连接 conn = pymysql.connect(host="localhost", user="root", password="123456", database="door_system", charset="utf8mb4") cursor = conn.cursor() def control_door(user_id, confidence): """模拟门禁控制,实际项目改为GPIO或串口信号""" if confidence < 60: cursor.execute( "INSERT INTO access_record (user_id, access_result) VALUES (%s, %s)", (user_id, "允许通行") ) conn.commit() print(f"用户 {user_id} 验证通过,门禁已打开") else: cursor.execute( "INSERT INTO access_record (user_id, access_result) VALUES (%s, %s)", (user_id, "未授权") ) conn.commit() print("验证失败,已记录未授权尝试") cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: print("获取视频帧失败") break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(80, 80)) for (x, y, w, h) in faces: face_roi = gray[y:y+h, x:x+w] face_roi = cv2.resize(face_roi, (200, 200)) # 预测用户ID和置信度 user_id, confidence = recognizer.predict(face_roi) control_door(user_id, confidence) # 在画面上画出人脸框和识别结果 cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) label_text = f"ID:{user_id} conf:{confidence:.1f}" cv2.putText(frame, label_text, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow("Door System", frame) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows()识别主循环里最关键的变量就是confidence这个阈值。你仔细看代码会发现,control_door函数里判断的是小于60才放行,而predict返回的置信度是“距离”,距离越小越像同一个人。这个60是经验值,换一个摄像头、换一种光照环境就必须重新校准。
4.3 新增用户与管理功能的实现思路
除了识别开锁,门禁系统还得有管理功能。最常见的需求是给新住户录入人脸,或者删除离职员工的记录。这套源码的思路是复用采集脚本加一个参数判断:录入时插入user_info表并采集照片;删除时先根据姓名查ID,再删除对应记录和样本文件。
删除用户的时候有一个数据库约束相关的坑:如果这个用户在access_record表里有记录,直接删除user_info会因外键约束失败。所以要先删子表记录再删主表记录,顺序不能反。
def delete_user(user_name): """删除用户,注意先删访问记录再删用户""" cursor.execute("SELECT id FROM user_info WHERE user_name = %s", (user_name,)) row = cursor.fetchone() if not row: print(f"用户 {user_name} 不存在") return user_id = row["id"] # 先删出入记录,否则外键约束报错 cursor.execute("DELETE FROM access_record WHERE user_id = %s", (user_id,)) # 再删用户记录 cursor.execute("DELETE FROM user_info WHERE id = %s", (user_id,)) conn.commit() print(f"用户 {user_name} 已删除")管理功能的核心价值在“关联性维护”。升级人脸特征需要重新训练模型,删除用户需要同时清理数据库记录和样本文件,这两件事但凡漏一个,系统就会产生“模型里有这个人,但库里找不到”或者反过来“库里有这个人,但模型不认他”的脏数据。建议每次做增删操作后,都重启一遍识别主程序,让模型和数据库重新对齐。
5. 避坑与常见排查:识别不准、乱码、摄像头离线三大难题
5.1 摄像头打不开:设备索引与资源释放问题
现象:运行采集脚本或识别主程序时,VideoCapture(0)返回False,或者程序直接卡死无响应。
原因:最常见的是设备索引不对。笔记本自带摄像头和USB摄像头同时存在时,索引0不一定是你想用的那个;另外,如果上一个程序没有正确释放摄像头资源,下一个程序就打不开同一个设备。
解决:先用一个临时脚本枚举所有摄像头索引,找到能用的那个,再把代码里的VideoCapture(0)改成对应的索引号。程序退出时一定要执行cap.release(),或者用try/finally保证资源被释放。
import cv2 # 枚举前3个摄像头索引,快速定位可用设备 for i in range(3): cap = cv2.VideoCapture(i) if cap.isOpened(): print(f"索引 {i} 可用") ret, frame = cap.read() if ret: cv2.imshow(f"camera {i}", frame) cv2.waitKey(500) cap.release() else: print(f"索引 {i} 不可用")我实际使用时遇到过一个更隐蔽的情形:摄像头被某个后台进程占用了。用VideoCapture的时候打开成功,但一读取画面就报错。后来发现是电脑上装的一个视频会议软件在后台挂着,把摄像头占了。关掉软件、杀掉残留进程之后,程序恢复正常。
5.2 识别准确率忽高忽低:光照与样本单一性的影响
现象:同一人站在摄像头前,有时候能识别,有时候识别不了,置信度波动很大,偶尔还出现认错人的情况。
原因:LBPH对光照变化非常敏感,光线从左边照和从右边照,人脸的局部二值模式直方图差异会很大。另外就是训练样本太“干净”——同一姿势、同一亮度拍了20张,遇到真实场景的光照变化就露馅。
解决:第一个层面是在图像预处理时加强直方图均衡化,让灰度分布更均匀,削弱光照影响;第二个层面是重采样本,刻意在不同时间段、不同角度、戴不戴眼镜各拍几张,把环境多样性做进去。
import cv2 def preprocess_face(face_roi): """人脸预处理:灰度转换 + 直方图均衡化 + 缩放""" # 输入已经是灰度图时跳过cvtColor gray = face_roi if len(face_roi.shape) == 2 else cv2.cvtColor(face_roi, cv2.COLOR_BGR2GRAY) # 直方图均衡化,增强对比度,缓解光照不均 equalized = cv2.equalizeHist(gray) # 统一尺寸 resized = cv2.resize(equalized, (200, 200)) return resized这个预处理函数在检测到人脸之后、送入predict之前调用一次,识别稳定性会有明显提升。还有一个容易被忽视的点:detectMultiScale检测出来的人脸框包含了一部分背景,如果背景特别杂乱或者人脸框里混入了衣服领子,特征里会掺进噪声。可以考虑把人脸框各方向内缩10个像素再裁剪,减少背景干扰。
5.3 中文乱码:Windows路径与MySQL字符集的双重陷阱
现象:训练样本时读取图片返回None,或者MySQL里显示的中文姓名变成一堆问号。
原因:这个问题是双重的,但根源一致——字符集处理不统一。Windows下OpenCV的imread不支持中文路径,路径里有中文就返回None;MySQL连接时如果不显式指定charset="utf8mb4",即使数据库表是utf8mb4,写入中文也会因为连接层字符集不一致而乱码。
解决:图像读取用np.fromfile配合cv2.imdecode绕开imread;MySQL连接参数固定加charset="utf8mb4"。这两个改动写进代码后,基本能杜绝中文相关的隐患。
import cv2 import numpy as np def read_image_unicode(img_path): """兼容中文路径的图片读取""" # fromfile读出来是字节流,imdecode解码成图像 img_array = np.fromfile(img_path, dtype=np.uint8) img = cv2.imdecode(img_array, cv2.IMREAD_COLOR) return img这个坑在Windows平台几乎必踩一次。我最初用cv2.imread读中文路径图片,返回的None导致后续所有操作全部报错,排查了半个小时才发现是路径编码的问题。从那以后,我在项目里所有涉及文件路径读取的地方,都统一走read_image_unicode这个函数,不再直接用imread。
5.4 置信度阈值拍脑袋定:识别结果和预期对不上
现象:换了一个环境后,之前在灯光良好时调好的阈值,现在要么放行太多陌生人,要么连本人都不给开门。
原因:LBPH的置信度本身是一个相对距离,它的大小受训练集规模、光照、人脸区域裁剪质量等多因素影响,并不存在一个万能的固定阈值。在不同运行环境下,同一个人的置信度可能从30漂移到70。
解决:把阈值调成一个可配置参数,放到配置文件或者系统开头,不要硬编码在业务代码里。调参的方法是采集一批“自己人”和“陌生人”样本,分别统计置信度分布,找到两类分布的分界点。这个工作花不了多少时间,但能避免很多现场翻车的尴尬。
# 把阈值放到配置区,方便现场调整 CONFIDENCE_THRESHOLD = 55 # 识别时先用预测置信度对比阈值 user_id, confidence = recognizer.predict(face_roi) if confidence < CONFIDENCE_THRESHOLD: # 放行逻辑 pass else: # 拒绝逻辑 pass我在实际项目中调整阈值的流程是:先采集20张本人测试样本,记录置信度最小值到最大值的区间;再采集20张同事的样本,记录置信度区间;最后取两个区间中间偏安全一侧的值作为阈值。这样做的逻辑是,宁可偶尔拒绝本人一次,也不能轻易放陌生人进来,门禁系统的安全性优先级高于便利性。
6. 自测准确率与两类差距:确认这套门禁真的能“上班”
源码跑通从来不是终点,验证才是。我的习惯是搭一个简单的自测脚本,把测试样本分成两类来看结果:一类是注册过的“本人”,一类是没注册过的“陌生人”。程序自动预测每一张测试图片,并统计通过率、拒识率和误识率。
import cv2 import numpy as np import os recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read("model.yml") # 分别统计四类结果:真正、假真、假拒、真拒 TP = FP = FN = TN = 0 # 测试集根目录,下面分known和unknown两个子目录 for category in ["known", "unknown"]: test_dir = os.path.join("test_set", category) for img_name in os.listdir(test_dir): img_path = os.path.join(test_dir, img_name) img_array = np.fromfile(img_path, dtype=np.uint8) img = cv2.imdecode(img_array, cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (200, 200)) label, confidence = recognizer.predict(img) is_known = (category == "known") if label != -1 and confidence < 55: if is_known: TP += 1 else: FP += 1 else: if is_known: FN += 1 else: TN += 1 print(f"正确放行(TP): {TP} 误放陌生人(FP): {FP}") print(f"漏放自己人(FN): {FN} 正确拒陌生(TN): {TN}")如果FP高,说明阈值太宽松,陌生人容易混进来,就往低了调;如果FN高,说明阈值太严格,自己人被挡在门外,就往高了调。这套验证流程下来,自己心里就有底了。
我在这套系统上踩过最深的坑就是阈值——最初用默认值80,结果公司同事一扫就开门,吓得我赶紧重测数据。后来我把调参流程固定成“先收集样本分布,再定阈值,最后跑一遍自测脚本确认”,每次部署新环境都强制走一遍这个流程,再没出过安全方面的幺蛾子。希望帮到你,愿你的门禁系统既认得出自己人,也挡得住陌生人。
本文还有配套的精品资源,点击获取