尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

深度学习人流量检测实战:YOLOv8+ByteTrack计数部署全流程

深度学习人流量检测实战:YOLOv8+ByteTrack计数部署全流程 简介目标检测与多目标跟踪是计算机视觉中的核心基础技术前者负责定位物体位置后者则通过跨帧关联维持身份一致性。在智慧园区、商场出入口等真实场景中基于这两项技术可实现高精度人数统计与方向判定解决传统红外或人工计数无法应对的拥挤、遮挡等问题。目标检测模型通常选用兼顾速度与精度的YOLOv8配合ByteTrack这类无需外观特征即可稳定关联轨迹的追踪器能有效降低ID切换带来的重复计数。进一步利用TensorRT进行FP16加速与跳帧策略可在单张消费级显卡上达到实时流畅处理。本文以人流量检测项目为线索系统讲解从数据清洗、模型训练、追踪器调参到计数线逻辑与工程部署的完整链路为实际落地提供可复用的工程参考。 去年接了一个智慧园区的项目需求很直接在主要出入口部署摄像头实时统计进出人数高峰期还要能触发预警。硬件预算有限不能上服务器集群就得靠单张消费级显卡把精度和帧率同时扛下来。折腾了一圈最后落地的方案就是基于深度学习的人流量检测——目标检测模型负责找到人多目标跟踪负责把框连成轨迹计数逻辑负责判定进出方向。这中间踩了不少坑从数据集清洗到追踪器参数调优每一步都有值得记录的经验整理出来供大家参考。这个项目适合下面几类人看正在做毕设或课程设计、想找一个人流量检测完整流程参考的同学需要在真实场景里部署人流量统计但预算有限的工程师以及已经跑通了YOLO检测、想进一步加上多目标追踪和计数逻辑的开发者。整个过程我会按项目从0到1的顺序来拆解不绕弯子直接说做了哪些事、为什么这样做、效果怎么样。1. 人流量检测项目的真实需求与技术选型1.1 场景需求决定了技术路线先聊一个核心问题人流量检测和普通的目标检测到底差在哪单纯做检测模型输出的只是一堆person的框但这只能告诉你画面里此刻有多少人回答不了这一小时进去了多少人现在应该是进还是出。真实业务要的是累计数字和流动方向这就意味着必须在检测之外加上追踪和计数两个模块。项目落地前我先梳理了一下需求边界免得到后面被业务方反复改需求需求项具体要求对应的技术环节人数识别画面中每个人都要被检出遮挡场景不能漏目标检测累积计数从早到晚的进出总人数不是瞬时人数多目标追踪计数方向判定区分进和出不能只给绝对值越线方向判断实时性摄像头画面不能明显卡顿轻量模型推理加速抗干扰背包、推婴儿车、并排行走不能导致重复计数追踪策略计数逻辑这套需求直接决定了我的技术选型方向检测模型要比两阶段方法轻追踪器不能引入大量的额外推理开销计数逻辑要简单且鲁棒。1.2 为什么选了YOLOv8加ByteTrack这条组合拳先说检测模型。我对比过Faster R-CNN、SSD、YOLOv5和YOLOv8在同样只用一张RTX 3060的情况下YOLOv8s在精度和速度上的平衡最理想。Faster R-CNN的mAP确实不低但单帧推理时间要50毫秒以上去掉NMS和后处理到追踪端就已经只剩20帧不到根本喂不动后续的计数逻辑。YOLOv8把Anchor-Free检测头和C2f结构结合起来实测在VisDrone和MOT17这类数据上YOLOv8s的mAP50能到70%以上同时单帧推理能控制在5毫秒级别TensorRT FP16下这就是量级的差距。再说追踪器。这里必须说一句DeepSORT在这个场景里不是最优解。DeepSORT需要额外训练一个ReID模型来提取外观特征模型大小和推理耗时都上来了而且当人穿深色衣服、背对摄像头时外观特征本身区分度就不高照样会跟丢。ByteTrack的思路完全不一样——它不依赖外观特征靠的是检测框质量分层匹配高分框直接做位置关联低分框在第二层补匹配对遮挡和目标短暂消失的处理反而更自然。实际效果就是在密集人流场景下ByteTrack的ID Switch比DeepSORT少约15%这个差距在计数场景里非常关键因为每次ID Switch都可能造成一次重复计数或漏计。这套组合的核心优势就是检测器承担人在哪里的任务追踪器承担哪个框是同一人的任务两者解耦哪边出问题就单独换哪边不用推倒重来。1.3 zip包里的项目结构长什么样解压之后我的目录是这样组织的建议你也这样规划后续扩展会省很多事person_flow_count/ ├── dataset/ │ ├── images/ │ ├── labels/ │ └── split.py ├── configs/ │ ├── dataset.yaml │ └── tracker.yaml ├── models/ │ └── yolo_tracker.py ├── scripts/ │ ├── train.py │ ├── track.py │ └── count.py ├── weights/ │ └── yolov8s_person.pt ├── utils/ │ ├── line_counter.py │ └── visualizer.py ├── demo.py └── requirements.txt细说两个容易被忽略的点。configs/tracker.yaml里我单独把ByteTrack的参数独立出来而不是硬编码在代码里因为追踪器参数调起来比检测模型更敏感后面我会讲具体参数的影响。weights/目录里放的是微调后的权重不是官方预训练权重这个细节等下在训练章节展开。2. 训练数据决定准确率上限的关键一步2.1 数据集从哪来公开数据集与自采数据的比例人流量检测的数据集很多人第一反应是拿COCO 80类直接训我只能说这样做的上限很低。COCO里的person标注很杂包含大量远景、小目标、截断的人而且类别间存在一些容易混淆的干扰项雕塑、海报上的人像都会被标成person。用于实时人流计数你需要的是一个类别纯净、场景聚焦的数据集。我的做法是用MOT17和MOT20的训练集提供密集行人场景这两个数据集来自真实监控视角遮挡和 crowded 场景多正好锻炼模型的抗遮挡能力补充CrowdHuman的部分数据它专门针对高密度人群设计每张图平均23人很多严重遮挡的标注对提升recall帮助很大从自己的监控摄像头里截取约2000帧画面覆盖不同时段早中晚、不同角度俯视和水平视角这部分数据很重要因为模型最终要适配的是你的真实部署环境而公开数据集和你的场景一定存在域差异。三类数据最终的比例大概控制在5:3:2。如果你完全采不到自采数据至少也要保证公开数据里有和你摄像头角度接近的场景否则装上去效果会打一个大折扣。2.2 标注格式与数据清洗标注格式我统一转成了YOLO的txt格式每行一个目标内容是类别id 中心点x 中心点y 宽度w 高度h全部除以图片宽高归一化。这里有一个关键点人流量检测项目类别只用person这一类就够了不要顺手把car、bus、bicycle标进去。多类别训练虽然对骨干网络没有坏处但会稀释单类的梯度贡献而且在推理时会产生其他类的框需要在计数逻辑里再次过滤纯属给自己找麻烦。标注之后一定要做清洗哪怕用的是公开数据也要过一遍。我写了一个脚本做了三件事过滤异常宽高比人的宽高比通常在0.2到0.8之间超过这个范围很可能是标注错误或者把广告牌、车门上的贴画标进去了过滤小目标小于16x16像素的框直接删掉因为经过模型下采样后这些小目标的特征已经丢失训练只会引入噪声人工抽检每500张图抽出一张做可视化主要看有没有把阴影、镜像、广告牌里的人当成person的标注。这一步清洗大概会滤掉5%~8%的标注框换来的是精度提升非常明显训练时loss曲线更平滑验证集mAP比不洗数据高2到3个点。2.3 数据增强策略怎么配YOLOv8自带的增强策略已经很成熟默认开启的mosaic、mixup、HSV抖动、随机仿射变换我基本都保留了但针对人流量检测场景我单独调整了两个参数hsv_h色相抖动从默认的0.015降到了0.005因为监控场景里颜色信息的价值很高太强的颜色扰动会让模型学到错误的颜色关联fliplr水平翻转保持开启但要注意翻转之后标注框的坐标要同步做水平映射YOLO会自己处理你不用操心。另外不要用太大的输入分辨率走完整训练流程。有人以为分辨率越高精度越高直接上1280训练速度掉一半不说推理速度也受影响。我用640作为训练尺寸实际部署时如果摄像头画面噪点多再用TensorRT的输入尺寸640或者768就够用了。小分辨率还有个好处推高batch size模型训练更容易收敛。这段想表达的核心经验是数据环节花的时间越长后面调模型的时间越短。很多人把人流量检测效果差归咎于网络结构其实数据本身就是最大的瓶颈。3. 模型训练全流程与环境配置3.1 环境搭建版本匹配是第一道坎训练环境用的是Ubuntu 22.04 CUDA 11.8 cuDNN 8.6 PyTorch 2.0.1。这里强调一个版本匹配的问题PyTorch的预编译包是绑定CUDA版本的pip install torch 默认装的CPU版本跑训练会慢到怀疑人生。建议直接通过PyTorch官网的安装命令指定CUDA版本例如pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118安装完一定要验证GPU是否可用这一步能省去后面很多排查时间python -c import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))如果输出是False一般就是NVIDIA驱动和CUDA之间版本不匹配。建议先装驱动再装CUDA Toolkit配套版本驱动版本尽量新一些兼容性更好。训练环境中我不建议用conda混装太多包YOLOv8的依赖其实很简单opencv-python、ultralytics、pandas、matplotlib就够装一个纯净的虚拟环境隔离出来最好。3.2 训练参数与调参记录我用的是YOLOv8s作为初始权重在自建数据集上跑了100个epoch。配置文件configs/dataset.yaml大致长这样path: ./dataset train: images/train val: images/val nc: 1 names: [person]训练命令yolo detect train dataconfigs/dataset.yaml modelyolov8s.pt epochs100 imgsz640 batch16 device0 patience20这里的patience20是需要注意的点代表20个epoch内如果验证集指标没有提升就提前结束。最开始我没开early stopping硬跑100个epoch结果第75轮之后val loss就开始反弹模型过拟合了。开了patience之后实际在第68轮就自动收敛省了时间也保住了泛化能力。几个关键参数的实测感受参数我的配置影响imgsz640提高分辨率能提精度但训练和推理时间都涨小项目没必要batch16显存不够就降到8太小的batch会引入噪声收敛不稳lr00.01默认值即可别乱调大会震荡发散optimizerSGDAdam收敛快但最终精度略低于SGD我还是一路用SGDmosaic1.0训练集目标密集时很有用帮助模型抗遮挡训练结束后我还会单独跑一遍测试集输出PR曲线和混淆矩阵。人流量检测特别要关注recall因为漏检一个人意味着计数少一个这个误差会一直累积下去。如果测试集recall低于90%优先增加数据或调低置信度阈值而不是盲目调高iou阈值。3.3 训练中的坑和对应的解法训练过程中我遇到了两个值得记录的坑坑一显存不足CUDA out of memory。8G显存的卡跑batch16很容易爆。解决办法不是硬降batch而是开启梯度累积batch8配合YOLOv8内部的累积机制或者干脆降到batch8继续跑。如果还想再大可以开启amp混合精度训练显存占用能再降一半加速也明显。坑二训练过程中mAP50假性下跌。第40轮左右mAP50突然掉到30%后来发现是mosaic增强导致标注框被切到图像边缘产生大量不完整真值。YOLOv8的增强逻辑本身会过滤掉过度失真的框但在极暗或者夜间图像上增强后的图像亮度太低模型一度学不到有效特征。解决办法是在数据配置里增加夜间图像的比例让增强后的图像分布接近真实场景。4. 从检测框到过线人数追踪与计数逻辑4.1 为什么检测完不能直接数框这是很多第一次做人流量统计的人最容易犯的错误。如果你直接数当前帧的检测框数量得到的是画面瞬时人数完全不是流量。人停下来排队的时候框的数量会增加人走过去之后数量又变成0这个数字没有任何业务意义。流量计数必须要做跨帧关联也就是追踪把同一行人在连续帧中的检测框串成一条轨迹然后判断这条轨迹是否跨越了一条我们设置的虚拟计数线跨越的方向就是进出的方向。所以计数逻辑的正确性上限实际上是被追踪的稳定性决定的。4.2 ByteTrack追踪器的接入与参数调优ByteTrack的核心逻辑不复杂分两步先用高置信度检测框做第一轮匹配位置IOU在0.2以上就算匹配上再用低置信度的检测框和剩余轨迹做第二轮匹配这样能把因为遮挡导致置信度下降的目标重新关联回来从而减少ID切换。接入方式可以直接通过Ultralytics的model.track接口from ultralytics import YOLO model YOLO(weights/best.pt) results model.track(camera_01.mp4, persistTrue, trackerconfigs/bytetrack.yaml, conf0.3, iou0.5, showTrue)但生产环境里我更推荐直接调用底层接口方便拿到每个track的坐标序列。核心的追踪配置在configs/bytetrack.yaml里调参经验如下tracker_type: bytetrack track_high_thresh: 0.5 track_low_thresh: 0.1 new_track_thresh: 0.6 track_buffer: 30 match_thresh: 0.8track_high_thresh0.5置信度高于0.5的框进入第一轮匹配。太低会把大量背景框关联成轨迹导致虚拟人数虚高track_low_thresh0.1低于0.5但高于0.1的框进入第二轮补匹配。这个值必须低于high threshold给遮挡目标留下机会track_buffer30轨迹最多存活30帧按30帧视频算就是1秒。人离开画面之后1秒才删除轨迹能有效防止短暂离开又被计一次的重复计数。但不要设得太大否则一个人站在原地轨迹会长时间保持过线判断会被干扰match_thresh0.8匹配阈值大于0.8才认为是同一人。太严容易跟丢太松又容易把不同的目标绑在一起。针对密集场景我一般设0.75到0.85之间。这里有个真实体验ByteTrack调参时最核心的是track_buffer和match_thresh的配合。如果发现计数结果忽高忽低多半是track_buffer太小导致同一人反复生成新ID。这个参数值得你在自己的数据上多试几组值用一小段视频反复回放观察ID变化。4.3 计数线与进出方向判断的实现追踪器给了每个目标的ID和中心点序列接下来就是计数逻辑本身。我用的方案是虚拟计数线加跨越方向判断在图像上定义一条线段比如进出口的划线位置每个目标被视为一个点中心点当这个点在相邻两帧从线段的一侧移动到另一侧时就认为完成了一次过线。判定的数学原理是向量的叉积方向。设计数线上的两个点为A(x1, y1)和B(x2, y2)目标点P(px, py)在线哪一侧等价于向量AB与AP的叉积符号def point_side(x1, y1, x2, y2, px, py): # 返回正数代表点在线的右侧负数代表左侧 return (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1)对于同一个track ID我维护一个最近的轨迹点列表每帧检查当前点和上一个点的叉积符号。符号发生了从正到负或从负到正的翻转就说明该目标跨越了计数线再根据翻转方向判断是进还是出。for track_id, history in track_points.items(): if len(history) 2: continue last_pt history[-2] # 上一帧中心点 curr_pt history[-1] # 当前帧中心点 side_before point_side(A_x, A_y, B_x, B_y, last_pt[0], last_pt[1]) side_after point_side(A_x, A_y, B_x, B_y, curr_pt[0], curr_pt[1]) if side_before * side_after 0: direction in if side_after 0 else out count[direction] 1 # 防止同一目标连续触发多次 track_counted[track_id] True这个方案实现简单但有一个坑一定要处理计数线方向与人的实际朝向可能不一致。比如人从下往上走在图像坐标系里是从线下方穿到线上方但业务定义可能是出而不是进。这种情况不需要改代码只要在计数线两端设置in_side和out_side两个区域再按实际业务定义把方向映射出去就行。我项目中的做法是直接给每条计数线配一个配置项lines: - name: 正门入口 start: [100, 400] end: [600, 420] in_side: left # 点在左侧记为进 out_side: right这套逻辑跑下来单通道视频分辨率1080p在CPU上的整体处理速度约25 FPSGPU上可以跑满实时。计数结果和人工统计对比误差率在5%以内。5. 部署优化让模型在真实摄像头下跑起来5.1 卡顿的根源与加速路线实验室里测试用的是预先录制好的视频帧率不高所以一开始没发现性能问题。真正接上200万像素的RTSP视频流之后问题立刻暴露画面每隔几秒就卡一下延迟能到3秒以上。排查发现瓶颈在三个地方CPU解码RTSP并不快YOLOv8s的PyTorch推理太慢检测帧率跟不上视频流的输入帧率。我的优化顺序是这样的用硬件解码OpenCV直接cv2.VideoCapture(rtsp_url)走的是软解非常吃CPU。我换成了FFmpeg GStreamer的管线把解码放到显卡上NVDECCPU占用直接降了一半。如果你用GStreamer不熟悉一个折中方案是用imageio-ffmpeg配合多线程抓帧也有明显改善只用TensorRT做推理PyTorch的Eager模式推理太慢中间有大量Python调度开销。我把YOLOv8s导出为TensorRT FP16引擎代码量不大yolo export modelweights/best.pt formatengine device0 halfTrue导出之后再加载model YOLO(weights/best.engine)实测FPS提升惊人RTX 3060上从约30 FPS提升至约90 FPS因为TensorRT会把网络结构优化并利用FP16算力手写的一些后处理还能进一步合并进去 3.帧率控制与跳帧策略监控视频源是25或30 FPS检测不需要每一帧都跑。实测只要每2帧检测一次配合ByteTrack的线性插值计数精度几乎不受影响计算压力直接减半。我最终的策略是检测帧率15 FPS追踪帧率等于视频帧率中间帧用上一帧的检测结果执行IOU关联既保证追踪连续性又控制算力消耗。5.2 实测数据分析这是部署完成后的实测数据RTX 3060 12G i5-12400F 32G内存方案推理耗时/帧实际FPS说明YOLOv8s PyTorch Eager28 ms约25CPU解码 GPU推理存在明显卡顿YOLOv8s TensorRT FP1611 ms约70推理时间大幅下降接近实时YOLOv8s TensorRT 跳帧11 ms每2帧跑一次稳定25~30实际视频流完全流畅计数稳定从表格可以看出真正让系统跑到流畅状态的关键是两个动作TensorRT导出 跳帧策略。如果你是在CPU上跑可以考虑YOLOv8n配合OpenVINO导出一样能跑出15 FPS以上的效果只是精度会稍微降低。5.3 工程化收尾的细节模型跑起来之后还有很多工程细节会决定项目能不能长期稳定运行RTSP断流重连摄像头偶尔会断开进程如果直接崩溃就完了。我写了一个重连循环尝试3次失败后等待10秒再重建VideoCapture对象同时保留上一次的计数快照避免数据清零计数结果的持久化每5分钟把当天的进/出总人数写入SQLite方便做报表。另外预留一个WebSocket推送接口方便接大屏展示边界区域的灯光差异晚上的监控画面噪点很多检测置信度会下降。我的做法是按时间段动态调整conf阈值白天0.35晚上0.25同时加一个轻量的自适应直方图均衡预处理晚上recall能提升5%左右日志与可视化除了输出计数数字我还把画了检测框、轨迹和计数线的视频帧保存成视频文件方便人工核查。这个很重要——没有可视化业务方很难相信你的数字是对的。这套流程从数据准备到部署大约花了两周时间。如果你自己做数据清洗和标注这块可能需要多花几天准备。人流量检测难点不在单点技术上每个模块单独看都不难真正的工程点在于把检测、追踪、计数、部署这些环节串起来并且让每个环节都匹配上真实场景的数据分布。希望这些经验能帮你少走一些弯路。本文还有配套的精品资源点击获取
返回列表