简介面向行人检测算法开发者与智能监控方案研究者这份自制数据集聚焦国内高铁站监控视角下的行人排队场景可用于训练和验证YOLO等目标检测模型解决通用行人数据与真实监控场景不匹配的问题。压缩包共9864个文件包含4932张JPEG监控截图与4932个配套XML标注文件整体体积947.09MBXML文件完整记录每个行人的边界框坐标JPEG图像保留真实监控环境中的复杂背景、遮挡与光照变化。目前已有709人学习下载适合需要真实场景训练样本的计算机视觉入门及进阶用户。借助这批数据开发者可免去从零采集和标注的高成本流程直接构建针对排队场景的行人检测模型并利用密集排队、人员重叠等典型状态检验算法的鲁棒性为实际安防系统提供可靠的检测支撑。1. 自制行人排队检测数据集监控视角下最难复现的一类样本行人检测在公开数据集里并不缺COCO、CrowdHuman 里随便都能找到上万张带人框的图。但真把通用模型部署到国内网点监控场景里做排队检测的人都会撞上同一个问题大厅地砖反光、柜台玻璃挡一半、队伍从镜头近处一直排到远处人贴着人、队尾还带拐弯这种画面在公开数据里几乎没有。排队检测的核心不是「认出人」而是「在密集且静止的人群里稳定地认出每一个人」。这份自制数据集就是冲着这个缺口来的图片全部来自国内监控视角下的真实排队场景标注了行人框和排队队列归属拿去做通用检测模型微调或排队计数验证都合适。适合正在做网点视频分析、窗口排队管理这类项目的开发者。2. 数据集底细目录结构、标注格式与三类典型排队场景拿到任何数据集第一件事不是打开看图片而是先摸清目录结构和标注格式。标注决定模型能学到什么目录决定你的训练脚本要不要改路径。这份数据集的目录组织和标注方式都按训练落地的习惯来排没有搞特殊格式。2.1 解压后的目录结构与文件关系queuedet/ ├── images/ # 全部监控帧按来源场景分目录 │ ├── scene_001/ # 第一个拍摄点连续帧序列 │ ├── scene_002/ │ └── scene_012/ ├── annotations/ # 每张图片对应的标注文件 │ ├── scene_001/ │ ├── scene_002/ │ └── scene_012/ ├── labels/ # 空的留给转换脚本输出 YOLO 格式 ├── train.txt # 训练集图片路径列表 ├── val.txt # 验证集图片路径列表 └── class_names.txt # 类别清单只有 person目录结构里最需要注意的是images和annotations是同样命名的分组目录train.txt和val.txt里写的是图片的绝对路径或相对路径。我一般建议下载后把train.txt里的路径改成自己机器上的实际路径避免训练时报 FileNotFoundError。labels目录留空不是缺文件是给你跑转换脚本用的这也是这套数据集比较方便的地方——原生标注不是 YOLO 格式但转换入口已经铺好。2.2 标注格式字段说明标注文件用的是 VOC XML 格式每张图片对应一个同名 XML 文件。打开其中一个看一下结构annotation folderscene_001/folder filenameframe_00231.jpg/filename size width1920/width height1080/height depth3/depth /size object nameperson/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin642/xmin ymin531/ymin xmax728/xmax ymax831/ymax /bndbox /object /annotationVOC 格式的字段语义很清楚bndbox四个值分别表示框的左上角 x、左上角 y、右下角 x、右下角 y单位是像素坐标系原点在图片左上角。truncated表示目标是否被图片边缘截断difficult表示是否因遮挡或模糊而难以辨认这两个字段在很多公开数据集里会被忽略但这份数据的标注里保留了下来。这里有个值得注意的点truncated1的框在排队场景里占比不低因为监控视角下队伍经常从画面边缘开始排。如果直接用现成脚本转 YOLO 格式difficult1的样本通常会被过滤掉但truncated1的我建议保留因为被截断的半个人也是排队现场的真实信息。转格式时别一刀切先看一下这两个字段的分布再定策略。2.3 场景构成、视角与光照分布数据集的场景构成直接决定模型能迁移到什么环境下用。我扫了一遍图片目录大致可以分成三类典型场景场景类型视角特征光照特征排队形态样本占比办事大厅斜俯视镜头在服务台侧上方顶灯均匀照明地砖反射明显单列长队队尾经常拐弯约 40%窗口柜台近水平俯视镜头距人较近局部强光玻璃反光短队列人与人间距小约 35%通道入口大角度俯视人尺度变化大明暗交替门口逆光多列并行队列交叉约 25%这种分布对训练的提示是不要用单一场景的划分方式训练和验证。我建议按场景分组做划分确保train.txt和val.txt里都覆盖三种场景而不是简单随机抽帧。否则你验证集里全是办事大厅模型在窗口柜台场景的表现就是黑匣子上线才发现泛化不行。另外要注意的是监控帧里大量存在人站着不动的情况连续帧之间差异极小划分训练集时最好按时间段切分而不是按帧随机切避免同一批静止人群同时出现在训练集和验证集里。3. 训练落地把标注转成 YOLO 格式并完成模型选型VOC XML 能直接喂给一些检测框架但大多数人在用的 YOLO 系列训练流程需要 txt 格式的标注。这一步卡住过不少刚接触数据集的开发者其实就是一个解析加归一化的过程。下面给出一个可以直接跑通的转换脚本然后说明训练参数怎么调。3.1 从 VOC XML 到 YOLO txt 的转换脚本import os import xml.etree.ElementTree as ET from pathlib import Path def convert_voc_to_yolo(xml_path: str, out_dir: str, class_names: list): 把单个 VOC XML 文件转成 YOLO 格式 txt xml_path: 标注文件路径 out_dir: 输出目录 class_names: 类别列表person 在索引 0 root ET.parse(xml_path).getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): cls obj.find(name).text if cls not in class_names: continue box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 归一化到 [0, 1]坐标系以图片左上角为原点 x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 裁剪越界坐标防止训练时 loss 异常 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(w, 1.0) h min(h, 1.0) lines.append(f{class_names.index(cls)} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: out_path Path(out_dir) / (Path(xml_path).stem .txt) out_path.write_text(\n.join(lines), encodingutf-8) # 没有目标的图片不生成 txtYOLO 训练会自动跳过空标注图 # 用法示例 class_names [person] xml_dir annotations out_dir labels os.makedirs(out_dir, exist_okTrue) for xml_file in Path(xml_dir).rglob(*.xml): convert_voc_to_yolo(str(xml_file), out_dir, class_names)这个脚本的逻辑分四步解析 XML 拿图片尺寸、遍历每个object取框坐标、把框的绝对像素坐标归一化到 0 到 1 之间、按 YOLO 格式写出 txt。归一化公式里的(x1x2)/2是框中心的 x 坐标除以图片宽度后坐标值就跟图片分辨率无关了换训练分辨率时不用重新标注。参数说明里最需要注意的是class_names的顺序它是类别索引的唯一依据。这个数据集只有 person 一类所以索引是 0。如果后面你自己加了 queue_head、queue_tail 这样的类别class_names[person, queue_head]的顺序必须和训练配置一致否则框和类别对不上训出来的是个错乱模型。另外脚本里对坐标做了裁剪是为了防止标注框超出图片边界的样本在训练时产生 NaN loss这类越界框在监控数据里很常见窗口柜台的样本尤其多。3.2 训练参数建议与模型选型数据集量级不大训练策略更适合用微调而不是从头训。我一般会先跑一个 YOLOv8n 打底确认数据没问题再换大一点的模型看收益。具体参数参考如下表参数推荐值说明modelyolov8n.pt / yolov8s.ptn 用来快速验证s 用来提精度imgsz640若场景里小目标过多再试 960显存占用约翻倍epochs150小数据集训太少会欠拟合200 以上收益递减batch16按显存调整出现 OOM 就减半close_mosaic10最后 10 轮关闭 mosaic避免密集人群框被切乱conf0.25推理时低于此阈值的框直接丢弃监控视角下的行人检测模型选型的核心权衡在速度和漏检率之间。n 模型在密集排队场景的漏检率明显偏高因为排队的人姿态高度相似、互相遮挡特征区分度低。s 模型会好不少但部署到几十路摄像头同时跑时延迟压力就上来了。我的做法是先拿 n 模型做数据验证确定数据集标注没有系统性错漏再上 s 模型做正式训练。模型选型不用纠结监控场景没有银弹跟你显卡和推理框架强相关。3.3 数据划分与数据增强策略按场景分组而不是按帧随机的划分方式前面已经提过。具体做法是把每个场景目录按时间段切出 80% 的训练和 20% 的验证而不是每十帧抽一帧放进验证集。排队场景里的人大多是静止的相邻帧内容几乎一样随机抽帧会造成训练集和验证集高度重合指标虚高上线即翻车。数据增强方面常规的翻转、缩放、亮度调整都要开但要注意一个坑水平翻转对排队场景是有副作用的。队列方向是有语义的队伍从左往右排和从右往左排虽然都是人但如果你后面要做越线检测或插队行为判断翻转后的训练数据会让模型对方向不敏感。所以如果只做检测水平翻转可以开如果打算在检测后面接行为判断训练检测器时就不要开水平翻转或者只在行为判定部分做方向归一化。4. 从检测到行为越线判定、队列计数与规则适配检测模型输出的是「哪里有人」但排队管理的业务需求是「这队有多少人」「有没有人越线」「有没有人插队」。这一步要把检测框跟业务规则接起来。监控场景里最常用的做法是定义排队区域和排队线然后基于检测框的几何特征做判定。4.1 越线判定为什么参考点要取框底边中点import numpy as np def check_cross_line(foot_point, line_start, line_end): 判断行人脚点相对排队线的位置关系 foot_point: (x, y)行人框底边中点 line_start, line_end: 排队线的两个端点 返回叉积值正负代表在线的哪一侧 px, py foot_point (x1, y1), (x2, y2) line_start, line_end # 二维向量叉积结果的正负表示点在线的哪一侧 cross (x2 - x1) * (py - y1) - (y2 - y1) * (px - x1) return cross这段代码的核心是二维向量叉积。排队线由两个点定义比如柜台前的地面贴纸线line_start和line_end是线上任意两个点。把行人框底边中点代入后叉积的正负表示人在线的哪一侧。如果连续几帧叉积值发生了符号翻转说明这个人越过了排队线这时候可以触发告警。为什么取底边中点而不是框中心这是监控场景里很实用的经验行人检测框的上下范围受姿态影响很大一个人弯腰系鞋带时框中心可能偏移几十像素但脚点的位置基本稳定。排队这个动作场景里人的上半身在活动、脚在排队线附近底边中点最接近真实站位的投影。取框中心做判定会造成「人没动框在动误报不停」的假警报。另外要注意排队线两端点的标定要在俯视畸变较小的画面区域做靠近镜头边缘的位置透视变形严重叉积判定的稳定性会明显下降。4.2 队列计数按排队区域统计而不是全图统计def count_queue_in_region(detections, region_vertices): 统计指定排队区域内的人数 detections: 检测结果列表每个元素是包含 x1, y1, x2, y2 的字典 region_vertices: 排队区域多边形顶点列表[(x1,y1), (x2,y2), ...] count 0 for det in detections: # 取底边中点作为人的位置 foot_x (det[x1] det[x2]) / 2 foot_y det[y2] if point_in_polygon(foot_x, foot_y, region_vertices): count 1 return count def point_in_polygon(px, py, vertices): 射线法判断点是否在多边形内vertices 按逆时针或顺时针排列均可 inside False j len(vertices) - 1 for i in range(len(vertices)): xi, yi vertices[i] xj, yj vertices[j] if ((yi py) ! (yj py)) and (px (xj - xi) * (py - yi) / (yj - yi) xi): inside not inside j i return inside排队计数的关键不是数出全图所有人而是把摄像头画面里你要盯的那条队圈出来。region_vertices是你在监控画面里手动框出的排队区域多边形。点是否在多边形内的判断用标准射线法逻辑是从该点向任意方向画一条射线统计它与多边形边的交点数奇数则在内部。这段代码在几百上千路摄像头场景下不会成为性能瓶颈因为判断是纯数学运算。排队计数的业务口径要注意一个问题队伍里如果有儿童或抱在怀里的人检测框会重叠得很厉害底边中点可能落在同一个位置计数器会把两个人算成一个人。这种场景如果常见建议结合检测框的面积做去重判断。面积明显小且被更大框覆盖的检测结果大概率是子目标需要单独处理而不是直接加进队列人数。4.3 插队与异常行为从单帧判定到轨迹判定越线判定是单帧几何判断插队检测则需要跨帧轨迹。常见做法是给每个检测框分配一个跟踪 ID然后看这个 ID 的运动轨迹是否逆着队列方向进入了排队区域。操作上检测模型接一个 ByteTrack 或 DeepSORT把连续帧的同一行人关联起来轨迹形成后再判断行为。这里有个边界要说清楚队尾的人回头取东西、工作人员在队伍旁边走过这些情况在轨迹层面都表现为「一个目标靠近队列区域」单靠轨迹判断一定会误报。我一般会在插队判定里加两个约束一是目标轨迹必须穿越排队线而不是平行移动二是目标最终停留在排队区域内超过 N 帧。N 的取值按场景节奏调办事大厅用 15 帧左右约 0.5 秒人流快的通道入口要调到 5 帧以内。这个阈值没有标准答案得用自己的数据跑几轮看误报率再定。5. 避坑指南标注、训练与评估中的五个常见翻车点自制数据集的坑通常不在算法而在数据本身的隐蔽问题。这些问题不跑一个完整训练周期很难暴露等模型训完才发现数据有问题浪费的算力时间就太可惜了。下面五条都是排队监控场景里反复出现的翻车点。5.1 标注阶段的三类隐患第一个常见翻车点是标注框口径混用。现象是训练出来的模型在同一场景里时而框全身、时而只框上半身准确率指标看着还行但一旦接到越线判定逻辑里底边中点就会乱跳。原因多半是制作标注时不同批次的人用了不同标准有人标全身、有人把头肩框当作人框、还有人把透明玻璃后的倒影也标了进去。解决方法是训练前跑一个统计脚本按框宽高比和框面积分布做个直方图排查全身框宽高比通常在 0.3 到 0.5 之间头肩框会明显偏方。查到异常框就回溯对应图片修正靠人眼抽查很难发现这种系统性偏差。第二个问题是排队起始帧的判定不一致。现象是验证集里相邻两帧的标注数量突变超过 30%模型在队伍刚形成的时间段频繁漏检。原因是每段视频的排队起始帧是不同人标注的标准没对齐前一个人从第 3 帧开始标人另一个人从第 10 帧才开始。解决方法是按时间段做抽样校验对同一个场景的连续帧检查相邻帧的标注目标数量变化突变点逐帧回看确认。这个校验脚本不复杂却能省下大量返工时间。第三个是反射与倒影视同真实行人。现象是模型在深色地砖场景里输出了「鬼影」检测排队人数虚高。原因是监控大厅地砖反射很常见标注时把倒影当作真实行人标了框。解决方法是标注时切换前后帧观察目标位移倒影位置相对地面固定且与真实行人距离很近出现成对框就要怀疑。处理倒影的正确做法是删掉倒影框而不是给倒影单独一个类别因为推理阶段模型没法区分哪些反光是地砖的物理属性。5.2 训练与评估阶段的隐蔽问题第四个翻车点是密集排队区漏检严重但 mAP 不降。现象是模型在队伍拥挤区域漏掉一半人但整体 mAP 还有 0.75 以上看起来一切正常。原因是排队人群里漏检的目标大多是静止且被遮挡的它们在验证集里占比小对整体指标影响有限却在排队计数业务里占比极高。解决方法是评估时单独统计排队密集子区域的 recall而不是只看全局 mAP。做法是把验证集的标注按排队区域切出来分别计算漏检率。排查时先把推理置信度阈值降到 0.15 看原始输出如果降到 0.15 还是漏检问题就不在阈值而在模型本身需要加针对密集遮挡的数据增强或换更大模型。第五个是光照突变导致检测结果闪烁。现象是同一队列人数在白天转阴天、或有人经过造成光影变化时检测框在连续帧里反复消失和出现。原因是监控相机的自动白平衡和增益在光照突变时会剧烈调整模型对过曝和欠曝的鲁棒性不足。单帧看每次都有检测结果所以 mAP 不掉但时序上队列人数的曲线会出现大量毛刺。解决方法是对检测结果做时序滤波给每个跟踪 ID 加一个指数移动平均的置信度平滑只有连续 N 帧都确认的越线事件才告警。这类问题在实验室里很难发现因为测试视频通常光照稳定真实网点环境才是翻车现场。6. 验证不只看 mAP排队场景特有的评价口径与调优技巧通用检测任务评估主要看 mAP 和 recall但排队检测的业务方关心的是另外三件事队伍人数数得准不准、越线事件有没有漏报、误报频率高不高。这三件事 mAP 反映不了。mAP 高只能说明框的位置和类别整体不错但排队场景里真正的难点是小偏移、遮挡和时序稳定性这些都需要专门的评价指标。我的做法是维护一个排队专用的验证脚本核心指标是队伍人数平均绝对误差缩写 MAE。计算方式很简单对每一帧分别统计人工标注的排队人数和模型输出的排队人数取差的绝对值求平均。这个指标直接对应业务上「队伍有多长」的感知比 mAP 直观得多。def queue_mae(pred_counts: list, gt_counts: list) - float: 计算排队人数平均绝对误差 pred_counts: 模型输出的每帧排队人数 gt_counts: 标注的每帧排队人数 if len(pred_counts) ! len(gt_counts): raise ValueError(预测序列和标注序列长度不一致) abs_errors [abs(p - g) for p, g in zip(pred_counts, gt_counts)] return sum(abs_errors) / len(abs_errors)使用这个指标时要注意统计粒度。按帧统计会把相邻帧的重叠信息重复计入同一队列在连续帧里的误差高度相关意义不大。我习惯按秒采样或按队列状态切换的时刻采样比如队伍从排队变为开始移动的那一刻单独统计。粒度决定指标的可解释性按帧统计出的 MAE 往往偏乐观因为人群静止时检测结果也稳定。调优技巧上最有效的一步是在训练前跑一遍标注统计分析重点看两个分布框的尺寸分布和遮挡程度分布。框尺寸分布告诉你该用多大的 imgsz如果大部分行人框只有 30×80 像素imgsz640 可能不够小目标分辨率太低信息在降采样时就丢了这时要上 960 或做切片推理。遮挡程度分布则决定是否需要专门的遮挡增强策略。这份数据集里办事大厅的场景框尺寸分布跨度很大近处的人是远处的三倍大这类样本分布如果不管模型会倾向于学近处大目标的特征远处小目标全漏掉。从那以后我每次接手数据集都强制自己先写一个标注统计脚本跑完确认类别分布、框尺寸分布和相邻帧标注跳变率再进训练。这套流程帮我避免过好几次「训完才发现数据有问题」的白费功夫。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?