基于OpenCvSharp的WinForm人脸识别打卡系统开发实践
2026/9/13 14:30:23 网站建设 项目流程

简介:基于C#和Windows Forms的人脸识别打卡系统完整源码,面向需要学习桌面端人脸考勤开发与计算机视觉应用的开发者,尤其适合作为毕业设计或企业级应用起步模板。资源包共六十九个文件,大小约三点三兆,不仅包含源程序文件与动态链接库,还提供窗体界面布局、示例人脸图片、配置数据、SQLite数据库以及Visual Studio解决方案与安装部署工程,覆盖从编译到发布的完整路径,目录结构清晰便于逐模块学习。系统实现了人脸图像采集、灰度化、直方图均衡化等预处理,以及基于开源视觉库的比对识别,并接入数据库完成员工管理与打卡记录;代码中体现多线程异步任务、事件驱动模型、单例与工厂设计模式,以及异常处理和日志记录,可帮助深入理解C#桌面应用与视觉技术的整合方法。资源已有四百六十八人学习,适合具备基础C#知识、想快速上手人脸识别实战项目的开发者参考。

1. 用 WinForm 开发一个人脸识别打卡系统,先想清楚这几件事

也许你拿到的是一个“winform开发的人脸识别打卡系统.zip”的源码包,也许你正准备自己从头写一个。这类系统的本质并不复杂:通过 WinForm 窗体打开本机摄像头,将视频帧送入人脸检测模型,定位人脸后提取特征,把特征和历史模板比对,相似度超过阈值就把考勤记录写入数据库。真正让不少开发者卡住的点通常不在 AI 识别,而在摄像头被占用、UI 刷新卡顿、重复打卡、识别失败后的兜底、特征模板更新这些问题上。人脸识别门禁机可以靠固件解决,但 WinForm 打卡系统要靠代码逻辑一处处处理。这里不假设你手上已有源码,而是按可以重新实现的路径来讲,把选型、库表、核心代码和验证方法全部串起来。

2. 人脸识别打卡系统的技术选型与离线人脸模型的取舍

2.1 识别引擎:云 API、商用 SDK、开源免费商用的人脸识别模型

人脸识别打卡系统启动的第一步是确定“谁来做人脸比对”。这里的选项可以归为三类。

第一类是云 API。百度、腾讯、阿里都有现成人脸识别接口,很多培训项目会直接调 HTTP 接口。云 API 的优点是精度高、部署代码少,缺点是一张张人脸照片都要上传到外部,内网考勤环境很难接受,而且每次识别都有一定网络延迟,摄像头前有人流量时就容易被卡到等待上。

第二类是商用离线 SDK。在国内 WinForm 里看到最多的是虹软 ArcFace,Windows 平台提供本地 DLL,支持 C# 调用,完全离线运行。它的精度和速度都不错,但申请需要开发密钥,商用还要过厂商审核关,并且不同版本之间的模型文件不能混用。如果团队已经有授权,可以直接用这种方式;如果没有授权,先走开源模型的验证路线更省事。

第三类是开源免费商用的人脸识别模型,典型组合是 OpenCV 的 YuNet 人脸检测模型加上 SFace 特征提取模型。YuNet 负责找到脸,SFace 负责把人脸映射成一个 512 维的浮点特征,两者都有 ONNX 权重,OpenCvSharp 可以直接加载。很多人在搜“开源免费商用的人脸识别模型”时默认 OpenCV 仓库里的权重都是 BSD 协议,实际并非如此,模型文件带的是各自项目或训练数据的 License,企业交付前要逐项确认。

下面的表是一个比较实用的选型对照:

方案离线性识别精度WinForm 集成成本主要风险
云 API需联网数据出内网、按调用计费、网络延迟
商用离线 SDK完全离线授权/密钥、平台绑定
OpenCV YuNet + SFace完全离线中高小脸/大角度弱、需确认模型许可证
OpenCV LBPH完全离线最低光照和角度变化时误识率高

就“打卡”这个场景来说,通常正对摄像头、距离固定、光线可控,不像大门口的人脸识别门禁机那样要兼顾各种角度。所以我一般会把 YuNet + SFace 定为默认方案,先跑通功能,再用一个月实际打卡日志去验证误识率;如果后续客户要求更高精度,再替换成虹软 SDK,识别服务接口保持不变即可。

