
简介面向目标检测初学者与课程实践者这份数据集可直接用于泊车位检测模型训练。内含一千张真实场景图片使用LabelImg标注软件标注提供VOC、COCO、YOLO三种格式标签并分文件夹存放省去格式转换环节适合快速上手YOLO系列算法。该压缩包共两千个文件除图片外还含XML标注文件、TXT标签文件、YAML配置、Python划分脚本以及环境搭建与训练教程的网页文档整体大小约二百三十八兆。配套的三个划分脚本能将图片和标签按比例切分为训练集、验证集、测试集教程覆盖Windows和Linux下的YOLO环境配置、自定义数据集训练流程并配有Ubuntu安装说明对新手较为友好。目前已有五百零七人学习使用可用于课程设计、毕业设计或泊车视觉项目开发能节省数据准备与入门调试时间。1. 泊车位目标检测一张1000张图片的数据集能带新手跑通整个YOLO流程吗拿到一个包含1000张泊车位图片、同时附带VOC、COCO、YOLO三种格式标签和划分脚本的数据集包我的第一反应是这足够让一个零基础的人把目标检测的完整链路跑通。停车位检测本质上是目标检测里的一个细分场景检测目标从“人、车、猫狗”换成“泊车位”但标注格式转换、数据集划分、YOLO训练这些核心步骤完全一样。很多人卡在第一步手里有数据却不知道VOC的xml文件怎么变成YOLO训练能直接读的txt文件或者以为划分数据集就是随便把文件名打乱一扔。这篇笔记就按标题里的内容一步步讲清楚三种标签格式的区别、划分脚本怎么用、以及拿这1000张图训练一个可用的泊车位检测模型需要调哪些参数。适合刚装好YOLO环境、准备拿真实数据练手的从业者也适合想把自己标注的数据集转成统一格式的老手。2. 一张泊车位图片和它的三种标签格式VOC、COCO、YOLO到底有什么区别2.1 VOC是xml、COCO是json、YOLO是txt三种格式的存储和坐标差异标题里的数据集之所以同时给了三种格式是因为不同训练框架的入口数据格式不一样。VOC格式源自Pascal VOC挑战赛每张图片对应一个同名的xml文件里面用object节点描述每个目标包含类别名称和bndbox下的xmin、ymin、xmax、ymax四个绝对像素坐标。COCO格式则把所有图片的标注汇总到一个json文件里标注信息分成images、annotations、categories三大段每个目标的坐标用bbox字段记录格式是[x, y, width, height]。YOLO格式最简单粗暴每张图片对应一个txt文件每行一个目标格式是class_id x_center y_center width height注意这里的四个数值全部是相对于图片宽高的归一化值范围在0到1之间。三种格式的核心差异不在“存储结构”而在“坐标表述方式”。VOC和COCO用的是绝对像素值或基于绝对像素的宽高YOLO用归一化比例COCO的bbox原点在框的左上角YOLO的中心点坐标在框的正中心。如果直接把VOC的xml内容原样拷进txt训练出来的模型会完全学不到位置信息。我自己处理数据集时习惯先建一个转换脚本把原始格式统一成YOLO格式因为ultralytics的YOLO训练接口默认读的就是txt标签。转换前先看一眼类别列表泊车位检测里通常只有两类“占用occupied”和“空闲empty”有的数据集合并成单类“泊车位”这决定了输出层的类别数。2.2 VOC转YOLO格式一个脚本看懂坐标归一化与类别映射把VOC的xml转换成YOLO的txt是最常见的操作我一般用下面这段Python脚本处理。它读取标注文件夹里的所有xml文件解析出每个object的类别名和边界框坐标再归一化后写入同名txt。import xml.etree.ElementTree as ET import os # 类名列表顺序就是YOLO标签里的class_id classes [occupied, empty] def voc_to_yolo(xml_dir, txt_dir): os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 图片宽高从xml的size节点读取 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) base_name os.path.splitext(xml_file)[0] with open(os.path.join(txt_dir, base_name .txt), w) as f_out: for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bndbox obj.find(bndbox) xmin int(float(bndbox.find(xmin).text)) ymin int(float(bndbox.find(ymin).text)) xmax int(float(bndbox.find(xmax).text)) ymax int(float(bndbox.find(ymax).text)) # 转成YOLO需要的中心点x、中心点y、宽、高全部除以图片宽高归一化 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 # 防止边界溢出夹紧到0~1之间 x_center min(max(x_center, 0), 1) y_center min(max(y_center, 0), 1) box_w min(max(box_w, 0), 1) box_h min(max(box_h, 0), 1) f_out.write(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}\n) voc_to_yolo(annotations/voc, labels/yolo)这段代码里的关键点是classes列表的顺序。YOLO标签文件里第一列的数字不是类别名称而是类别在列表里的索引classes [occupied, empty]意味着occupied对应class_id 0empty对应class_id 1。如果xml里出现了列表之外的类别名脚本会直接跳过避免训练时类别数对不上。归一化的位置计算是先求绝对像素中心点再除以图片宽高顺序不能反过来否则数值会整体偏移。COCO转YOLO的原理也一样只是解析json里的bbox字段后要把[x, y, w, h]转换成中心点格式x_center x w/2y_center y h/2再分别除以图片宽高。这里容易犯的一个错误是忘记json里的bbox坐标是int类型直接做除法会得到整数结果归一化后全是0训练直接崩。转完后随手打印前几个txt文件的内容看看数值范围如果出现大于1的数说明某张图的标注框越界了。2.3 YOLO训练时为什么要用txt而不是json或xml加载效率和anchor匹配逻辑很多人会问既然VOC和COCO格式更通用为什么YOLO偏偏要一种txt格式核心原因有两个。第一是加载效率ultralytics在训练时会用torch.load配合数据集索引文件逐张读取图片再根据txt标签快速构建训练张量txt文件每行定长、结构简单不需要像json那样先反序列化整个大字典也不需要像xml那样做DOM解析数据流水线的预处理时间能省下一大截。对于1000张图片这种规模可能感知不强但换成几万张的工业数据集差别会直接反映在GPU利用率上。第二个原因是YOLO系列的anchor匹配机制。YOLO把输入图片划分成网格每个网格负责预测中心点落在自己范围内的目标标签在进入损失函数前要被编码成“网格偏移量”和“anchor宽高比例”。txt里直接存储归一化的中心点和宽高省去了训练时再对绝对坐标做归一化的步骤也避免了因图片尺寸不一致导致标签失效的问题。VOC的xml里记录的是原图绝对像素坐标如果训练时做了letterbox缩放或Mosaic增强坐标必须重新映射YOLO的归一化坐标天然免疫这些几何变换模型在imgsz640下读到的标签和原图640x640时完全一致。3. 划分脚本怎么用从1000张图中切割出train/val/test的正确姿势3.1 按7:2:1划分脚本随机打乱、种子固定、防止数据集泄漏标题里提到“划分脚本”它解决的是训练集、验证集、测试集怎么切的问题。常见做法是按7:2:1的比例把1000张图分成700张训练、200张验证、100张测试。如果数据集包只给了train和val两个目录测试集可以临时从val里匀或者不管。核心是脚本里必须做三件事随机打乱、固定随机种子、同时移动图片和对应的标签文件。import os import random import shutil random.seed(42) # 固定种子保证每次运行划分结果一致 image_dir images label_dir labels/yolo train_dir dataset/images/train val_dir dataset/images/val test_dir dataset/images/test images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(images) # 按 7:2:1 切分 total len(images) train_end int(total * 0.7) val_end int(total * 0.9) splits { train: images[:train_end], val: images[train_end:val_end], test: images[val_end:], } for split_name, split_images in splits.items(): os.makedirs(os.path.join(train_dir.replace(train, split_name), .., split_name, images), exist_okTrue) # 简化的目录创建逻辑实际建议用pathlib统一管理 os.makedirs(fdataset/{split_name}/labels, exist_okTrue) for img in split_images: base os.path.splitext(img)[0] shutil.copy(os.path.join(image_dir, img), fdataset/{split_name}/images/{img}) label_src os.path.join(label_dir, base .txt) if os.path.exists(label_src): shutil.copy(label_src, fdataset/{split_name}/labels/{base}.txt) else: print(f警告: 图片 {img} 没有对应的标签文件)这里有个隐藏要求图片和标签必须同进同退。很多人只移动了图片训练时YOLO会在labels目录里找不到对应txt直接跳过这张图最后出现“训练了700张但实际只用了500张”的情况。划分前的随机种子固定也很重要random.seed(42)保证每次运行脚本得到相同的划分结果否则你上午训练和下午训练用的验证集不一样模型的指标没法对比。如果后续要调参一定不要换种子。划分比例不是拍脑袋定的。1000张图片的数据量偏小val取20%也就是200张已经足够观察模型是否过拟合test取10%是为了最后做一次“盲测”。如果你的数据集本身存在严重类别不平衡比如“占用”类有800个框而“空闲”类只有200个框建议先按类别比例做分层划分而不是简单随机否则验证集里可能一张空闲车位的图都没有。3.2 泊车位场景的特殊划分同一停车场不同帧的相似度会骗过验证集泊车位检测这个任务有个独特问题数据集中同一个停车场的不同图片可能是在不同时间、不同角度下拍摄的画面高度相似。如果划分脚本只做全局随机训练集和验证集里很可能出现“同一批车位、同一个摄像头视角”的图片验证集loss降得很低看起来模型泛化得很好实际换一个停车场就废掉。这个坑我踩过当时用全局随机划分跑出来的mAP有0.85结果拿到另一栋楼的地下车库测试几乎全漏检。解决办法是按“场景分组”划分。先把文件名的前缀提取出来比如parking_lot_a_001.jpg、parking_lot_a_002.jpg按parking_lot_a分组再对组进行随机划分保证同一个场景的所有帧只出现在一个集合里。这样验证集的场景分布和训练集不重叠才能真实反映模型的泛化能力。如果原始文件名没有规律就手动看一眼图片内容把同一地点拍摄的图分到一组。对泊车位数据来说宁可牺牲一点训练集数量也要让验证集“硬”一点。3.3 划分后检查文件数量与标签一致性推荐用这三行命令行划分完不要急着训练先做一致性检查。我通常用以下三个命令确认图片和标签没有错位# 统计各集合图片数量 find dataset/train/images -name *.jpg | wc -l # 统计各集合标签数量 find dataset/train/labels -name *.txt | wc -l # 找出有图片但没有标签的文件名 comm -23 (ls dataset/train/images | sed s/.jpg//) (ls dataset/train/labels | sed s/.txt//)第一个命令确认图片总数第二个命令确认标签总数正常情况两者应该完全相等。第三个命令使用comm比较两个文件名列表打印出“有图无标签”的文件名。如果输出非空说明有图片被单独挪进了训练集需要补齐或移除。另一个验证思路是检查txt文件内容是否为空0字节的标签文件会让训练器报“Corrupt JPEG”或者“empty labels”错误用find dataset -name *.txt -size 0就能扫出来。4. 把训练跑起来用YOLO训练泊车位检测的设置、参数和时长预估4.1 先装环境配data.yaml缺一步都会让训练直接崩掉标题里的“训练教程”环节最常见的方式是基于ultralytics的YOLO仓库来跑。环境配置的关键是Python版本、PyTorch版本和ultralytics包三者匹配我一般用Python 3.10配合PyTorch 2.x的CUDA版本然后一条命令装完依赖# 创建虚拟环境避免系统Python环境被搞乱 conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics装完后先跑一次yolo detect predict拉取预训练权重确认CUDA可用。如果只用CPU跑1000张图训练会慢得让人失去耐心建议至少用一块6GB显存的显卡。环境装好后最重要的文件是data.yamlYOLO靠它找到数据集路径和类别信息# data.yaml train: dataset/train/images val: dataset/val/images test: dataset/test/images nc: 2 names: [occupied, empty]注意train和val指向的是图片目录而非标签目录YOLO会根据图片路径自动把/images/替换成/labels/寻找同名txt标签。如果你的数据集目录结构不是这种约定比如标签放在别的文件夹需要在yaml里手动指定标签路径否则会报“label not found”错误。nc和names必须和前面转换脚本里的classes列表完全一致occupied是第0类还是第1类取决于训练时用哪个列表换顺序就等于换标签模型会把“占用”学成“空闲”。4.2 1000张图怎么训练预训练权重、冻结层和数据增强的三板斧图片数量只有1000张直接从头训练YOLO几乎不可能收敛。常见做法是加载COCO预训练权重做迁移学习让模型继承通用的特征提取能力只微调最后几层检测头。训练命令如下yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ workers4 \ device0 \ cacheTruemodelyolov8s.pt是最关键的一项它的作用是加载COCO预训练权重而不是从头初始化。epochs100对1000张图的小数据集足够配合patience20表示连续20轮验证指标不提升就早停。cacheTrue让数据加载时直接缓存到内存或磁盘省去每次epoch都做磁盘IO的时间。batch16要按GPU显存调整6GB显存跑yolov8s这个批次已经偏大如果报CUDA Out Of Memory就降到8。小数据集训练时我还会在训练命令行里手动打开增强项。ultralytics默认开启Mosaic、翻转、色彩抖动等增强这本身对小数据集是好事但Mosaic增强在训练后期可能拖慢收敛。建议前50轮保持默认增强50轮后关掉Mosaic再微调几轮yolo detect train \ datadata.yaml \ modelruns/detect/train/weights/last.pt \ epochs20 \ imgsz640 \ batch16 \ mosaic0.0 # 关闭马赛克增强让模型在干净数据上精调这里用last.pt接续训练而不是从头再来后面的mosaic0.0是关闭增强的开关。注意泊车位检测的目标通常是固定的框长宽比接近真实车位关闭Mosaic和随机裁剪能减少车位被裁掉一半的情况。4.3 训练过程的三个观察点loss曲线、验证mAP和类别混淆矩阵训练开始后不要干等着跑完每轮结束要看三个指标。第一个是box_loss代表边界框回归的损失值正常趋势是稳步下降如果训练到第30轮还在0.1以上震荡说明anchor设置和车位目标的宽高比不匹配。第二个是mAP50这是判断模型能不能用的主要指标泊车位检测做到0.85以上的mAP50算合格0.9以上算优秀。第三个是类别分布用yolo detect val输出的混淆矩阵看“占用”和“空闲”是否都被正确检出。训练结束后runs/detect/train/目录下会生成weights/best.pt和weights/last.pt前者是验证集指标最高的一轮后者是最后一轮的权重。迁移学习场景下我永远优先加载best.pt做后续测试因为last.pt可能已经过拟合。如果训练日志里mAP50一直很低而loss正常下降回数据集检查标注框有没有画错——很多人把虚线车位框的框线本身标注进去了模型当然学不会。5. 泊车位目标检测避坑五条最常见的训练翻车记录5.1 现象val的mAP很高换一个停车场场景全部漏检这是小数据集做目标检测最容易踩的坑。训练时验证集mAP达到0.9导出模型后跑到别的停车场测试一张图只能检出零星几个车位。原因是划分数据集时用了全局随机某个停车场的几十张相似图片被同时分进训练集和验证集模型实际上“背”下了这个特定视角的车位外观。解决方法是按场景分组重新划分数据确保同一个停车场的图片只在训练集或只在验证集如果数据不够就减少验证集比例用test集在另一个停车场拍摄的图上做一次盲测。模型评估看的是“没见过的车位”上的表现而不是“没见过的图片”。5.2 现象训练完检测不到空车位只检测到有车的泊位检测结果总是漏掉空车位占用类车位却检得很好。原因通常是类别不平衡数据集中“占用”类标注框数量远大于“空闲”类模型学到的是“找车位轮廓”而不是“区分占空”。更隐蔽的原因是两类标注框的形态差异很大“占用”类框里有车体轮廓特征丰富“空闲”类框只有地面标线特征弱同样的学习率下模型自然偏向学特征强的类别。解决思路有两个一是给“空闲”类提高loss权重在data.yaml里设置cls参数增大分类损失的系数二是做类别平衡采样保证每个batch里两类样本数量接近ultralytics里可以开启class_weight选项。5.3 现象txt标签里出现0字节文件训练时报错训练到一半打印出类似WARNING: corrupt image或empty labels的提示然后跳过某张图。原因是VOC转YOLO时有些xml文件里没有有效的object节点生成的txt是空文件或者是标注框坐标越界被脚本过滤后整行没写进去。暴力解决办法是在划分脚本里加一个过滤条件扫描转换后的txt把0字节文件对应的图片从数据集里剔除find labels -name *.txt -size 0 -exec sh -c mv $1 /tmp/empty_labels/ _ {} \;更稳的办法是用ultralytics自带的yolo check命令验证标签格式。5.4 现象loss下降到0.02但验证指标不再上升训练日志里训练loss每轮都在降看着很舒服但mAP50在第60轮后开始波动甚至回调。这是典型的过拟合特征1000张图训练100轮很容易把标注噪声也背下来。解决方法是提前用早停或者把patience从20改小到10另一个有效手段是增加dropout和权重衰减在训练命令里加上dropout0.1和weight_decay0.0005。最重要的是加载best.pt而不是last.pt做推理best.pt所在的那一轮通常还在正常泛化区间内。5.5 现象摄像头视角换一个检测框整体偏移同一个停车场摄像头装在3米高和5米高拍出来的画面里车位框大小差异极大模型的检测框会出现系统性偏移。这是小样本数据集测试时常见的“视角过拟合”。处理办法是在训练时打开degrees10做小角度旋转增强同时用translate0.1做平移增强模拟视角变化。如果数据集里不同视角的图片本来就有优先保证划分时每个视角都在训练集和验证集里都有对应样本。不要寄希望于把模型“调大”换成yolov8m不会根治视角泛化问题本质是训练数据覆盖不足。6. 验证泊车位检测效果从mAP数字到逐帧画框的最后一公里6.1 输出验证集预测图与混淆矩阵用官方脚本一步到位mAP是一个数字但部署前必须看可视化结果。我训练完第一件事是跑验证并保存预测图yolo detect val \ modelruns/detect/train/weights/best.pt \ datadata.yaml \ save_jsonTrue \ save_confTrue \ plotsTrueplotsTrue会在验证集每张图上画出预测框、类别名和置信度直接翻看这些图能快速发现系统性问题。save_jsonTrue会输出COCO格式的检测结果文件方便你用自己的脚本计算各类别的AP。重点看两类图片一类是“占用”车位被误判成“空闲”的这类错误通常来自形态非常相似的车位另一类是漏检的车位线模糊或光照反光时模型容易看不见。6.2 单帧推理与视频流检测确认边界框坐标和置信度阈值验证完静态图再拿一段停车场监控视频做实时推理测试。常用命令yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcetest_video.mp4 \ conf0.4 \ saveTrueconf0.4是最小置信度阈值。泊车位检测的阈值选择有明显倾向如果用于车位引导系统漏检一个空车位比误报一个占用位更影响体验可以把阈值调低到0.25如果用于收费闸机统计车辆误报会导致计费出错阈值要调高到0.6。4K分辨率的视频建议在推理前把输入尺寸从640提升到960imgsz960能明显改善远距离小车位的检出率代价是推理速度下降约一倍。6.3 从检测框到可用功能关键点回归是泊车位检测的进阶方向到这里用这个1000张图片的数据集训练YOLO泊车位检测模型的流程已经完整跑通。但说实话纯边界框检测做泊车引导有个天然缺陷框不能告诉你车位的四个角点在哪也就无法判断车辆能否准确停入。从业方案里常见的进阶做法是改用YOLO的关键点检测把每个车位的四个角点作为关键点输出再根据角点几何关系推算车位朝向和空位面积。你也可以把“占用”和“空闲”的分类任务升级成实例分割用YOLO-seg模型分割出车位的精确轮廓对斜线车位的适应性更强。我的习惯是每次训练完都把best.pt、data.yaml、划分脚本的随机种子和最终的超参数一起存档标注数据的迭代一定会再训练没有记录就等于没有发生过这次实验。数据集1000张不够我通常先用它确定模型结构和参数范围再决定是补标注还是做数据增强。技术和流程都是这套东西真正拉开效果差距的是对数据的理解和反复试错。希望帮到你。本文还有配套的精品资源点击获取