多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

基于深度学习的人流量检测系统毕设实战:从模型训练到部署

基于深度学习的人流量检测系统毕设实战:从模型训练到部署 简介这是一份面向计算机专业毕业设计的高分项目基于深度学习实现人流量检测系统包含完整Python源码与项目说明。整套资源不仅适合正在筹备毕设的学生也可用于课程设计或期末大作业核心代码经过严格调试可正常运行。压缩包共1235个文件约61.54MB主要包含Python脚本、前端页面、图片样本以及说明文档等类型其中html/js/css等前端资源数量丰富便于在浏览器中直观查看检测界面py/pyc文件对应模型训练、检测逻辑与后端服务。已有264人学习/下载适合作为项目实战参考。从文件结构来看资源覆盖了从界面展示、后端接口到算法模型的完整链路并附有项目说明、配置文件和运行依赖可帮助读者搭建本地环境、复现检测流程也能为论文撰写和答辩展示提供基础素材。1. 基于深度学习的人流量检测系统这套毕设源码到底能不能直接跑做毕设选人流量检测方向最怕的就是拿到一份“看起来完整”的源码结果配环境配了两天跑起来全是玄学报错。我拆这份项目的时候先看了它的核心链路深度学习目标检测模型负责把画面里的人框出来后端再做计数和统计最终落到一个可视化界面上。这套资源定位很清楚就是给计算机相关专业做毕设、课设用的代码经过调试能运行项目说明也不少适合想快速落地一个“能演示、能答辩”系统的人。它解决的痛点很实际你不必从零搭建模型也不用纠结数据标注流程拿到手主要工作是读懂代码、调好参数、把数据换成自己的场景。适合的人群是正在赶毕设的学生、想练手目标检测实战的初学者以及需要一份完整工程作为参考的在职学习者。我建议你先不要急着跑训练先把整份代码的目录结构、模型选型、数据组织方式过一遍再决定从哪里下手。这份心态能帮你省掉后面大量“跑不通”的时间。2. 从模型选型到代码结构先搞懂这个系统是怎么拼起来的2.1 为什么选深度学习目标检测做人数统计人流量检测本质上是一个目标检测任务输入一帧画面输出画面里每个人的位置框然后统计框的数量。传统做法用背景差分法或HOGSVM但在人群密集、遮挡严重的场景下效果很差背景一乱、光照一变漏检和误检就失控了。换成深度学习模型之后检测精度和泛化能力明显上了台阶这也是毕设评审老师最容易认可的“技术含量”所在。这套系统走的主流路线是两段式检测模型负责出框后处理逻辑负责计数、跨帧去重。检测模型可选YOLO系列因为YOLO在精度和速度之间平衡得最好CPU勉强能跑有GPU的话实时性完全够用。对于毕设场景不需要用太高阶的模型YOLOv5或者YOLOv8的nano/small版本就已经足够既不会让显存爆炸也能在答辩现场用普通笔记本做一次“半实时”演示。选YOLO还有一个非常现实的理由生态成熟。预训练权重大量公开标注工具、格式转换脚本、部署文档都齐遇到问题搜得到解决方案。相比自己写一个Faster R-CNN的训练管线YOLO的工程化程度能让你少熬好几个夜。2.2 代码目录功能拆解每个目录是干什么的拿到压缩包解压之后你第一眼看到的结构应该和大多数深度学习工程类似但有几个关键目录决定了你的工作路径。我拆解后的典型结构如下表所示目录/文件职责你需要关注的程度detector/模型定义、推理逻辑封装高决定检测效果data/数据集存放、标注文件高换成自己的数据主要改这里utils/工具函数、可视化、指标计算中打印日志和画框用train.py训练入口脚本高调参主战场inference.py单张图片/视频推理入口高答辩演示靠它web/或ui/简单可视化界面中有界面演示效果更好configs/模型配置、超参数配置高改参数别改代码requirements.txtPython依赖清单高环境装不上先看这里打开代码时你还会看到一堆和核心功能无关的文件比如controller.ashx、UploadHandler.cs、CrawlerHandler.cs之类这是打包时把某个网页上传组件混进来了和深度学习没有任何关系直接忽略即可不影响系统运行。建议你拿到代码第一件事全局搜索import torch或from ultralytics确认模型框架是哪一套再找到config文件确认默认的模型权重路径。这两步做完你对整个项目的运行方式就有底了。2.3 推理链路从一帧图片到人数数字理解这套系统的代码关键要读懂一条推理链路图像读取 → 预处理resize、归一化 → 模型前向推理 → 输出解析坐标框、置信度 → NMS去重 → 计数统计。每一步在代码里都有对应的函数读代码时按这个链路捋基本不会迷路。import cv2 import torch # 加载模型weights 换成你训练好的权重文件路径 model torch.hub.load(ultralytics/yolov5, custom, pathweights/best.pt, force_reloadTrue) # 读取一帧图像 img cv2.imread(data/test.jpg) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 模型推理size 控制输入分辨率越大越慢但小目标召回更好 results model(img_rgb, size640) # 提取检测框、置信度、类别 boxes results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls] person_boxes [box for box in boxes if int(box[5]) 0] # COCO 中 person 类别 id 为 0 print(f当前画面人数: {len(person_boxes)})这段代码是完整的推理骨架size640是输入分辨率通常用默认值就可以如果你的摄像头画面里人比较小可以调到size800代价是每帧推理时间变长。类别过滤那里注意 COCO 数据集里 person 的类别 id 是 0很多人跑通了模型但计数全是 0就是忘了过滤类别把所有目标都算进去了。用torch.hub加载模型的优点是代码简洁但缺点是它依赖网络下载配置。如果你离线环境跑建议改成直接加载本地权重model torch.hub.load(ultralytics/yolov5, custom, pathweights/best.pt, force_reloadTrue, local_files_onlyTrue)加上local_files_onlyTrue之后torch hub 不会再尝试联网能避免不少墙内网络超时的尴尬。3. 把数据集换成自己的场景标注与格式转换完整操作3.1 标注工具选择与标注规范毕设系统要演示“实际效果”最有效的办法是拿自己拍摄的视频或图片来做测试。用现成COCO预训练模型测照片效果不会差但答辩老师可能会追问“你的模型在什么数据上验证过”。所以最好准备一小批自己的数据做几十张标注微调一轮让系统“看起来”真的适配了你的场景。我建议你控制标注量200张图片起步500张以内就能满足课堂场景的微调。场景尽量覆盖不同光照、不同人群密度、不同角度避免模型过拟合到单一环境。标注工具我常用LabelImg纯Python环境装起来方便标注完直接保存成VOC格式的XML文件。如果你追求更快用X-AnyLabeling 或者 Roboflow 在线标注也可以但导出格式要注意兼容。标注规范只有一个硬性要求人的类别名称统一写成person不要出现people、human这种同义词否则格式转换脚本会丢类别。3.2 VOC格式转YOLO格式转换脚本与四个边界坑YOLO系模型训练需要的是TXT格式的标签每一行表示一个目标格式是class_id x_center y_center width height坐标都是相对于图片宽高的归一化值。标注工具默认输出VOC的XML格式所以转换这一步几乎绕不开。import xml.etree.ElementTree as ET import os from pathlib import Path def convert_voc_to_yolo(xml_file, out_dir, classes): tree ET.parse(xml_file) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) out_lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bndbox obj.find(bndbox) x_min float(bndbox.find(xmin).text) y_min float(bndbox.find(ymin).text) x_max float(bndbox.find(xmax).text) y_max float(bndbox.find(ymax).text) # 计算归一化的中心点坐标和宽高 x_center (x_min x_max) / 2.0 / img_w y_center (y_min y_max) / 2.0 / img_h width (x_max - x_min) / img_w height (y_max - y_min) / img_h # 边界检查坐标不能超出 [0, 1]超出说明标注框出界了 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) width min(max(width, 0.0), 1.0) height min(max(height, 0.0), 1.0) out_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if out_lines: txt_name Path(xml_file).stem .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(out_lines)) classes [person] xml_dir data/annotations out_dir data/labels os.makedirs(out_dir, exist_okTrue) for xml_file in Path(xml_dir).glob(*.xml): convert_voc_to_yolo(str(xml_file), out_dir, classes)这个脚本我实际用过多次有四个边界问题你大概率会遇到。第一个标注框坐标没有归一化导致训练直接报错本质上是因为某些标注工具导出时用的是裁剪后的坐标而你读取的图片尺寸是原图。解决方法是确保xml里的size字段和实际图片分辨率一致。第二个标注框超出图片边界坐标值大于1会导致训练时的loss变成nan所以代码里必须做clamp操作。第三个一张图里如果全是空标注YOLO训练会忽略该图这是正常的不用删图。第四个标注文件里混入了非目标类别的对象比如把椅子也标上了训练时会拼命把椅子当人精度上不去。3.3 数据集目录组织与训练验证集划分YOLO训练对目录结构有默认要求我一般按下面的方式组织data/ ├── images/ │ ├── train/ │ ├── val/ ├── labels/ │ ├── train/ │ └── val/ ├── data.yamldata.yaml内容要注意路径写绝对路径还是相对路径YOLOv5 默认支持相对路径但我个人建议写绝对路径因为很多人在训练时翻车就是路径没写对。# data.yaml train: data/images/train val: data/images/val nc: 1 names: [person]划分比例按 8:2 就够200张图的话 160张训练、40张验证。关键点图片和标注文件必须同名且图片必须放在images目录、标注放在labels目录YOLO的Dataset类会自动根据图片路径找对应的txt文件如果你放错位置它会跳过那些图训练集数量会莫名其妙少一截。4. 训练与调参把模型调到能过答辩的水平4.1 训练脚本与关键超参说明训练这一步是整套系统里最容易翻车也最玄学的环节。资源里自带的训练脚本一般情况下可以直接跑但你要理解每一行在干什么才能填对参数。典型训练启动命令如下python train.py --data data/data.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640 --device 0--data指定数据集配置文件--weights指定预训练权重--epochs训练轮数--batch-size取决于你的显存--img训练输入分辨率--device 0表示用第一张显卡CPU训练就写--device cpu。参数选型经验如果显存只有8G--batch-size不要超过16--img从640开始如果你的图片里人特别小可以试试--img 1280显存直接翻四倍一般设备扛不住那就先从数据侧下手——把图裁成小区域再标注。--epochs我一般从100开始训练微调的时候50就够了因为之前有预训练权重。如果你发现验证集mAP一直在50左右上不去优先调--hyp里的lr0很多情况下是学习率太高导致loss震荡。训练过程中要看几个关键指标box_loss、obj_loss、cls_loss在训练集和验证集上的曲线以及 mAP0.5。如果训练集的loss一直降但验证集mAP不涨八成是过拟合了把--epochs降下来或者给数据加一点增强。如果两个loss都不降大概率是数据集标签格式问题回上一章检查txt内容。训练完成后会在runs/train/exp*/weights/下生成best.pt和last.pt从命名就能猜到两者区别评估和推理永远用best.pt不要贪方便用last.pt。4.2 模型轻量化考量CPU能不能撑住实时推理毕设评审现场不一定有GPU很多时候你只能用笔记本CPU做演示所以模型轻量化是一个必须提前考虑的问题。YOLOv5s 在CPU上的推理速度大约是300到500毫秒每帧对于一个人流量统计系统这只能算“准实时”用来做视频演示会有点卡顿。我的建议是训练时直接从yolov5s.pt开始微调不要用yolov5m或yolov5l。如果你的CPU比较弱训练完之后做一步模型剪枝或蒸馏把模型尺寸进一步压缩。但剪枝操作有一定复杂度时间紧的话更稳妥的办法是降低输入分辨率。把--img从640降到416推理时间差不多缩短一半精度损失在密集场景下才会比较明显做课堂演示基本看不出差别。另外一个常见技巧是把模型导出成TorchScript或ONNX格式在CPU上的推理速度会有肉眼可见的提升。导出方法import torch model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt) model.model.model[-1].export True torchscript_model model.module if hasattr(model, module) else model torch.jit.save(torchscript_model, weights/best.torchscript)导出完在部署脚本里直接用torch.jit.load加载推理方式和原来一致不需要额外改代码。如果只做演示不追求极致速度这一步可以留着最后做优先把检测效果做到位。4.3 训练结果验证mAP指标怎么读训练完不是看训练集loss也不是直接跑视频而是先跑验证集指标。运行python val.py --data data/data.yaml --weights runs/train/exp/weights/best.pt输出里重点关注mAP0.5和mAP0.5:0.95。对于人流量检测任务mAP0.5能达到0.85以上就算很理想0.8以上可以接受。如果只有0.5到0.6说明模型还没有收敛需要加训练轮数或检查数据。mAP0.5:0.95是更严苛的指标这个数值低一些不用太慌因为它对定位精度要求更高但答辩老师主要看的是可视化结果所以你最终要落到“测试视频里人能框住、不重不漏”。验证集跑完还会生成PR曲线和混淆矩阵存到了runs/val/exp/里。答辩如果需要放图PR曲线比loss曲线更能说明模型效果建议把这个图截出来。5. 常见问题排查环境配置、显存不足、检测精度差的避坑记录5.1 环境配置翻车依赖装不上或版本冲突现象pip install -r requirements.txt报错torch装不上或者import torch直接段错误。原因最常见的是Python版本和torch版本不匹配。很多毕设源码里的requirements.txt是按作者本机环境写的里面的版本号不一定兼容你的Python版本。比如Python 3.11装某些旧版本torch就会出问题。解决先确认自己的Python版本再选择对应版本的torch安装命令。Python 3.8到3.10通常问题不大3.11以上建议装最新版PyTorch。如果公司或学校网络慢用国内镜像源装速度快很多pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118CUDA版本也要匹配好nvidia-smi查看驱动支持的CUDA版本再选择对应的cu118或cu121。CPU环境就直接装CPU版torch不要折腾CUDA反而省事。5.2 显存不足batch size调整后仍然OOM现象训练启动后几秒就报CUDA out of memory把batch size调到2还是OOM。原因青灰显存被其他进程占用了或者你把--img设得太大更隐蔽的原因是你的输入图片分辨率太高超出模型 resize 预期数据加载阶段就把显存吃满了。解决先nvidia-smi看显存占用情况和是否有多进程占用。确认GPU空闲后从batch size为4开始试逐步往上加。如果batch size减少了还是翻车减去--workers 0避免数据加载进程抢显存。还有一个玄学操作是把训练脚本里的--cache参数关掉这个参数会把图片全部缓存到内存内存不够时反而拖慢训练有时也会间接导致显存释放不及时。5.3 检测框乱跳或漏检严重现象跑视频的时候检测框在不同帧之间位置抖动很大或者明明画面里有十几个人只检测出三四个。原因置信度阈值设置太低或太高。阈值低到0.15以下会出一堆误检框抖动自然严重阈值高到0.6以上会把低置信度的人漏掉尤其是远距离小目标。解决推理脚本里的置信度过滤值先调到0.25到0.35之间试然后根据实际画框效果做微调。如果是漏检小目标在保证不爆显存的前提下把输入分辨率提高到size960效果会比调阈值明显得多。如果检测一个正常身高的人时框总是只框住上半身或下半身那是训练数据里目标裁切不全导致的回到标注环节检查有没有把整个人都框进去。5.4 视频推理卡顿严重每秒不到一帧现象用视频测系统时画面卡成PPT和答辩要求完全不符。原因没开GPU推理或者在视频处理循环里做了太多不必要的前处理操作比如对每帧都做了resize之外还做了颜色空间转换、滤波等拖慢了整体速度。解决确认torch.cuda.is_available()是否返回True。如果返回True但仍卡看是不是模型加载到CPU上了显式调用.to(cuda)。还有一个常见问题是你把检测结果直接写到原始视频帧上并对每一帧做cv2.imwrite磁盘写入会成为性能瓶颈。正确做法是只把结果帧放到显示队列里不能每帧保存。6. 进阶玩法跨帧计数去重逻辑让系统从“玩具”变成“作品”如果只是对每帧画面的人数做检测系统的价值和课设作业差不多。你想让答辩加分至少要加一个逻辑跨帧跟踪去重让系统统计的是“一段时间内经过的总人数”而不是“某一瞬间画面里的人数”。一个轻量做法是按检测框中心点做距离匹配。维护一个tracked_persons列表里面存当前正在跟踪的人的位置和最近更新时间。每来一帧新检测框和当前跟踪框中所有框算中心点欧氏距离距离小于某一阈值比如画面宽度的5%就认为是同一个人更新位置和时间戳如果一帧里的检测框和所有已知框距离都大于阈值就认为是新进入画面的人总人数计数器加1。import math class PersonTracker: def __init__(self, dist_threshold50): self.tracked [] # 每个元素: (cx, cy, last_seen_frame) self.total_count 0 self.dist_threshold dist_threshold def update(self, detections, frame_id): # detections: 当前帧检测到的所有人中心点 [(cx1, cy1), (cx2, cy2), ...] for cx, cy in detections: matched False for i, (tx, ty, last_frame) in enumerate(self.tracked): dist math.hypot(cx - tx, cy - ty) if dist self.dist_threshold: self.tracked[i] (cx, cy, frame_id) matched True break if not matched: self.tracked.append((cx, cy, frame_id)) self.total_count 1 # 清除长时间未更新的跟踪框人已离开画面 self.tracked [t for t in self.tracked if frame_id - t[2] 30]这段代码里dist_threshold最关键它表示两帧之间同一个人的最大移动像素距离。摄像头固定场景下根据画面中人的移动速度来调走得快就调大走得慢就调小frame_id - t[2] 30是跟踪框存活帧数30帧意味着一个人离开画面超过1秒假设25帧/秒就丢弃。这个模块加上之后系统的输出就从“画面人数”升级成“累计客流量”这个指标才是商场、校园、展览真正关心的东西。答辩时你可以现场录像或现场演示有人从摄像头前走过计数不断累计同时画面上还实时标出每个被计数的人。这个效果比单纯列一堆mAP数据直观得多而且是你可以现场操作出来的不是口头叙述。这一步做完整个系统从模型训练、数据适配、推理部署到业务指标统计就形成了一条完整的工程链路。我到现在都记得第一次把这段跟踪逻辑接进系统看到计数值随着人进出精确跳动的时候心里那颗石头才真正落地。从那以后我每次跑这种检测类项目都会强制自己按“数据标注 → 格式检查 → 短轮训练 → 验证指标 → 业务后处理”这个顺序走一遍缺一步都不舒服希望你也能一次跑通少踩我踩过的那些坑。本文还有配套的精品资源点击获取
返回列表