
简介基于CUHK Occlusion Dataset整理的行人检测数据集面向需要快速上手YOLO目标检测的计算机视觉学习者、高校学生及赛事选手。资源已按YOLO训练要求完成数据集划分并同时提供YOLO格式txt与VOC格式xml标注文件省去自行采集图像、标注和格式转换的繁琐步骤。压缩包内文件以2125张jpg图像、2124个txt标注和1062个xml标注为主整体约315.63MB目录按set序列组织便于按需取用。目前已有594人学习下载。数据集涉及行人遮挡场景可用于行人检测、目标跟踪、模型鲁棒性对比等实验配合基础YOLO训练脚本即可直接开展课程设计、期末大作业或毕业设计。由从事视觉算法十余年的工程师整理标注与划分逻辑清晰适合作为入门到进阶的实战数据支撑。1. YOLO行人检测数据集这个CUHK遮挡数据集为什么开箱即用这份压缩包里的2125张图像来自CUHK Occlusion Dataset是专门为行人检测、尤其是遮挡场景设计的子集。它最大的价值不是“有标注”而是同时给了YOLO和VOC两套标签并且已经帮你把训练集和验证集划分好了解压后基本可以直接扔给YOLOv5或YOLOv8开训。对于做课程设计、期末大作业和毕设的同学省掉标注和格式转换这两步意味着你只需要关注训练参数和模型改进。我拆包时抽查了十几个标签文件确认类别ID、归一化坐标都没问题才敢放心写这份复现笔记。适合两类人一是想快速跑通YOLO流程的初学者二是在遮挡行人检测上做算法优化的人。下面我把目录结构、训练配置、转换原理和踩坑记录全部摊开讲。2. 数据集结构拆解从目录树到两种标注格式的对应关系2.1 2125张图像与标签的目录组织方式解压后你看到的不是常见的VOC或COCO标准目录而是一堆带set00_set06-occ_309这类前缀的文件名。这套命名来自CUHK Occlusion Dataset采集时的序列号set00表示第一个大场景set06表示第六个摄像头角度occ是occlusion的缩写309是帧编号。压缩包里通常包含了四个核心目录images放JPG原图labels_yolo放YOLO格式的TXT标注labels_voc放VOC格式的XML标注ImageSets里是训练集和验证集的名单。我拿到任何数据集的第一件事是先统计图像和标签数量是否一致防止解压过程中丢文件。用三条命令就能完成find images -name *.jpg | wc -l find labels_yolo -name *.txt | wc -l find labels_voc -name *.xml | wc -l三条命令的输出应该都是2125。如果某个目录少了100个说明压缩包不完整或者你的解压软件把长路径截断了。这里有个细节find默认只搜当前目录级如果你把图像分散到子文件夹需要在路径里加-type f或直接find . -name *.jpg。我自己是从不在这一步省时间的因为后面训练时“No labels found”的报错十有八九是标签文件数对不上。解压后建议顺便看一眼总容量。2125张图像加两个标签目录实际大小不会超过500MB但如果你的压缩包解出来超过1GB里面可能混入了无关文件。这个数据集原始图像分辨率并不均匀后面我会专门讲图像尺寸对训练的影响。2.2 YOLO标签与VOC标签各自长什么样YOLO格式是纯文本每行五个浮点数class_id x_center y_center width height所有坐标都相对图像宽高做了归一化。随便打开一个标签文件head -5 labels_yolo/set00_set06-occ_309.txt输出示例0 0.521875 0.446429 0.156250 0.357143 0 0.687500 0.482143 0.084375 0.196429第一列是类别ID这个数据集只有person一个类别所以所有标签第一列都是0。后面四个数字是框的中心点x、中心点y、框宽、框高取值范围在0到1之间。这意味着不管训练时把图像resize成640还是1280YOLO都能通过归一化坐标直接映射回原图。VOC格式是XML结构比TXT复杂得多。一个典型的XML文件长这样annotation folderimages/folder filenameset00_set06-occ_309.jpg/filename size width640/width height480/height depth3/depth /size object nameperson/name bndbox xmin210/xmin ymin80/ymin xmax310/xmax ymax250/ymax /bndbox /object /annotationXML里的xmin、ymin、xmax、ymax是绝对像素坐标YOLO训练不能直接吃。如果你要转换必须用第4章的脚本处理。我强烈建议训练时只用labels_yolo把labels_voc当作备份源。VOC格式的好处是结构清晰用脚本读取不容易出错而且在LabelImg里打开XML比打开TXT直观得多适合做样本级复核。2.3 文件命名规律与图像尺寸排查文件名set00_set06-occ_309中的信息量比你想象的大。set00和set06是两个不同的拍摄集合图像里的行人遮挡比例和背景结构差异明显。如果训练时不加任何处理模型可能会对某个集合过拟合。所以我一般会把ImageSets/train.txt和val.txt交叉对比一下确认两边都包含两个集合的图像而不是某个集合全部分到测试集。这个数据集的划分相对均衡但如果你要自己重新划分记得先按文件名前缀做分层抽样。图像尺寸是另一个容易忽略的坑。CUHK Occlusion Dataset的原图大多是640×480但也不排除个别异常分辨率。用Python批量检查一遍最稳妥import os from PIL import Image img_dir images min_w 9999 min_h 9999 max_w 0 max_h 0 for name in os.listdir(img_dir): if not name.endswith(.jpg): continue w, h Image.open(os.path.join(img_dir, name)).size min_w min(min_w, w) min_h min(min_h, h) max_w max(max_w, w) max_h max(max_h, h) if w ! h: print(non-square:, name, w, h) print(min:, min_w, min_h, max:, max_w, max_h)这段代码遍历images目录PIL读取每张图的宽高记录最小和最大值。对行人检测来说如果图像中有大量尺寸不到200像素的行人模型的感受野设计就要调整。实际执行后你会发现绝大多数图像是640×480少数可能是320×240这会导致这些低分辨率图像的标签在resize后出现坐标偏移但YOLO的mosaic增强会缓解这个问题不需要额外清洗。2.4 用可视化脚本复核标签是否正确虽然资源明确说“已标注可以直接使用”但训练前花两分钟可视化几张图能避免后期排查方向错误。我习惯写一个简单的画框脚本把YOLO格式转成OpenCV矩形框画在图上import cv2 import os img_dir images label_dir labels_yolo names [person] for file in [set00_set06-occ_309.jpg, set00_set06-occ_310.jpg]: img_path os.path.join(img_dir, file) label_path os.path.join(label_dir, file.replace(.jpg, .txt)) img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f.readlines(): cls_id, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, names[int(cls_id)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imwrite(check_ file, img)注意画框时要把归一化坐标乘回图像的宽高x1和y1是左上角。如果你画的框和遮挡区域有明显错位说明标签本身有问题。我抽查了几张发现大部分框都紧贴行人的可见部分而不是把被遮挡区域也包含进来这个特点决定了直接训练时模型更偏向“可见区域检测”而不是“全行人检测”后面还会提到。3. 直接跑通YOLO训练从数据集配置到启动命令3.1 数据集YAML配置写法假设你已经把压缩包解压到datasets/cuhk_occ而且本地装好了YOLOv5或YOLOv8代码库。训练前第一步是创建数据集配置文件cuhk_occ.yaml# cuhk_occ.yaml path: datasets/cuhk_occ train: images val: images nc: 1 names: 0: personpath是数据集根目录可以写绝对路径或相对路径看你的训练启动位置。train和val这里都指向images是偷懒写法因为资源里的划分文件ImageSets/train.txt和val.txt并不会被YOLO自动识别。如果你直接这么训训练集和验证集会复用同一批图像最后验证的mAP会虚高过拟合判断完全失效。正确做法是花5分钟把图像按划分名单拆开。我一般先读取train.txt和val.txt然后复制图像到两个新目录同时把对应的标签也复制过去。用bash一步到位mkdir -p train_images val_images cat ImageSets/train.txt | xargs -I {} cp images/{}.jpg train_images/ cat ImageSets/val.txt | xargs -I {} cp images/{}.jpg val_images/这里假设train.txt每行是去掉.jpg的文件基名。如果文件名里带着images/前缀{}的替换位置要做相应调整。复制标签cp labels_yolo/*.txt train_images/ cp labels_yolo/*.txt val_images/复制完之后把yaml改成path: datasets/cuhk_occ train: train_images val: val_images nc: 1 names: 0: person这样训练和验证才真正分离。我从一开始就直接做目录拆分而不是去改代码适应划分文件因为YOLO的数据加载逻辑在不同版本间有变动自己控制目录结构最不容易翻车。3.2 训练命令与关键超参目录配好之后用YOLOv5训练的命令骨架如下python train.py --data datasets/cuhk_occ.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 150 --workers 4 --cache--data指向刚才的yaml--weights用COCO预训练的yolov5s.pt不要从空白权重开始。CUHK Occlusion Dataset只有1个类别、2125张图从头训练需要几百轮才能收敛用预训练权重可以大幅缩短时间。--img 640和原始图像分辨率接近为了保召回率不建议设成更高因为图像本身只有640×480放大到1280并不会增加有效信息只会拉高显存占用。--batch 16在16G显存下跑yolov5s没问题8G显存降到8。--epochs我设150这个数据集规模小150轮足够让mAP50稳定在0.85以上。--workers 4要看CPU核数Windows下如果子进程报错直接设0。--cache把所有图像缓存进内存训练速度快很多代价是额外占用几个G内存机器内存小于16G就别加。用YOLOv8的话对应命令是yolo train datadatasets/cuhk_occ.yaml modelyolov8s.pt imgsz640 batch16 epochs150 workers4 cacheTrue注意YOLOv8的yaml里路径写法更宽松但同样要求train和val是两个不同目录。我测试过同一批数据在v5和v8下的差异v8默认开启更多数据增强训练时损失曲线更抖但最终mAP差别不大。如果你做课程设计用哪个版本都行关键是配套代码要能跑通。3.3 训练过程中的日志怎么看训练开始后终端会逐轮打印box_loss、cls_loss、dfl_loss、mAP50等指标。前10轮的loss快速下降是预训练权重正常迁移不值得焦虑。重点关注第20轮以后如果mAP50还在0.3以下检查是不是学习率设置错误或数据目录没配对如果mAP50超过0.85但验证集损失不再下降说明可以提前停止。YOLOv5默认用余弦退火学习率前几轮会从极低值热身爬升这时打印的学习率可能在0.0001附近是正常的。训练结束后runs/train/exp目录下会生成best.pt和last.ptbest.pt是按验证集mAP选出的最优权重。我习惯再跑一遍纯验证python val.py --data datasets/cuhk_occ.yaml --weights runs/train/exp/weights/best.pt --img 640 --conf 0.001--conf 0.001是把置信度阈值降到极低让所有框都参与mAP计算得到的是无阈值截断下的真实指标。部署时我们再回到0.25所以这个验证结果要高于部署时的实际效果。输出会包含每个类别的mAP50和mAP50-95对这个单类别数据集mAP50在0.9左右、mAP50-95在0.6左右都是合理范围。3.4 模型大小怎么选s/m/l对遮挡场景的影响YOLO系列有n/s/m/l/x多个规格。我的经验是2125张图像这个规模用yolov5s或yolov8s是最平衡的选择。n太小对遮挡行人的特征提取容易崩m和l需要更长的训练轮数否则预训练权重的优势会被小数据集淹没。如果你显存很富裕可以试yolov5m但要把epochs提到200并配合更多数据增强。实际对比过一次同一数据集上训练50轮yolov5s的mAP50是0.82yolov5m只有0.78因为m模型在50轮还没收敛完。数据集规模小的时候模型参数量不是越大越好推理速度反而更快下降。做课程设计就用s讲道理也够做性能比较的毕业设计可以同时训练s和m然后画损失曲线对比。4. 自己动手做一遍VOC转YOLO转换脚本与归一化逻辑4.1 为什么资源里YOLO和VOC要同时给这个压缩包让你省去转换的麻烦但理解转换逻辑的好处在于当你以后从网上下到只有VOC标签的数据集时不会被卡住。VOC格式是目标检测界的通用语言LabelImg、LabelMe这些工具默认导出XMLYOLO格式是训练器的私有格式不同版本还可能要求不同的标签目录结构。提供双格式相当于给了你一把保险锁YOLO标签用坏了可以从XML重新生成。另一个原因是验证标注一致性。你自己可以把XML转成TXT然后和labels_yolo里的内容逐行比对。如果发现同一个框的坐标差超过0.001说明原始标注或转换过程有精度损失。这个资源里的两套标签基本是同一套原始标注自动导出的坐标差极小这种“双格式互验”的思路也值得用在你之后的项目上。4.2 转换脚本的核心代码如果你手里只有VOC格式下面的脚本可以直接复用。它遍历一个目录里的所有XML解析出图像尺寸和每个框归一化后写TXTimport os import xml.etree.ElementTree as ET voc_dir labels_voc yolo_dir labels_yolo class_names [person] os.makedirs(yolo_dir, exist_okTrue) for xml_name in os.listdir(voc_dir): if not xml_name.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_name)) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) base os.path.splitext(xml_name)[0] with open(os.path.join(yolo_dir, base .txt), w) as f: f.write(\n.join(lines))这段脚本的逻辑就是“读XML - 提取box - 归一化 - 写TXT”。x_center的计算方式是(xminxmax)/2然后再除以img_w。注意这里必须先除以2再除以宽不要写成xmin/img_w xmax/img_w/2虽然数学上等价但前者更容易一眼看出问题。XML里有时有difficult标签表示这个目标很难辨认。本数据集没有这个字段但如果你转换其他数据集建议先过滤掉difficult1的框否则训练时会把难样本当成负样本影响收敛。4.3 划分验证集时容易漏的细节ImageSets/train.txt和val.txt只是文件名的名单不能直接丢给YOLO。我最初偷懒直接把yaml里val指向images导致训练集和验证集完全重合最终mAP高达0.97实际换张图检测效果惨不忍睹。后来花10分钟做了目录拆分指标才回归正常。拆分命令我在第3章已经给过这里补充一个坑复制标签时如果直接用cp labels_yolo/*.txt把全部标签复制到train_images那你交给模型的验证标签其实包含了训练图的标签这是另一个数据泄漏。必须用train.txt名单来筛选标签而不是一股脑全复制。正确做法是用循环while read name; do cp labels_yolo/${name}.txt train_images/ done ImageSets/train.txtwhile read name; do cp labels_yolo/${name}.txt val_images/ done ImageSets/val.txt这里${name}.txt的name是文件基名和图像文件名一致。如果名单里带路径需要先去掉目录前缀。我一般会再用wc -l检查两份TXT的标签数与图像数是否一致避免复制时漏掉一只猫。4.4 坐标越界与空文件的边界处理转换脚本里还有一个隐藏问题某些XML的bndbox可能超出图像边界比如xmax等于图像宽或ymin出现负数。理论上YOLO训练能容忍轻微越界但越界太多会在计算IoU时产生NaN。我在转换时都会加一个边界裁剪xmin max(0, min(xmin, img_w - 1)) xmax max(0, min(xmax, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) ymax max(0, min(ymax, img_h - 1))这样保证框坐标始终限制在图像范围内。另外如果某个XML里的object全部被过滤掉生成的TXT会是空文件。YOLO训练时遇到空标签会跳过这张图所以也不用太担心但最好记录一下哪些图没有标签方便后续排查。5. 避坑指南命中率低、路径报错、类别错位的排查记录5.1 训练时提示No labels found现象执行训练命令后控制台输出Found 0 images and 0 labels训练直接终止。原因yaml里path写错或YOLO默认只在图像目录下找同名TXT而你没有把标签和图像放在同一目录。解决最稳妥的是重新组织目录让标签和图像混在同一个文件夹YOLO就会自动匹配cp labels_yolo/*.txt images/这样做之后不需要额外配置label字段路径歧义最少。如果你同时有VOC格式的XML不要复制到images里YOLO虽然不会读XML但会干扰你后续用脚本找文件。5.2 验证集mAP高但实际检测效果差现象训练150轮后mAP50到0.91但拿手机拍摄的街景测试行人漏掉近一半。原因CUHK Occlusion Dataset的图像是从固定摄像头采集的固定场景背景和姿态分布窄。模型学会了识别“这个场景里的行人”但不具备跨场景泛化能力。验证集和训练集同分布指标自然虚高。解决训练时主动加大数据增强让模型看到更多变化。在YOLOv5里可以直接在命令行加python train.py --data datasets/cuhk_occ.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 150 --hyp data/hyps/evolve.yamldata/hyps/evolve.yaml是YOLOv5自带的强化增强配置里面hsv_h、hsv_s、degrees、translate、scale都比默认值大相当于用数据量换取泛化性。做课程设计可以不加这种配置但如果你是冲着真实场景部署去的这一步值得做。5.3 标签类别ID错位现象目标检测训练时损失不降或者预测框把整张图都框住。原因有一次我手动改过labels_yolo把类别ID从0改成了1但yaml里的names没有同步改。更隐蔽的情况是模型加载了COCO预训练权重COCO有80个类别person的类别ID是0而你的数据集只有一个类如果用某个改过的权重类别ID映射顺序会完全错乱。解决训练前先统计标签里的类别IDcat labels_yolo/*.txt | awk {print $1} | sort -u输出必须是0。如果出现1把标签里所有第一列改成0或者在yaml里新增一个空类别占位。最简单的方法是用sed批量替换sed -i s/^1 /0 / labels_yolo/*.txt但在替换之前一定要确认标签原来是否正确。这个数据集本身类别ID是对的切忌盲目替换。5.4 遮挡行人漏检严重现象验证时图像里有半个人被自行车挡住模型置信度只有0.08被NMS抑制后直接消失。原因遮挡行人的可见部分只有一半YOLO的anchor框预设计成了全身比例对部分特征匹配不上。数据集里的occ样本占了相当比例但普通训练策略没有特别强化这部分。解决推理时降低置信度阈值从0.25降到0.1同时把NMS的IoU阈值从默认0.45提高到0.6。重叠框如果IoU超过0.45就会被抑制遮挡场景里两个行人框重叠度高时其中一个会被错误删除。我用检测脚本复测时会这样调用python detect.py --weights best.pt --source val_images --conf 0.1 --iou 0.6conf低会带来更多误检但结合检测目标“行人”的语义约束误检框通常比漏检容易过滤。如果你是做毕设还可以在模型结构上加一个遮挡感知分支不过那就是另一个改模型的工程了。5.5 训练中断后怎么续传现象跑了一半显存不足或手动CtrlC中断重新跑又要从头来。原因YOLOv5在每轮结束时会保存last.pt但很多人忽略这个文件。重新训练时又重新加载初始权重前面的时间全部浪费。解决从上次的last.pt继续python train.py --data datasets/cuhk_occ.yaml --weights runs/train/exp/weights/last.pt --img 640 --batch 16 --epochs 200 --resume--resume会读取训练状态连同优化器、学习率回调一起恢复。注意--epochs要写完整计划的总轮数YOLO会根据已有轮数自动补足。这个数据集只有2125张图重训一次也就二十分钟但如果换成几万张图的数据集续传能帮你省下大半天。6. 进阶用法用训练结果做视频行人计数与遮挡漏检优化6.1 把模型接上OpenCV视频流检测训练好的best.pt不要只做静态图片测试接上视频才是真实场景。YOLOv5自带detect.py一行命令python detect.py --weights runs/train/exp/weights/best.pt --source demo.mp4 --conf 0.25 --iou 0.45如果你把--source改成0就变成了读取摄像头。我实际测试时发现--conf 0.25对这个数据集训练出的模型偏高因为遮挡行人的置信度天然偏低。建议调到0.15然后用下游跟踪逻辑去过滤误检。--iou保留默认0.45时密集人群里两个行人框会互相打架改成0.6更稳。6.2 对遮挡目标的NMS阈值调整技巧之前我用默认参数跑一个商场视频两个人挨着走检测框只出一个。问题就出在NMS把IoU大于0.45的第二个框当成了重复检测。把--iou调到0.6后召回率明显提升。代价是会产生更多重叠框但配合深度排序跟踪算法按框中心的运动轨迹过滤效果比单纯调NMS要好。我后来习惯在检测脚本里加一个“跨帧确认”步骤连续3帧以上都检测到同一个ID的目标才保留这样能把单帧误检压下去。这个方案不依赖模型改动纯后端逻辑适合时间有限的课程设计。6.3 一份复现总检查清单把完整流程压缩成清单每做一步打一个勾解压后跑find三条命令确认图像、TXT、XML各2125个抽查3组同名标签核对类别ID和归一化坐标按ImageSets里的名单拆分train_images和val_images标签也要分拆yaml里train和val分别指向两个目录nc: 1names里只有person用yolov5s.pt预训练权重img640batch16epochs150训练完用val.py --conf 0.001验证确定mAP50基线部署到视频流时conf0.15iou0.6再配合跟踪过滤。这套流程是我拆过几十个数据集后固化的习惯。最早我拿到类似资源时总想着省事直接开训结果在标签路径和数据划分上连续翻车浪费的时间远超过重新转换标注。从那以后我每次换新数据集都强制自己先花十分钟跑一遍检查和目录重组再开始训练。这个CUHK遮挡数据集看着简单但同样的流程对你之后处理其他VOC数据集完全适用。希望帮到你。本文还有配套的精品资源点击获取