尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLO人脸检测与人脸计数实战:从模型训练到视频跟踪去重

YOLO人脸检测与人脸计数实战:从模型训练到视频跟踪去重 简介基于YOLOv5的人脸检测与人脸计数复现项目面向计算机视觉与深度学习学习者可帮助理解目标检测原理、YOLOv5网络结构及模型训练调优过程。资源包共1264个文件涵盖json标注、png/bmp/jpg图像、yaml配置文件、python训练与测试脚本、pt模型权重、mp4演示视频及docx过程说明文档等整体体积约340.72MB目录层次清晰便于按需定位和查阅。包内提供了从数据预处理、模型配置、训练验证到结果可视化与计数分析的一整套复现流程并附带TensorBoard日志、测试图像与可视化输出覆盖数据标注、训练评估和模型部署等环节可供读者对照实验记录逐步实践也能为其他目标检测任务提供迁移参考。目前已有1455人浏览学习适合希望快速上手YOLOv5并深入理解人脸检测与计数实现细节的开发者参考。1. 基于YOLO的人脸检测与人脸计数,复现的先决条件是分清两条计数链路人脸检测在目标检测里算得上最容易上手的场景:单类别、目标尺度固定、公开权重多。真正让问题变味的是人脸计数:一张照片里数清楚有几个人,统计检测框数就行;一段视频里统计累计出现过几个人,必须跨越几十上百帧给同一张脸维护临时身份,否则同一个人每一帧都被重复统计。复现代码跑不到理想结果,问题往往不是检出率不够,而是计数口径没定义清楚。基于YOLO也不等于直接调用API,它在单帧上只输出边界框和置信度,后端计数逻辑要自己设计。YOLO版本间的损失函数、后处理和训练参数差异也会直接影响复现成本。拿到一个打包好的复现包,先确认有没有人脸专用权重、数据集配置和环境依赖清单,再把图片和视频两条计数链路分别验证,下面把选型、实现、训练和验证依次讲透。2. YOLO版本选型、数据集与人脸检测的环境准备2.1 先回答YOLO第几代了:复现优先选 v8,而不是最新版复现者最先遇到的问题是YOLO第几代了。从 v5 到 v8 再到 v11,每一代都在改检测头和训练策略,但复现项目追求的是稳定性和可查性:能跑通、能改参、能导出。v8 的生态当前最成熟,Ultralytics 仓库把检测、训练、导出一条龙都封装好了,社区里与人脸检测和计数相关的示例代码大多基于 v8 接口,遇到报错时搜到的解决方案也最多。v11 在训练和推理效率上有改进,但对单类别人脸检测这个场景帮助有限,反而可能因为依赖新特性导致旧复现代码无法直接运行。YOLO 版本复现难度依赖封装社区示例量本场景推荐度v5低独立实现很多一般v8低Ultralytics 统一接口最多推荐v11中Ultralytics 新接口较少性能优先时可选我一般会固定使用 v8 的小模型yolov8n起步,原因有三个:复现包里的代码最可能对接这一套接口;n 模型在 CPU 上也能完成推理,便于先验证流程;换成 s、m、l 模型时只需要改一行权重路径,训练和推理代码不用动。如果拿到的 rar 包本身指定了版本,优先跟着包内requirements.txt或说明文档走,而不是擅自升级版本,否则很容易在 API 参数上出现不兼容。人脸检测和通用检测有一个容易被忽略的差别:COCO 权重里的 person 类检测的是整个人,不是脸。直接用yolov8n.pt数出来的是人头的数量,在人群照片里误差很大。复现人脸计数,权重必须来自人脸数据集,比如 WIDER FACE 上微调过的模型。这一点在环境配置之前就要明确,否则后面所有步骤都会建立在错误的预训练模型上。2.2 人脸计数用得最多的数据集与标注口径WIDER FACE 是复现人脸检测绕不开的数据集,包含 32203 张图像和大约 39 万个人脸标注框,图像来自 61 个真实社会场景,覆盖遮挡、尺度变化、密集人群和光照变化。数据集把验证集按难度分成 Easy、Medium、Hard 三个子集,复现完成后用这套子集做指标对比,能很快确认当前权重和主流结果的差距落在哪一档。标注口径上有个细节:WIDER FACE 的人脸框是带角度的旋转框,YOLO 默认输出 axis-aligned 的外接矩形框。转换时需要先计算旋转框的外接矩形,这个过程会把框的面积放大,引入背景噪声。简单场景问题不大,密集场景下相邻人脸的外接矩形容易彼此重叠,对后面 NMS 阈值的要求更高。path: widerface_faces train: images/train val: images/val nc: 1 names: [face]这是单类别训练时最基础的 data.yaml 配置。nc必须写成 1,names列表里也只有一个face。它决定了输出类别数、分类损失函数的维度以及后处理时的类别过滤行为。很多复现失败都发生在这一行:拿 COCO 的 80 类配置改一半就训练,分类损失维度对不上,训练不收敛。2.3 环境配置:从一张普通显卡到 CPU 推理给出一个可以直接照抄的安装序列。虚拟环境建议用 Python 3.10,和当前 Ultralytics 版本兼容性最好。python -m venv .venv source .venv/bin/activate pip install ultralytics pip install torch2.1.2 torchvision0.16.2python -m venv创建独立虚拟环境,避免依赖污染系统 Python;pip install ultralytics会连带安装 opencv-python、numpy、matplotlib 等依赖;torch 的版本需要和显卡驱动匹配。如果只是验证复现流程,CPU 版本的 torch 也能跑,推理速度从毫秒级变成秒级。AMD 显卡跑 YOLO 在近几年的复现中也常见,ROCm 对支持的显卡型号和驱动版本有明确列表,安装前先用rocm-smi确认显卡能被读到,再装对应版本的 torch,装完后用torch.cuda.is_available()验证,返回True说明环境通过。配置完成后直接跑一次预处理检查:yolo predict modelyolov8n.pt sourcetest.jpg。这一步不是为了确认识别准确率,而是验证 PyTorch、OpenCV 和模型文件完整。如果这里报错,优先排查显卡驱动的 CUDA 版本和 torch 版本是否匹配,而不是急着改代码。3. 复现核心流程:检测、后处理与人脸计数3.1 最小可运行代码:加载人脸权重、推理、输出检测数量假定复现包里的人脸权重文件叫face_det.pt,下面这段代码完成从加载模型到输出全部人脸框和计数的完整流程。from ultralytics import YOLO model YOLO(face_det.pt) # 人脸专用权重,单类别 results model.predict( sourcegroup_photo.jpg, conf0.25, # 置信度阈值,低于此值的框直接丢弃 iou0.45, # NMS 的 IoU 阈值 imgsz640, # 推理时图像的缩放尺寸 device0, # 0 表示第一张 GPU,CPU 用 -1 ) for r in results: boxes r.boxes.xyxy.cpu().numpy() scores r.boxes.conf.cpu().numpy() for x1, y1, x2, y2, score in zip(boxes, scores): print(f{x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f},score{score:.3f}) print(f检出人脸数:{len(boxes)})model.predict内部已封装图像预处理、特征提取、解码、NMS 和坐标还原。conf直接影响计数结果:调到 0.1 会捡回漏检的小脸,但也混入更多误检;调到 0.5 以上,密集人群场景会丢掉大量侧脸和小脸。iou值越小,对重叠框的抑制越强,相邻人脸靠得近时,两个真实人脸可能被合并成一个框。imgsz640是通用默认值,对 WIDER FACE 里偏小的人脸,可以提高到 960 或 1280,推理时间会成倍增加。如果复现包没提供专用权重,又没有训练条件,可以先用 WIDER FACE 预训练的人脸模型走通流程,至少在验证阶段不会出现把整个人当一个框数出来的问题。3.2 yolo后处理流程:解码、类别过滤、NMS 参数怎么调直接调用predict时,后处理对用户不可见。但模型一旦导出成 ONNX 或 TensorRT,后处理就要自己写。yolo后处理流程按顺序分四步:特征图解码、类别置信度计算、置信度阈值过滤、NMS。v5 的输出是三个尺度的特征图,每个网格预测多个 anchor,解码时要把预测偏移量加到 anchor 中心点再乘上缩放步长还原到原图坐标。v8 换成了 anchor-free 设计,每个位置直接预测到四条边的距离,解码公式更简单,手动实现如下。import numpy as np def decode_v8_head(preds, stride, conf_thres0.25): # preds: [batch, 4 num_classes, h, w],单图时移除 batch 维度 pred np.squeeze(preds, axis0) cls_scores pred[4:, :, :] # 分类分支 cls_max cls_scores.max(axis0) # 每个位置的最大类别分 mask cls_max conf_thres # 置信度过滤 boxes pred[:4, :, :][:, mask] # 被保留位置的坐标 return boxes.T, cls_max[mask]这段代码还原 v8 解码的核心:从特征图分离坐标分支和分类分支,分类分支取最大值作为该位置的置信度,按conf_thres过滤。复现时发现导出模型的输出全是乱框,绝大多数问题是解码时 stride 配错了尺度,或者是过滤阈值太低,让上千个低质量框一起进入 NMS。调参经验上,人脸场景的置信度阈值放在 0.25 到 0.4 之间,IOU 阈值放在 0.4 到 0.5 之间。两帧同一张脸在 NMS 后应尽量只保留一个框,计数阶段还要按位置跨帧关联,重叠框会严重干扰后续统计。场景conf 推荐值IOU 推荐值说明单张合影0.250.45默认,平衡漏检与误检视频密集人群0.350.50提高精确率,减少低质量框干扰小脸为主0.150.40牺牲部分误检率,捡回小脸3.3 计数逻辑:一帧数一次与视频去重是两条路静态计数只需统计图片中 NMS 之后保留的检测框数量。NMS 阈值从 0.45 改成 0.60,重叠严重的人群照片里计数结果可能相差 5% 到 10%,所以复现文档里必须记录 NMS 参数,否则指标不可比。视频场景的人脸计数必须解决身份一致性问题。YOLO 只能给每一帧的检测框,不能判断当前帧的某张脸和上一帧的某张脸是不是同一个人。不做跟踪的话,一段 10 秒的视频会被数出几百个人脸。常见做法是接一个多目标跟踪器,用 IOU 或外观特征做跨帧关联,给每个人脸一个临时 ID,计数时统计不同 ID 的数量,而不是每一帧的框数。import torch from boxmot import BYTETracker from ultralytics import YOLO model YOLO(face_det.pt) tracker BYTETracker(frame_rate30) # video_reader 是 OpenCV 逐帧读取的封装 for frame in video_reader: r model.predict(frame, conf0.25, iou0.45, device0)[0] dets torch.cat([r.boxes.xyxy, r.boxes.conf.unsqueeze(1)], dim1) tracks tracker.update(dets, frame) unique_ids {int(t[-1]) for t in tracks} # 每行最后一列是跟踪 ID print(f当前累积独立人数:{len(unique_ids)})BYTETracker是复现项目里常用的轻量跟踪器,先用高分框做 IOU 关联,再用低分框补充匹配,对遮挡的容忍度比单纯 IOU 匹配高。update每次接收当前帧的检测框和分数,输出带跟踪 ID 的轨迹。计数时对 ID 做集合去重,才能得到累计人数。最容易踩的坑是检测框抖动:同一张脸在相邻帧的位置变化超过跟踪器设定的最大匹配距离,会被误判成两个人,计数虚高,这个误差要用 5.2 里的跟踪指标来量化。4. 训练人脸检测模型:数据标注、训练参数与损失函数4.1 标注工具与 YOLO 数据格式的转换复现包没有现成权重,或者要针对自己的现场场景重新训练时,第一步是准备数据集。LabelImg 适合少量图片的桌面标注,CVAT 适合团队协作,Roboflow 能把不规范格式批量转成 YOLO 格式。无论用哪一款,最终产物都是和图片同名的 txt 文件,每一行代表一个目标:类别索引、中心点 x、中心点 y、框宽、框高,四个数值相对图片尺寸做归一化。0 0.523437 0.401042 0.102083 0.127083 0 0.710938 0.391667 0.093750 0.118750上面两个框对应一张 640x480 的图片,第一行表示一个目标中心在图片横向 52.3%、纵向 40.1% 处,宽度占 10.2%,高度占 12.7%。标注时所有框都要落在图片边界内,越界的坐标会被训练代码直接裁剪,看似无害,实际会使框的中心偏移。标注完成后按 8:2 划分训练集和验证集,目录结构保持和 2.2 里的 data.yaml 一致,WIDER FACE 数据也需要转成这个格式才能用于 YOLO 训练。4.2 yolo模型训练与必须改的参数给出可以直接抄用的训练命令,逐项解释关键参数。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience15 \ cos_lrTrue \ projectruns/train_facemodelyolov8n.pt从预训练权重开始微调,收敛比从零开始快很多;epochs100对人脸单类别通常足够,第 50 轮附近验证集精度基本不再上升;imgsz640是标准选择,图片中小脸居多时可改为 960,但 batch 要相应调小,否则显存溢出;batch16在单卡 12GB 显存下较安全,8GB 显存降到 8;lr00.01是微调常用初始学习率,从零训练时用 0.001 更稳;patience15是早停参数,验证损失连续 15 轮不下降就自动结束;cos_lrTrue让学习率按余弦曲线衰减,密集小目标场景比固定衰减更平滑。参数推荐值说明imgsz640 / 960小脸多时用 960batch8 / 168GB 显存用 8,12GB 以上用 16lr00.01微调时的初始学习率patience15验证损失连续 N 轮不降则停止cos_lrTrue余弦衰减,收敛更平滑训练过程中盯三个指标:每轮的 box_loss、cls_loss、dfl_loss。cls_loss 控制分类置信度,它持续下降但验证 mAP 不动,说明过拟合到了训练集的背景纹理;dfl_loss 负责分布焦点损失,和框的定位精度关系更大,小脸密集场景里 dfl_loss 不降,通常说明标注框本身有大量不一致。4.3 人脸检测的损失函数构成与常见调试手段YOLOv8 的损失函数由三部分组成。分类损失是 BCE 二分类,检测头对每个类别单独做逻辑回归;回归损失是 CIoU 结合 DFL,DFL 把框的四条边建模成概率分布再取期望,小目标定位更细腻,代价是 dfl_loss 的收敛曲线对标注质量非常敏感。密集场景漏检或重复计数时,先不要改网络结构,按顺序排查三件事。第一,确认训练集里小脸的数量,把训练尺寸提到 960 重新训练,常常比换模型有效。第二,检查标注框质量,WIDER FACE 转出的外接矩形在密集人群里互相遮盖,如果验证集里大量真实框和预测框的 IoU 只有 0.3,考虑放宽 NMS 阈值。第三,检查类别是否平衡,单类别人脸不存在类别不平衡,但如果错误保留背景标签,负样本远多于正样本,cls_loss 长期不收敛,这时应去掉背景类别并适当调低 conf 阈值。4.4 复现包里最常见的 3 个训练坑第一个坑是版本混用。训练时用的是 v8 的yolov8n.yaml,推理时加载的却是 v5 权重,后缀都是 .pt,内部结构完全不兼容,输出结果要么报错要么错乱。第二个坑是数据划分泄漏,WIDER FACE 同一场景被切进训练集和验证集,验证指标虚高,复现时按场景划分而不是按图片随机划分。第三个坑是早停参数设置过小,patience 设成 5 时训练会在第 20 轮被中断,模型远未收敛,复现不出论文指标,翻训练日志经常看到的是训练被提前终止,而不是参数配置错误。5. 从能数出来到数得准:指标验证与视频计数的进阶技巧5.1 用 WIDER FACE 三个难度子集做复现验收跑通画框和计数只完成一半。复现报告里的指标和实际效果对不上,是最常见的验收误差。图片检测方面,把 WIDER FACE 验证集按 Easy、Medium、Hard 三个子集分别跑一遍,统计 mAP0.5,能立刻看出权重在哪类场景上偏弱。Hard 子集有大量宽度不足 20 像素的小脸和严重遮挡,置信度阈值要放到 0.1 左右,配合 IOU 阈值为 0.4 的后处理,记录不同阈值下的计数总量,和人工标定的人脸数对比,偏差收敛到 5% 以内才算流程可用。5.2 yolo多目标跟踪的指标怎么得到视频计数单看检出帧数没有意义,要评估一张脸被重复数了几次,看的是跟踪质量。多目标跟踪指标里,HOTA 均衡检测和关联两个维度,IDF1 衡量 ID 是否被正确保留。计算方法是把跟踪器输出的轨迹整理成 MOT 格式的文本文件,每行包含帧号、ID、框坐标、置信度,再与人工标注的真实轨迹用公开评测脚本比对。IDF1 明显低于 mAP 时,说明检测正常但 ID 切换频繁,计数必然虚高,此时应调大跟踪器的最大失配帧数,并引入外观特征做关联,而不是只靠 IOU。5.3 给复现包补一个端到端的计数落盘脚本复现包里如果只有检测代码,我一般会补一个落盘脚本,让整个流程可验收:输入一段视频,输出 JSON,包含总帧数、独立人数和每帧的框坐标。脚本中记录每帧的检测耗时和跟踪耗时,便于对比不同硬件。import json result { total_frames: total_frames, unique_person_count: len(unique_ids), per_frame_boxes: per_frame_boxes, } with open(face_count_result.json, w) as f: json.dump(result, f, indent2)跑完一段 30 秒的视频,把输出的独立人数和逐帧最大框数分开看。独立人数明显小于单帧最大框数,说明去重有效;如果两者几乎相等,跟踪关联基本失效,先回到 3.2 检查 IOU 阈值和跟踪器的最大失配帧数。这一套做完,复现包的检测和计数链路才算真正闭环。本文还有配套的精品资源点击获取
返回列表