2.2 WinForm 摄像头采集:AForge.NET 和 OpenCvSharp 怎么选

人脸识别的实时视频源在 WinForm 里主要有两条路线。第一是 AForge.NET 的 VideoCaptureDevice,走 DirectShow,十多年前很多 WinForm 摄像头程序都是这么写的,资料很多,但库的维护基本停止,新的摄像头驱动兼容性只能靠运气。第二是 OpenCvSharp 的 VideoCapture,OpenCV 在 Windows 上会先尝试 Media Foundation,再回退到 DirectShow,兼容性更好,而且人脸检测和特征提取一样依赖 OpenCvSharp,可以在同一个 C# 类型体系里完成,不需要在 AForge 和 OpenCV 之间来回转 Mat。后面代码都是用 OpenCvSharp 写的。

安装包只要两个:OpenCvSharp4OpenCvSharp4.runtime.win。前者是托管程序集,后者是原生运行时,包含 opencv_world 的 DLL。需要留意项目目标平台和 runtime 包架构保持一致,32 位系统选 x86,64 位系统选 x64。如果编译平台是 AnyCPU,在部分 Windows 上会优先启动 64 位进程,但 runtime 里 DLL 没有被正确复制到输出目录,程序启动时就会抛出找不到 opencv_world 的异常。处理方法是把项目属性里“首选 32 位”取消,目标平台改为 x64,或者直接把输出目录里的 opencv 相关 DLL 手工复制到位。

摄像头接入成功后,还要考虑分辨率和帧率设置。用 VideoCapture 打开后,执行_capture.Set(VideoCaptureProperties.FrameWidth, 640)FrameHeight, 480,很多摄像头默认给的是 1280x720 甚至更高,识别一帧的耗时和像素数量不是线性关系,YuNet 内部也会把图像缩放到 320x320,做特征提取时还要把对齐后的人脸区域缩放,过高分辨率只会让 CPU 白白多烧。考勤场景里 640x480 足够看清人脸,也足够对齐模型。

如果要同时支持多个摄像头,可以把 VideoCapture 的构造函数参数做成一个下拉框,枚举 DirectShow 设备名,再根据名称索引打开。这里有一个常见细节:WinForm 程序关闭时如果不显式调用 Release 和 Dispose,摄像头的资源锁不会立刻释放,再次打开时会报“设备被占用”。所以窗体的关闭事件里一定要先停止定时器,再释放 VideoCapture。

2.3 WinForm 界面美化和布局:主题包与摄像头预览如何兼容

在开发打卡系统时,客户往往会拿“winform界面美化”和“c# winform主题实现的方法”作为需求点,希望界面看起来像现代 SaaS 系统。WinForm 实现美观界面更多靠第三方主题库和自定义控件,常见的 AntdUI、SunnyUI、MaterialSkin 都能改变窗体外框、按钮、输入框和消息弹窗的样式。但人脸识别画面是实时刷新内容,主题库不应该侵入 PictureBox 的绘制流程。

我的布局方案是把主窗体拆成表格布局:左侧占约三分之二的摄像头预览区,右侧是一个上下叠放的结果面板。预览区用 Panel 固定比例,PictureBox 的 SizeMode 设为 Zoom,保证不同分辨率摄像头都不会变形;右侧的结果面板用 Label 显示姓名、部门、打卡时间和相似度,底部留出考勤按钮。整个布局用 TableLayoutPanel 这个 WinForm 布局器来控制,窗口缩放时预览区和面板宽度按比例变化,不写固定像素值,也不为每个控件写 Resize 事件。

主题库通常负责全局字体颜色和控件样式,不参与摄像头帧绘制。如果要用 AntdUI 的弹出输入框,最好把它做成独立对话框,在识别失败并需要手工补卡时调用,避免弹出动画和摄像头刷新线程互相抢占 UI 队列。记住一点:WinForm 打卡系统的界面本质是“状态反馈”,打开摄像头时就显示“识别中”,识别成功就立刻把名字和考勤时间放大显示出来,这才是用户最想要的美化。

3. 考勤打卡系统的人脸特征存储与数据库设计

3.1 员工表、人脸特征表、打卡记录表的字段设计

