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

文章详情

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

人流量检测毕设实战:从YOLO到CSRNet的完整技术路线

人流量检测毕设实战:从YOLO到CSRNet的完整技术路线 简介这是一份经导师指导认可的高分毕业设计项目基于深度学习实现人流量检测系统内含Python源码与项目说明文档面向计算机相关专业正在准备毕业设计、课程设计或期末大作业的学生也适合需要项目实战练手的学习者。压缩包共含1235个文件体积约61.78MB主要包括76个Python程序文件、382个HTML页面、194个JavaScript脚本以及大量PNG和GIF图片用于界面展示与过程记录另附ipynb分析笔记、PDF说明文档和多种配置文件目录结构清晰完整。项目经过严格调试确保可运行内容覆盖模型构建、数据预处理、人流检测逻辑、后端服务与前端展示的串联实现配套说明文档可帮助理解整体架构与关键细节。目前已有435人学习下载可用于毕业设计参考、二次开发基础或课程答辩演示。1. 人流量检测毕设真正的门槛不在模型在数据与计数“人流量检测系统”这个毕设题如今几乎默认和深度学习绑定在一起摄像头采集画面模型识别人头程序把检测框换算成人数再用 Python 把训练、推理、展示串成一条完整链路。听起来是深度学习入门题但每年在这上面翻车的人并不少——不是模型跑不起来而是数据集准备不干净、计数逻辑太粗糙导致答辩演示时漏检误检满天飞。这篇笔记面向正在做毕业设计或课程设计的同学我会按一套能落地的方案讲清楚技术路线怎么选、训练命令怎么写、统计逻辑怎么设计、哪些坑必须绕开。最终交付的“源码项目说明”应该是一个能当场跑通、能讲出设计依据、也能扛住老师追问的完整系统。2. 人流量检测技术路线怎么选从 YOLO 到 CSRNet 的决策清单2.1 目标检测路线YOLO 为什么是毕业设计的默认答案人流量检测最直接的做法是“把人头找出来再数个数”。这就落到目标检测任务上模型输出每个目标的边界框、类别和置信度程序对边界框做过滤和统计人数自然就出来了。在目标检测算法里YOLO 系列几乎成了毕业设计的事实标准原因很现实ultralytics 提供的 Python 包把数据加载、训练、验证、导出封装得很完整你不用从零写损失函数和数据管道能省下大量时间去处理数据集和系统逻辑。YOLO 本身是一阶段检测器核心思想是把图片划分成网格每个网格直接预测边界框和类别训练和推理速度都快。对毕设场景来说速度意味着你可以用普通笔记本的 CPU 跑推理演示用一张入门级显卡完成微调而基于候选区域的 Faster R-CNN 虽然在小目标上可能更稳但训练和推理都要重得多在同样的硬件条件下很难把效果调到好看。我的建议是如果题目没有硬性指定算法第一版直接用 YOLO 做通跑通后再在论文里谈改进。2.2 密度图回归路线CSRNet 在超密集场景的优势与代价另一种学术味更重的路线是人群计数Crowd Counting代表作是 CSRNet。它不输出“每个人在哪”而是输入一张图、输出一张密度图每个像素的值表示该位置的人群密度对密度图求和就得到总人数。这个思路在超密集场景演唱会、地铁站台、节假日景区有明显优势因为人头互相遮挡时检测框会大量重叠甚至粘连而密度图回归天然容忍这种拥挤。但走这条路的代价也不小第一训练数据需要“点标注”也就是在每个人头中心打一个点再通过高斯核生成密度图标注成本比画边界框更高第二密度图回归的计数结果很难可视化成“框住每个人”的效果答辩演示时说服力不如检测框直观第三CSRNet 的 PyTorch 实现大多是论文配套代码数据加载、评估脚本都要自己调工程量明显大于直接用 YOLO。所以这条路线更适合论文创新点明确、导师要求“不能只做目标检测”的情况。2.3 选型对照表与答辩话术对比维度YOLO 目标检测CSRNet 密度图回归核心输出检测框类别置信度密度图总人数标注成本画边界框中等点标注更费人力拥挤场景效果密集遮挡时漏检明显密度估计更鲁棒训练难度封装完善容易上手需自行处理数据管道推理速度快CPU 也能跑相对较重演示效果直观适合答辩只有热力图需额外加工答辩被问“你为什么选这个方案”时我一般会这样答以 YOLO 为基线系统满足实时性和可视化要求在论文中把“拥挤场景下的漏检问题”作为改进动机尝试密度图回归作为对照实验。这样既证明你了解前沿方法又守住了工程落地的主线比单纯说“YOLO 好用”更有说服力。选型本身不是背书而是把你的选题背景、数据条件和硬件约束讲清楚。3. 用 Python 在本地跑通最小人流量检测系统环境、数据与训练3.1 环境装配Python 版本、PyTorch 与 ultralytics 的搭配拿到题目后第一件事不是写代码而是把环境固定下来。人流量检测涉及 Python、PyTorch、OpenCV 和 ultralytics 四个核心依赖版本搭配不好会出现各种奇怪的报错。我的习惯是新建一个干净的 Python 3.10 或 3.11 虚拟环境然后统一用 pip 安装最新稳定版依赖避免用系统全局环境否则很容易把其他项目的包搞坏。# 创建虚拟环境并激活Windows 与 Linux/macOS 命令略有差异 python -m venv crowd_env # Windows: crowd_env\Scripts\activate # Linux/macOS: source crowd_env/bin/activate # 安装核心依赖PyTorch 按官网命令装 CPU 或 CUDA 版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics opencv-python这段命令背后的逻辑是PyTorch 是深度学习框架本体YOLO 的训练和推理都建立在它之上ultralytics 是 YOLO 的 Python 包封装了模型结构、训练流程和导出工具opencv-python 负责视频流读取和画面绘制。如果你在 VSCode 里配 Python 环境记得让解释器指向 crowd_env终端里也要先激活虚拟环境再跑命令不然会出现“明明装了包却 import 不到”的典型问题。3.2 数据集准备人头标注、VOC 转 YOLO 格式的转换脚本模型能不能检测出人七分靠数据。毕设项目一般有三种数据来源用公开人头数据集、自己用 LabelImg 标注、或者把公开 VOC 格式数据集转成 YOLO 格式。最省事的路径是找公开人头检测数据集比如 SCUT_HEAD 或 Brainwash 这类学术数据集但要注意下载后往往是 VOC 或自定义格式需要先写转换脚本。YOLO 训练要求的标注格式是 txt 文件每一行代表一个目标class_id x_center y_center width height其中坐标值都是相对于图片宽高的归一化比例取值范围 0 到 1。这个格式特别容易出错因为坐标不是绝对像素而是比例很多同学在转换时漏了除以图片宽高导致训练出来的模型什么都检测不到。下面给出一个从 VOC XML 转 YOLO txt 的脚本骨架import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 坐标归一化到 [0, 1]中心点表示法 x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(lines)) # 使用示例class_names 里 0 对应 person voc_to_yolo(data/annotations/0001.xml, data/labels, [person])这段代码的关键点在于解析 XML 拿到原始坐标后先求中心点再除以图片宽高。如果漏了归一化边界框的数值会远大于 1模型训练时直接“飞掉”。转换完成后我强烈建议随机抽几张图用 OpenCV 把标注框画回图上做可视化检查这是成本最低的后悔药。标签文件夹里出现空 txt 文件、或者类别 id 对应错视觉上都会立刻暴露。3.3 训练一份能检测人头的最小模型命令与超参数数据准备好后训练模型反而是最“无脑”的一步因为 ultralytics 把整个训练流程收敛成了一条命令。做毕设不追求刷榜目标是快速得到一个效果可接受的权重所以我会先用轻量级模型跑通再考虑换大模型提升精度。# data.yaml 里指定 train/val 图片路径和类别数 # model 用轻量级预训练权重显存不够就选 yolov8n yolo detect train datadata.yaml modelyolov8n.pt epochs80 imgsz640 batch8 device0这里的参数含义和调整方法epochs 设 80 比较稳妥太少容易欠拟合太多在小数据集上会过拟合imgsz 是训练分辨率640 是默认值分辨率越高小目标越好检但显存压力越大batch 是每次迭代喂给 GPU 的图片数8G 显存跑 yolov8n 没问题如果 OOM 就先减半device0 表示用第一块 GPU纯 CPU 环境可以改成 devicecpu但训练速度会慢一个量级。训练结束后weights 文件夹里会出现 best.pt 和 last.pt后续推理统一用 best.pt。3.4 推理脚本把检测框换算成人流量的核心逻辑训练完成后检测本身已经“黑盒化”了你需要做的是把模型的输出变成业务上的人数统计。这也是整个系统里最有工程含量的一步因为“模型检测出人”和“系统完成人流量统计”之间还差着阈值过滤、类别过滤和坐标逻辑。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) frame cv2.imread(test.jpg) results model(frame, conf0.35, iou0.5, verboseFalse) boxes results[0].boxes person_count 0 for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) if cls_id 0 and conf 0.35: person_count 1 x1, y1, x2, y2 map(int, box.xyxy[0]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, fcount: {person_count}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)这段代码里 conf 是置信度阈值低于它的检测框会被丢掉iou 是非极大值抑制的阈值用于合并重叠框。这两个参数是整个计数逻辑的“玄学源头”阈值设太高远处的小人头被漏掉人数偏少阈值设太低误检增多人数虚高。我一般会在验证集上多试几个档位找到漏检和误检的平衡点而不是凭空拍一个 0.25 或 0.5。另外要养成随时用 verboseFalse 的习惯否则每帧推理都会往终端打一堆信息处理视频时看不到过程输出。4. 人流量统计与可视化界面从检测框到答辩演示4.1 单帧计数、区域计数与跨帧去重很多人做到“能检测”就停了但毕设题目里“人流量检测系统”的“系统”二字要求你把检测结果变成可持续统计的流量数据。最朴素的做法是单帧计数也就是每帧画面数一次人头。这在人少、摄像头固定的场景下够用但一旦人流连续通过同一个人出现在连续帧里就会被反复计数导致流量数据虚高。解决重复计数有三种常见做法区域计数、跨帧去重和跟踪计数。区域计数是在画面中划定一个虚拟门或者多边形区域只统计进入或离开这个区域的目标跨帧去重是记录目标中心点轨迹如果连续多帧出现在相近位置就认定为同一个人跟踪计数则是用 ByteTrack 或 DeepSORT 这类多目标跟踪算法给每个目标分配 ID统计独立 ID 数量。毕设体量下我推荐先用区域计数配合简单的中心点去重理由是不需要引入额外的跟踪模型答辩时也更容易把逻辑讲清楚。4.2 视频流处理OpenCV 帧读取与跳帧策略实际演示时输入往往是一段监控视频或者摄像头实时画面而不是单张图片。视频流处理的坑在于逐帧推理会让画面卡顿因为模型推理速度跟不上视频帧率。解决办法是跳帧——每隔 N 帧做一次检测中间帧直接沿用最近一次的结果。import cv2 from ultralytics import YOLO cap cv2.VideoCapture(test_video.mp4) model YOLO(runs/detect/train/weights/best.pt) frame_skip 3 # 每 3 帧检测一次其余帧沿用结果 current_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break if int(cap.get(cv2.CAP_PROP_POS_FRAMES)) % frame_skip 0: results model(frame, conf0.35, verboseFalse) current_count len(results[0].boxes) annotated frame.copy() cv2.putText(annotated, fcount: {current_count}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imshow(crowd counting, annotated) if cv2.waitKey(1) 0xFF ord(q): break跳帧策略里 frame_skip3 意味着每三帧只推理一次推理开销立刻降到原来的三分之一。如果你用 CPU 跑推理建议把 imgsz 降到 480同时把 frame_skip 调大一些如果你用 GPU跳帧可以设为 1 或 2。这段代码还演示了一个重要习惯不要把模型调用写在每个分支里而是先读帧、再按条件推理、最后统一展示这样代码结构清晰也方便你后续加入流量统计列表。4.3 一个简单的 Web 展示界面与流量曲线纯 OpenCV 弹窗适合自己调试但答辩演示时老师更愿意看到一个带流量曲线的界面因为曲线能直观展示“什么时候人多、什么时候人少”。毕设项目最常见的做法是用 Flask 起一个轻量 Web 服务后端把每帧统计到的人数追加到列表里前端用 ECharts 把曲线画出来。不推荐在毕设里花太多时间做复杂的 Vue 前端Flask HTML 模板已经足够。from flask import Flask, Response, render_template, jsonify import cv2 from ultralytics import YOLO app Flask(__name__) model YOLO(runs/detect/train/weights/best.pt) count_history [] def generate_frames(): cap cv2.VideoCapture(0) # 默认摄像头也可以是视频文件 while True: ret, frame cap.read() if not ret: break results model(frame, conf0.35, verboseFalse) count len(results[0].boxes) count_history.append(count) if len(count_history) 300: count_history.pop(0) annotated results[0].plot() # ultralytics 自带画框接口 _, jpeg cv2.imencode(.jpg, annotated) yield (b--frame\r\n bContent-Type: image/jpeg\r\n\r\n jpeg.tobytes() b\r\n) app.route(/) def index(): return render_template(index.html) app.route(/video) def video(): return Response(generate_frames(), mimetypemultipart/x-mixed-replace; boundaryframe) app.route(/api/count) def api_count(): return jsonify(count_history)这个用 Flask 写的示例展示了系统集成的典型分层generate_frames 是检测与视频流推送层/api/count 是数据接口层前端页面单独画曲线。答辩时你可以先展示实时画面再打开曲线页面说明“数据是按时间轴累积的”。这个架构和真实监控系统非常接近老师一般不会追问为什么不用消息队列或者数据库因为毕设的核心是打通链路而不是做高并发。5. 人流量检测项目避坑指南数据集、训练与部署的五个关键问题5.1 训练到一半显存溢出OOM 的连锁翻车现象训练刚开始几轮就报 CUDA out of memory或者跑了一段时间才崩终端提示“RuntimeError: CUDA out of memory”。原因显存占用由模型参数量、输入分辨率、batch size 和梯度累积共同决定。yolov8n 虽然轻量但如果把 imgsz 调到 1280 同时 batch 设 168G 显存一样扛不住。解决先把 batch 减半到 4imgsz 降回 640如果还崩就换更轻的 yolov8n 并开启梯度累积ultralytics 里可以通过yolo train ... batch4搭配optimizerSGD这类参数组合控制占用。还有一个隐蔽问题不要同时开太多其他程序占显存训练前用nvidia-smi看一下显存占用是基本习惯。5.2 训练完什么都检测不到标签格式的隐藏炸弹现象loss 正常下降准确率曲线也不难看但推理时模型对任何一张图都输出空检测框。原因八成是标签文件写错了。最典型的是坐标没有归一化或者类别 id 全部为 0 但训练配置里类别数大于 1。我见过最离谱的案例是 VOC 转 YOLO 时把 x_center 写成了 x1 (x2 - x1) / 2虽然这个公式在数学上看起来差不多但在某些边界框上会越界导致训练样本里混入了大量非法标签。解决转换脚本跑完后马上做标签可视化抽查。写一个小脚本把 txt 坐标乘以图片宽高画回去眼睛看一遍能发现 99% 的格式问题。这个动作成本很低但能救回来几天的训练时间。5.3 loss 降了但 mAP 上不去数据划分与类别分布的问题现象训练 loss 降得挺漂亮但验证集的 mAP 一直卡在 0.3 以下明显不正常。原因最常见的是 train/val 划分不当。如果划分时把同一段视频的连续帧分别分到训练集和验证集模型在验证时等于“背答案”mAP 虚高反过来如果训练集和验证集场景差异巨大室内训、室外测mAP 就会低得离谱。另一个原因是小目标太多模型对密集小头学习不充分。解决划分数据集时按视频片段或按场景目录划分不要随机切帧训练时开启 mosaic 增强并适当上调 imgsz 到 960。如果目标是大量小头可以试试 SAHI 切图推理把大图切成小块分别检测再合并结果这是密集小目标场景下非常见效的工程手段。5.4 中文路径与 Windows 环境引发的类库报错现象代码在 macOS 或 Linux 上跑得好好的拿到 Windows 上一运行就报错常出现 FileNotFoundError 或者 UnicodeDecodeError。原因ultralytics 的数据缓存文件和 OpenCV 的 cv2.imread 对中文路径支持不好项目放在“下载/毕业设计/人流量检测”这类中文目录下光路径问题就能卡一整天。解决整套项目路径保持全英文用户名、目录名、文件名都不要出现中文。这看起来像玄学但在 Windows 上用 OpenCV 和深度学习框架时是血泪教训cv2.imread 读取中文路径会静默返回 None而 ultralytics 在保存训练日志时也会被中文路径干扰。项目根目录从开始就命名为 crowd_detection能省掉大量无意义的排错时间。5.5 ONNX 导出后推理结果不一致预处理与 NMS 的差异现象PyTorch 推理正常但用yolo export导出 ONNX 后用 onnxruntime 推理检测框和置信度对不上。原因ultralytics 导出 ONNX 时会去掉训练时附加的预处理细节特别是在输入归一化、letterbox 缩放和 NMS 后处理上PyTorch 模型和 ONNX 模型的行为不完全一致。解决导出时固定输入尺寸比如imgsz640并且不要在导出后手动改图像缩放逻辑。用 onnxruntime 推理时严格复现点除 255、letterbox 缩放等预处理步骤。如果只是做毕设演示建议直接用 PyTorch 模型跑推理ONNX 导出作为论文里的“部署可行性验证”罗列即可不必强行替换推理后端。6. 答辩前值得做的小实验用 mAP 和热力图证明检测器可靠系统能跑通只是第一步答辩老师最常问的是“你的模型效果到底怎么样”。这时候光说“检测挺准的”没有说服力需要拿数据说话。我的习惯是训练结束后立刻跑一次验证集评估把 mAP50 和 mAP50-95 记录到论文实验章节里。# 用验证集评估 best.pt输出各类别 mAP 和召回率 yolo detect val datadata.yaml modelruns/detect/train/weights/best.pt另一个值得做的小实验是统计“置信度阈值对计数结果的影响”。取一段固定 5 分钟的视频分别用 0.2、0.3、0.4、0.5 四个阈值跑计数画出人数曲线。你会发现阈值越低人数曲线毛刺越多阈值越高曲线越平滑但峰值被削掉。把这张曲线图放进论文再配一段“本系统在 0.35 阈值下取得漏检与误检平衡”的说明整个实验章节立刻扎实不少。我这边还习惯把热力图加进去作为辅助验证用 matplotlib 把检测框中心点叠加到画面上能直观看出系统是否存在“永远检测不到画面边缘的人”这种空间盲区。这个习惯是当年被答辩老师问“你凭什么说这个模型适合这个场景”问出来的教训。现在每做完一个检测项目我都会这么做一遍验证已经成了固定动作。等你做完这些实验把数据和截图补进项目说明文档这套体系才真正闭环。希望帮到你。本文还有配套的精品资源点击获取
返回列表