简介面向计算机相关专业毕业设计与人流量检测实战的完整项目源码包基于深度学习完成系统设计内含 Python 源码与项目说明文档可作为高分毕业设计参考辅助完成从环境配置、模型搭建到结果展示的完整流程。项目经过严格调试能够直接运行同时适用于课程设计、期末大作业。压缩包共 1235 个文件大小约 61.78MB以 76 个 Python 源文件为核心配合 382 个 HTML 页面、194 个 JS 脚本、208 张 PNG 图片以及 CSS、配置与说明文档等覆盖前端展示、后端接口、模型调用和部署配置模块。目前已有 435 人学习/下载。包内项目说明与工程目录结构清晰便于快速定位入口文件和核心逻辑从前端页面到后端处理均有相应实现能帮助理解人流量检测系统的完整交互流程也适合在现有代码基础上扩展与二次开发。1. 人流量检测作为毕设我能用它干什么、到底要交什么如果你的毕业设计题目是“基于深度学习的人流量检测系统设计与实现”那你大概率已经查过一圈资料发现网上要么是单纯讲 YOLO 原理的教程要么是连数据集都没有的“半成品”。这个题目真正要交付的东西其实很明确一个能对图片或视频中的人进行识别、计数并输出结果的系统配套 Python 源码、训练好的模型和一份能讲清楚“为什么这么做”的说明文档。和做口腔疾病图像识别这类深度学习视觉毕设一样核心方法论是相通的——先选定模型再解决数据最后把检测逻辑封装成可以演示的系统。本文不教你重新发明目标检测而是按“选型、准备数据、训练、串成系统、避坑”这条路线把每一步做成你能直接复现的作业。新手按命令走能跑通熟手直接看参数和边界坑。2. 方案选型为什么毕设几乎都落在 YOLO 计数逻辑上2.1 检测方案对比为什么不是 Faster R-CNN也不是 SSD人流量检测本质是目标检测加上计数后处理。现阶段能落地且易解释的路线主要是三条Faster R-CNN、SSD 和 YOLO 系列。Faster R-CNN 准确率高但两阶段结构在视频推理时帧率低如果你要做实时演示画面一卡一卡很减分。SSD 速度尚可但小目标效果一般而人流场景里远处的人往往只占几十个像素正好打在短板上。YOLO 系列是目前毕设里最稳妥的选择v8 前后的版本在精度和速度之间平衡得最好官方有预训练权重数据格式相对友好而且你答辩时能拿“准确率和实时性如何权衡”这个点展开讲。我一般建议选 YOLOv8 或 YOLOv5 作为检测基座前者接口更现代后者生态文档更多。如果你用的是 PyTorch两者都原生支持不用额外装奇怪的依赖。对于人数统计不要直接在检测框数量上求和而是按检测框中心点做区域判断加上简单的帧间去重逻辑。后面第四章会给出可运行代码。2.2 系统模块拆分拿到题目先画好这四个部分拿到题目后不要立刻去抄源码先把这个系统拆成四个模块后面写代码和写论文都按这个骨架展开数据模块图像或视频输入支持摄像头更佳但演示环境未必有 USB 摄像头所以先保证图片和视频文件能跑通。检测模块利用 YOLO 模型推理出画面中每个人的边界框、置信度和类别且只保留 person 类。计数模块对检测框中心点做区域判定划分“入口区、出口区、停留区”统计当前人数和累计人数。可视化与输出模块在帧上绘制检测框和计数结果可将结果导出为 CSV 或标注后的视频。这四块基本对应你要写的源码包里的四个目录或四个文件。结构清晰还有一个好处论文的“系统设计”章节可以按图说话不用对着一个巨型脚本硬编故事。环境搭建上用 conda 或 venv 隔离 Python 虚拟环境避免把系统 Python 搞乱。很多同学在这一步就翻车直接在全局环境 pip install然后被各种依赖冲突折腾一整晚。3. 数据准备与模型训练从标注格式转换到权重复现3.1 数据集从哪里来开源数据集和自建小样本怎么搭配人流检测的训练数据通常有两个来源。第一是开源数据集常见做法是使用 CrowdHuman、MOT Challenge 或 SCUT-HEAD 等公开数据集它们都带人框标注。如果你的题目要求场景限定在校园或商场那么更合理的做法是用开源数据预训练再补充你自己拍摄的几十到几百张目标场景图片做微调。不要指望自建几百张就能从零训出一个可用模型深度学习模型的收敛依赖数据多样性场景太少会出现严重的过拟合。最简单可行的流程是下载开源数据用其中的人流量相关子集做训练自建数据只做测试和少量微调。数据标注工具常见做法是用 LabelImg 或 Label Studio前者轻量后者支持多人协作。标注时只需画矩形框类别填 person。注意框要贴合人的轮廓不要上下各留一大截空白这会影响 IoU 计算也容易让模型学会“框得比人还大”的错误惯性。3.2 把 VOC 或 COCO 格式转换成 YOLO 格式的脚本开源数据集里 CrowdHuman 常用 VOC 风格标注或者直接给 JSON。YOLO 系列训练需要的格式是每张图片对应一个同名 .txt 文本文件每行是“类别 中心点x归一化 中心点y归一化 框宽归一化 框高归一化”。这个转换不写脚本会很痛苦。下面是一个把 VOC XML 转成 YOLO txt 的最小脚本按实际路径改一下就能用。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_dir, txt_dir, class_names): os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) txt_name os.path.splitext(xml_file)[0] .txt 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) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: with open(os.path.join(txt_dir, txt_name), w) as f: f.write(\n.join(lines)) if __name__ __main__: class_names [person] voc_to_yolo(data/VOC/annotations, data/VOC/labels, class_names)这段脚本的核心是解析 XML 里的bndbox坐标然后除以图片宽高做归一化。有一点容易忽略XML 里size下的宽高必须和实际图片一致否则框会错位。我在实际处理中遇到过图片 PNG 是 RGBA 通道但标注尺寸按 RGB 读的情况导致的直接后果是训练时 mAP 看着正常推理时框总是偏左上。所以转换完一定要抽样可视化检查不要直接开训。3.3 训练命令与关键参数选择数据准备好之后用官方仓库的训练入口即可。以 YOLOv8 为例安装 ultralytics 包后命令行直接跑yolo train datadataset.yaml modelyolov8n.pt epochs50 imgsz640 batch16 device0如果数据集目录结构是按 images 和 labels 组织的dataset.yaml内容类似path: ./datasets/flow train: images/train val: images/val names: 0: person训练时最常见的参数选择是模型规模用yolov8n或yolov8s毕设场景不需要追求最大精度显存有限时 n 版本迭代更快imgsz用 640人流场景远距离小目标可以试 960但显存占用会明显涨epochs不用盲目上 30050 到 80 轮足够看清收敛趋势训练日志里box_loss和cls_loss不再明显下降就说明可以停了。batch大小以显存不爆为原则6GB 显存跑 n 版本设 16 没问题跑 s 版本要降到 8。梯度累积不熟不要轻易开保持默认策略更稳定。训练结束后在runs/detect/train目录下看results.png和confusion_matrix.png前者能看出 loss 曲线是否正常收敛后者能看出误检主要集中在哪一类上。4. 把检测模型串成人流量统计系统计数逻辑与可视化4.1 检测封装置信度过滤和类别过滤为什么不能省模型训练好之后要做的第一件事不是直接数人而是写一个检测封装函数只提取 person 类结果并过滤掉低置信度的框。这一步是计数精度翻车的主要来源。原始 YOLO 推理输出会包含很多置信度只有 0.3 左右的框这些框在密集人群中大量存在不及时过滤会把人数虚高。同时模型如果同时训练了多个类别必须显式过滤出 person 类否则会把包、车、树都算成人流量。下面给出一个封装 YOLOv8 推理的检测函数输出每帧中所有人的中心点和边界框供计数模块调用。from ultralytics import YOLO def detect_persons(frame, model_pathruns/detect/train/weights/best.pt, conf_thres0.35, iou_thres0.5): model YOLO(model_path) results model.predict(frame, confconf_thres, iouiou_thres, verboseFalse) persons [] for box in results[0].boxes: cls_id int(box.cls[0]) label results[0].names[cls_id] if label ! person: continue x1, y1, x2, y2 map(float, box.xyxy[0]) conf float(box.conf[0]) cx (x1 x2) / 2.0 cy (y1 y2) / 2.0 persons.append({bbox: [x1, y1, x2, y2], center: (cx, cy), conf: conf}) return persons这段代码的输出结构很直接people 列表里每个元素包含检测框、中心点坐标和置信度。conf_thres是保命参数在白天正常光线下建议设 0.35 到 0.4夜间或摄像头角度较偏的场景调低到 0.25。iou_thres控制重叠框的抑制强度密集人群里人挨着人的情况阈值太严会合并掉真实目标建议保持 0.5 左右。注意这个函数每次调用都加载一次模型实际运行时应把模型初始化移到函数外只加载一次否则视频流处理时会非常慢。4.2 区域计数与帧间去重只统计中心点是不够的在单张图片上直接统计persons的长度就是当前人数。但视频流里同一个路人会在连续几十帧反复出现直接累加会把人数放大几十倍。常见的做法是“按帧计数”和“按进入事件计数”两种。校园入口场景通常要的是累计通过人数这时就得做去重或区域判定。如果只做当前画面人数统计直接取当前帧的检测框数量即可这个值会随时间波动正好画成折线图。下面给一个简单的按区域计数的示例把画面中线当作分界统计从下往上穿过中线的目标数。这里用目标中心点做判断并把连续帧间位移小于阈值的检测视为同一目标避免重复计数。class FlowCounter: def __init__(self, line_y480, min_move5): self.line_y line_y self.min_move min_move self.tracks [] self.passed 0 def update(self, persons): for p in persons: cx, cy p[center] matched None for t in self.tracks: dist abs(t[cx] - cx) abs(t[cy] - cy) if dist self.min_move: matched t break if matched is None: self.tracks.append({cx: cx, cy: cy, last_y: cy}) else: if matched[last_y] self.line_y cy: self.passed 1 matched[last_y] cy self.tracks [t for t in self.tracks if t[cy] cy - 2] return self.passed这个类的逻辑很朴素目标中心点在上边界下方出现当它的 y 坐标从小于分界线变为大于分界线时判定为进入一次。min_move调太大会把不同的人误判成同一个目标调太小同一个人的检测框只要轻微抖动就算成新目标在 720p 画面下一段 50 米通道内取 5 到 10 是比较稳的范围。真实场景里这种简易追踪在人群密集、遮挡频繁的时段精度会明显下降但如果你的毕设定位是“系统设计与实现”这种简单方法足够支撑你完成演示和答辩。4.3 把结果输出成可视化界面或保存文件演示时如果用 Flask 起一个本地网页在网页里实时显示检测结果和人数折线通常是毕设答辩中最出效果的做法。我一般建议让模型处理视频帧并把带框的视频保存为 mp4再同时统计每帧人数输出 CSV。下面是保存标注视频的代码片段核心是把检测框画到帧上并按帧写入人数。import cv2 def make_video_with_count(source_video, out_video): cap cv2.VideoCapture(source_video) fps cap.get(cv2.CAP_PROP_FPS) w int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) h int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) writer cv2.VideoWriter(out_video, cv2.VideoWriter_fourcc(*mp4v), fps, (w, h)) counter FlowCounter(line_yh * 0.6) while True: ok, frame cap.read() if not ok: break persons detect_persons(frame, conf_thres0.35) passed counter.update(persons) for p in persons: x1, y1, x2, y2 p[bbox] cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, fPASS: {passed}, (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) writer.write(frame) cap.release() writer.release()这段代码的坑在于detect_persons内部每次加载模型视频里每一帧都要重复加载一次整个视频处理会慢到无法接受。正确做法是在make_video_with_count外层加载一次模型把 model 传进函数或者在 detect 函数外面用全局缓存。另外视频编码用mp4v通用性最好Windows 上也能直接播放换成avc1在一些播放器里反而不能识别。输出 CSV 很简单每帧记录时间戳和当前人数即可后面画图表或者做数据分析会非常省事。5. 人流量检测避坑指南4 个最容易让毕设翻车的地方5.1 训练集里人多但标注框漏标导致漏检率高现象模型训练完测试时一个画面里五六个人只检测出两三个。 原因训练集里有很多人被漏标了或者标注框只框了上半身。模型学到的模式是“人只有半截”自然漏检。 解决用标签质量检查工具把每张图的标注框渲染出来看一遍漏标多的图片直接删掉不要留在训练集里。另外要保证标注框覆盖整个人体轮廓头部被截掉一点没关系但身体中线不能偏。5.2 计数逻辑直接统计检测框数量导致人数剧烈抖动现象同一段视频上一帧统计 12 人下一帧突然变成 20 人再过两帧又变回 13 人。 原因不是检测模型出了问题而是低置信度的误检框在连续帧之间不稳定出现。在某些交叠严重或光照差的帧里模型把一个手臂误认为一个人产生了瞬时高值。 解决置信度阈值从 0.25 提高到 0.4并加入“连续 N 帧都被检测到才算一次有效计数”的滑窗确认逻辑。这个方法在运动场景里配合min_move调参可以明显压低抖动。5.3 白天正常、夜间或逆光场景直接崩现象同一个模型白天测试效果不错一到楼道或傍晚场景人数骤减画面上全是漏检。 原因模型在训练时见过的大多是白天光照正常的数据没学过低照度下的成像特征。这不是模型玄学问题是数据分布问题。 解决收集目标场景的夜间数据尽量抽出一两百张做微调如果实在没有夜间数据可以在推理前对帧做 CLAHE 对比度增强然后再送进模型。代价是推理帧率会下降一点。如果摄像头是仰角安装的还要检查图片里人是否出现严重的上下裁切这种情况下模型训练时见过的完整人体比例与现场不符也会导致漏检。5.4 训练 loss 不降反升或者前几轮就炸掉现象loss 曲线在最开始剧烈波动然后 transformer 式的断崖上升最终收敛到无法使用。 原因最常见的是学习率太高。另一个工地现场遇到的问题是把归一化坐标写错标注文件里某个坐标值大于 1模型训练时会被拽到错误方向。还有一个可能dataset.yaml里路径写错模型实际上读到了空数据集训练时 loss 自然不稳定。 解决先跑一个只含十几张图的极小数据集如果训练能正常收敛再放大把学习率从默认 0.01 降到 0.001 跑一遍对照检查 labels 目录里所有 txt 文件的坐标值是否都小于等于 1。这个做法在调任何检测模型时都适用用最小配置排除系统性问题。6. 帧间去重的进阶技巧用轨迹平滑提高计数的可信度最后这章不讲大而全的优化只讲一个能立竿见影的细节对检测中心点做时间维度的平滑处理。简易追踪里目标的中心点在连续帧之间会有小幅度抖动直接把坐标用于区域判定会产生临界帧反复误判。可以维护一个目标轨迹列表存储最近 5 帧的中心点坐标只有当当前帧的坐标与轨迹均值的距离小于 1.5 倍最大抖动幅度时才算同一目标否则开新轨迹。def smooth_center(track, new_center, max_jitter12.0): xs [p[0] for p in track] [new_center[0]] ys [p[1] for p in track] [new_center[1]] return (sum(xs) / len(xs), sum(ys) / len(ys))这个技巧的核心是不要直接使用单帧的检测坐标去触发计数事件而是使用平滑后的轨迹坐标。触发越过门限的判定条件不变但坐标来源变了之后边界抖动造成的重复计数明显减少。我现在的习惯是每次调整检测参数后会固定同一段测试视频分别统计平滑前后的误差绝对值比较人群通过量相对参考值的偏差。这个办法能把很多“看起来合理、实际不稳”的系统在交付前排掉。希望这段思路能帮你把“基于深度学习的人流量检测系统”这个题目从头到尾串透彻。不管最后提交的源码包结构是什么样请务必保证里面包含一份能完整复现的 README、训练好的权重文件和你自己录制的一段能跑通的演示视频这会让你省去大量答辩时的解释成本。本文还有配套的精品资源点击获取