尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLO的人群计数实战:从数据准备到模型部署全流程解析

基于YOLO的人群计数实战:从数据准备到模型部署全流程解析 简介目标检测是计算机视觉的核心任务之一其原理是通过算法自动识别图像或视频中的特定物体并定位其位置。在众多目标检测算法中YOLOYou Only Look Once以其单次前向传播即可完成定位和分类的极速特性脱颖而出在实时性要求高的场景中具有显著技术价值。这一特性使其在安防监控、智慧零售、交通管理等需要实时分析人流的应用场景中成为理想选择。本文聚焦于利用YOLO实现高效准确的人群计数深入探讨了从数据标注、模型训练调优到工程化部署的完整流程。针对实际项目中常见的漏检、性能瓶颈等问题提供了基于YOLOv8的实战解决方案与调优技巧例如通过调整图像尺寸imgsz和置信度阈值conf来平衡检测精度与速度帮助开发者构建鲁棒的人群计数系统。1. 项目概述从“数人头”到智能感知“数人头”这件事听起来简单但在实际场景中却是个老大难问题。无论是大型商超想分析客流高峰期还是交通枢纽需要监控人流密度以保障安全甚至是大型活动现场的人流管控传统的人工计数或基于简单图像处理的方法要么成本高昂要么在复杂场景下如遮挡、光照变化、人群密集准确率惨不忍睹。这就是“基于YOLO的人群计数实现”这个项目要解决的核心痛点。简单来说这个项目就是利用当前目标检测领域的“当红炸子鸡”——YOLOYou Only Look Once算法来自动、实时、准确地统计图像或视频画面中的人数。YOLO以其“单次前向传播”即可完成目标定位和分类的极速特性而闻名这使得它非常适合对实时性要求高的视频流分析任务。我们不是简单地调用一个现成的API而是要深入其里从数据准备、模型选择与调整、训练优化到最终的部署应用走完一个完整的AI项目闭环。对于计算机视觉的初学者这是一个绝佳的入门实战项目对于有经验的开发者这也是一个探索YOLO模型优化、工程化部署的练兵场。接下来我将以一个从业者的视角拆解这个项目的每一个关键环节分享我踩过的坑和总结出的实用技巧。2. 核心思路与技术选型为什么是YOLO在动手之前我们必须想清楚技术路线。人群计数的主流方法大致可以分为三类基于检测的、基于密度估计的和基于回归的。基于密度估计的方法如MCNN擅长处理非常密集的人群它会生成一个人头密度热图再积分得到总人数但在中等密度或稀疏场景下无法提供每个人的具体位置。基于回归的方法则更“黑盒”直接从图像特征映射到人数丢失了所有空间信息。为什么最终锁定YOLO基于检测的方法其核心优势在于“可解释性”和“多功能性”。YOLO不仅能告诉我们“有多少人”还能清晰地框出“每个人在哪里”。这对于许多下游应用至关重要比如区域人数统计只统计特定区域如商店入口、电梯口内的人数。异常行为检测的基础基于人的位置可以进一步分析聚集、逆行、摔倒等行为。人流轨迹分析通过跨帧的检测框关联可以绘制人的移动路径。YOLO的“快”是另一个决定性因素。最新的YOLOv8、YOLOv9乃至社区活跃的YOLOv10、YOLOv11在速度和精度上不断取得平衡。对于需要处理摄像头实时视频流的应用每秒30帧FPS以上的处理速度是基本要求YOLO系列通常都能轻松满足。注意没有“银弹”算法。如果你的场景是演唱会散场、春运火车站这种“人贴人”的超高密度场景纯YOLO检测可能会因为严重的遮挡而导致漏检率上升。这时可以考虑“YOLO检测框 密度图校正”的混合方案或者直接采用更先进的基于Transformer的密集人群计数模型。但对于绝大多数商场、办公室、十字路口等场景YOLO足矣。模型版本选择YOLO家族庞大从经典的v3、v5到Ultralytics官方维护的v8、v9还有各种社区改进版。对于新手我强烈推荐从YOLOv8开始。理由如下生态完善Ultralytics提供了极其友好的Python APIultralytics包文档清晰从安装、训练到部署都有详细示例。精度与速度俱佳v8在COCO数据集上的表现有目共睹且提供了n纳米、s小、m中、l大、x超大五种尺度的预训练模型方便根据你的硬件和精度需求进行权衡。功能全面除了目标检测还无缝支持分类、分割、姿态估计任务方便项目后期扩展。因此本项目的技术栈明确为Python OpenCV PyTorch Ultralytics YOLOv8。我们将使用YOLOv8的检测模型来识别图像中的“person”类别。3. 数据准备项目的基石与第一个大坑模型训练的好坏七分靠数据三分靠调参。人群计数项目的数据准备有它的特殊性。3.1 数据收集与标注你需要大量包含行人的图片。来源可以是公开数据集也可以是自己采集的监控视频截图。公开数据集推荐COCO包含大量通用场景的“person”标注是很好的预训练和基准数据。CrowdHuman专门针对行人检测的数据集遮挡情况多难度大适合训练鲁棒性强的模型。ShanghaiTech经典的人群计数数据集但通常用于密度估计我们需要将其标注格式转换为YOLO所需的格式。标注工具LabelImg、CVAT、Roboflow都是不错的选择。标注时关键原则是紧密包围行人主体。对于被部分遮挡的人尽可能根据可见部分框出。对于非常小的人比如远处的人群如果像素小于20x20你需要慎重考虑是否标注因为小目标检测本身就是难点标注不清会引入噪声。3.2 YOLO数据格式详解YOLO需要的标签文件是.txt格式与图片同名每行代表一个目标。格式为class_id center_x center_y width height这里的坐标是归一化后的即相对于图片宽度和高度的比例值范围在0到1之间。例如一张800x600的图片上有一个人其边界框左上角坐标为(200, 100)宽高为(100, 200)。那么计算过程如下center_x (200 100/2) / 800 250 / 800 0.3125center_y (100 200/2) / 600 200 / 600 ≈ 0.3333width 100 / 800 0.125height 200 / 600 ≈ 0.3333对应的标签行就是0 0.3125 0.3333 0.125 0.3333假设person的class_id是0。3.3 数据集组织与划分一个标准的YOLO数据集目录结构如下datasets/ └── crowd_counting/ ├── train/ │ ├── images/ # 存放训练图片 │ └── labels/ # 存放对应的.txt标签文件 ├── val/ │ ├── images/ │ └── labels/ └── data.yaml # 数据集配置文件data.yaml文件是核心它告诉YOLO去哪里找数据以及有哪些类别。path: ../datasets/crowd_counting # 数据集根目录 train: train/images # 训练集路径相对path val: val/images # 验证集路径 # test: test/images # 可选测试集 # 类别列表 names: 0: person实操心得数据划分的比例通常是 train:val 8:2 或 7:3。确保验证集能代表你最终的应用场景如不同的光照、角度、密度。一个常见的错误是验证集和训练集场景过于相似导致在训练集上表现好一上真实场景就“拉胯”。4. 模型训练与调优从“能用”到“好用”有了高质量的数据我们就可以开始训练了。使用Ultralytics库训练变得异常简单但背后的调优门道不少。4.1 环境搭建与初步训练首先安装必要的包pip install ultralytics opencv-python然后一段最简单的训练代码可能长这样from ultralytics import YOLO # 加载一个预训练模型例如YOLOv8n是体积最小、速度最快的版本 model YOLO(yolov8n.pt) # 开始训练 results model.train( datadatasets/crowd_counting/data.yaml, epochs100, imgsz640, batch16, device0, # 使用GPU如果是CPU则设为cpu projectcrowd_counting_train, nameexp1 )这行代码会开始训练一个100轮epoch的模型输入图片会被缩放到640x640批次大小batch size为16。训练过程会自动下载yolov8n.pt预训练权重这能极大加速收敛并提升最终性能。4.2 关键超参数解析与调优上面的代码只是开始要让模型在你的特定场景下表现优异必须理解并调整这些参数imgsz图像尺寸这是最重要的参数之一。YOLO要求输入图片为正方形。更大的尺寸如1280能保留更多细节尤其有利于小目标检测但会显著增加显存消耗和计算时间。通常从640开始如果发现远处的小人检测不到可以尝试增大到960或1280但必须同步调整batch大小。batch批次大小一次训练输入多少张图片。越大训练越稳定梯度估计越准但需要更多显存。如果出现“CUDA out of memory”错误首先降低batch或者减小imgsz。epochs训练轮数不是越多越好。训练过程可以通过观察验证集上的指标如mAP50-95来判断是否过拟合。当验证集指标不再上升甚至开始下降时就应该提前停止训练。Ultralytics内置了早停EarlyStopping和模型保存策略。patience早停耐心值。如果验证集指标在连续patience个epoch内没有提升则停止训练。默认是50对于人群计数可以设为30或更小因为模型可能收敛很快。lr0初始学习率学习率决定了参数更新的步长。太大的学习率会导致训练震荡甚至发散太小则收敛缓慢。YOLOv8有自动调整学习率的能力通常不需要手动修改除非你经验非常丰富。4.3 数据增强低成本提升模型鲁棒性数据增强是提升模型泛化能力、防止过拟合的利器。YOLOv8内置了丰富的数据增强策略可以通过参数控制results model.train( data..., epochs100, ... # 数据增强参数 hsv_h0.015, # 色调增强幅度 hsv_s0.7, # 饱和度增强幅度 hsv_v0.4, # 明度增强幅度 degrees10.0, # 随机旋转角度 translate0.1, # 随机平移幅度 scale0.5, # 随机缩放幅度 shear0.0, # 随机剪切幅度 flipud0.0, # 上下翻转概率 fliplr0.5, # 左右翻转概率对于行人左右翻转是安全的 mosaic1.0, # Mosaic数据增强的概率将4张图拼成1张 mixup0.0, # Mixup数据增强的概率 )对于人群计数fliplr0.5左右翻转非常有用可以成倍增加数据多样性。mosaic增强能帮助模型学习在复杂背景下定位目标但可能会让小目标变得更小需要根据你的数据特点调整。如果场景光照变化大适当增加hsv系列的参数。4.4 训练监控与指标解读训练开始后Ultralytics会在project/name目录下生成大量有用的文件其中最重要的是results.csv和TensorBoard日志。你需要重点关注以下指标train/box_loss训练集上的边界框回归损失应持续下降。val/box_loss验证集上的边界框回归损失也应下降如果后期上升可能是过拟合。metrics/mAP50(B)在IoU阈值为0.5时的平均精度AP这是衡量检测好坏的核心指标越高越好。metrics/mAP50-95(B)IoU阈值从0.5到0.95步长0.05的平均AP的均值是更严格的指标。通过TensorBoard可以可视化这些曲线直观判断训练状态。一个健康的训练过程训练损失和验证损失都应平稳下降mAP指标稳步上升并最终趋于平缓。5. 模型推理与计数实现从检测框到人数模型训练好后我们会得到一个best.pt文件。接下来就是加载模型对新的图片或视频进行推理并完成计数。5.1 单张图片推理与计数from ultralytics import YOLO import cv2 # 加载训练好的最佳模型 model YOLO(crowd_counting_train/exp1/weights/best.pt) # 读取图片 img cv2.imread(test_image.jpg) # 进行推理 conf为置信度阈值 iou为NMS的IoU阈值 results model(img, conf0.25, iou0.45)[0] # 取第一个结果因为只有一张图 # 获取检测到的所有边界框 boxes results.boxes if boxes is not None: # 筛选出类别为‘person’的检测框 (cls 0) person_boxes boxes[boxes.cls 0] # 统计人数 person_count len(person_boxes) print(f检测到人数: {person_count}) # 可视化在原图上绘制框和计数 annotated_img results.plot() # Ultralytics内置的绘图函数非常方便 # 或者用OpenCV手动绘制 for box in person_boxes: x1, y1, x2, y2 box.xyxy[0].int().tolist() # 获取整数坐标 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 在图片左上角添加计数文字 cv2.putText(img, fCount: {person_count}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imwrite(result.jpg, img) else: print(未检测到任何人。)5.2 视频流实时计数实时视频计数的核心是循环读取视频帧并对每一帧进行上述推理和计数操作。为了提升性能有两个关键技巧模型预热在循环开始前先用一张虚拟图片跑一次推理触发模型的JIT编译和CUDA内核初始化避免第一帧速度慢。跳过帧处理对于高帧率视频如30fps如果不需要每帧都分析可以设置一个跳帧间隔比如每3帧处理一次能大幅降低计算负荷。import cv2 from ultralytics import YOLO import time model YOLO(best.pt) cap cv2.VideoCapture(test_video.mp4) # 或者填入0使用摄像头 frame_skip 2 # 每3帧处理1帧 frame_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break frame_count 1 if frame_count % (frame_skip 1) ! 0: continue # 跳过指定帧数 # 推理 start_time time.time() results model(frame, conf0.25, iou0.45, verboseFalse)[0] # verboseFalse关闭控制台输出 inference_time time.time() - start_time fps 1 / inference_time # 计数与绘制 boxes results.boxes count 0 if boxes is not None: count len(boxes[boxes.cls 0]) annotated_frame results.plot() # 显示计数和FPS cv2.putText(annotated_frame, fPersons: {count}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(annotated_frame, fFPS: {fps:.1f}, (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2) cv2.imshow(Crowd Counting, annotated_frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()5.3 区域人数统计ROI Counting很多时候我们只关心特定区域的人数比如商店的收银台、地铁的闸机口。这需要引入区域检测。import numpy as np # 定义多边形区域例如一个四边形的四个点 [x, y] roi_polygon np.array([[100, 100], [700, 100], [700, 400], [100, 400]], np.int32) roi_polygon roi_polygon.reshape((-1, 1, 2)) count_inside 0 for box in person_boxes: # 计算检测框的中心点 x_center int((box.xyxy[0][0] box.xyxy[0][2]) / 2) y_center int((box.xyxy[0][1] box.xyxy[0][3]) / 2) center_point (x_center, y_center) # 判断中心点是否在多边形区域内 if cv2.pointPolygonTest(roi_polygon, center_point, False) 0: count_inside 1 # 可以给框上不同颜色以示区分 cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 0), 2) # 蓝色表示区域内 else: cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 绿色表示区域外 print(f区域内人数: {count_inside})这种方法简单有效其原理是判断每个行人检测框的中心点是否落在预设的多边形区域内。6. 性能优化与部署实战让模型在实验室跑起来只是第一步要真正用起来还需要考虑性能和部署。6.1 模型导出与优化YOLOv8训练出的.pt文件是PyTorch模型。为了获得更快的推理速度尤其是在CPU或边缘设备上我们需要将其导出为优化后的格式。ONNX一种开放的模型交换格式可以被多种推理引擎如OpenVINO, TensorRT, ONNX Runtime支持。这是部署的第一步。model.export(formatonnx, imgsz640, simplifyTrue)TensorRTNVIDIA GPU上的终极加速方案。通过export(formatengine)可以将模型转换为TensorRT引擎获得数倍甚至数十倍的性能提升。但这个过程对环境和版本要求严格。OpenVINOIntel针对其CPU、集成显卡和神经计算棒优化的工具套件。对于在Intel设备上部署是绝佳选择。6.2 使用ONNX Runtime进行推理导出ONNX后我们可以脱离庞大的PyTorch库用更轻量的ONNX Runtime进行推理这在资源受限的环境中非常有用。import onnxruntime as ort import numpy as np import cv2 # 创建ONNX Runtime会话 session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) # 或CUDAExecutionProvider # 预处理函数将图片调整为模型输入尺寸并归一化 def preprocess(img, input_size640): img cv2.resize(img, (input_size, input_size)) img img.transpose(2, 0, 1) # HWC to CHW img img.astype(np.float32) / 255.0 # 归一化 img np.expand_dims(img, axis0) # 添加batch维度 return img img cv2.imread(test.jpg) input_tensor preprocess(img) # 运行推理 outputs session.run(None, {session.get_inputs()[0].name: input_tensor}) # outputs是一个列表包含了模型的所有输出需要根据模型结构进行后处理如解码边界框、NMS后处理部分相对复杂需要根据你导出模型时的设置如是否包含NMS来编写对应的解码代码。Ultralytics导出的ONNX模型默认包含NMS输出是经过筛选的检测结果格式为[batch, num_detections, 6]其中最后一维是[x1, y1, x2, y2, confidence, class_id]这大大简化了部署。6.3 工程化考量一个完整的人群计数系统远不止一个模型多摄像头管理需要设计一个调度器平衡多个视频流的处理负载。结果存储与展示计数结果需要存入数据库如MySQL, InfluxDB并通过Web界面如Flask, Django ECharts实时展示历史曲线和热力图。报警机制当某个区域人数超过阈值时触发声音、灯光或消息推送报警。模型更新设计一个管道当发现模型在新数据上性能下降时能够自动收集新数据、重新训练并无缝更新线上模型MLOps。7. 常见问题与排查技巧实录在实际操作中你一定会遇到各种各样的问题。这里记录了几个最典型的“坑”和我的解决方法。7.1 训练指标全是0或NaN可能原因1学习率过高。这是最常见的原因。过高的学习率导致梯度爆炸模型参数变成NaN。解决使用预训练模型model YOLO(yolov8n.pt)开始训练而不是从头训练。预训练模型提供了很好的初始点允许使用相对稳定的学习率。可能原因2数据标注格式错误。检查你的标签文件.txt确保坐标是归一化后的值0~1并且没有超出范围。一个快速检查脚本是必要的。可能原因3数据集路径或data.yaml配置错误。确保data.yaml中的path、train、val路径都是正确的并且图片和标签文件一一对应没有缺失。可能原因4批次大小batch size过大导致显存溢出破坏了训练过程。解决逐步减小batch直到训练能稳定进行。同时可以尝试减小imgsz。7.2 模型推理时漏检严重特别是小目标可能原因1输入图像尺寸imgsz太小。模型在训练时看到的“人”可能因为下采样而变得非常小丢失了特征。解决重新以更大的imgsz如1280训练模型。注意这会增加计算负担。可能原因2置信度阈值conf设置过高。模型可能检测到了但因为置信度低于阈值而被过滤掉了。解决在推理时尝试降低conf例如从0.25降到0.1观察是否有多余的检测框出现。同时可以配合调整NMS的iou阈值防止降低置信度后产生大量重叠框。可能原因3训练数据中缺乏类似场景或尺度的小目标样本。解决在数据集中增加包含远处、小尺寸行人的图片并确保标注准确。7.3 同一个目标被重复检测一个框里套几个小框可能原因NMS的IoU阈值iou设置过低。NMS非极大值抑制的作用是合并重叠的检测框。如果iou设得太低比如0.1它可能无法合并那些高度重叠但略有偏移的框。解决适当提高推理时的iou参数比如从0.45提高到0.6或0.7。但要注意如果两个真实的人靠得很近过高的iou可能会把他们都抑制掉需要根据场景权衡。7.4 视频推理速度慢FPS低可能原因1模型太大。你使用了yolov8l.pt或yolov8x.pt这样的大模型。解决换用更小的模型如yolov8n.pt或yolov8s.pt。在精度和速度之间做权衡。可以使用model YOLO(yolov8n.pt)加载小模型并用你的数据做轻量级微调few-shot fine-tuning。可能原因2没有使用GPU或GPU驱动/CUDA环境有问题。解决确保device0参数已设置并通过nvidia-smi命令确认PyTorch能正确识别和使用GPU。可能原因3每帧图片的预处理和后处理耗时太长。解决优化你的代码。例如使用OpenCV的cv2.resize代替PIL的Image.resize将后处理如画框、写字的操作移到单独的线程或使用更高效的绘图库。终极方案如前所述将模型导出为TensorRT或OpenVINO格式并进行INT8量化能获得最大的速度提升。7.5 在特定场景如逆光、夜间下性能下降根本原因训练数据分布与测试场景不匹配。模型没见过这样的数据。解决这就是“领域自适应”问题。最直接有效的方法就是在你的训练数据集中加入大量逆光、夜间的图片并进行标注。如果数据收集困难可以尝试使用数据增强来模拟这些场景例如在训练时随机调整图片的亮度、对比度、伽马值甚至添加模拟的噪声。虽然不如真实数据但能在一定程度上提升鲁棒性。这个项目从标题上看只是一个简单的“计数”实现但深入下去它串联起了计算机视觉项目从数据、训练、优化到部署的完整链条。每一个环节都有值得深挖的细节和技巧。我个人的体会是不要只满足于跑通代码多问几个“为什么”多尝试调整不同的参数多看看训练过程中的损失曲线和评估指标你才能真正掌握它并能够将其灵活应用到其他类似的目标检测任务中去。最后一个小技巧定期用一批固定的、覆盖各种难度的“测试集”来评估你的模型这比单一的验证集指标更能反映模型的真实水平。本文还有配套的精品资源点击获取
返回列表