资讯动态

基于YOLO的商场客流统计系统实战:从目标检测到精准计数

发布时间:2026/10/18 4:20:53 来源:尧图企业网站定制
做商场客流统计这个需求最早是我被一位做商业地产运营的朋友问住了。他当时很直接我只想知道今天有多少人进了商场哪个入口人多有没有办法不用闸机、不用红外拿摄像头就能数出来 我当时第一反应是YOLO系列——目标检测框架里它最成熟社区资料最多遇到问题一搜就有答案v5、v8、v10三个版本加起来覆盖了大半个实战场景。后来我意识到这项目真正的难点不在能不能检测到人而在检测完之后怎么把画面里的人变成一个可靠的计数结果。这篇文章就围绕这个完整链路来写从数据集准备、模型选型与训练到跨线计数逻辑、UI界面再到实测中的排坑记录。适合的人群很明确——学过一点深度学习、跑通过YOLO demo但还没把一个检测模型变成一套完整可交付系统的人。我会把每个环节里那些文档上不会写的取舍和教训也一并说清楚。1. 项目立项时最容易被忽略的问题客流统计到底在统计什么1.1 商场运营方真正想要的不是一个人数先说需求侧。商场运营方嘴上说的是统计客流但细分下来通常是三件事第一分时段人流量曲线用来判断高峰期、指导排班和活动策划第二各入口/楼层的分流对比用来评估不同入口的吸引力甚至衡量广告位价值第三进店率的转化分析经过的人和进来的人比例如何。这三件事对应的技术方案完全不同。如果只要一个粗略曲线红外对射、Wi-Fi探针都能做但精度和细粒度都不够。红外对射只能给有没有人穿过两个人并排走就变成一个人推着购物车更是直接误判。Wi-Fi探针则受手机MAC地址随机化影响统计口径越来越不准。视觉方案的优势在于能准确给出独立个体的数量和运动方向而且是可解释的——管理者可以在屏幕上直接看到计数依据而不是面对一个黑盒数字。所以立项时我做的第一件事不是急着选模型而是把需求翻译成技术指标单通道出入口、双向统计、5分钟内误差控制在10%以内、显示历史曲线。这四个指标决定了后面所有的设计方向。1.2 为什么检测目标选人头而不是整个人第二个关键决策是检测目标。很多入门项目会直接检测person这个类别但在商场监控场景下这并不明智。俯视或斜俯视的摄像头画面里行人的下半身经常被柜台、推车、其他人遮挡拥挤时人体框互相重叠NMS一压漏检率立刻上来。反过来人头在俯视角度下遮挡最少且轮廓相对稳定检测框也小两个挨着走的人头框不容易大面积重叠加。还有一个很现实的原因检测整个人的计算量更大而实际计数时我们只需要一个稳定的锚点。用头肩或纯人头框模型学习难度更低数据标注成本也更低。我做标注规范时定的就是只标完整可见的头部超过一半被遮挡不标侧脸、后脑勺都算帽子和头发覆盖不影响。这听起来像小事但标注标准不统一数据集再大也白搭。1.3 用YOLO系列这个决策的底层逻辑目标检测领域现在可选方案其实挺多Faster R-CNN、SSD、DETR、YOLO系列各有人用。我选YOLO系列的原因有三个维度。第一是工程成熟度。从v5开始官方仓库已经把训练、验证、导出、部署的链路包装得很完整了甚至不需要自己写数据加载器YOLO格式的标注文件夹一丢进去就能跑。相比之下DETR这类要自己处理匹配逻辑和优化器细节对做项目而不是做研究的人来说时间成本高不少。第二是推理速度。客流统计系统往往要同时接4路、8路摄像头每路都要实时推理。单卡、单线程、TensorRT部署的情况下YOLO系的吞吐量优势非常明显尤其是v8和v10这些新版本在骨干网络上的优化能让每帧推理时间压到几毫秒到十几毫秒量级。而两阶段检测器在这个场景下基本没有竞争力。第三是版本生态。v5、v8、v10三个版本并存的局面导致网上教程特别多踩坑案例也特别多。真遇到问题搜出来的已解决资源比任何其他框架都丰富。对一个交付型项目来说这是巨大的隐性优势。2. 数据集构建这是整套系统里最花时间的一步2.1 数据来源组合真实场景为主公开数据为辅很多做demo的人习惯直接下载公开检测数据集来训练这在通用物体检测上没问题但客流统计场景有强烈的视角偏置。公开数据集大多来自街景、行车记录仪、日常生活照片角度是平视而商场摄像头往往是3到5米高的斜俯视两种视角下人头的视觉特征差异很大。平视数据训出来模型放到俯视画面上漏检率能翻倍。我的做法是三源混搭第一个来源是公开的行人/人头检测数据集用于提供基础的人头特征第二个来源是商场实地拍摄的监控录像截图这是主力覆盖早中晚不同光照、晴天阴天、周末节假日拥挤程度第三个来源是人工模拟渲染图——用三维场景软件在俯视角度下生成一批带标注的虚拟行人用于补充极端姿态和遮挡样本。三者的配比大约为3:6:1。只靠实地采集的话工作量巨大且标注周期长只靠公开数据的话场景偏移问题严重。混合之后训练集规模达到了8000多张图片、标注框9万多个其中实地数据占比最重。2.2 标注和清洗里踩过的细节坑标注工作用的是常规开源标注工具标注格式统一转成YOLO的txt格式每个txt文件对应一张图片每行是类别编号 中心点x 中心点y 宽度 高度坐标值都做了归一化。需要特别注意的是类别编号必须全程统一中间一旦改过类别定义前面标注全要返工。我只设了一个类别head。清洗数据是这一步真正拉开差距的地方。我筛掉了几类图片第一类是无意义的纯背景比如空无一人的过道第二类是标注质量存疑的比如头被大片遮挡但标了框第三类是模糊到连人都看不清的训练这种样本只会让模型学到错误特征。别觉得可惜宁缺毋滥。还有一个原则负样本要留一点。完全没有目标的图片放少量进去模型才知道没人的时候应该输出空结果而不是对着墙上的装饰画乱猜。2.3 数据增强的取舍mosaic不是越多越好YOLO系列的训练脚本默认开了mosaic增强把四张图拼在一起训练。这在通用场景很有用可以增加小目标样本多样性。但商场客流画面本身就是高密度人群mosaic拼接后原本就挨得近的人头框会进一步压缩导致模型学到人头之间距离很近的偏置推理时反而容易把两个人头框合并或漏掉。我的调整是mosaic关闭或者只在前半段训练开启后半段关闭同时加大亮度扰动和噪声扰动的权重——监控摄像头实拍画面普遍偏暗、有噪点、有色偏模型在脏数据上见过越多考试时越稳。翻转增强我也用了因为出入口左右两个方向都有人走模型不能有方向偏好。3. 三个版本同场训练yolov5、v8、v10的实测对比3.1 先统一口径一样的输入尺寸、一样的epoch、一样的显卡对比模型型号之前必须统一训练口径否则对比没有意义。我这边全套训练在一张消费级显卡上完成输入分辨率统一为640x640单卡batch size调到显存能承受的上限训练轮数统一为100轮预训练权重来自各版本官方提供的COCO权重。评估集也完全固定——从数据集中按场景划分出500张图片、7000多个标注框确保三个模型面对的是同一套考题。这三个版本选的具体模型是YOLOv5s、YOLOv8s、YOLOv10n。为什么这么配v5方面s是精度和速度的均衡点v8方面s同样是对标v5s的定位v10我试了n因为它的卖点是无NMS推理n版本在速度上的优势最能体现正好用来测极限速度路线是否可行。3.2 各版本的核心机制差异与训练表现YOLOv5s的骨干是CSPDarknetanchor-based检测头训练最稳定几乎不需要调参。它的表现在三个版本里不是最好的但胜在怎么训都不会崩对于新手非常友好。100轮训练后在验证集上mAP50能到0.86左右mAP50-95大约0.52。YOLOv8s换成了anchor-free检测头和C2f模块收敛速度比v5慢一点但最终精度上限更高。它的mAP50到了0.88mAP50-95约0.55。在拥挤场景下的漏检率明显低于v5特别是对密集排列的小人头v8的边框回归更准确。YOLOv10n是这一轮里让我比较纠结的一个。它采用了一致的双标签分配和无NMS设计推理时省掉了非极大值抑制这一步单帧推理时间大幅缩短。精度上mAP50只有0.82mAP50-95约0.48但速度几乎是v8s的两倍多。对实时性要求极端的场景v10的价值就出来了。3.3 一张表看懂三个模型怎么选我把三个模型在固定显卡上的实测数据整理成了下表方便直接对比模型版本mAP50mAP50-95单帧推理时间(ms)显存占用(GB)目标产物YOLOv5s0.860.529.82.1稳定、易部署YOLOv8s0.880.5512.62.6精度高、密集场景抗性强YOLOv10n0.820.485.41.7速度极快、无NMS这个表是当时的实测结果不同环境会浮动但趋势是稳定的v5是最稳的老黄牛v8是精度担当v10是速度代表。3.4 最终选型v8s做主力v5s做备份模型我最终把YOLOv8s定为主力模型理由有三条第一商场的密集人群场景是常态mAP50-95高意味着在严格IoU阈值下也能保住框的质量这对下游计数逻辑很重要第二12.6毫秒的推理时间完全满足实时需求8路摄像头并联也不到100毫秒够用第三v8的API和生态太成熟了从检测到分割到姿态估计一条龙后续如果要拓展空置率分析佩戴口罩检测等功能不用换框架。v5s我保留一份作为低配机器或者边缘设备上的备选方案——当客户那边的硬件比较老旧、驱动不支持新框架时v5s的兼容性反而能救命。v10在这个系统里暂时没有上位因为它的精度在密集商场场景下吃了亏但它值得持续关注等无NMS模型的精度追上来之后可能就是最优解。4. 计数逻辑才是系统的灵魂从检测框到客流数字4.1 画一条虚拟检测线让每个目标给出方向模型输出的只是一堆框要变成进和出两个数字必须定义事件。最常用也最稳定的方案是虚拟检测线法在一个出入口的画面中画一条横向线段作为分界线当行人的位置从线上方移动到线下方判定为进入反之判定为离开。这里要决定用哪个点代表人。我用的是检测框底边中点。理由很直观人的脚底或头框下缘在连续帧之间的抖动最小。如果用头框中心人头轻微摇晃就会在检测线附近反复横跳导致一次通过被记成多次。底边中点虽然也会随身体前倾变化但相对稳定得多。实际测试中使用底边中点比使用中心点减少约30%的重复计数。检测线的位置也不是随便画的。它应该尽量靠近画面中段偏下、行人路径相对固定的地方避免离画面边缘太近——边缘处常常只露出半个身子还没跨线就消失了。4.2 简化版跟踪器不引入DeepSORT也能干活要让同一个人的多个连续帧对应同一个ID需要做目标跟踪。很多人一上来就上DeepSORT、ByteTrack这些重方案实际在这个场景里一个基于IoU和质心距离的轻量匹配器就够用。我实现的逻辑很直接对当前帧的每个检测框计算它与上一帧所有框的IoU再计算质心欧氏距离。优先用IoU匹配如果IoU低于阈值比如0.1但质心距离小于某个像素值比如40像素也视为同一目标。每个目标维护一个固定长度的轨迹列表用于后续方向判断。整个过程几十行代码单线程跑起来毫无压力。为什么不引入深度学习重识别模型核心是性价比。商场出入口通行速度不快帧间位移小简单的几何匹配已经能保持帧间ID连续。重识别模型又重又慢运行起来还容易受光照和遮挡干扰在没有跨摄像头接力需求之前投入产出比太低。4.3 方向判定和去重最隐蔽的坑在这里检测线有了跟踪有了接下来是方向判定。这里有个隐蔽问题当一个目标在检测线附近徘徊或者画面里跟丢了又重新出现系统很容易给他发一个新ID造成重复计数。我的解决方案是连续帧确认 短时间遗忘。首先一个目标必须连续3帧以上、且位置一直朝线的同一侧移动才判定跨线成功。单帧跨越不计数。其次目标消失后保留其ID约1.5秒如果同一个位置附近出现一个足够相似的新目标就把旧ID续给他而不是新建一个。这就避免了一个人从遮挡物后走出来被当成两个人的尴尬。还有一类重复计数的元凶是走回来拿东西、原地停留接电话的人。处理办法是记录每个ID首次跨线的时间如果在短时间内比如10秒内反向跨线系统认为是折返需要抵消一次计数。这个逻辑不复杂但能让最终数据贴近运营的真实感受。4.4 密集人群下的漏检补偿策略商场最拥挤的时间段模型漏检不可避免。漏检直接表现为某个ID轨迹长度不够3帧就被中断方向判定了自然受损。我试过几招都有一定效果第一招是降低检测置信度阈值从0.5降到0.35。代价是误检增多但结合跟踪器可以过滤掉大部分孤立误报——如果一个框在连续几帧里都有大概率是真目标。第二招是对轨迹进行插值一个ID在A帧有框、C帧有框、B帧丢了用A和C的坐标线性插值补出B帧位置保持轨迹连续性。第三招是调整检测线附近区域的非极大值抑制阈值让人头框在拥挤时不容易被压掉。这三招叠起来在实测人流超过20人同屏的极端画面里计数误差能控制在10%以内。没有这些兜底逻辑光靠调模型参数误差经常会到20%以上。5. UI界面与整套系统的组装让模型变成能用的产品5.1 桌面端技术选型PySide6 OpenCV是稳妥组合模型和计数逻辑做完之后必须有一个让运营人员能直接用的界面否则这套东西始终停留在代码能跑的阶段。我选的是PySide6做桌面客户端配合OpenCV做视频处理。为什么不用网页前端这种内部工具的用户就两三个桌面端部署简单、不依赖服务器和网络环境双击就能启动对非技术人员最友好。为什么不用纯OpenCV的highgui它的控件能力太弱画个实时曲线、做交互按钮都很费劲。PySide6的信号槽机制天然适合UI和检测线程之间的解耦而且Python生态里Python与YOLO的推理代码集成几乎零成本。5.2 界面布局和数据流设计界面分三个区域左边是实时视频画面带上检测框、检测线和进出计数数字右上是一个折线图展示每小时进出趋势右下是统计表格按天和小时汇总支持导出CSV。数据流是标准的生产者-消费者模型采集线程负责从摄像头读取帧推理线程负责YOLO检测跟踪与计数模块在推理线程里同步执行然后把计数事件放进队列UI线程从队列里取数据刷新画面和图表。这样UI永远不会因为推理卡顿而假死。一个容易被忽略的细节是画面显示。OpenCV默认的BGR格式在Qt里显示时要做格式转换、还要缩放如果直接在最上层画检测框再转CPU占用会高不少。更聪明的做法是把检测框画在一个覆盖层上视频帧只负责显示这样UI流畅度有明显提升。5.3 摄像头接入与RTSP断流重连商场摄影头通常通过RTSP协议推流。用OpenCV的VideoCapture直接打开RTSP地址也能跑但生产环境里有两个大坑。第一个坑是延迟。RTSP默认的缓存机制会让画面延迟几秒到十几秒不等计数结果完全没法看。解决方法是设置缓冲区大小把OpenCV读取的输入队列压到最小只留一帧。第二个坑是断流。摄像头偶发重启、网络瞬时抖动都会让VideoCapture卡死不重连的话系统就永远停在最后一帧。我在采集线程里做了一个健康检测如果连续5秒没有新帧就销毁当前VideoCapture对象过2秒重新创建并连接。实测断流后基本能在10秒内恢复。5.4 多路摄像头并行线程模型与显存控制一台机器接多路摄像头是刚需场景。我的做法是一路摄像头一个采集线程推理线程配置成独立进程池共享同一个检测模型实例。这里有个经验模型对象不要在每个线程里各加载一份。8路视频会有8个推理任务同时跑显存直接被撑爆。正确的姿势是使用共享模型加锁或者在一个推理服务进程内维护一个任务队列各路视频把帧送进来推理完再取出结果。这样不管接多少路视频显存占用只取决于模型本身而不是视频路数。我实测8路1080p视频、单张显卡整体负载在50%以内系统稳定性足够支撑商场一整天的运行。6. 实测结果与排坑记录从跑通Demo到稳定运行6.1 模拟现场测试评价维度的选择比数字本身更重要系统开发完成后我在一个模拟出入口环境里做了验证。测试环境模拟商场入口的双通道布局两个通道都有人流方向既有进也有出。测试方式是人工统计录像中的人员进出数量与系统输出做对比。这里我想特别说一句验证时不能只看总误差还要拆开出入口、时段和方向来看。比如两个通道的误差可能一个3%、一个12%平均数看着只有7%但实际问题是第二通道的光线太暗。所以我建议把所有数据按入口方向半小时时段拆成小维度评估准确率低于90%的格子要单独排查成因。我最终的实测数据是总人数统计误差3.2%双向计数方向准确率97.4%单帧延迟12.8毫秒连续运行72小时无崩溃。这个结果对于客流趋势分析已经完全够用了。6.2 三个最让人头疼的问题及排查过程第一个问题是台阶处的人流误判。商场出入口常常有台阶行人跨线时人和检测线之间有高度差导致底边中点的运动轨迹呈锯齿状方向判定偶发反转。排查后我改成了对轨迹做斜率平滑计算连续5帧的中位移动方向忽略单帧的抖动。因为人流方向本身具有惯性这个方法简单且有效。第二个问题是推婴儿车和购物车的场景。婴儿车会让目标底边中点偏移购物车则会短暂遮挡行人。这些问题导致ID频繁切换和重复计数。我调整了跟踪器的匹配阈值把质心距离的容忍范围放宽同时给每个ID增加了一个最高可信方向分数只要轨迹上绝大多数帧的方向一致偶尔的抖动不算反向。第三个问题是显存泄漏。最初版本在连续运行几个小时后显存占用缓慢爬升最终触发OOM。用资源监控一看问题出在日志记录模块——每次推理结果都往日志列表里塞数据列表越积越大。这是个纯工程问题解决方式是定时清理、只保留最近1000条记录并在每次推理前显式释放中间张量。排查这类问题工具上的耐心和经验上的嗅觉缺一不可。6.3 关于系统后续方向从客流统计到更精细的运营分析这套系统做完之后我最大的体会是客流统计只是入口真正有价值的是人流数据之上的分析。顺着这套框架可以扩展的空间还很大。比如跨摄像头接力——在商场不同区域布置摄像头结合行人重识别把同一个人的轨迹拼出来就能得到顾客在商场里的完整动线这对店铺布局优化很有价值。再比如区域热度分析——不需要计数只需要在画面里划分网格统计每个格子的目标停留时间和出现频次就能生成热力图帮运营判断哪个展位最吸睛。这些功能在检测层都不用重新训练只需要在上层做不同的吃数据方式。还有一个现实约束是隐私合规。客流统计系统的数据必须做到只统计、不识别——不保存人脸特征、不做身份关联、统计结果只以聚合形式呈现。我在系统的数据存储模块里做了字段级限制视频流默认不落盘只保存计数结果日志。做视觉项目的同行这条底线一定要守住。回顾整个过程这类项目真正考验的不是某一步有多深而是所有环节串联起来之后还能不能稳定工作。检测、跟踪、计数、UI、部署任何一个环节掉链子整套系统就无法真正交付。耐心打磨数据把工程细节做扎实才是这种实战项目最大的价值所在。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