拓冰建站拓冰建站
首页 / 资讯中心 / 正文

YOLOv8到v12密集行人检测实战:SpringBoot与大模型智能分析系统

1. 项目概述与整体思路拆解1.1 为什么盯上“密集行人检测”这个场景先说结论行人检测和密集行人检测看起来只差两个字实际上完全不是一个难度级别的东西。早几年我做单摄像头行人检测场景里三五个人YOLOv5随便跑跑就能有不错的mAP。直到有一次接了商场客流统计的项目一个镜头里同时出现几十号人有遮挡、有尺度变化、有灯光干扰才发现原来的模型和方案根本扛不住。密集场景里最典型的问题有三个小目标太多导致漏检人与人重叠导致误检检测框抖动导致跟踪计数不稳定。这些不是调参能救的需要从模型选型、数据标注、后处理逻辑三个层面同时调整。这个项目的标题里把YOLOv8/v10/v11/v12都列出来了说实话这不是炫技而是密集场景下必须做的“版本对比选型”。不同代际的YOLO在骨干网络、C2f/C2fCIB模块、损失函数、标签分配策略上都有差异同样一批数据训练出来的效果差距是肉眼可见的。我在项目里采用的方式是用同一份数据集分别训练四个版本然后用统一的评估脚本对比mAP50、mAP50-95和推理速度最后再决定线上用哪个权重。1.2 系统整体架构设计这套系统不是单纯做一个模型推理Demo而是完整的前后端分离工程。标题里已经划定了范围YOLO系列负责检测SpringBoot负责后端服务的承载千问和DeepSeek负责检测结果的下游智能分析Web交互界面负责可视化和人工干预。我最终落地的架构可以拆成五个模块数据层YOLO格式的标注数据集包含密集行人场景的图片和 labels 文本文件统一放在指定目录训练前按比例切分为 train/val/test。模型服务层使用 YOLO 官方训练脚本训练权重推理时通过 Python 侧封装一个检测服务对外暴露 HTTP 接口或者直接把 ONNX 导出后交给 Java 侧调用。后端服务层SpringBoot 提供 REST API包括用户管理、检测任务创建、历史记录查询、结果反馈修正等接口同时负责调用 Python 检测服务和外部大模型API。智能分析层千问Qwen和 DeepSeek 通过API接入接收检测结果JSON输出对场景的语义理解比如“该时段东南角人流密度较高建议增派疏导人员”。前端展示层Vue 或 React 单独工程通过 Axios 调用 SpringBoot 接口实时展示检测画面、密度热力图、告警信息和分析报告。这种分层的好处是各模块可以独立迭代。比如模型版本升级只需要替换模型服务的权重文件前端和后端都不需要改动。后面接入新的AI分析能力也是在智能分析层做适配不会动到主干业务。2. 模型选型YOLOv8/v10/v11/v12的取舍逻辑2.1 四个版本的核心差异对比很多新手看到YOLOv8、YOLOv10、YOLOv11、YOLOv12以为只是版本号更新其实每个版本的改动思路完全不一样放到密集行人检测场景里影响最大的是下面几张表里的差异。版本核心改进点对密集行人场景的影响YOLOv8C2f模块、Anchor-Free解耦头、TaskAlignedAssigner标签分配基础扎实小目标召回率比v5明显提升YOLOv10NMS-free训练策略、双标签分配、轻量化设计去掉NMS后推理更快但密集场景下输出头冗余略有增加YOLOv11C3k2模块、更强的特征提取、PSA注意力小目标特征保留更好对远处人群更友好YOLOv12注意力机制优化、训练收敛更快、多头区域注意力精度上限最高但对显存和训练时间要求更高考虑到项目是密集行人场景我个人的建议是如果对推理速度要求苛刻优先YOLOv10如果追求精度上限且机器显存够用优先YOLOv12如果做学术对比或者需要稳定兼容老代码YOLOv8是最稳妥的选择。YOLOv11算是均衡型选手单人和小群体场景表现都不错但密集拥挤场景下还是能感觉到和v12的差距。2.2 训练配置的实操参数直接给出一套我实测下来比较稳的训练命令基于YOLOv8的官方仓库yolo detect train \ datadataset/pedestrian.yaml \ modelyolov8m.pt \ epochs200 \ imgsz640 \ batch16 \ device0 \ optimizerSGD \ lr00.01 \ lrf0.01 \ momentum0.937 \ weight_decay0.0005 \ warmup_epochs3.0 \ mosaic1.0 \ close_mosaic10几个参数是密集场景下必须注意的imgsz建议640起步如果人群中小目标占比高可以考虑抬到960但显存占用会明显上升训练时间也几乎翻倍。close_mosaic10最后的10个epoch关闭Mosaic增强这一条非常关键。Mosaic会把四张图拼在一起如果图片里人群本身就很密集拼接之后目标尺度会变得更复杂模型在最后阶段需要的反而是接近真实分布的样本。batch根据显存调整我用的8GB显存跑yolov8mbatch设16会爆显存实际设8才稳定新手不要盲目抄大batch。标签分配策略上我建议用默认的TaskAlignedSubmission它按“分类得分IOU”联合排序来选择正样本在密集行人这种目标互相遮挡的场景下比单纯IOU分配要抗干扰很多。2.3 版本对比实验怎么做才科学做版本对比最忌讳的是直接拿不同repo的默认参数跑这样对比出来的差异根本说不清楚是模型问题还是训练配置问题。我在项目里统一做了三件事统一数据集和预处理所有版本训练集、验证集完全一致增强策略保持一致图片尺寸统一。统一训练轮数和优化器epochs、batch、学习率策略全部相同避免某个版本因为训练不充分被误判。统一评估标准和硬件环境全部导出为ONNX后在同一台机器的同一张测试图上跑推理记录GPU耗时和CPU耗时。最终对比结果里YOLOv12在mAP50-95上比YOLOv8高了约2到3个百分点但推理耗时也增加了约15%。YOLOv10在速度上优势明显但密集遮挡场景下的漏检率稍高。这套对比方法建议自己复现不要直接抄网上的结论因为数据集不同结论可能会反转。3. SpringBoot后端与模型服务的三种集成方式3.1 方式一Python推理服务 HTTP调用这是我最推荐的方式部署灵活模型升级不影响Java进程。具体实现是用FastAPI或Flask包一层YOLO推理逻辑暴露POST /detect接口SpringBoot端通过RestTemplate或WebClient调用。Python侧核心代码参考from fastapi import FastAPI, UploadFile, File from ultralytics import YOLO app FastAPI() model YOLO(best.pt) app.post(/detect) async def detect(file: UploadFile File(...)): img_bytes await file.read() results model.predict(img_bytes, conf0.25, iou0.45, imgsz640) boxes results[0].boxes return { count: len(boxes), boxes: boxes.xyxy.tolist(), conf: boxes.conf.tolist() } if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)SpringBoot端的调用代码RestTemplate restTemplate new RestTemplate(); MultiValueMapString, Object body new LinkedMultiValueMap(); body.add(file, new FileSystemResource(imagePath)); HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); HttpEntityMultiValueMapString, Object entity new HttpEntity(body, headers); JSONObject result restTemplate.postForObject(http://127.0.0.1:8000/detect, entity, JSONObject.class);注意大文件上传时SpringBoot的spring.servlet.multipart.max-file-size默认只有1MB必须调大否则图片稍微大点就直接报错。3.2 方式二导出ONNX用Java推理如果不想额外维护一个Python进程可以把YOLO权重导出成ONNX再用Java侧的ONNX Runtime推理。这种方式部署更简单一个Jar包搞定但开发和调试成本更高。导出命令yolo export modelbest.pt formatonnx imgsz640 dynamicTruedynamicTrue保证输入尺寸不固定实际使用更灵活。Java端引入onnxruntime依赖后大概几百行代码就能完成预处理、推理、后处理的全流程。核心的难点在NMS的Java实现和坐标换算如果图省事建议还是走方式一的HTTP调用省下的部署复杂度远不如开发成本高。3.3 方式三Redis队列异步解耦如果检测请求量很大比如摄像头实时推流需要并发处理我建议在Python服务和SpringBoot之间加一个Redis消息队列。SpringBoot把检测任务写进队列Python服务消费队列执行检测再把结果写回另一个队列。这一套做下来系统的吞吐能力会有质的提升还方便后续扩展多消费者并行处理。优先级建议中小项目选方式一快速见效对部署环境有洁癖的选方式二要做实时大规模接入的选方式三但工作量会大一个量级。4. 千问和DeepSeek的智能分析接入4.1 大模型在检测系统里能干什么检测模型输出的是框和类别但这只能回答“哪里有几个人”回答不了“这个场景有没有问题”“需不需要关注”。千问和DeepSeek在这里是充当语义分析层的角色。可以把检测结果JSON拼成Prompt让大模型输出业务结论。典型的用法包括给定时间段内人流密度变化判断是否接近拥挤阈值。检测到倒地、聚集、徘徊等异常行为模式生成告警说明。结合历史同时间段数据输出趋势预测和调度建议。4.2 API接入与提示词设计千问和DeepSeek都有兼容的HTTP接口SpringBoot端用OpenAI SDK风格调用即可。先配置好API Key和Base URL然后写一个AiAnalysisService。核心提示词模板如下你是一个商场客流分析助手。根据以下YOLO检测结果JSON分析当前场景的人流情况并输出结论 {detection_json} 要求 1. 统计当前画面总人数。 2. 判断是否存在人群拥挤、局部聚集等风险。 3. 给出针对运营的简短建议。 4. 输出格式JSON对象包含 count、risk_level、analysis、suggestion 四个字段。这里有个关键细节提示词里强制约束输出格式为JSON然后在代码里解析这样稳定性和可维护性会好很多。如果让大模型自由发挥输出的文本格式五花八门后面解析起来会崩溃。4.3 成本控制与频控策略大模型API不是免费的频繁调用会把单次检测的成本拉高。我的策略是分级调用低风险场景默认不调用大模型只返回检测结果。只有人数超过阈值或者出现特定异常行为时才调用千问/DeepSeek做深度分析。分析结果做缓存相同时间窗口内的请求直接复用。实测下来这种策略能把API成本压到原来的十分之一以下同时用户并不会感知到分析延迟的明显变化。5. 数据集准备从KITTI标注到YOLO格式的完整转换5.1 数据来源与格式要求密集行人检测对数据量要求很高。KITTI、MOT17、CrowdHuman是三个很适合尝试的公开数据集。KITTI的标注和YOLO格式差异比较大需要转换CrowdHuman本身就是密集人群场景训练效果最好。KITTI原始标注格式Pedestrian 0 0 0.0 0 0 712.40 143.00 812.90 307.92 0 0 0 -0.23 0.00 0.00 0.00YOLO格式是归一化的类别加中心坐标加宽高0 0.545623 0.352537 0.048828 0.118750转换脚本核心逻辑for line in kitti_lines: parts line.strip().split() cls parts[0] if cls not in target_classes: continue bbox_left, bbox_top float(parts[4]), float(parts[5]) bbox_right, bbox_bottom float(parts[6]), float(parts[7]) img_w, img_h 1242, 375 x_center ((bbox_left bbox_right) / 2) / img_w y_center ((bbox_top bbox_bottom) / 2) / img_h width (bbox_right - bbox_left) / img_w height (bbox_bottom - bbox_top) / img_h out.write(f0 {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n)5.2 密集场景标注的独家心得如果自定义标注工具推荐用LabelImg或X-AnyLabeling但标注密集场景有几个通用要点标注被遮挡的行人时只要能判断出完整的人形轮廓就按完整框标注不要只框露出的部分否则模型会被遮挡区域的局部特征带偏。标签的置信度统一密集场景下人小且模糊容易产生漏标宁可多花时间反复审核也不要只标一轮就训练。数据增强不要盲开密集场景本身尺度多样过度增强会导致训练不稳定尤其是旋转和透视增强要克制使用。5.3 数据目录组织一个标准的YOLO数据集目录应该长这样dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── pedestrian.yamlpedestrian.yaml里指定路径和类别path: dataset train: images/train val: images/val test: images/test nc: 1 names: [person]6. 前端交互界面与前后端分离实战6.1 页面功能设计与交互流程前端我用的Vue3加Element Plus页面结构分三块检测控制台图片上传、视频上传、实时HTTP接口连接状态、检测参数调节置信度阈值、IOU阈值。结果展示区渲染带检测框的图片或视频帧左侧显示实时统计面板包括人数、密度等级、平均置信度。AI分析面板展示千问和DeepSeek生成的文本结论和建议同时支持人工修正标注修正结果回传后端存入数据库。检测流程是前端上传图片到SpringBootSpringBoot转发给Python检测服务拿到检测框JSON后再返回给前端前端用Canvas把框画在图片上。视频场景则采用抽帧检测的方式每5帧检测一次中间帧用最近结果复用保证流畅度。6.2 接口设计与数据模型这里给出核心的接口约定和数据模型。检测任务的表结构大概是这样CREATE TABLE detection_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, image_url VARCHAR(255) NOT NULL, status TINYINT DEFAULT 0, person_count INT DEFAULT 0, risk_level VARCHAR(16) DEFAULT low, result_json TEXT, ai_analysis TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );SpringBoot里用MyBatis-Plus操作关键接口就三个POST/api/detect上传检测图片返回检测结果。GET/api/task/{id}获取单个检测任务详情和阿analysis结果。GET/api/task/list分页获取历史记录。6.3 跨域与联调踩坑前后端分离最常见的坑就是跨域。SpringBoot要配置CorsFilterConfiguration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedMethod(*); config.addAllowedHeader(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }另一个坑是前端Axios默认只发application/json但Python检测服务接收的是multipart/form-data所以SpringBoot作为中间层必须做一次格式转换再转发这个环节最容易出现文件流未关闭导致的连接泄漏记得在finally里释放资源。7. 部署实践经验与常见问题速查7.1 环境部署配置参考整个系统分三块部署前端静态文件用Nginx托管SpringBoot以Jar包方式跑在Java 17环境Python检测服务和SpringBoot同机部署在8000端口。部署目录规划/opt/pedestrian-system/ ├── backend/ │ └── pedestrian-backend.jar ├── frontend/ │ └── dist/ ├── python-service/ │ ├── best.pt │ ├── app.py │ └── requirements.txt └── nginx/ └── conf.d/Nginx只需要配置前端路由转发/api路径反向代理到http://127.0.0.1:8080/detect-stream路径代理到http://127.0.0.1:8000这样前端一个域名就能访问完整系统。7.2 问题排查与踩坑实录密集行人检测系统坑很多我按频率整理成一张排查表现象原因解决方案检测结果里框的数量异常多置信度阈值设太低调高conf阈值至0.3以上密集场景建议0.25到0.35区间找平衡点训练时显存溢出batch太大或imgsz太大减小batch到4或8或者开启amp混合精度训练YOLOv12训练后导出ONNX失败动态轴和某些算子的兼容问题固定输入尺寸导出并换用特定Opset版本重试千问API返回频繁超限并发调用过多加信号量限流同一时间窗口内只放行一个分析请求SpringBoot上传大图直接报错默认Multipart限制在配置文件中设置spring.servlet.multipart.max-file-size: 20MB前端视频流画面卡顿每帧都做检测改为抽帧检测加结果缓存检测间隔可配置还有一个容易被忽略的坑是类目映射。如果训练时类别索引是0但Python服务里names列表顺序写错会导致所有检测框都标记成错误类别。建议在测试时先打印模型的names属性确保和后端约定一致。7.3 性能优化经验线上运行后我做了三处性能优化效果非常明显推理预热Python服务启动后先跑一次空图片推理把模型加载和显存分配等耗时操作提前完成避免第一个请求就慢好几秒。单例模型对象FastAPI的路由函数里不要重复加载YOLO模型模型对象做成模块级单例每次预测只做推理不做加载。前端惰性渲染图片数量多时Canvas绘制开销很大改用虚拟滚动和懒加载只渲染可视区域的检测框。优化后单张图片从上传到画面显示的总耗时从平均1.8秒降到了0.9秒左右视觉流畅度提升非常明显。8. 后续扩展方向这个系统做完检测、分析、展示三条主线后还可以往下走几步。我目前的计划是加上ReID和ByteTrack做跨摄像头目标跟踪然后是告警策略引擎让用户可以配置“超过多少人触发提醒”“哪个区域聚集超过多久告警”这类业务规则。另外视频流接入方面ONVIF协议的IPC接入已经在规划中接入后这套系统就能真正变成一套小型的智慧安防调度平台。密集行人检测这个方向不会过时尤其是和LLM结合后从“看见”到“理解”的能力提升带来的想象空间比单纯做检测要大得多。回到最开始的问题这套系统的价值不在“跑了几个模型”而在于把检测、分析、展示、反馈串成了一条完整的产品链路。如果你正好也在做类似场景希望这篇记录能帮你少走几步弯路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门