
简介这是一套面向计算机专业毕业设计场景的深度学习人流量检测系统完整项目适合正在准备毕设或进行项目实战练习的本科生、研究生及自学者。项目以Python为主要开发语言结合深度学习模型实现人员目标检测与流量统计配套项目说明文档覆盖数据、模型、前端展示与后端逻辑等完整链路。压缩包共1235个文件约61.54MB主要包含Python源码py、交互界面html/css/js、图像与动效素材png/gif以及ipynb模型调试笔记、t7权重文件和配置文件等便于按目录结构直接运行与二次开发。目前已有263人学习下载项目经导师指导并严格调试可作为课程设计或期末大作业参考。通过学习可以掌握从模型训练、目标检测到人流量统计结果可视化的整套实现思路并拿到可直接运行的分层代码与说明文档对提升实战能力和完成毕设答辩都有实际帮助。1. 人流量检测系统的技术真相先检测、再跟踪、最后计数一个摄像头架在商场出入口、教室门口或者走廊尽头视频里一群人来回走动老板或导师要的是一句话“今天下午三点到四点这个门进了多少人、出了多少人。” 人流量检测系统干的就是这件事。它跟人脸识别完全不是一回事不关心谁是谁只关心“有没有人”和“人往哪走”。很多初次接触这个方向的同学以为把 YOLO 跑起来、框出人就完事了结果发现同一个员工在画面里来回走了三趟系统给他算了三遍。真正的落地形态是深度学习负责把人从画面里“找出来”跟踪算法负责把同一个人的跨帧轨迹“连起来”最后用一条虚拟判定线把“进”和“出”分开统计。这个方案特别适合作为毕业设计技术链条完整、可视化效果好、指标可量化而且每一层都有独立的优化空间。2. 用YOLO做行人检测模型选型、训练配置与精度调优2.1 为什么选YOLO而不是Faster R-CNN或CSRNet人流量检测系统的第一层是“检测”也就是在每个视频帧里找出所有行人的位置框。常见的选择有 YOLO 系列、Faster R-CNN 和专门做密度估计的 CSRNet毕业生和工程实践里绝大多数都选 YOLO原因很直接这个任务要求实时性摄像头是连续视频流不能为了数人让画面卡成幻灯片。Faster R-CNN 精度上限确实高但两阶段检测器在 CPU 上跑一帧要几百毫秒在 GPU 上也只能跑到每秒十几帧放到实际项目里很难支撑一路摄像头的实时统计。CSRNet 走的是另一条路它不输出检测框而是直接生成人群密度图在密集场景下准确率很惊艳但它有一个致命问题只能给出一片区域的总人数无法区分“哪个人往哪个方向走”自然也没法做进出双向计数。人流量检测系统要的是边界框 轨迹 方向YOLO 是三者兼顾的最优解。模型版本的选择也有讲究。YOLOv5s 在常见的 GTX 1060 上推理单帧只需 5 到 8 毫秒精度足够应付多数室内外场景如果部署机器只有 CPU可以考虑 YOLOv5n 或者把输入尺寸从 640 降到 416代价是小目标漏检率会上升。训练自己数据集的话不建议一上来用 YOLOv8v8 的 C2f 结构训练更慢、显存占用更高对毕设这种数据量级来说提升有限YOLOv5 的资料和踩坑记录最多照着改最省时间。2.2 环境配置这套系统用到的最小技术栈先确认电脑上有显卡哪怕是笔记本的 GTX 1650 也能跑。需要用到的核心库是 PyTorch、OpenCV、NumPy跟踪部分用 deep-sort-realtime 或者自己写 IOU 匹配。建议用 conda 建一个干净环境避免把日常开发环境搞乱套conda create -n crowd_count python3.9 -y conda activate crowd_count pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python numpy pandas pip install ultralytics命令行参数说明python3.9是为了兼容 PyTorch 的预编译包--index-url指定 CUDA 11.8 的 PyTorch 版本如果你的显卡驱动只支持 CUDA 12 就改成cu121不确定就在终端里输nvidia-smi看右上角的版本ultralytics提供加载 YOLOv8 的接口但下面训练部分仍用 YOLOv5 的仓库更稳。如果你连 Python 安装和环境变量都还没配好先花二十分钟把 conda 装明白这属于深度学习环境配置里的基础操作后面所有步骤都建立在这套环境下。2.3 数据集准备用现成行人数据集而不是自己标自己标注行人数据是纯体力活几百张图就要标一整天而且框的规则不统一会直接影响训练效果。主流做法是用两个公开数据集的组合COCO 的 person 类别子集 CrowdHuman 的部分训练图片。COCO 里大多是常规视角的行人CrowdHuman 里则是密集场景和遮挡很多两者结合能让模型同时适应稀疏和拥挤的场面。把 COCO 转成 YOLO 格式的脚本网上有现成的核心是把标注文件里的 category_id 为 1 的类别单独抽出来坐标从 “左上角 宽高” 转成 “中心点 宽高” 并归一化到 0 到 1 之间。转换完以后每个 txt 文件里每行是一条标注格式是class_id x_center y_center width heightclass_id填 0因为这里只检测一个类别。数据准备好以后按 8:1:1 的比例划分 train / val / test目录结构长这样datasets/person/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/如果画面里的行人大量出现在画面边缘且不完整别急着删掉把这类样本单独拉出来做个 hard example 子集训练时用--cache选项让模型反复看到它们对边缘漏检的改善非常明显。2.4 训练命令与三个必调参数数据准备好了就开始训练我用的是 YOLOv5 官方仓库训练命令长这样cd yolov5 python train.py --data person.yaml --weights yolov5s.pt --epochs 80 --batch-size 16 --img 640 --device 0person.yaml是数据集配置文件里面写清楚train、val的路径和类别数量nc: 1再加一行names: [person]。--epochs 80在这个数据量下够用了再多了就开始过拟合训练集 loss 继续降但验证集 mAP 不再涨--batch-size 16根据显存调整6GB 显存可以降到 8。--img 640是输入分辨率想多照顾小目标就设成 640实在卡就 512。训练结束后看两个指标val/0.95表示 IoU 阈值 0.05 到 0.95 的平均精度超过 0.5 就说明模型能用了metrics/precision看的是“检测出的框里有多少真的是人”做计数系统这个指标比召回率更影响最终精度。如果召回率太低说明漏检多把--conf-thres的默认值 0.25 往下调如果误检多背景被当成行人就往上调。置信度阈值是这套系统里最值得反复试的参数它直接决定下游跟踪和计数的输入质量属于那种调好了立竿见影、调不好整套系统跟着翻车的关键旋钮。2.5 用训练好的权重跑视频推理的最小脚本训练完成后用你自己的视频验证模型的泛化能力。不用跑官方 detect.py直接写二十几行脚本更可控import cv2 import torch # 加载训练好的模型conf_thres0.35 过滤低置信度框 model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt, force_reloadTrue) model.conf 0.35 model.iou 0.45 cap cv2.VideoCapture(test_video.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame) # results.xyxy[0] 是当前帧所有检测框每行: x1 y1 x2 y2 confidence class boxes results.xyxy[0][:, :4].cpu().numpy() for box in boxes: x1, y1, x2, y2 [int(v) for v in box] cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明torch.hub.load从本地加载训练好的权重文件force_reloadTrue保证每次都重新读取权重而不是用缓存。model.conf和model.iou是在加载后直接覆盖模型默认值conf0.35表示置信度低于 35% 的框全部丢弃iou0.45是 NMS 的 IoU 阈值重叠超过 45% 的两个框只保留分数高的那个。这段脚本跑通了说明检测这一层已经过关可以进入跟踪阶段。3. 用DeepSORT做跨帧跟踪从检测框到进出人数统计3.1 为什么单帧检测框不够用重复计数和漏计数的根源如果只对每一帧做独立检测然后数框的个数结果会一团糟。一个人站在原地不动每秒 25 帧他就被数了 25 次两个人擦肩而过跟踪断开了又被当成新目标重新计数一个人从画面边缘走到中心前面几帧因为是半身被漏检计数就对不上。人流量检测系统的第二层核心是给每个检测框分配一个稳定 ID让“同一个人的不同帧属于同一条轨迹”。这样计数逻辑就变成了统计轨迹的数量、方向和存活时间而不是统计框的数量。最常见的做法是用 DeepSORT它在 SORT简单的 IOU 匹配跟踪基础上加了一个外观特征提取网络让目标在短时间遮挡后还能重新匹配上。由于我们只跟踪行人这一类别外观特征网络的实际增益比多类别跟踪小但它的容错性更好。如果只想把毕设做稳用 SORT 的 IOU 匹配就够了还能少写一堆特征提取代码。开源仓库里 deep_sort_pytorch 和 deep-sort-realtime 都是成熟选择前者用起来啰嗦一点后者封装更友好可以在一个类里完成 predict 和 update。3.2 DeepSORT的三个核心参数max_dist、max_age、min_hits深度学习的跟踪器参数比检测参数更“玄学”很多跑完效果不理想的案例问题就出在几个默认参数上。先说max_dist它控制的是两个检测框之间允许的最大特征距离距离超过阈值就认为不是同一个人这个值设大了会把两个相近的行人串成一条轨迹设小了又会频繁断轨。max_age表示一条轨迹最多能“失忆”多少帧超过这个帧数无人匹配就彻底删除轨迹这个值设大了会让幽灵轨迹在画面里飘很久设小了又扛不住行人被短暂遮挡。min_hits是新轨迹转正需要连续匹配上的帧数低于这个帧数的检测不会被计入合法轨迹专门用来过滤单帧的假阳性检测。一个在出入口场景里反复试出来还算稳定的组合是max_dist0.2max_age30min_hits3。注意max_dist不是像素距离而是特征向量之间的余弦距离或马氏距离在 deep-sort-realtime 里默认用的是余弦距离取值范围 0 到 10.2 相当于要求特征高度相似。如果你的画面里行人频繁被柱子或门框遮挡把max_age调大到 50 能明显减少轨迹断裂缺点是计算量稍涨。3.3 计数逻辑虚拟线判定与进出方向判断检测和跟踪都就绪后就要写计数的核心逻辑了。常用做法是画一条横跨画面的虚拟线或一个虚拟矩形区域然后判断轨迹中心点与这条线的关系。我习惯的做法是这样的先设置线的 y 坐标然后记录每个轨迹上一次的中心点位置如果当前帧中心点从线上方穿过到下方记为“出”从下方穿到上方记为“进”。class CountLine: def __init__(self, line_y): self.line_y line_y self.count_in 0 self.count_out 0 self.previous_y {} # 轨迹ID - 上一帧中心y坐标 def update(self, track_id, center_y): if track_id not in self.previous_y: self.previous_y[track_id] center_y return prev_y self.previous_y[track_id] if prev_y self.line_y and center_y self.line_y: self.count_out 1 # 从下方穿到上方记为出 elif prev_y self.line_y and center_y self.line_y: self.count_in 1 # 从上方穿到下方记为进 self.previous_y[track_id] center_y逻辑说明这个类维护一个字典记录每个轨迹 ID 上一次的中心点 y 坐标每次update时比较prev_y和当前center_y与line_y的位置关系从而判断穿越方向。这里的两个分支分别对应“向下穿线”和“向上穿线”具体哪个是进哪个是出取决于虚拟线在画面中的位置。这个方法有个边界情况如果一个人站在线上左右摇晃会被重复计数。解决办法是加一个计数冷却时间同一个轨迹 ID 在穿越后 30 帧内不再触发第二次计数代码里用一个last_count_frame字典记录即可。计数这一层很容易被忽略的是跟踪器输出的track_id在轨迹重新匹配后会复用旧 ID如果你用 ID 去怼词典会发现一个人走出画面再走回来被算成了同一个人穿越两次。解决方法是把previous_y的更新放在计数之后且一旦max_age超时把保留数据删掉。3.4 人数统计可视化从计数结果到人流量曲线毕业设计答辩时评委要看的是直观结果光有一堆数字不够。最后一个步骤是把每五分钟或每小时的进出人数汇成折线图快速看出客流高峰时段。用 pandas 做聚合matplotlib 出图import pandas as pd import matplotlib.pyplot as plt # records 是 (timestamp, direction) 的列表direction 取值 in / out df pd.DataFrame(records, columns[time, direction]) df[time] pd.to_datetime(df[time]) df.set_index(time, inplaceTrue) hourly df.groupby([pd.Grouper(freq5min), direction]).size().unstack() hourly.plot(kindline, figsize(12, 5)) plt.title(Pedestrian Flow Statistics) plt.xlabel(Time) plt.ylabel(People Count) plt.legend([Enter, Exit]) plt.savefig(flow_curve.png, dpi150)参数说明freq5min决定了统计粒度出入口人流密集就设小一点稀疏就设成 30 分钟。unstack()把 direction 字段变成列方便两个方向各画一条折线。这份可视化材料直接截图放进毕业论文的效果图里比单纯贴代码有说服力得多也是“高分项目”观感的重要组成部分。4. 人流量检测系统最常见的5个避坑记录现象、原因与解决4.1 检测框抖动导致同一个人被反复计数现象一个站立不动的人在画面边缘计数器在几分钟内涨了十几个人。原因有两个一是置信度阈值偏低模型把同一人的不同部位轮流识别成独立目标比如上半身和全身交替被框出二是跟踪器min_hits1单个检测框就能成为合法轨迹于是抖动框被当成新轨迹反复计数。解决把检测置信度阈值从 0.25 提到 0.4并设置min_hits3以上让轨迹必须连续三帧匹配才进入合法列表虚报立刻消失。4.2 俯视摄像头下行人的检测框拉宽导致跟踪粘连现象安装在进门头顶的摄像头拍出来的人只有头顶和肩膀检测框又宽又扁两个人挨着走时框几乎重叠跟踪器经常把两个人合并成一个人。原因训练数据里这类俯视角度样本太少模型学到的行人框形状是竖直的矩形俯视视角的宽高比完全超出分布。解决从自己摄像头录十分钟视频用 LabelImg 标出俯视行人样本混进训练集里微调二十个 epoch不要从零训练用训练好的权重做--weights继续训练。如果实在没有时间标注就在代码里对接近水平宽高比的框做一次过滤把特别扁的框视为无效检测。4.3 远处小目标漏检计数明显偏少现象画面里的远处行人比如走廊尽头高度只有十几个像素检测器完全不理他们导致进出统计比人工数少 20%。原因输入分辨率 416 或 640 下网络对小于 16x16 像素的目标特征提取能力太弱加上训练集里小目标样本占比少。解决三个方向把推理分辨率调到 768在训练配置里开启多尺度训练YOLOv5 默认在训练中随机缩放输入尺寸能增加小目标的多样性训练数据增强里加一个随机裁剪操作把大图裁成局部特写喂进模型。这里的血泪经验是调分辨率最省事但推理时间几乎翻倍如果硬件是 GPU 还好CPU 部署就别加到头了。4.4 跟踪ID频繁切换导致同一人多次计数现象一个人穿过被柱子遮挡的区域后出来时的 ID 变了旧轨迹在max_age超时后被删除新轨迹触发一次新的穿越计数相当于一个人被算了两遍。原因遮挡期间没有任何检测框与旧轨迹匹配max_age到期后轨迹被清理目标重新出现时被当作新 ID。解决先把max_age从默认的 30 秒调大但这是治标治本的办法是在计数逻辑里加入“轨迹中心点位置预测”在update函数里用卡尔曼滤波预测目标被遮挡期间的中心点位置如果重新出现的框位置与预测位置接近则沿用旧轨迹 ID而不是新开轨迹。DeepSORT 自带卡尔曼滤波的预测输出直接调用predict之后再update即可。4.5 模型在换场景后准确率暴跌现象在宿舍门口训练好的模型拿到教学楼门口测试检测框开始飘误检率飙升。原因训练集里只有宿舍场景的固定视角和固定光线模型把背景也当成了判别特征换场景后背景变了立刻翻车。解决这是典型的过拟合到场景不是模型结构问题。一条快速缓解路径是在数据预处理时对图像做随机 HSV 扰动把亮度和饱和度变化范围调大让模型不再依赖特定光线的背景纹理。如果时间允许去目标场景录 20 分钟视频标注 500 到 1000 张图做微调这个数据量就能把场景适应问题压下去。毕设答辩时如果被问到“换摄像头还能用吗”直接说“需要在新场景做少量数据微调”这是一个诚实且专业的回答。5. 项目说明文档的高分写法实验对比表、答辩演示与验收技巧5.1 项目说明文档的四页结构毕设文档不需要厚到五十页但最少得有四个部分问题定义、技术方案、实验对比、部署和使用说明。问题定义里写清楚“要解决的是双向进出人数统计不是画面人数快照”一句话就能拉开和普通检测项目的距离。技术方案部分按检测、跟踪、计数三层展开引用你用到的模型和关键参数。实验对比表是评委最爱翻的一页直接做成下面这种格式说服力比大段文字强得多方案准确率推理速度GTX 1060误检情况仅 YOLO 帧计数58%5 ms / 帧重复计数严重YOLO SORT 轨迹计数82%8 ms / 帧遮挡后 ID 丢失YOLO DeepSORT 冷却机制91%12 ms / 帧极少数拥挤场景误计实际跑出来的数据跟这个表不一定一样关键是结构要保持三行就足够体现每一步优化的收益。部署说明里写清楚 Python 版本、依赖清单、启动命令让别人能在自己机器上复现这是毕业设计和真实工程之间的重要分界线。5.2 答辩演示时最容易出彩的三个技巧第一准备一条预录好的演示视频而不是现场连摄像头。现场演示的翻车率极高光线变化、行人太少、模型恰好误检都会让评委对项目的可靠性产生怀疑。预录视频可以选择一个画面里持续有行人穿过的时段配合终端输出的实时计数日志效果比现场实时推理更稳定。第二演示时故意跑一段“密集通过”的镜头然后用 Pause 键停在某一帧指着画面上的轨迹 ID 说“这个人从进画面到现在是同一个 ID所以不会重复计数”。评委听不懂特征匹配的细节但看得懂轨迹连续性这是整个系统里最直观的亮点。第三准备一个“失败案例”页。选一段模型漏检的短片段分析原因并说明改进空间。这会让答辩印象分明显提升因为绝大多数毕设都只展示成功结果主动呈现失败案例反而显得技术态度成熟。5.3 验收时绕不开的指标口径人流量检测系统验收时最容易起争执的地方是系统报的数字跟人站在门口手动按计数器数出来的对不上。这里有一个口径问题需要提前写进文档系统的“准确率”指什么。建议统一用两个口径检测级 IoU 评估模型框出了多少比例的真实行人和业务级计数评估固定时段内的进/出人数误差率。计数误差率小于等于 8% 就被认为是可接受系统这个标准在很多工程验收里都适用。另外把测试时段拉长到至少一小时按每十分钟一个时间段做误差统计比只看总量更能暴露异常地段。我做这类系统时养成的习惯是所有计数日志保留原始 CSV并在每条记录里附上触发计数的帧号和轨迹 ID。出了问题回查时直接跳到对应帧看跟踪器当时的匹配状态而不是对着一个孤立数字瞎猜。这个习惯在毕业设计答辩时救了我一次——评委指着一处计数异常追问我当场翻出那几帧的轨迹状态把事故原因解释得清清楚楚。希望这个思路能帮你在自己的项目里少走一段弯路也祝方案和文档都能顺利过关。本文还有配套的精品资源点击获取