人脸识别打卡系统做的时间长了,最怕的不是识别模型带不动,而是数据表设计太随意。一开始只有一个打卡记录表和员工字段,后来要加部门、转岗、人脸模板重新录入,就会越改越乱。所以建表要按“员工实体、人脸实体、考勤事件”三个维度拆开。

先给出一套可以直接在 SQLite 里运行的 DDL:

CREATE TABLE Employee ( EmployeeId INTEGER PRIMARY KEY AUTOINCREMENT, UserName NVARCHAR(50) NOT NULL, Department NVARCHAR(100), CardNo NVARCHAR(20), PhotoPath NVARCHAR(200), Status INTEGER DEFAULT 1 ); CREATE TABLE FaceTemplate ( EmployeeId INTEGER PRIMARY KEY, FeatureData BLOB NOT NULL, FeatureDim INTEGER DEFAULT 512, UpdateTime DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (EmployeeId) REFERENCES Employee(EmployeeId) ); CREATE TABLE AttendanceRecord ( RecordId INTEGER PRIMARY KEY AUTOINCREMENT, EmployeeId INTEGER NOT NULL, PunchTime DATETIME, PunchType INTEGER, ImagePath NVARCHAR(200), SimilarityScore REAL, IsValid INTEGER DEFAULT 1, FOREIGN KEY (EmployeeId) REFERENCES Employee(EmployeeId) );

单独把 FaceTemplate 放在一张表里,而不是和 Employee 的字段混在一起,是因为人脸模板可能因为员工拍照背景变化、换发型或更换识别模型而重新生成。每次重新注册人脸时,只需要 UPDATE FaceTemplate,不需要动 Employee。FeatureData 用 BLOB 保存 SFace 模型产出的 512 维 float,启动时一次性读入内存列表;AttendanceRecord 表中的 ImagePath 保存抓拍原图,没有考勤争议时不需要频繁查询,只有报表回看时才去读图片文件。

字段的取舍还要注意到“时间”和“类型”的区分:PunchTime 用 DATETIME,不要拆成年月日时分秒五个字段;PunchType 用整数表示上下班,取值和考勤规则对应,在代码中使用枚举而不是散落的魔法数字。迟到、早退、缺勤这些结果不要在打卡时立即算定,因为企业考勤规则经常变化,最好由独立的报表模块基于 PunchTime 和员工的排班表计算,打卡系统只负责准确记录。

3.2 注册流程与识别打卡流程:从模板录入到重复打卡拦截

考勤打卡系统功能设计要把两个核心流程定义清楚:人脸注册流程和日常打卡流程。

注册流程不只是一个“拍一张照、存进去”的动作。人在摄像头前不可能每个角度都正好面对镜头,所以注册至少要采集三张到五张不同姿态的图,逐张做检测和特征提取,把得到的特征都存进 FaceTemplate。由于一个 EmployeeId 对应多个模板,如果 FaceTemplate 表继续使用 EmployeeId 主键,就无法满足多模板,需要改成自增模板 Id。实际操作中,我会把模板表扩展为FaceTemplateId INTEGER PRIMARY KEY AUTOINCREMENT,再给 EmployeeId 建普通索引,查询时把某个员工的所有模板一次性加载。

注册时还要检查人脸质量。最简单的质量检查包括人脸框最小尺寸、图片亮度均值和对焦模糊度。人脸框小于摄像头高度三分之一时,提醒“请靠近一点”;亮度均值低于 80 或高于 200 时,提醒光线不足或过曝;模糊度可以用 Laplacian 方差粗略判断。注册阶段卡得严一点,识别阶段就能少很多误判。

日常打卡流程则要解决三个具体问题。第一是识别速度:启动时把全部特征模板读入内存,不要在每帧识别时查数据库。第二是识别成功后如何写记录:写记录前按EmployeeId + 当日日期 + PunchType查一次库,存在记录则只刷新界面提示“今日已打卡”,不存在才插入新记录。第三是识别失败后的重试:连续识别超过五秒没有结果时,把画面缩到小窗,展示“请重试”并给出擦镜头、调整角度的提示,避免员工以为系统死机。

3.3 为什么人脸特征向量要单独存,而不是只存照片

