尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于YOLOv5和DeepSort的人流量监测WebApp实现

发布时间:2026/9/24 22:30:04

资讯中心
01
ARTICLE

基于YOLOv5和DeepSort的人流量监测WebApp实现

基于YOLOv5和DeepSort的人流量监测WebApp实现
简介这是一套基于Yolov5与DeepSort的人流量监测WebApp完整项目源码面向计算机视觉开发者、智慧安防从业者及AI应用初学者解决监控场景中实时人体检测、跨帧追踪与流量统计的落地问题。压缩包共318个文件大小64.37MB主体为163个Python脚本、52个YAML配置文件另有Markdown/RST文档、PTH/PT权重文件、Shell部署脚本、Dockerfile以及CUDA/C扩展源码兼顾模型推理、参数配置、容器部署与二次开发。目前已有289人学习实现上Yolov5负责单次前向预测人体边界框DeepSort借助卡尔曼滤波和外观特征完成连续跟踪最终通过Streamlit呈现检测画面与人数统计图表形成可运行的端到端方案。压缩包内包含模型权重、预处理与后处理脚本、推理演示视频和使用说明用户按目录结构运行启动脚本即可部署。也可基于现有代码进一步调优模型、替换数据集或扩展功能适合作为智慧安防与人流统计项目的参考起点。1. 人流量监测为什么非要 YOLOv5 加 DeepSort先看清楚它在解决什么问题先放结论只用一个目标检测模型做的人流量统计在真实场景里基本是废的。摄像头下面人群一旦出现遮挡、并行、折返检测框还在但你根本分不清下一秒这个框到底是刚才那个人还是新进来的人。没有跨帧身份关联人数会重复统计进出方向更无从谈起。而「基于Yolov5和DeepSort的人流量监测 WebApp」这个完整方案核心就是用 YOLOv5 负责「框出每一帧里的人」用 DeepSort 负责「给每个人一个稳定的 ID 并跟踪到底部」最后把这些结果通过网络接口送给前端页面形成一个能实时看、能查历史、能设定进出规则的落地工具。它不是给算法研究员做论文实验的是给商场、园区、校园、展厅这类有监控摄像头的管理方做日常运营用的。你需要把一段视频流变成「当前在场人数、今日累计进出、各区域密度」这样的业务数据并且能在一台普通服务器上长期跑。本文聊的就是这条落地路径。2. 任务拆解与选型为什么 YOLOv5 和 DeepSort 是 WebApp 当下最稳的组合2.1 检测和跟踪谁先谁后没有身份的人流数据没有意义人流量监测的数据链条是「检测出人 → 关联出身份 → 统计出业务」。三者不能混为一谈。YOLOv5 输出的每一帧结果是若干矩形框每帧互相独立它不知道上一帧里的第 3 个人和这一帧里的第 5 个人是不是同一个人。DeepSort 解决的就是这个问题它基于目标间的外观特征和运动特征为每个检测框分配一个全局递增的跟踪 ID。延时设计上必须一帧一帧串行处理。先跑 YOLOv5 得到检测框再把检测框交给 DeepSort 去匹配已有轨迹而不是反过来。有些工程为了省时间会隔帧检测、中间帧用跟踪结果补这是可以做的优化但 WebApp 第一版不建议这么搞先保证逻辑链路干净再谈性能。2.2 选型对比四层技术栈的取舍整个 WebApp 涉及四层选型每一层都有替代品但组合起来最省心的就是下表这套。层级本方案选型备选方案选择理由人员检测器YOLOv5可在 nano / small 间切换YOLOv8 / RT-DETR / PP-YOLOE权重文件多、跨版本兼容好DeepSort 适配代码几乎开箱即用多目标跟踪器DeepSortPyTorch 实现ByteTrack / StrongSORT / OC-SORTByteTrack 精度更高但依赖检测质量DeepSort 对低帧率视频更稳后端服务FastAPI UvicornFlask / Django自带异步后续接视频流接口更自然前端页面Vue 3 EChartsReact / 原生 HTML实时热区和趋势图生态好且开发成本低这里多说一句选型逻辑。如果项目周期很紧不要在一开始就试图把跟踪换成 StrongSORT 这种最新改进版。DeepSort 的代码路径是这三四年被踩得最熟的网上参数讨论多遇到问题容易搜到答案。精度不够时先调检测器的置信度和跟踪器的匹配阈值通常能把 MOTA 拉回可用水平。2.3 系统模块边界从摄像头取流到前端渲染的完整链路常见做法是把整个系统拆成三个独立模块各自进程隔离。采集与预处理模块负责读取 RTSP / 本地视频文件做抽帧、缩放、归一化输出一张标准尺寸的 RGB 图。核心算法进程加载 YOLOv5 和 DeepSort串行执行检测与跟踪把每个人的轨迹点、实时框、进出判定结果推给结果队列。业务服务层从结果队列取数据写入数据库同时通过 WebSocket 推给前端展示。我一般会强调「模块进程隔离」这件事。很多第一次做 WebApp 的人把检测器和后端框架放进同一个 Python 进程表面看没问题但 PyTorch 的推理和 Uvicorn 的事件循环在一起时非常容易互相阻塞页面一打开 GPU 利用率反而掉下去。保持解耦后续优化才有空间。3. 跑通检测与跟踪核心链路从 YOLOv5 检测框到 DeepSort 跟踪 ID3.1 环境准备与目录结构以 conda 约束 YOLOv5 依赖先建一个干净的 conda 环境把 YOLOv5 和 DeepSort 的依赖收敛在一起。TensorRT 或 ONNX 导出不是第一版要做的事先保证这套代码能在 GPU 上跑通。conda create -n flow_monitor python3.9 -y conda activate flow_monitor conda install pytorch torchvision torchaudio pytorch-cuda11.8 -c pytorch -c nvidia -y pip install -r requirements.txt其中requirements.txt里至少包含opencv-python、numpy、scipy、lap0.4.0、fastapi、uvicorn、websockets。有一个常见的坑需要提前指出DeepSort 里做匈牙利匹配用的lap库在 Python 3.9 以上容易编译失败建议直接装lap0.4.0的 wheel不要图省事用scipy里的 linear_sum_assignment 替换速度差很多。目录结构建议如下flow_monitor/ ├── weights/ │ ├── yolov5s.pt │ └── deep_sort_pytorch/ ├── detectors/ ├── trackers/ ├── api/ # FastAPI 路由 ├── static/ # 前端文件 └── config.yaml # 所有可调参数集中放这里有人习惯把参数散落在多个 py 文件里维护起来非常痛苦。把所有可以调的东西集中在config.yaml后面调参时会感谢自己。3.2 检测结果转跟踪输入核心转换函数解析DeepSort 接收的不是 YOLOv5 的原始输出而是标准化的检测结果格式。这里有一个必须处理干净的转换函数它决定跟踪器能不能稳定工作。import numpy as np def detect_to_track_input(detections, conf_thres0.5): 将 YOLOv5 的检测结果转为 DeepSort 需要的格式 :param detections: list每个元素是 [x1, y1, x2, y2, conf, cls] :return bbox_xywh: array格式为 [cx, cy, w, h]DeepSort 内部使用中心点坐标 :return confs: 每个框的置信度 :return clss: 类别 ID这里只保留类别 0person bbox_xywh [] confs [] clss [] for det in detections: x1, y1, x2, y2, conf, cls det if cls ! 0 or conf conf_thres: continue w x2 - x1 h y2 - y1 cx x1 w / 2 cy y1 h / 2 bbox_xywh.append([cx, cy, w, h]) confs.append(conf) clss.append(cls) if len(bbox_xywh) 0: return np.array([]), np.array([]), np.array([]) return np.array(bbox_xywh), np.array(confs), np.array(clss)逻辑说明YOLOv5 输出的框是左上角和右下角坐标而 DeepSort 的update方法接受的是中心点坐标加宽高。这一步不做或者做错跟踪框会整体偏移直观表现是 ID 和检测框对不上。另一个需要注意的点是类别过滤必须在转换前完成只保留cls 0的人类别。参数说明conf_thres是置信度阈值它的选择直接决定跟踪稳定性。设得太低如 0.3会把椅子、柱子上的人形花纹也算进来ID 数量爆炸设得太高如 0.8远处的小目标全部丢掉跟踪轨迹断掉。YOLOv5s 在一般监控视角下用 0.5 起步比较合适。3.3 跟踪器管理与业务计数实现真正的「人流量」口径跑通单帧跟踪后还需要解决业务计数问题。每次都新建 DeepSort 实例、或者把历史轨迹全保存下来都是错误做法。正确做法是维护一个「活跃轨迹」集合每帧用跟踪结果更新它并从中提取业务指标。class FlowTracker: def __init__(self, max_cosine_distance0.4, max_age30): from deep_sort import DeepSort # 初始化 DeepSort 跟踪器 self.deepsort DeepSort( model_pathweights/deep_sort_pytorch/deep_sort_pytorch.pt, max_distmax_cosine_distance, max_iou_distance0.7, max_agemax_age, n_init3, nn_budget100, ) # active_tracks: key 为 track_idvalue 为最近 N 帧的位置和判定状态 self.active_tracks {} self.cumulative_count 0 # 今日累计人次 def update(self, frame, bbox_xywh, confs, clss): # DeepSort 内部完成卡尔曼滤波预测和级联匹配 outputs self.deepsort.update(bbox_xywh, confs, clss, frame) if len(outputs) 0: return current_ids set() for track in outputs: track_id track[4] x1, y1, x2, y2 track[:4] current_ids.add(track_id) if track_id not in self.active_tracks: # 新 ID 出现累计人次 1 self.active_tracks[track_id] {entry: True, last_pos: (x1, y1)} self.cumulative_count 1 else: self.active_tracks[track_id][last_pos] (x1, y1) # 清理已经消失的轨迹 self.active_tracks { tid: info for tid, info in self.active_tracks.items() if tid in current_ids } return outputs逻辑说明DeepSort 的update方法内部完成了卡尔曼滤波预测、外观特征匹配和 IoU 匹配三件事外部不需要也不应该干预匹配过程。这份代码的关键在current_ids的维护——只有当前帧还存在的 ID 才会保留消失的轨迹直接丢弃这样累计计数才不会膨胀。参数说明max_dist0.4是外观特征的最大余弦距离超过这个值视为不同的人。它和n_init3配合使用一个新检测框要连续 3 帧被匹配成功才会创建正式轨迹否则丢弃。max_age30表示轨迹丢失超过 30 帧后删除。这三个参数是 DeepSort 调优的核心比调什么网络结构都管用。很多人跟踪 ID 乱跳原因就是max_dist设得太小或者n_init太大行人一遮挡就丢了。4. 服务化与前端展示把算法进程变成可访问的 WebApp4.1 用 FastAPI 封装推理服务后端接口与异步模型加载算法逻辑跑通后接下来面对的是「怎么让前端调用它」。常见做法是把检测、跟踪、计数器整体封装成一个推理服务类对外暴露一个/api/flow接口。FastAPI 天然支持异步不会像 Flask 那样把并发请求堵死在同步代码上。from fastapi import FastAPI, WebSocket import uvicorn import asyncio import json from core.flow_tracker import FlowTracker from core.detector import YOLODetector app FastAPI() detector YOLODetector(weightsweights/yolov5s.pt, devicecuda:0) tracker FlowTracker() # 全局单例保证跟踪 ID 连续 # 全局队列数据采集线程把每帧跟踪结果写入此队列 result_queue asyncio.Queue(maxsize100) app.websocket(/ws/flow) async def flow_ws(websocket: WebSocket): await websocket.accept() while True: data await result_queue.get() await websocket.send_text(json.dumps(data)) app.get(/api/stats) def get_stats(): return { current_count: tracker.get_current_count(), cumulative_count: tracker.get_cumulative_count(), } if __name__ __main__: uvicorn.run(app, host0.0.0.0, port8000)逻辑说明FlowTracker被声明为全局单例这一点极其重要。如果每个请求都新建一个 tracker跟踪 ID 会从 0 重新开始所有历史统计全部失效。视频采集线程把每帧的检测跟踪结果放入result_queueWebSocket 端点把结果推给前端HTTP 接口/api/stats则服务端轮询当前统计信息。参数说明maxsize100的队列是个背压保护。当消费速度跟不上时队列会丢弃最旧的数据或阻塞写入避免内存无限增长。这里用的是丢弃旧数据策略实时的含义是最新状态不是全部历史。如果要做完整历史录像那需要换消息队列方案当前 WebApp 阶段没必要。4.2 人流量判定规则入口线、徘徊与区域驻留怎么落地检测和跟踪跑通只是拿到了「谁在哪」的数据真正的业务口径要靠规则层定义。实际部署时判断规则会集中在config.yaml里描述这里给一组常用规则做参考。业务需求实现思路口径说明进 / 出人数在画面中画一条虚拟分界线记录每个 ID 前后两帧中心点的 y 坐标跨线方向跨线后该 ID 进入「已计数」状态区域人数与密度用多个矩形 ROI判断各 ID 中心点落在哪个区域可以同时统计「区域最大驻留人数」徘徊 / 滞留对单个 ID 计算其轨迹中心点位移速度和持续存活帧数当位移小于阈值且帧数较大时标记适合做「异常聚集」预警同时在场人数直接读active_tracks的长度注意是「瞬时值」而非「累计值」前端展示的实时在线人数规则层我最想强调的还是「跨线只计一次」这个逻辑。很多第一次实现的人会写「如果人的坐标超过线就计数」但这种写法在一个人徘徊在线附近时会产生大量重复计数。正确思路是记录每个 ID 是否已经跨过线跨过之后即便再回到线这边也不重复计数同时当一个 ID 从视野中消失超过某个阈值后将它的记录删除允许再次进入时重新计数。4.3 前端展示与数据链路从轮询到 WebSocket 的实时性演进前端第一版不需要做得很复杂一个视频区域、一个热力区域、一个实时趋势图就够用。视频流推荐用 MJPEG 流而不是 WebRTC因为 MJPEG 是 JPEG 图片序列天然每一帧都携带完整信息丢帧不会造成画面花屏或卡死。很多 WebApp 项目在视频预览上强上 WebRTC结果在长连接不稳定时出现各种兼容问题投入产出比很低。数据刷新链路更推荐 WebSocket。HTTP 轮询适合看历史统计比如 5 分钟粒度的人流量报表但「当前场内有多少人」这种数据如果在 Web 端 1 秒轮询一次既消耗服务器资源看到的结果也不连续。WebSocket 一帧推一次结果时前端要做节流显示层 2 秒刷新一次图表就够了没必要 1 秒刷一次让用户盯着一堆数字跳动。5. 实战避坑与排查人流量监测 WebApp 最常见的五个问题5.1 必坑跟踪 ID 频繁跳变一个人被识别成五个人现象是画面里同一个行人走过去跟踪框上的 ID 从 12 变成 45 再变成 78在场人数飙升。原因是检测框不稳定行人稍微一重叠就丢检测DeepSort 接不到检测框轨迹被迫中断重新出现时就被分配了新的 ID。这和 DeepSort 的算法本身没有直接关系根子在检测器上。解决的方向分三步先调高 YOLOv5 的置信度阈值到 0.55 以上把低置信度的抖动框过滤掉再把n_init调整为 5让轨迹确认变得更严格最后考虑在处理器空闲时做轻量级数据增强包括色彩抖动和平移。这三个调整做完ID 跳变问题通常能改善大半。注意不要用更高的检测置信度去强压遮挡问题那会导致漏检率上升。5.2 边坑GPU 利用率只跑到 30%但帧率还是上不去很多人在 WebApp 上线后发现 RTX 3060 跑 YOLOv5s 应该能跑到 60 FPS但实际有时只有 20 FPSGPU 利用率还很低。排查后发现瓶颈根本不在推理而在视频解码和 BGR 转换上。OpenCV 的VideoCapture.read()在 RTSP 流上的性能开销很大尤其当摄像头是 H.265 编码时OpenCV 解码是软解CPU 直接跑满。解决办法是别用 OpenCV 直接拉 RTSP改用 ffmpeg 子进程解码把解码后的原始帧通过管道读入。另一种方式是用 PyAV 库替代 OpenCV 的读取部分也能显著降低解码开销。GPU 利用率和帧率是一对反直觉的东西先查解码链路再查推理链路别上来就调批量大小。5.3 坑Web 页面长时间运行后内存持续增长直到卡死表现是 WebApp 挂机一个晚上第二天页面打开非常慢服务器内存被吃光。原因多半不是前端而是 Python 后端某个地方把每帧的结果追加到了 list 里没有清理。尤其要注意前端热力图的坐标序列、后端队列里的历史帧、以及 WebSocket 推送缓冲这三处都是内存泄漏高发区。比较隐蔽的一个坑是 OpenCV 的imencode在生成 MJPEG 流时会对每一帧产生一个 JPEG 字节数组这个数组如果挂在上一个 response 对象上没有及时释放会持续堆积。排查时可以先监控内存曲线再逐段注释代码定位通常二十分钟能找出问题点。5.4 坑夜间场景检测数量跳水人全被漏掉了夜间或暗光场景下YOLOv5 的检测效果会明显下降这不是模型错是输入数据分布变了。很多项目在白天调好的置信度阈值在夜间完全不可用。解决方向不是盲目调低置信度而是做光照自适应在预处理阶段加入直方图均衡化或自动白平衡或者根据当前帧的平均亮度动态切换置信度阈值。亮度低于某个阈值时把conf_thres从 0.5 降到 0.35并适当提高max_age让跟踪轨迹在检测暂时丢失时能撑得更久。5.5 回避不了的问题单人来回走进线计数的逻辑被反复触发这是因为规则的实现有问题而不是模型问题。计数逻辑没有记录单个 ID 是否跨过线只要坐标越过线就加一导致人站在线附近稍微晃动一下系统就自动给他进了三次。正确做法是每个跟踪 ID 维护一个布尔状态初始值为 False只有在该 ID 处于 False 状态且跨过线时才改为 True 并计数当该 ID 确实从另一侧离开视野或消失超过阈值后重置这个状态。这五个问题做完一轮排查后系统通常能稳定跑起来接下来才是谈精度验证和继续优化。6. 进阶验证与效果验收用 MOTA 和 IDSW 两条指标说话系统跑通以后不要直接说「效果很好」先用量化指标验证。找一段 5 分钟的包含进出、徘徊、遮挡的监控视频人工标注一部分帧计算 MOTA多目标跟踪准确率和 IDSWID 切换次数这两个指标。MOTA 是综合了漏检、误检和 ID 切换的评价指标数值越高越好IDSW 间接衡量了 ID 稳定性这个值越低越好。一般置信度阈值调整合理时常见监控场景下 MOTA 能到 0.85 以上IDSW 小于等于 20 次。这个指标不是给论文用的是给实际业务验证用的。如果你的 WebApp 要交付给运营方告诉他们「准确率 95%」没有说服力告诉他们「在 5 分钟 46 人的视频里系统识别出了 44 人ID 切换 12 次绝大多数是遮挡导致的」才可信。验证通过后推荐做一个热区图报表按 5 分钟粒度把每个人在每个区域的驻留时长累加并按颜色映射生成热力图层叠加到地图上。这是人流监测 WebApp 最有业务价值的输出比实时数字更受运营方欢迎。我自己做这类项目时有一个固定习惯每次调参前把config.yaml复制一份并带上时间戳运行一天后对比配置和效果慢慢就能总结出自己场景下的参数经验。这个习惯帮我避免了很多次「改了半天调回去了」的返工。这套方案不是性能最强的但它是把人流监测做成能长期运行 WebApp 的最短路径希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。