简介面向目标检测学习者的斑马线人行横道检测数据集含192张jpg图片及对应Pascal VOC、YOLO格式标注类别为单一zebra_crossing总标注框数542个适合行人过街、交通元素检测等场景的模型训练与验证。需注意图像全部来自模拟拍摄并非真实道路环境介意真实场景的读者请谨慎下载数据集亦不保证训练所得模型或权重文件的精度但标注经人工复核矩形框准确且格式规范。资源共578个文件以jpg图像、xml标注、txt标注三类为主yolo与voc标注均与图片一一对应可直接接入常见检测框架整体为33.64MB的7z压缩包解压后目录清晰、便于按需取用。已有178人学习适合初学者练习数据标注规范、熟悉目标检测数据组织方式的轻量样本集。1. 斑马线人行横道检测数据集只有192张先别急着劝退做智能交通项目的人第一次看到“斑马线人行横道检测数据集VOCYOLO格式192张1类别.7z”这种标题时第一反应多半是192张图一个类别够干什么我也这么想过。但真正用起来的流程里“数据量小”远不如“格式混乱”致命。这个7z压缩包把VOC和YOLO两套标注都打包好意味着解压之后即可按官方目录结构把图片和标签喂给YOLO训练脚本省掉写XML转TXT脚本这一步。适合谁适合正在做路口违章、行人过街预警或者刚接触目标检测的CV工程师。别指望这192张图直接训出一个生产级模型但用来跑通“数据体检→目录重建→预训练权重微调→结果验证”这条完整链路这份数据足够当敲门砖。2. VOC和YOLO双格式为什么斑马线数据集要同时给两套标注标题把VOC和YOLO并列放在一起不是冗余。数据集同时提供两套标注的最大价值是让你能用一份母版去验证另一份而不是拿到手之后就永远依赖某一种工具链。实际项目里老代码读写VOC新代码基本默认YOLO两边都有需求缺一种就得多写一次转换脚本。2.1 Pascal VOC是一套“能打开看”的规范XML坐标是绝对值Pascal VOC是从早期目标检测竞赛沿用下来的老规范目录和文件组织都有固定习惯。最常见的一套结构是Annotations目录放XML标注JPEGImages目录放原图ImageSets/Main里放train.txt和val.txt这类划分清单。我把典型内容整理成一张表方便你解压后对照。路径典型内容作用Annotations/xxx.xml记录每个目标的类别名和边界框坐标JPEGImages/xxx.jpg原图命名与XML保持一致ImageSets/Main/train.txt / val.txt告诉训练脚本哪些图片用来训练、哪些用来验证XML标注的核心是object节点和bndbox子节点坐标是像素绝对值原点在图像左上角。下面是一段常见结构示例具体类别名以你解压到的为准。!-- 一段VOC标注示例object节点与bndbox坐标 -- annotation filenameimg_001.jpg/filename size width1920/width height1080/height depth3/depth /size object namecrosswalk/name bndbox xmin410/xmin ymin520/ymin xmax1560/xmax ymax640/ymax /bndbox /object /annotation这段XML里size给出原图宽高训练脚本读取bndbox时拿它计算归一化坐标。xmin/ymin是左上角xmax/ymax是右下角同一张图里有几条斑马线就出现几个object节点。绝对坐标最大的优点就是可视化直接用labelImg打开同目录jpeg框的位置一眼能看出是否标歪。正因如此我一般把VOC当“母版”保留视觉检查、人工修正都依赖它。2.2 YOLO标注1类别意味着每行第一列固定是0YOLO格式不依赖XML目录树每张图片对应一个同名的txt每行描述一个目标。行内顺序是类别索引、归一化中心点x、归一化中心点y、归一化宽、归一化高。因为这个数据集只有1个类别所以每一行的第一列永远是0不会出现1或者2。# 对应img_001.jpg的YOLO标签每行格式class_id cx cy w h 0 0.5135 0.5370 0.5989 0.1111 0 0.7284 0.4553 0.1875 0.1851归一化的意思是所有数值都用“除以原图宽高”得到中心点x和中心点y都在0到1之间宽高也在0到1之间。这种设计让训练时无论原图是1920×1080还是1280×720模型读到的都是一个固定尺度的描述。对这份1类别数据集来说标签体检非常容易如果txt里出现非0的第一列说明数据混入了其他类别或转换脚本有bug。训练前花两分钟检查这个能省掉后面很多定位问题的功夫。2.3 双格式的真正意义VOC当母版、YOLO当训练输入省掉转换坑VOC和YOLO之间本质上是坐标表达方式的转换。从XML的绝对坐标转成YOLO的归一化相对坐标常见做法是这样一个函数# 将VOC的bndbox绝对坐标转为YOLO归一化坐标w和h来自XML的size def voc_box_to_yolo(xmin, ymin, xmax, ymax, img_w, img_h): x_c (xmin xmax) / 2 / img_w y_c (ymin ymax) / 2 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h return x_c, y_c, box_w, box_h这个函数看起来简单实际踩坑点全在img_w和img_h上。如果XML里记录的size和JPG真实尺寸不一致比如图片被转码过或被人为裁剪过转换出来的框就会整体偏移。所以我转换前会重新用PIL读一遍图片宽高覆盖XML里的size而不是直接信任标注文件。数据集同时给出VOC和YOLO两份等于省掉了这一层转换风险。你在使用时要做的只是确认两份标注确实对应同一批图片、同一个类别名别把VOC里的类别名“crosswalk”和YOLO里的类别定义搞成两套词汇。3. 解压7z并体检192张斑马线图片训练前别跳过的三件事压缩包后缀是7z第一步自然是正确解压。这步看起来简单但我在多个项目里见过因为解压工具不对、路径带中文、顶层目录混乱导致的连锁问题。训练脚本报路径错误时回头一查往往是压缩包压根没完整解开。3.1 Linux下解压7z先装p7zip再写对解压命令Linux系统默认不一定带7z命令先安装p7zip工具包常见发行版用apt就能装。# Ubuntu/Debian下安装7z命令行工具 sudo apt update sudo apt install -y p7zip-full # 解压到指定目录注意-o后面没有空格 7z x 斑马线人行横道检测数据集VOCYOLO格式192张1类别.7z -o./zebra_data7z x表示解压并保留目录结构-o./zebra_data指定输出目录。注意命令里压缩包名用双引号包住否则号和空格容易被shell解析错。解压后先确认顶层目录是不是预期的Annotations、JPEGImages、labels组合。如果出现中文乱码多数是Windows环境下打包时使用了非UTF-8编码建议把整个数据集复制到纯英文路径再继续。3.2 Windows下用7-Zip解压不要在资源管理器里直接打开Windows自带的zip支持不能解压7z必须装第三方工具。7-Zip是最常用的选择右键压缩包选择“7-Zip - 提取到当前文件夹”即可。7-Zip文件管理器自带一个类似侧边栏的文件树你可以先点开压缩包内部结构确认是不是带顶层目录再决定整体解压还是先看内容。不过浏览归浏览训练前仍然要完整解压到磁盘不要把文件留在压缩包内直接引用路径YOLO训练脚本可不会自动解压。Windows下最常见的问题不是7z解不开而是解压路径里带了中文或空格导致Python的glob和OpenCV读取文件时遇到编码异常。我的建议是解压到类似D:/datasets/zebra_data这样的纯英文路径。3.3 用Python写一份数据集体检脚本数量、类别、尺寸一次查清解压完成后先别急着配训练参数用短短几十行代码做一次体检比训练中途翻车再排查高效得多。下面的脚本统计JPG、YOLO txt、VOC XML的数量再检查YOLO标签里有没有非0类别索引。# 体检脚本核对图片数量、YOLO标签数量和VOC标签数量 import glob import os base ./zebra_data # 扫描所有jpg、txt、xml jpgs sorted(glob.glob(f{base}/**/*.jpg, recursiveTrue)) txts sorted(glob.glob(f{base}/**/*.txt, recursiveTrue)) xmls sorted(glob.glob(f{base}/**/*.xml, recursiveTrue)) print(扫描到JPG数量:, len(jpgs)) print(扫描到TXT数量:, len(txts)) print(扫描到XML数量:, len(xmls)) # 检查YOLO标签里是否出现非0类别索引 bad [] for txt in txts: # 只检查看起来像YOLO标签的文件行内至少5列 for line in open(txt, r, encodingutf-8): parts line.strip().split() if len(parts) 5 and parts[0] ! 0: bad.append((os.path.basename(txt), parts[0])) print(非0类别标签数量:, len(bad)) if bad: print(示例:, bad[:5])这里用glob的recursiveTrue参数把嵌套目录里的文件都找出来不用关心压缩包解压后是平铺还是分层。有一点要注意如果同时存在VOC和YOLO两份文件扫描到的JPG和TXT数量很可能大于192因为同一批图被两个子目录各保存了一份。这不一定是错误而是双格式的常见布局。真正要看的是TXT和JPG按文件名一一对应的数量以及XML的object类别是否统一。3.4 抽几十张图看bbox分布先搞清楚小目标占比再定训练参数斑马线这个目标有特殊性它是细长的条状区域部分图里只占画面很小一部分。只用192张直接训练如果大多是小目标imgsz设成640很容易丢失细节。我用一个简单的脚本统计所有YOLO标签的归一化宽和高重点看中位数和小于0.1的比例。# 统计YOLO标签中归一化宽高分布判断小目标占比 import glob import numpy as np widths, heights [], [] for txt in glob.glob(./zebra_data/**/*.txt, recursiveTrue): for line in open(txt, r, encodingutf-8): parts line.strip().split() if len(parts) 5: # 形如 yolo 标签的行 widths.append(float(parts[3])) heights.append(float(parts[4])) print(宽度中位数:, round(float(np.median(widths)), 3)) print(高度中位数:, round(float(np.median(heights)), 3)) print(宽度小于0.1的比例:, round(float(np.mean([w 0.1 for w in widths])), 3))宽度中位数如果小于0.1说明这套数据的斑马线大多数是横向细条缩小到640分辨率后可能只剩十几个像素宽检测难度直线上升。遇到这种情况训练时要考虑把imgsz提到960或1280并且优先用预训练模型去微调而不是从零训练。4. 把192张斑马线图片变成可训练的数据集目录重建与data.yaml体检完就要正式进入训练准备阶段。192张图虽然不多但目录不整理好YOLO训练脚本一样跑不起来。这里说的整理不是简单地“把图放一起”而是按YOLO约定的images和labels分轨。4.1 YOLO训练目录不是“jpg和txt躺一起”需要分轨组织Ultralytics YOLO系列默认的结构是images/train、images/val和labels/train、labels/val。图片和标签必须在各自分支下保持同名jpg在images/train/a.jpg那么txt必须在labels/train/a.txt。压缩包里的原始布局不一定完全符合这个约定尤其是同时带VOC目录时通常需要重建一个干净的工作目录。目标结构如下zebra_dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/我一般不在原始解压目录里直接改而是新建一个训练专用目录从源头保证数据链路干净。这样做的好处是后续做数据增强、删脏图、补充样本时不会意外改坏VOC母版。4.2 按9:1划分train/val小样本更要随机且可复现划分训练集和验证集时随机种子必须固定否则每次跑出来的指标都不一样你很难判断改动是来自模型还是来自数据划分。下面的脚本按9:1划分并把图片和同名txt同步复制到新目录。# 按9:1随机划分并同步jpg与同名txt python - EOF import os, random, shutil, glob # 找出所有jpg并筛选出YOLO图片目录下的部分避免把VOC的JPEGImages也算进来 all_jpgs glob.glob(./zebra_data/**/*.jpg, recursiveTrue) jpgs [p for p in all_jpgs if images in p.replace(\\, /)] random.seed(42) random.shuffle(jpgs) split int(len(jpgs) * 0.9) train_files, val_files jpgs[:split], jpgs[split:] for split_name, files in [(train, train_files), (val, val_files)]: os.makedirs(fzebra_dataset/images/{split_name}, exist_okTrue) os.makedirs(fzebra_dataset/labels/{split_name}, exist_okTrue) for jpg in files: shutil.copy(jpg, fzebra_dataset/images/{split_name}/) txt os.path.splitext(jpg)[0] .txt if os.path.exists(txt): shutil.copy(txt, fzebra_dataset/labels/{split_name}/) else: print(缺少标签:, jpg) EOF注意脚本里用images in p过滤掉了VOC的JPEGImages目录避免同一个图被复制两次。随机种子固定为42每次运行结果相同。小样本不建议用7:3因为验证集只有57张指标抖动太大9:1能保留更多训练图但验证集偏小所以对比实验时一定要固定同一份划分不要每次换随机种子。4.3 data.yaml的写法path、nc、names三者必须一致data.yaml是YOLO训练的数据配置入口。它的内容不算复杂但最容易出问题的就是nc、names和真实标签对不上。下面是一份示例具体类别名以你解压出来的XML或txt为准。# data.yaml放在zebra_dataset同级目录下 path: ./zebra_dataset train: images/train val: images/val nc: 1 names: [crosswalk]path最好写成绝对路径因为训练脚本可能从不同目录启动相对路径会漂移。train和val是相对于path的子路径不是从根目录写起的完整路径。nc是类别总数这份数据是1类别所以填1。names列表长度必须等于nc否则Ultralytics会直接报索引越界。names里的内容不影响训练数学过程但它决定可视化时显示的文字如果你后面想用中文显示也可以改成斑马线但要保证和VOC的object name语义一致。4.4 最小训练命令先在PyCharm里装好ultralytics再微调yolov8n用PyCharm做YOLO训练的人越来越多但很多人忘了先给解释器装ultralytics包命令一敲就报ModuleNotFoundError。先执行pip install ultralytics确认终端里能呼出yolo命令。192张1类别不算大推荐从最小的yolov8n预训练权重开始不要一上来就选yolov8l或者自定义网络。微调命令如下# 用COCO预训练权重做迁移学习192张图不适合从头训练 yolo detect train \ datazebra.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0modelyolov8n.pt的意义是加载COCO预训练权重模型已经见过大量物体轮廓斑马线这种条状目标能借助底层特征快速收敛。imgsz640是常用起点如果第3章统计发现小目标较多可以提到960。batch16在192张图里一个epoch只有大约11个迭代训练速度很快。epochs100不一定真能跑满你要盯着验证loss如果后面不再下降就早点停。5. 192张1类别数据训练的五个坑现象、原因与排查记录小样本训练最怕的不是模型不行而是报错和异常结果让人摸不着头脑。这里把我在类似数据上踩过的坑整理成五个排查记录每一条按照现象、原因、解决的顺序拆开方便你对号入座。5.1 训练开始即报“All labels empty”或“assertion labels.shape”现象训练脚本启动后很快中断报错信息里提到“All labels empty”或者直接在一个文件名对应的标签数组上做断言失败。原因images目录和labels目录下文件名对不上jpg存在但同名txt不存在或者txt内容是空文件。这种问题多发生在解压7z后文件不完整、复制脚本漏掉部分标签、或者原数据集本身就有缺漏。解决先跑一段对照命令找出“图有但标签没有”的文件。# 对照jpg和txt文件名找出没有标签的图片 for img in images/train/*.jpg; do txtlabels/train/$(basename ${img%.jpg}).txt [ ! -f $txt ] echo missing: $img done如果命令输出很多行说明第4章的划分或复制逻辑有问题。尽早养成解压后立刻体检的习惯能少走几小时弯路。这里有个容易忽略的细节如果压缩包里同时有VOC和YOLO两份标签脚本扫描时可能把VOC里ImageSets/Main的txt也算成标签导致统计数据虚高。遇到数量对不上时先确认过滤条件再骂数据。5.2 loss正常下降验证mAP却徘徊不动现象训练日志里box_loss平稳下降但验证集mAP50始终在30%左右上不去。原因训练集和验证集来自高度相似的场景模型在“背题”而不是“学会泛化”。192张图如果都从同一个路口或同一段视频抽帧模型记住了背景换个路口就失灵。解决重新检查划分方式尽量做场景维度去重不要随机切分连帧图。如果压缩包内的确是一段连续帧可以把验证集挑成时间间隔较大的图或者直接不上内部验证改用一段完全没见过的实拍视频来评估。血泪经验是小样本模型的mAP高不代表场景泛化好必须先看验证集是否和训练集“长得太像”。5.3 推理时把树影、栏杆、路面裂缝框成目标现象模型在测试图上不停输出框但框里的内容全是光影纹理不是真正的斑马线。原因1类别标注只把“完整斑马线”当作正样本背景里的竖向条纹、明暗交替纹理和斑马线长相相似小样本过拟合会把纹理当本质。夜间场景尤其严重高亮路灯和黑色沥青交错误检率飙升。解决检查训练样本的bbox是否裁进了太多车胎、栏杆、人行道地砖如果负样本难找靠Mosaic增强和随机擦除能削弱模型对环境纹理的依赖。更重要的是验证时不要只看白天晴天的视频专门准备一段夜间或雨天的画面误检问题立刻现原形。5.4 原图接近4Kimgsz1280直接OOM640又丢目标现象训练报显存不足要么调小batch还是崩要么用低分辨率后loss难以下降。原因输入分辨率同时决定显存开销和小目标保留程度。斑马线在4K原图里可能只有几十像素宽缩到640就只剩个位数像素检测器基本看不到。解决优先保住imgsz用960配上更小的batch8如果还超显存再做图像切块。不要先调anchor也不要先换大模型先把输入分辨率调整到自己显卡能承受的上限。这里的一个判断标准是如果imgsz640时loss明显比960差但显存又不够就该怀疑是分辨率瓶颈而不是模型结构问题。5.5 模型训练没报错但输出框永远偏向画面中心现象所有预测框集中在图像中央区域边缘的目标要么不框要么框错位置。原因只有1个类别时分类分支没有区分压力模型会偷懒去学一个“位置先验”而标注框如果集中在画面中央这种偷懒很容易成功。解决先统计所有bbox中心点坐标的分布如果确实高度集中在数据增强里增加随机平移和仿射变换人为把目标挪到边缘。另一个技巧是用测试图覆盖网格看模型在九宫格边角的检测结果如果只有中间一格稳定说明定位能力被样本分布限制了。这是小样本的常见通病不能全怪训练参数。6. 让这192张斑马线数据物尽其用验证技巧与小样本微调建议6.1 混淆矩阵不是摆设先分清漏检还是误检训练结束后Ultralytics会在runs/detect/train下生成confusion_matrix.png、results.png和val_batch*.jpg。很多人只看loss曲线其实对192张的数据量来说那些曲线参考价值有限。我的习惯是先打开混淆矩阵看对角线的值。如果漏检率明显高于误检率优先加数据如果误检率更高先清理难负样本。两个方向的处理手段完全不同别一上来就调anchor。验证集图片也要翻一遍看预测框是不是画在了真正斑马线附近视觉确认永远比数字可靠。6.2 用一段网图和路段视频做“黑匣子”回归导出onnx前后都试一次微调后的模型不能只在验证集上自嗨。我会找一段完全没见过的路口视频或几张网上抓的实景图跑一次推理同时记录FPS。如果目标要上线还要用导出ONNX的方式再做一轮验证。导出命令很简单。# 导出ONNX部署侧常用导出后必须重新测精度和速度 yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640导出后的模型用同一段视频再跑一遍有时精度会下降原因是预处理细节不同。192张图只够证明“方向能跑通”能不能进入部署阶段取决于外部视频回归和onnx导出后的稳定性。这个黑匣子测试过程比训练日志里那一堆指标更诚实。6.3 小样本的“后悔药”先冻结主干微调再解冻全参我的最后一个习惯是不过早全参数微调。先用预训练权重冻结主干只微调检测头跑几十epoch看基线。如果过拟合严重再解冻最后几层。Ultralytics用freeze参数控制层数比如freeze10表示前10层不参与更新。这招在192张图这种规模下很有用它能保住COCO数据里学到的轮廓、纹理和几何先验让模型不至于被少量斑马线样本带偏。总之这192张1类别数据集不是不能练而是不能当成“新任务从零训”。把它当作流程验证样本和冒烟测试数据在此基础上扩展自己的场景图与精标注才是这个7z压缩包最值的用法。希望这些解压、体检、训练和验证的步骤能帮到你。本文还有配套的精品资源点击获取