很多早期项目只在数据库里保存摄像头抓拍的图片,甚至有人把识别逻辑做成 OpenCV 模板匹配,在文件夹里用图片做相似查找,这是非常不可靠的。照片受像素、光照和压缩影响,识别时要反复读取文件做特征提取,速度慢且误率高。人脸特征向量把一张脸压缩到固定维度的浮点数组,比对本质是计算两个数组的余弦相似度,在几千个模板范围里,毫秒级就能完成。

保存特征向量,同时保留照片,是最优组合。FaceTemplate 负责识别,Employee.PhotoPath 和 AttendanceRecord.ImagePath 负责人工回溯。如果识别模型从 YuNet 更换成另一个网络模型,只需要清空 FaceTemplate 表,重新给全体员工采集一次特征,历史打卡记录完全不用动;如果只存照片,这种迁移就要重新训练和重写比对逻辑。

模板更新还有一个容易被忽略的细节:员工每天在不同时段来打卡,拍摄到的照片质量会慢慢变化。系统最好定期把“高相似度但没达到阈值”的抓拍保存下来,供管理员挑选后更新模板。用这样渐进式的模板更新,对抗胡须、发型、眼镜等外貌变化,比调整识别阈值更有效。

4. WinForm 人脸识别打卡系统的核心代码:摄像头帧、人脸检测与特征比对

4.1 使用 OpenCvSharp 打开摄像头并渲染到 PictureBox

这一节给出一个可运行的最小 WinForm 摄像头窗口。实现目标:点击按钮启动摄像头,VideoCapture 持续读取帧,画面显示在 PictureBox 上。代码如下:

using System; using System.Drawing; using System.Windows.Forms; using OpenCvSharp; using OpenCvSharp.Extensions; public partial class FrmCamera : Form { private VideoCapture _capture; private Timer _timer; public FrmCamera() { InitializeComponent(); _timer = new Timer(); _timer.Interval = 50; _timer.Tick += TimerOnTick; } private void BtnStart_Click(object sender, EventArgs e) { if (_capture != null) { MessageBox.Show("摄像头已经在运行"); return; } _capture = new VideoCapture(0); _capture.Set(VideoCaptureProperties.FrameWidth, 640); _capture.Set(VideoCaptureProperties.FrameHeight, 480); if (!_capture.IsOpened()) { MessageBox.Show("无法打开摄像头,请检查驱动或设备占用"); _capture.Dispose(); _capture = null; return; } _timer.Start(); } private void TimerOnTick(object sender, EventArgs e) { using (var frame = new Mat()) { if (!_capture.Read(frame) || frame.Empty()) return; using (var bmp = BitmapConverter.ToBitmap(frame)) { var old = pictureBox.Image; pictureBox.Image = (Bitmap)bmp.Clone(); old?.Dispose(); } } } protected override void OnFormClosing(FormClosingEventArgs e) { _timer.Stop(); _capture?.Release(); _capture?.Dispose(); _capture = null; base.OnFormClosing(e); } }

代码后要解释的参数:new VideoCapture(0)的索引 0 表示系统默认摄像头;如果电脑同时有内置和 USB 外接摄像头,外接摄像头可能对应 1 或 2,可以把索引暴露给设置页面,启动前由用户选择。FrameWidthFrameHeight设置为 640×480,主要为了让大多数集成摄像头发送 MJPEG 或 YUY2 格式而不是巨大负载的原始帧,识别模型也会在内部降采样到 320×320,太高分辨率只会浪费 CPU。_timer.Interval = 50是 20fps,在不做识别时已经够顺畅,做识别时可以把 Interval 调到 66,避免 UI 线程排队。

关于BitmapConverter.ToBitmap的释放:每次 Timer 事件都创建新的 Bitmap,旧图必须 Dispose。直接用pictureBox.Image替换并释放旧图,可以减少 GDI 句柄泄漏。更成熟的方式是使用双缓冲 Bitmap,不过这里为了展示原理先把代码保持最小化。

4.2 加载 YuNet 与 SFace 模型,封装人脸识别服务

摄像头画面只解决了“能看到人”,下一步是把检测和识别封装成一个服务类。假设模型文件已经放在Application.StartupPath\Models目录下,模板列表由数据库加载并装入内存:

