ARTICLE DETAIL

资讯详情

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

YOLO泊车位目标检测数据集:格式转换、划分与训练全流程实战

YOLO泊车位目标检测数据集:格式转换、划分与训练全流程实战 简介一套面向目标检测学习者和车辆视觉项目的YOLO泊车位数据集包围绕停车场车位检测任务提供真实场景的高质量图片标注框精细场景覆盖丰富适合用于YOLO系列模型训练、课程设计与毕业设计。压缩包共2000个文件主体为XML与TXT标签文件分别对应VOC和YOLO格式另有HTML教程、Python脚本及YAML配置包体约238.49MB。标签同时提供COCO(JSON)格式可直接接入主流检测框架。随包附带Linux与Windows双平台的YOLO环境搭建和训练案例教程以及训练集、验证集、测试集划分脚本可按需求灵活重组数据集。目前已有507人学习下载适合需要快速获得可用泊车位数据集并完成模型训练与验证的开发者。1. YOLO泊车位目标检测数据集先讲清楚这份资源能干什么做停车场相关项目的人应该都有过这种经历模型训练跑了一半才发现手里的标签格式跟框架对不上要么从VOC转YOLO时坐标算错要么训练集里混进了没标签的图一个epoch下来loss直接飞了。这份YOLO泊车位目标检测数据集一共1000张真实场景图片用labelimg标注同时给了voc(xml)、coco(json)、yolo(txt)三种格式的标签分开放在不同文件夹配套三个划分脚本和Linux/Windows双平台的环境搭建、训练教程。适合正在做停车位检测、车辆占用识别或者刚入门目标检测、想拿现成数据跑通YOLO全流程的人。省掉的是自己找图、打标签、转格式、写划分脚本这几步最耗时间的事。2. 三种标签格式的结构差异VOC、COCO与YOLO的坐标换算拿到数据集先别急着训练得搞清楚同一个标注在三种格式里是怎么存的。很多人翻车就翻在以为三种格式只是扩展名不同实际上坐标体系、类别存储方式、文件组织逻辑完全不一样。2.1 VOC的XML标签从object节点看目标描述VOC格式每个图片对应一个同名XML文件根节点是annotation里面记录了图片的尺寸信息width、height、depth然后每个目标是一个object节点。object节点下有name类别名、pose、truncated、difficult以及bndbox——四个值xmin、ymin、xmax、ymax都是像素坐标左上角和右下角。一份典型的泊车位标注大概长这样annotation folderimages/folder filenameparking_001.jpg/filename size width1920/width height1080/height depth3/depth /size object nameparking_space/name bndbox xmin214/xmin ymin388/ymin xmax684/xmax ymax762/ymax /bndbox /object /annotationbndbox里的四个值直接对应图片上的像素位置理解起来最直观。但它有个隐含问题VOC格式本身不带图片路径只存文件名训练时需要额外维护一个图片到标注的映射关系。另外XML文件会随bbox数量膨胀一个场景里几十个车位就有几十个object节点解析起来虽然不慢但文件体积明显大于TXT格式。2.2 COCO的JSON标签从annotations数组到分割掩码COCO格式把所有标注塞进一个JSON文件顶层结构是images、annotations、categories三个数组。images数组记录每张图的id、file_name、width、heightannotations数组里每个元素是一个目标通过image_id关联到图片bbox字段存的是[x, y, width, height]注意这里的x、y是左上角坐标但宽高是框的宽和高不是右下角坐标area是面积category_id对应categories里的类别编号。{ images: [ {id: 1, file_name: parking_001.jpg, width: 1920, height: 1080} ], annotations: [ { id: 1, image_id: 1, category_id: 1, bbox: [214, 388, 470, 374], area: 175780, iscrowd: 0 } ], categories: [ {id: 1, name: parking_space} ] }COCO的bbox是[x, y, width, height]这个细节值得特别注意。从VOC转COCO时width要算成xmax - xminheight算成ymax - ymin而不是直接把xmax、ymax塞进去。转错的话检测框会从右下角往左上方向扩出去训练出来全是错位框。COCO格式的优势是单文件搞定所有标注适合大规模数据集但单人维护时改一处标注就得重新处理整个JSON不如XML和TXT灵活。2.3 YOLO的TXT标签归一化坐标的换算逻辑YOLO的TXT格式跟上面两种完全不同它是每行一个目标格式是class_id x_center y_center width height。class_id是从0开始的整数不是类别名字符串。后四个值全部是归一化坐标即除以图片宽高后的比例值范围在0到1之间。0 0.233854 0.532407 0.244792 0.346296 0 0.618750 0.437963 0.186458 0.327778第二行那个0.618750的换算过程是这样的# 假设原标注像素坐标: xmin788, ymin248, xmax1166, ymax602 # 图片宽高: width1920, height1080 x_center (xmin xmax) / 2.0 / width # (7881166)/2/1920 0.5088 y_center (ymin ymax) / 2.0 / height # (248602)/2/1080 0.3935 box_width (xmax - xmin) / width # (1166-788)/1920 0.1969 box_height (ymax - ymin) / height # (602-248)/1080 0.3278这个换算逻辑训练前必须吃透。YOLO的x_center、y_center是中心点坐标不是左上角width和height是框的宽高比例不是右下角坐标。很多人第一次转格式时直接把xmin除以width当成x_center结果框全部偏到左上角。判断标签转没转对有个土办法随便打开一张图按换算公式把txt里的五个值反算回像素坐标用OpenCV画框叠在原图上肉眼看位置对不对这一步比看任何文档都管用。2.4 三种格式互转时的常见误用这个数据集直接给了三种格式省去了自己转格式的麻烦但训练时到底用哪种还是得分场景说。YOLO系列框架原生吃TXT格式数据集里yolo标签文件夹可以直接被YOLO的训练脚本读取如果后续要跑MMDetection或者做数据增广分析COCO格式更友好要可视化标注或者用老的工具链VOC的XML更合适。需要特别提醒的是格式转换过程中最容易出错的是类别编号映射。VOC和COCO的类别是字符串或独立编号从0开始还是从1开始不同工具做法不一样YOLO的类别必须从0开始连续编号。假如标签里出现编号2但总共只有两个类别那肯定是转换时编号没对上训练时要么报错要么分类全乱。提示拿到数据集后先随机抽查几张图把三种格式的同一条标注都打印出来比对确认坐标换算一致再动手训练。3. 数据集划分脚本从train_list.txt到ImageSets的全流程数据划分看着简单很多人就手动建个文件夹往里面拖图片但拖完之后训练时频繁报错——找不到标签、样本泄露、验证集和训练集有重叠。这套数据集里给了三个划分脚本覆盖两种场景一种是把图片和标签物理复制到新文件夹另一种是只生成txt索引文件。下面逐个拆。3.1 三个脚本的分工与选择三个脚本的功能差别得先搞清楚不然容易用错场景脚本文件产出物适用场景训练集、验证集、测试集划分脚本图片标签划分写入新文件夹.py生成train/val/test三个文件夹图片和标签一起复制过去原始数据很乱想整理出干净的目录结构训练集、验证集划分脚本图片标签划分写入新文件夹.py只生成train和val两份不想单独留测试集或者数据量少、想把更多样本拿去训练split_train_val生成ImageSets下txt文件划分脚本.py生成ImageSets/Main下的train.txt、val.txt、test.txt框架训练时按txt读取样本列表不复制文件第一种和第二种是物理划分会把图片和对应标签真实复制到新文件夹优点是结果肉眼可见、结构清晰缺点是占双倍磁盘空间而且复制过程如果没带标签文件训练时就找不到标签。第三种是逻辑划分只写出文件路径清单训练时按清单读取灵活度高改划分比例只需重新跑一遍脚本。3.2 脚本一训练集、验证集、测试集划分写入新文件夹这个脚本是三个里最常用的核心逻辑是扫描原图文件夹按设定比例随机抽样然后把图片和同名标签一起复制到目标目录。关键代码逻辑类似这段import os import random import shutil # 配置区 source_images JPEGImages # 原图目录 source_labels Annotations # 标签目录这里以voc的xml为例 target_root split_dataset # 输出根目录 train_ratio 0.7 # 训练集比例 val_ratio 0.2 # 验证集比例 # test_ratio 1 - train_ratio - val_ratio剩下来的就是测试集 # 1. 先打乱所有图片文件名 all_files [f for f in os.listdir(source_images) if f.lower().endswith((.jpg, .jpeg, .png))] random.shuffle(all_files) # 2. 按比例切分索引 train_cnt int(len(all_files) * train_ratio) val_cnt int(len(all_files) * val_ratio) train_files all_files[:train_cnt] val_files all_files[train_cnt:train_cnt val_cnt] test_files all_files[train_cnt val_cnt:] # 3. 复制图片和同名标签到目标目录 for split, file_list in [(train, train_files), (val, val_files), (test, test_files)]: img_out os.path.join(target_root, split, images) lbl_out os.path.join(target_root, split, labels) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for fname in file_list: # 图片复制 shutil.copy(os.path.join(source_images, fname), os.path.join(img_out, fname)) # 同名标签复制注意扩展名替换 xml_name os.path.splitext(fname)[0] .xml lbl_src os.path.join(source_labels, xml_name) if os.path.exists(lbl_src): shutil.copy(lbl_src, os.path.join(lbl_out, xml_name)) else: print(f[警告] 缺少标签: {xml_name})逻辑说明random.shuffle先打乱文件列表再按比例切分保证每张图只进一个子集不会出现训练集和验证集样本重叠的问题。复制标签时用os.path.splitext替换扩展名保证图片和标签文件名一致。脚本里打印警告的那个else分支很关键——如果某些图片没有标签训练时YOLO会跳过这些图但数据量小时会拉低整体样本有效率。参数说明train_ratio和val_ratio是核心参数常见配置是7:2:1或8:1:1。数据量低于300张时我一般建议把测试集砍掉让出样本给训练用验证集做完整体检够用数据量超过2000张再考虑单独留测试集。随机种子如果在脚本里没固定每次跑出来的划分结果都不一样需要可复现实验时记得自己加一行random.seed(42)。3.3 脚本二只分训练集和验证集的场景脚本二和脚本一结构几乎一样只是去掉测试集部分输出只有train和val两个文件夹。什么时候用这个两种情况一是样本总量不大比如只有几百张留测试集会浪费样本二是你的训练框架验证方式比较特殊比如用K折交叉验证那每次只需分成两份。这个脚本改动的最小配置是train_ratio 0.8 # 训练集比例 val_ratio 0.2 # 验证集比例剩余即为测试集这里不生成这里的0.8和0.2不是拍脑袋定的。训练集比例太低模型见过的样本不够泊车位的各种角度、光照、遮挡情况覆盖不全验证集比例太低模型好坏评估不够稳定。我个人习惯在1000张量级的数据上1:9到2:8之间调先按8:2跑一版baseline再用验证集Loss判断是欠拟合还是过拟合。3.4 脚本三生成ImageSets下的txt划分文件这个脚本走的是另一条路线它不复制文件而是生成三个txt每个txt里存图片文件名不带扩展名不带路径输出到ImageSets/Main目录下。这是从VOC继承过来的训练习惯很多YOLO版本的训练脚本仍然支持这种读取方式尤其在训练前要手动控制每个epoch采样哪些图时很好用。生成逻辑类似这样import os import random image_dir JPEGImages output_dir ImageSets/Main os.makedirs(output_dir, exist_okTrue) files [os.path.splitext(f)[0] for f in os.listdir(image_dir) if f.lower().endswith((.jpg, .jpeg, .png))] random.shuffle(files) train_ratio 0.7 val_ratio 0.2 train_cnt int(len(files) * train_ratio) val_cnt int(len(files) * val_ratio) with open(os.path.join(output_dir, train.txt), w) as f: f.write(\n.join(files[:train_cnt])) with open(os.path.join(output_dir, val.txt), w) as f: f.write(\n.join(files[train_cnt:train_cnt val_cnt])) with open(os.path.join(output_dir, test.txt), w) as f: f.write(\n.join(files[train_cnt val_cnt:]))核心点在于写入txt的只有stem文件名不带图片目录、不带扩展名。因为不同训练框架对路径拼接规则不一样写成纯净的文件名交给框架时由配置文件里的img_dir变量去拼接这是兼容性最强的做法。如果你看见train.txt里意外带了绝对路径训练时换台机器就会全部失效这种情况只能重新生成。另外train_list.txt这个文件在资源里单独存在它本质上就是train.txt的变体有的教程里叫train_list.txt有的叫train.txt内容一样是图片名列表别因为名字不同就误以为缺文件。4. 把数据集跑进YOLO训练环境搭建与训练参数划分好数据之后就是训练。这套教程给了Linux和Windows两个版本的环境搭建说明以及对应平台的训练教程。YOLO在Linux下跑训练是主流做法但不少人在Windows上做标注和初步实验所以两条路都通。关键不是跟着教程敲一遍命令而是理解每一步解决的是什么问题、参数怎么改。4.1 环境搭建Linux和Windows两条路Linux下的环境搭建核心是CUDA、cuDNN、PyTorch三件套版本配对是最大的坑。先确认显卡驱动支持的最高CUDA版本再倒推PyTorch版本。比如驱动支持CUDA 11.8那就装cuda11.8对应的PyTorch用conda创建独立环境是最省心的做法# 创建独立环境Python版本按框架要求选YOLOv5/v8通常支持3.8-3.10 conda create -n yolo python3.8 -y conda activate yolo # 安装PyTorch注意cuda参数务必和驱动匹配 pip install torch1.13.1cu117 torchvision0.14.1cu117 \ --extra-index-url https://download.pytorch.org/whl/cu117 # 安装YOLO依赖 pip install -r requirements.txt参数说明cu117表示CUDA 11.7装错了会报Torch not compiled with CUDA enabled或运行torch.cuda.is_available()返回False。这个检查命令是第一步先跑它确认GPU可用再继续省得后面白等。Windows下的安装逻辑一样只是多了个Visual Studio Build Tools的坑——编译某些依赖时需要用到C编译环境。另外如果你只有NVIDIA显卡务必装好驱动再装CUDA Toolkit顺序反了会各种报错。没有NVIDIA显卡的话CPU训练也可以跑就是慢教程里给的CPU版本安装命令把cu117那一段换成cpu就行。4.2 数据集目录结构调整与yaml配置拿YOLOv5或YOLOv8来举例训练脚本要求的目录结构是这样的dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/对应到这个泊车位数据集就是把第三章节里脚本一生成的split_dataset目录改下名字和层级。然后写一个数据集配置文件比如parking.yaml内容# 数据集配置 path: /path/to/dataset # 数据集根目录绝对路径 train: images/train # 训练集图片目录相对path val: images/val # 验证集图片目录 test: images/test # 测试集图片目录可选 # 类别定义 nc: 1 # 类别数量这里只有车位一类 names: [parking_space] # 类别名字和标注时的类别名保持一致yaml里的nc和names是训练脚本读类别数的唯一来源。如果标签txt里的class_id大于等于nc训练时要么报错要么panic这个后面避坑章节展开说。path字段用绝对路径最稳别用相对路径换目录跑训练是报错高发区。4.3 训练命令、参数含义和一次完整运行基准训练命令长这样# --data 指向刚才写的yaml文件 # --weights 用预训练权重还是从头训练yolov5s.pt是COCO预训练 # --epochs 训练轮数 # --batch-size 每批样本数按显存调 # --img 训练图片尺寸默认640 python train.py --data parking.yaml --weights yolov5s.pt \ --epochs 100 --batch-size 16 --img 640逐参数说epochs在1000张的单类数据上100轮是起步过拟合严重就降到50~60欠拟合就往150以上试batch-size主要受显存制约6G显存跑640分辨率配yolov5s16是稳的12G可以上328G就乖乖用8img尺寸默认640能适配大多数情况泊车位这类目标不算很小640够用想提精度可以试试960但显存占用和训练时间都会明显上涨。训练过程中重点看两个输出一是每个epoch打印的box_loss、obj_loss、cls_loss二是val阶段的mAP。前20个epoch loss下降慢是正常的如果50轮之后还在原地晃先把学习率调小默认0.01对单类小数据集可能偏大或检查标签格式。训练跑完会生成runs/train/exp目录weights里存了best.pt和last.pt。用best.pt做推理测试# 推理单张图片 python detect.py --weights runs/train/exp/weights/best.pt \ --source test_images/parking_001.jpg --conf-thres 0.5 # 推理整个文件夹 python detect.py --weights runs/train/exp/weights/best.pt \ --source test_images/ --conf-thres 0.5 --save-txtconf-thres是置信度阈值小于这个值的检测框会被过滤掉。泊车位检测场景里如果漏检多就把阈值往下调比如0.3误检多就往上调比如0.7这个参数是在训练完成后调推理效果最直接的旋钮。4.4 用测试集验证训练效果val阶段用的验证集参与了训练过程的早停和调参模型多少会对它产生一定适应所以最终的泛化能力评估要用测试集。把上面yaml里的test字段指向划分好的test文件夹跑python val.py --data parking.yaml --weights runs/train/exp/weights/best.pt \ --task test --iou-thres 0.5iou-thres是判断预测框与真实框是否匹配的IoU阈值0.5是常规标准0.75是更严格的标准。泊车位检测这种目标尺度相对统一的任务0.5和0.75都报一下两者差距大说明框的定位精度还有提升空间。5. 避坑格式转换、路径错位与训练崩溃的排查清单这份资源里踩坑最多的三个环节一个是标签格式本身有问题第二个是目录结构不对导致训练读不到数据第三个是训练过程的资源分配。下面按现象到原因到解决的顺序列几条实际案例。5.1 标注框偏移或越界归一化坐标算错现象训练能跑通loss也能降但检测出来的框要么偏到目标左上角要么框比车位小一圈肉眼可见地对不准。原因TXT标签的坐标换算错了。最常见的是把xmin除以width当成x_center或者把xmax、ymax直接除以宽高当成框的宽高。换算公式必须是中心点坐标归一化即(xmin xmax) / 2 / width宽高是(xmax - xmin) / width。解决写一段脚本用OpenCV把txt里的五个值画回原图import cv2 img cv2.imread(parking_001.jpg) h, w img.shape[:2] with open(parking_001.txt) as f: for line in f: cid, 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.imwrite(check.jpg, img)框贴合车位框就说明标签没问题偏了就是换算逻辑写错了。这个检查值得放在所有训练之前它省下的调试时间远超写脚本的时间。5.2 训练时找不到标签图片与标签文件名对不上现象训练刚启动就报错提示unable to find label file或者大量WARNING: skipping labelYOLO跳过了一部分甚至全部图片。原因分三种。一是划分脚本只复制了图片没复制标签检查一下split后的labels目录有没有文件二是图片和标签的文件名不一致比如图片叫parking_001.jpg标签叫001.txt三是目录结构不对YOLO要求images和labels在同级目录下按train/val子目录一一对应图片在images/train标签就必须在labels/train。解决写个一次性检查脚本扫描全目录import os img_dir split_dataset/train/images lbl_dir split_dataset/train/labels imgs set(os.path.splitext(f)[0] for f in os.listdir(img_dir)) lbls set(os.path.splitext(f)[0] for f in os.listdir(lbl_dir)) print(有图无标签:, len(imgs - lbls)) print(有标签无图:, len(lbls - imgs))只要输出不为零先把文件补齐再决定要不要重新划分。这条血的教训是不要手动拖文件补因为补完还得重新确认对应关系直接回到划分脚本用同一套文件名生成逻辑重新划分一劳永逸。5.3 类别编号错乱loss降不下去的元凶现象训练时loss一直不下来或者训练过程正常结束但预测结果全是一个类。原因标签txt里的类别编号跟yaml配置的classes对不上。比如标注时类别编号写了1但yaml里nc设置为1、names只有一项那class_id1已经越界了。另一种情况是多类别数据集里编号从1开始编号但YOLO要求从0开始等于每个类都错了一位。解决遍历所有标签文件统计出现的类别编号# 查看所有标签里的类别编号分布 cat split_dataset/train/labels/*.txt | awk {print $1} | sort | uniq -c输出应该类似1000 0这样只有编号0出现1000次。如果出现数字1说明编号从1开始了需要批量减1。这个坑在数据集是别人给的、标注软件自动生成时最容易踩拿到数据集第一件事就是跑这个统计命令。5.4 显存不足训练中断参数组合的平衡现象训练跑到一半报CUDA out of memory有时是几个epoch之后才炸不是一开始就炸。原因batch-size和输入图片尺寸的组合超出了显存容量。由于每个epoch的数据是随机采样的某些batch里图片分辨率高或目标数多显存峰值就会超过容量。解决按显存大小给出保守配置参考显存图片尺寸batch-size预期效果4GB5128能跑精度一般6GB64016常规配置12GB64032推荐24GB96016精度最高训练慢再提示一个技巧训练命令里显存这块有几个缓解选项YOLOv5的train.py里加--workers 2降低数据加载内存压力--cache预加载数据可以加速但更吃内存按实际机器情况取舍。真炸了就把batch-size减半别动图片尺寸因为图片尺寸关系到模型最终感受野改了还得重新选超参。6. 进阶用划分脚本形成评估闭环把mAP做实训练跑通只是开始想确认模型在真实场景里到底行不行还得在评估环节下功夫。先要理清train.txt、val.txt、test.txt三个文件分别应该用在哪一环。train.txt配合训练批次的采样val.txt在做训练中验证时使用test.txt则是在训练结束后做最终评估时使用——这样划分的意义在于模型在test.txt上没有任何记忆它给出的mAP才是相对真实的泛化水平。如果你想更严格一点可以用python val.py --task test单独跑一遍test.txt不要用训练结束时的val结果当最终成绩。第二个值得做的自检是数据分布统计。泊车位图片里可能各有各的情况比如某些时段光照强烈、某些场景是俯拍视角。如果一个子集的图片数量占比过低训练时容易欠拟合。展开来说import os for split in [train, val, test]: img_dir fsplit_dataset/{split}/images cnt len(os.listdir(img_dir)) if os.path.exists(img_dir) else 0 print(f{split}: {cnt} 张 (占比 {cnt/1000:.1%}))如果train占比不到60%mAP大概率会偏低解决方法是重新划分或者数据增强里把亮度、对比度扰动调大一些。第三件重要的事是置信度阈值和IoU阈值。yolov8的detect.py里conf-thres默认0.25实际部署时这个值通常不够用。在泊车位检测场景里宁可多出几个误检框、也不要漏掉真正的车位建议把conf-thres压到0.3以下反过来如果是做占用计数、要求高精度那0.7以上的阈值更合适。调阈值前可以先把不同阈值下的预测结果批量输出再统计精确率和召回率的变化曲线挑出两者交叉点附近的值使用。从那以后我每次拿到一批新数据集都会强制走一遍流程先抽查三种格式里同一条标注再跑一遍标签编号统计再划分数据、可视化验证划分结果最后才进训练。这套流程看着多花十几分钟实际上省掉了后面至少一个晚上的调试时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表