
做这个项目之前我其实刚在一场大型展会的监控大屏前站了半小时。屏幕里是人头攒动的通道安保人员全靠肉眼盯一个人最多同时看四路画面看久了必然疲劳。所以当手上拿到“密集行人检测”这个需求的时候我第一反应不是“这东西能用YOLO”而是“这个系统要解决的核心问题是让人从屏幕前解放出来”。整套系统最终长这样底层用YOLO系列模型做行人目标检测服务端用SpringBoot承载业务接口和数据流转前端用Vue搭了一个可视化的Web交互界面检测结果出来后再丢给千问或DeepSeek做语义层面的智能分析把“哪里人多、风险等级如何、该不该预警”这些判断也一起做了。这篇文章把整个设计和实现过程完整复盘一遍包括踩到过的坑和调优思路给正在做类似AIWeb项目的朋友一个可以抄作业的参考。1. 项目整体设计与技术选型思路1.1 核心需求拆解密集行人检测到底要解决什么问题先别急着选模型密集行人检测和普通的目标检测有本质上的区别。普通场景下检测行人目标之间间距大遮挡少YOLO随便一个版本都能跑出不错的效果。但密集场景不一样人挨着人你遮我我挡你一个行人漏检、两个行人框成一个人这些都是家常便饭。更要命的是密集场景往往伴随大量小目标比如监控画面远端的人群可能只有几十个像素大小这对模型的特征提取能力要求很高。所以这个项目从一开始我就把需求拆成了三层。第一层是目标检测要求模型能尽可能多地框出行人并且尽可能减少漏检和误检第二层是业务逻辑检测结果不能光画个框就完事得能统计人数、计算密度、定位密集区域、触发告警第三层是智能分析系统要能理解“当前场景有没有风险”比如某条通道人数突然暴涨光靠阈值判断容易误报这时候需要大模型来做语义层面的分析。这三层需求直接决定了技术选型。检测层锁定YOLO系列因为它在工程落地上的生态太成熟了训练、导出、部署都有完整的链路业务层用SpringBoot因为这套系统最终要接监控设备、要落库、要出接口给别的平台调用SpringBoot的生态做这些事情事半功倍智能分析层接大模型千问和DeepSeek在中文语义理解上表现足够好而且API接入成本低。整体思路定了下面逐个说选型的考量。1.2 YOLOv8/YOLOv10/YOLOv11/YOLOv12到底选哪个YOLO系列现在的版本迭代非常快很多人一上来就问到底用哪个版本我的建议是先看你的硬件条件和部署环境再看你对实时性的要求最后才是测试集上的mAP。给大家整理一下我在项目里实际对比过的版本情况版本核心优势适用场景我的使用建议YOLOv8生态最成熟资料最多部署方案最全快速落地、参考案例多如果你是第一次上手闭眼选它遇到问题能找到大量现成答案YOLOv10无NMS设计推理速度更快高并发、高帧率、边缘设备对实时性有硬性要求时优选省掉NMS能明显降低延迟YOLOv11在v8基础上做了特征融合和训练策略优化精度略高精度优先、训练资源尚可适合想要更好精度的场景训练技巧更先进YOLOv12引入了注意力机制改进对小目标和密集场景有增益密集行人、小目标检测适合拿来做实验对比看注意力机制对于你的数据是否有明显提升我在这个项目里最终以YOLOv11为主力模型因为密集行人场景对精度更敏感漏检一个行人可能就意味着漏掉一次预警机会。同时保留了YOLOv8和YOLOv12的推理接口方便后续切换和对比。这里要提醒一句不要光看官方给的COCO预训练模型指标密集行人场景你最好用自己的数据集重新训练或者微调COCO的类别分布跟真实监控场景差别很大。1.3 服务端为什么是SpringBoot前端为什么要拆出去这个系统不是纯算法Demo它要能长期运行在服务器上要有人维护要能跟现有的业务系统对接。Java生态在这方面有明显的工程优势SpringBoot框架让开发效率提升了一大截只需要引入spring-boot-starter-web就能把HTTP服务跑起来再配合MyBatis-Plus操作数据库、Spring Security做权限控制整个服务端的骨架就往企业级标准靠了。前后端分离是我从一开始就坚持的方案。原因很简单算法工程师要频繁调整检测参数、替换模型、看推理结果而后端工程师要维护接口前端工程师要做大屏展示和交互如果前后端不分离每一次算法调整都要牵扯前端效率会低到让人崩溃。分离之后后端只暴露业务接口前端通过HTTP调用算法的所有变更都封装在服务端前端零感知。部署上也灵活前端可以扔到Nginx后端独立跑在Java进程里互不阻塞。2. 密集行人检测的核心难点与YOLO工程适配2.1 密集场景的三大痛点遮挡、小目标、人群堆叠密集行人检测最让人头疼的不是模型不够大而是场景本身的复杂性。遮挡是最常见的问题行人A的身体挡住行人B的大半部分模型如果只学会“看到头就检测”那被遮挡的行人很容易漏检。这时候要做的不是单纯堆模型深度而是让模型学到更强的局部特征比如通过注意力机制让模型关注到头的轮廓、肩膀的弧度、背包的边缘这些局部线索。小目标问题同样棘手。监控摄像头拍到的远端人群每个人在画面里可能只有16x16像素对于YOLO这种基于下采样特征图的目标检测器来说小目标经过多次下采样之后特征几乎丢光了。这也是我选择YOLOv12做对比的原因之一它在特征提取过程中加入了注意力机制理论上对小目标更友好。人群堆叠则是另一个维度的问题当人密度极高时检测框之间高度重叠常规的NMS后处理会误删很多有效检测框。针对这个问题可以从训练和后处理两个方向同时优化。训练侧用WBF加权框融合替代NMS用置信度加权和位置加权的方式融合多个检测框对密集场景的提升非常直观。2.2 训练侧工程优化数据增强与损失函数策略数据增强是密集行人检测的重头戏。常规的Mosaic增强、随机翻转、随机裁剪肯定要做但我额外加了三组针对密集场景的增强策略。第一是Copy-Paste增强把图片中的行人实例随机复制粘贴到同一张图的其他位置人为制造更密集的训练样本。第二是小目标增强把训练图中的大尺寸实例随机缩小后放到图上强迫模型学习小目标的特征。第三是遮挡模拟在训练时随机用黑色矩形块遮挡行人区域模拟实际场景中可能的遮挡情况。损失函数的选择也值得单独说一下。YOLO系列默认使用CIoU Loss或DFL但密集场景下我会偏向于使用带有中心点距离惩罚的损失因为在人群重叠时模型容易把两个靠近的行人预测成同一个目标中心点距离惩罚可以逼迫模型输出更精确的框中心位置。另外如果在训练过程中发现正负样本极度不平衡可以去关注一下Focal Loss的配置让模型专注学习那些难以识别的拥挤区域的样本。特征融合层面我实际试下来最有用的做法是增加一个更高分辨率的检测头。YOLO默认是小、中、大三组检测头但在密集行人场景我会额外增加一组更浅层的检测头直接作用于原图1/4分辨率的特征图用来捕捉更小的目标。代价是推理速度会掉一些但密集场景下精度优先是更合理的选择。2.3 推理侧工程优化分块推理与批处理策略模型训练好之后推理侧的优化也直接影响用户体验。监控画面往往是1920x1080甚至更高分辨率如果直接缩放到YOLO的默认输入尺寸比如640x640远处的行人会被压缩得完全看不清。我的做法是引入分块推理逻辑将原始画面切割成多块有重叠区域的分块每块分别输入模型检测再把检测结果合并回原图坐标。这样可以避免直接缩放导致的信息丢失但推理耗时也会成倍增加。这里有个优化技巧先用小尺寸模型快速扫描整帧画面判断画面中是否有人群密集区域如果判断结果为“稀疏”直接返回低精度结果如果判断为“密集”才启动分块推理和高精度模型。这种分级推理策略能把平均推理耗时控制在可接受范围内同时在真正需要精度的时刻全力输出。批处理优化同样值得投入精力。如果系统需要同时处理多路监控画面不要为每一路视频单独起一个推理线程那样会频繁切换GPU上下文。更合理的做法是把多路视频的帧收集到一个队列里凑满一个batch再统一送入GPU推理实测在4路1080P输入下批处理比单路推理的吞吐量提升了3倍左右。3. 服务端架构落地与Web交互界面实现3.1 SpringBoot工程结构与核心模块划分后台工程我按功能边界拆成了几个模块避免所有代码堆在一个包里后面越写越乱。controller暴露HTTP接口接收前端请求不写业务逻辑service业务逻辑层负责调用检测引擎、拼装数据、处理缓存mapper持久层用MyBatis-Plus操作MySQL保存检测记录和告警记录engine算法引擎层负责加载YOLO模型、执行推理、封装结果config配置类包括跨域配置、线程池配置、模型初始化的Bean配置这里要特别说一下算法引擎层的设计。模型推理作为一个Spring Bean在应用启动时加载model文件放在服务器固定目录下通过配置文件指定路径。这样模型切换时不需要重新编译代码改一行配置就能换模型。检测结果用一个独立的Result类封装包含每个检测框的坐标、置信度、类别以及整个画面的人数、密度等级这些统计信息。3.2 检测服务的统一输入设计图片、视频文件、RTSP流密集行人检测系统的输入来源通常有三种上传的静态图片、上传的视频文件、RTSP视频流。三种输入的处理逻辑差异很大但对外暴露的接口应该尽可能统一。我定义了一个统一的检测接口入参是数据源标识和回调地址系统内部根据数据源类型启动不同的处理链路。图片走单次推理视频文件通过FFmpeg拉帧逐帧检测RTSP流则启动一个常驻的拉流线程持续检测。RTSP流处理是这个项目里的重头戏。我用FFmpeg命令行工具从RTSP地址拉视频帧输出到本地管道Java代码读管道里的帧数据再送入推理引擎。FFmpeg的命令大概是这样的ffmpeg -rtsp_transport tcp -i rtsp://your_camera_stream -vf fps5 -f rawvideo -pix_fmt bgr24 pipe:1-fps 5的意思是把码率压到每秒5帧实际经验是密集行人检测不需要每帧都做5~8帧足够覆盖大多数异常事件。TCP传输比UDP更稳定在弱网环境下不会疯狂丢包。前端的展示层我用了Vue 3加ECharts。ECharts在这里的用处不是画普通的折线图而是做人群热力图和人数趋势图。检测出来的框坐标会被换算成热力图的权重值人群越密集的区域颜色越深运维人员一眼就能扫出全场最拥挤的位置。实时检测结果通过WebSocket推送到前端前端收到消息后更新视频画面上的检测框同时刷新曲线数据。3.3 前后端接口联调与数据交互约定前后端分离的项目接口约定是最容易扯皮的地方。我的习惯是先定义好统一响应结构所有接口返回格式固定{ code: 200, message: success, data: { totalCount: 128, densityLevel: 3, boxes: [ {x: 120, y: 80, width: 50, height: 120, conf: 0.89}, {x: 350, y: 200, width: 45, height: 110, conf: 0.92} ], analysis: 当前区域人流较密集建议关注通道拐角处。 } }这个结构同时覆盖了检测结果和智能分析结果前端不用为算法结果和AI分析各写一套解析逻辑。跨域问题在开发环境通过CrossOrigin注解解决生产环境统一走Nginx反向代理前端请求/api前缀的地址Nginx把流量转发到SpringBoot服务。4. 千问DeepSeek智能分析链路的实现4.1 检测之后的智能分析到底分析什么很多人不理解YOLO检测完不就有结果了吗为什么还要接大模型举个例子YOLO输出“画面中有128个人”但128个人算拥挤吗这要看画面面积、通道宽度、场景类型。100个人在车站大厅可能不算什么但100个人挤在10米宽的通道里就是严重拥堵。YOLO不会回答这个问题它只能框出人不能理解场景语义。千问和DeepSeek的价值就在这。我把YOLO检测输出的结构化数据、场景信息、历史统计值拼成一个Prompt发给大模型让模型基于这些数据做综合分析输出包括当前人流状态描述、风险等级、建议采取的措施等结果。这一步相当于在目标检测之上加了一层轻量级的场景理解能力。4.2 大模型接入的两种方式与选型考量大模型的接入方式我对比了两种。一种是调用官方API千问的qwen-plus和DeepSeek的deepseek-chat都有HTTP接口调用简单效果稳定适合快速上线。另一种是在本地部署开源权重比如千问的Qwen2.5系列或者DeepSeek的蒸馏版本用Ollama或者vLLM跑起来数据不出内网适合有数据安全要求的场景。实际项目中我没有二选一而是做成了一个可配置的适配器。配置中心里写一个ai.provider字段可以切换qwen、deepseek或local三种模式。默认线上走DeepSeek API因为它的中文理解能力突出且成本低内网测试环境走Ollama本地模型需要换模型的时候改一行配置就行代码不动。调用大模型要走异步检测是毫秒级大模型分析要2~3秒不能把请求链路变成串行。4.3 Prompt设计与结构化输出控制大模型分析质量的高低七成取决于Prompt设计。我踩过不少坑最典型的是模型输出格式不稳定有时候给纯文本有时候带Markdown前端解析容易出问题。现在的做法是强制要求模型输出JSON并且在Prompt里给出严格的输出Schema。下面是一个实测效果很稳定的Prompt模板你是一个客流态势分析助手。根据以下检测数据分析当前场景的客流状态 场景类型地铁站安检口 检测结果{totalCount: 128, densityLevel: 3, boxesCount: 128} 历史平均人数45人 请输出JSON格式结果 { state: 拥挤/正常/稀疏, riskLevel: 高/中/低, reason: 简要分析原因不超过50字, suggestion: 给出不超过50字的行动建议 }实测下来千问和DeepSeek对JSON输出的遵循度都很高偶尔出现解析失败时我在代码里做了兜底——如果JSON解析异常就把原始文本原样透传给前端而不是直接报错。这里还有个细节大模型分析的结果要做缓存同样的场景和类似的检测结果在短时间重复请求时直接返回缓存数据避免重复调用API产生不必要的费用与延迟。5. 数据准备与模型训练实战5.1 YOLO格式数据集的构建与增强数据集是密集行人检测的命脉这个项目我用了公开数据集加自采数据的组合。公开数据集方面CrowdHuman和WiderPerson是密集行人检测最适合的两个其中CrowdHuman专门为人群场景标注平均每张图有超过20个行人和我们的场景高度匹配。VisDrone也有行人类别但更多是无人机视角跟监控视角差异偏大我只在微调阶段少量混合使用。如果手上是KITTI这类自动驾驶数据集转YOLO格式也不难把原本的KITTI标注文本里的类别映射一下坐标从绝对坐标转成归一化坐标就完事了。标注工具我用的是X-AnyLabeling它在LabelImg的基础上做了大量增强支持半自动标注可以先用YOLOv8的预训练模型自动生成一批伪标签人工修正后作为训练集极大节省标注时间。密集场景标注有一个经验即便目标之间有严重重叠只要肉眼能看出是独立的行人就要画出独立的框不要因为遮挡就跳过。模型对重叠目标的学习能力取决于标注的完整性。训练参数参考如下主要针对密集小目标场景做了调整参数推荐值说明imgsz1280或1536分辨率越高小目标检测越好但显存消耗翻倍epochs150~300数据量大就多训数据量小要靠早停防过拟合batch按显存来8GB显存建议4~824GB可以到16~32mosaic1.0默认开启即可copy_paste0.5密集场景我把这个值调到0.5以上close_mosaic10最后10个epoch关闭Mosaic让训练收敛更稳定训练过程中的指标不能只看mAP还要关注中等尺寸目标和小尺寸目标的AP值。如果你的数据集中等目标AP高但小目标AP不到40%说明模型对小目标拟合不足优先调整分辨率和数据增强而不是盲目加深网络。5.2 模型导出与跨环境部署训练完成后的模型要部署进SpringBoot工程常用两种方案。第一种是把PyTorch模型导出为ONNX格式在Java端用ONNX Runtime加载推理第二种是保留Python服务用Flask或者FastAPI包装模型接口Java通过HTTP调用。我强烈推荐第一种Java直接加载ONNX跑推理省去了Python服务的维护成本也减少了网络调用的延迟。导出命令很简单yolo export modelbest.pt formatonnx dynamicFalse imgsz1280注意dynamicFalse固定输入尺寸在推理时能减少内存分配的开销。导出后在Java里使用onnxruntime依赖加载模型准备好输入Tensor执行推理输出就是检测框、置信度和类别ID。如果用NVIDIA的GPU并且需要极致性能还可以把ONNX进一步导出为TensorRT格式用半精度推理速度能再提升一倍。5.3 关于AMD显卡能不能跑YOLO这个问题被问得很多我直接说结论AMD的RX 580这类显卡能跑YOLO但只推荐用于CPU推理和模型导出不推荐用来训练。原因是YOLO训练依赖CUDAAMD显卡虽然有ROCm但Windows平台支持很差Linux下也要看显卡型号是否在支持列表。RX 580属于老架构ROCm的支持基本不完善强行训练只剩一句劝退。我实测过在RX 580上用CPU跑YOLOv8n的640x640推理一帧大约需要300毫秒离线分析视频勉强能接受实时监控就扛不住了。所以如果你手里的机器是AMD显卡两条路是现实的第一用Google Colab或云GPU实例训练模型训练完导出ONNX后本地用CPU推理做测试第二买一张NVIDIA的入门卡比如RTX 3050或者2060训练速度和推理体验直接翻天覆地。6. 部署、性能调优与常见问题排查实录6.1 生产环境的部署架构整个系统的部署我分了三个部分。前端是Vue构建后的静态文件放到Nginx的/usr/share/nginx/html目录Nginx同时负责反向代理/api和/ws路径到Java服务。Java服务用nohup java -jar跑在另一个端口内存配置视模型大小而定YOLOv11s的ONNX模型大约需要2GB堆内存加200MB的模型加载空间建议给JVM至少4GB。MySQL单独部署保存检测记录和告警历史用MyBatis-Plus操作。生产环境有一个Java侧很容易踩的坑ONNX Runtime加载模型后默认使用CPU执行要让模型跑在GPU上必须显式指定ExecutionMode和CUDA提供程序否则即使有GPU也在用CPU摸鱼。初始化推理会话的代码要放在PostConstruct里在Spring容器启动后自动初始化避免第一个请求触发模型加载带来漫长的等待。我试过没预热模型第一次请求花了12秒之后的请求才恢复正常这在大屏演示时是致命的。6.2 性能调优的三个着力点密集行人检测系统的性能瓶颈通常不在某一个环节而是体现在全链路。我的调优优先级是模型推理速度、数据传输效率、业务逻辑耗时。模型推理速度方面优先确认GPU推理有没有真正生效用半精度FP16把推理时间压到最低同时为关键场景开启分块推理开关。数据传输效率方面视频流处理时先把帧压缩成JPEG再走WebSocket推送压缩后的帧大小通常只有原始BGR帧的20%带宽压力大幅下降。业务逻辑耗时方面数据库写入采用异步批处理检测结果先缓存在本地队列每5秒批量落库一次不要每检测一帧就触发一次INSERT。6.3 常见问题与排查方法速查表这个项目从头到尾我在实践中遇到的问题整理成表格分享出来对照排查能省很多时间问题现象可能原因排查与解决模型推理很慢ONNX Runtime在CPU上执行检查Java日志中是否启用CUDAProvider确认GPU推理生效视频画面卡顿拉流帧率过高推理跟不上调整FFmpeg的-fps参数压到5~8帧前端收不到实时结果WebSocket连接被Nginx断开Nginx配置WebSocket Upgrade头开启proxy_read_timeout人群密集但检测率偏低模型对小目标不敏感提高输入分辨率开启分块推理微调模型RTSP流中断后无法恢复拉流进程退出没有重启用看门狗线程监控拉流进程异常退出后自动拉起大模型分析偶发超时请求量过大或网络波动配置合理的超时参数并启用缓存超时后返回降级文案还有两个值得单独说一说的细节。端口冲突问题在部署到客户服务器时特别常见SpringBoot默认8080经常被占推荐在启动脚本里显式指定--server.port9090。另一个是模型文件的版本管理ONNX模型文件一定要记录对应的训练集版本和指标情况否则模型换了一版表现变差了连回滚的依据都没有。7. 一些收尾的经验与后续可扩展的方向这个项目做下来我最大的感触是YOLO加上SpringBoot的这套组合真正把“算法能力”和“工程能力”焊在了一起。YOLO负责解决“看得见”的问题SpringBoot负责解决“用得上”的问题千问和DeepSeek则往上走了一层让系统从“能检测”进化到“能理解”。这三层配合起来才是一个可以交付给实际业务使用的系统而不是一个孤零零的算法Demo。最后再分享一个小技巧模型训练阶段可以把YOLOv8、YOLOv10、YOLOv11、YOLOv12在同一个验证集上的指标输出到一份JSON里系统运行时通过配置切换模型每晚定时任务自动跑一遍指标对比数据积累多了之后哪个模型在哪个场景下表现最好就不再是拍脑袋决定了全部由数据说话。后续这个系统还有几个值得做的扩展方向。接入ByteTrack或DeepSort做跨帧跟踪就能把“人数检测”升级为“人流轨迹分析”加入ReID特征提取可以做到多摄像头接力跟踪覆盖一个行人的完整动线告警逻辑可以从硬编码规则改成规则引擎让业务人员自己配置什么条件触发什么级别的告警。不管往哪个方向扩展底层这套“YOLO检测SpringBoot服务化前后端分离大模型分析”的骨架都不会变这才是这个项目最有价值的部分。