using OpenCvSharp; public class FaceRecognitionService : IDisposable { private readonly FaceDetectorYN _detector; private readonly FaceRecognizerSF _recognizer; private readonly List<(int EmployeeId, float[] Feature)> _templates; public FaceRecognitionService(string yunetPath, string sfacePath, List<(int, float[])> templates) { var modelSize = new Size(320, 320); _detector = FaceDetectorYN.Create(yunetPath, "", modelSize); _detector.setInputSize(new Size(640, 480)); _recognizer = FaceRecognizerSF.Create(sfacePath, ""); _templates = templates; } public (int EmployeeId, float Score)? Recognize(Mat frame, float threshold = 0.363f) { using var faces = new Mat(); _detector.detect(frame, faces); if (faces.Rows == 0) return null; float bestScore = threshold; int bestEmployeeId = -1; for (int i = 0; i < faces.Rows; i++) { using var aligned = new Mat(); using var feature = new Mat(); _recognizer.alignCrop(frame, faces.Row(i), aligned); _recognizer.feature(aligned, feature); float[] current = feature.GetArray<float>(); foreach (var (empId, template) in _templates) { float score = CosineSimilarity(template, current); if (score > bestScore) { bestScore = score; bestEmployeeId = empId; } } } return bestEmployeeId == -1 ? null : (bestEmployeeId, bestScore); } private static float CosineSimilarity(float[] a, float[] b) { double dot = 0, na = 0, nb = 0; for (int i = 0; i < a.Length; i++) { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i]; } return (float)(dot / (Math.Sqrt(na) * Math.Sqrt(nb) + 1e-10)); } public void Dispose() { _detector?.Dispose(); _recognizer?.Dispose(); } }

参数说明要写到关键位置:FaceDetectorYN.Create的第一个参数是 ONNX 文件路径,第二个参数是配置字符串;在 OpenCvSharp 里配置文件通常传空字符串即可。模型输入尺寸分别调成 320×320 和 640×480,是因为 YuNet 支持动态输入,构造时用 320×320 做初始输入,调用 setInputSize 后按当前帧的具体尺寸设置,检测速度在一帧中大约 10ms 到 40ms,取决于 CPU 和分辨率。

faces.Row(i)取出的是一个人的检测结果,alignCrop会根据人脸关键点把人脸做仿射对齐,消除侧脸带来的角度误差,然后feature方法输出一个浮点向量。这里特别强调:如果省略alignCrop,把原始人脸图直接送进 feature,识别准确率会下降很多,因为这已经是模型训练时约定好的输入格式。

阈值 0.363 来自 SFace 在公开数据集上的常用推荐值,但它不是绝对正确的。实际项目里要先用一个视频样本集跑一遍,观察同类员工的相似度分布和不同员工的相似度分布。这两个分布的重叠区才是需要调参的区域。若重叠很大,单纯调阈值解决不了,要从注册质量、摄像头角度、光线三方面找原因。

4.3 识别完成后的打卡落库:异步写入和重复判断

识别服务返回(EmployeeId, Score)后,打卡主窗体要做三件事:显示识别结果、去重判断、异步落库。去重判断如果在数据库查询阻塞里做,摄像头帧率就会被拖慢,所以可以把某个员工“当天已打”的状态放在内存的一个 ConcurrentDictionary 里,key 是EmployeeId + PunchType,value 是打卡时间。每个自然日零点清理一次,这样大多数重复打卡在内存里就能拦截掉,只有缓存记录不存在时才回头查数据库。

落库使用如下逻辑:

async Task InsertAttendanceAsync(AttendanceRecord record) { await Task.Run(() => { using var conn = new SQLiteConnection(_connectionString); conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = @" INSERT INTO AttendanceRecord(EmployeeId, PunchTime, PunchType, ImagePath, SimilarityScore, IsValid) VALUES(@empId, @time, @type, @img, @score, 1)"; cmd.Parameters.AddWithValue("@empId", record.EmployeeId); cmd.Parameters.AddWithValue("@time", DateTime.Now); cmd.Parameters.AddWithValue("@type", record.PunchType); cmd.Parameters.AddWithValue("@img", record.ImagePath ?? ""); cmd.Parameters.AddWithValue("@score", record.Score); cmd.ExecuteNonQuery(); }); }

代码逻辑并不复杂,真正要注意的是 SQLite 的并发写。多个打卡操作同时执行 INSERT 时,SQLite 会返回 database is locked。正确做法是把所有写操作放入一个队列,由一个后台线程串行执行。界面上的识别线程只负责入队,不直接访问数据库连接;这样既避免卡顿,又避免文件锁冲突。

此外,ImagePath 保存抓拍图片时要使用相对路径,推荐按日期分成目录,例如AttendanceImage\20250408\12_20250408_084512_emp001.jpg。图片文件的管理权属于考勤系统本身,数据库只存字符串,不会让 SQLite 文件变得膨胀。每张图片都要定期清理,比如保留六个月,超过期限的归档到备份磁盘。

5. 把 WinForm 人脸识别打卡系统调整到能日常用的三个细节

5.1 用帧间差分做一个轻量活体检测

在人脸识别打卡系统中,最粗暴的攻击方式是一张手机照片放在摄像头前。离线方案中不方便频繁调用商用活体 SDK,简单的帧间差分就有过滤效果。原理是:照片是静帧,真人身体会有微小的晃动,相邻帧取人脸区域做灰度差,运动帧数量达到阈值才认为是一次真人打卡。代码示例到这个环节都在同一个思路:

public bool CheckLiveness(IEnumerable<Mat> facePatches) { Mat previous = null; int motionFrames = 0; foreach (var patch in facePatches) { using var gray = new Mat(); Cv2.CvtColor(patch, gray, ColorConversionCodes.BGR2GRAY); Cv2.Resize(gray, gray, new Size(64, 64)); if (previous != null) { using var diff = new Mat(); Cv2.Absdiff(previous, gray, diff); if (Cv2.Mean(diff).Val0 > 3.0) motionFrames++; } previous = gray.Clone(); } return motionFrames >= 2; }

这个函数接收连续若干帧的人脸区域切图,要求至少有 5 到 8 帧,如果里面有两帧的像素差均值超过阈值,就认为是活的。阈值 3.0 是 64×64 灰度图下的经验值;摄像头分辨率更高时先 Resize 到 64×64,阈值不需要变。若画面光线快速变化,裸差分也会误判,所以在注册时要顺便记录当时的亮度,打卡时发现亮度突变就要求重新调整位置。

5.2 识别失败之后的兜底:员工工号输入与人工补卡

即使识别模型和活体检测都正常,还是有员工会突然遮挡摄像头、口罩戴得不规范,或者发型变化导致相似度不过。打卡系统必须有降级通路。常见做法是连续识别失败超过 3 次后,在界面右下角显示“人工打卡”按钮,点击后用与主题统一的输入框录入工号。如果使用了 AntdUI,这种弹出输入框可以直接复用它的 InputDialog 组件。

人工打卡录入的工号会走和正常识别相同的数据写入逻辑,只是 ImagePath 字段记录manual,SimilarityScore 写入 -1,IsValid 保留 1。这样报表统计能把人工补卡和自动识别区分开,管理员每天核对时重点看 SimilarityScore=-1 的记录。考勤系统不应该把员工卡在门口去“锻炼识别率”,兜底通道通常比调高模型精度更直接。

5.3 阈值校准和误识率验证:用真实视频采集数据

每天跑完一轮识别之后,要能判断系统阈值是否合适。验证方案并不复杂:选择 5 到 8 个测试者,每个人录制正面、左右侧、带眼镜、不带眼镜、暗光、亮光八段视频,再用员工库里的全部模板做比对。容易出现的两种情况是:一个员工多次被系统认成另一个员工,说明阈值太低,要观察误识相似度集中在哪个区间,再把阈值往上调;员工本人一天内频繁被拒,正确识别率低于 90%,就应该先补录更多角度模板,再看是否需要下调阈值。

现象可能原因调整方向
陌生人误识别成员工阈值太低调高阈值,例如从 0.36 调到 0.45
员工本人被拒注册照片太少补充多角度模板,再考虑调低阈值
上午正常,下午频繁失败光线变化大优先补光,而不是修改全局阈值
某台摄像头打分普遍偏低摄像头分辨率或对焦问题单独校准或更换摄像头

日常运维记录里,人工补卡数量占全部打卡记录的比例,是比单个识别分数更可靠的指标。如果一周内这个比例超过 5%,先不要动模型参数,去检查摄像头安装高度、环境光线和注册照片质量;只有在这些环境因素都正常后,再回到阈值参数表做微调。把每次阈值调整和对应的误识率变化记录下来,形成项目自己的调参日志,后续维护者也就不会被“官方阈值”架住手脚。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询