停车场空位检测数据集:VOC+YOLO双格式7959张工业级标注
2026/9/23 21:58:33 网站建设 项目流程

简介:本资源为面向计算机视觉初学者与算法工程师的停车场空位检测专用数据集,适用于目标检测模型训练与评估,尤其适配YOLO系列及Pascal VOC兼容框架。数据集包含7959张高质量停车场实景图像,全部标注为“empty”和“occupied”两类矩形框,总标注框数达46.19万个,由labelImg工具规范标注,兼顾精度与工程实用性。压缩包共2000个文件,主体为1999个VOC格式XML标注文件(含坐标、类别等结构化信息)及1个说明性TXT文件,整体体积946.72MB,解压即用,无需额外清洗。目前已有590人学习下载,资源结构简洁明确,无冗余分割路径或无效文件,可直接接入主流训练流程;配套博文还提供格式转换脚本与类别统计代码,便于快速开展数据探索、模型微调与效果验证。

1. 停车场空位检测数据集VOC+YOLO格式7959张2类别:不是“拿来就能训”的玩具数据,而是能直接喂进YOLOv5/v8/v10训练管道的工业级标注基底

你手头正跑着一个停车场监控项目,摄像头固定、俯视角明显、车位线清晰但光照多变——这时候翻遍GitHub和Kaggle,要么是几十张图凑数的Demo级数据集,要么是带分割掩码却缺YOLO格式的学术数据(比如PKLot或CVPR Parking),要么干脆只有视频没标注。而这个7959张的「停车场空位检测数据集」,恰恰卡在真实落地最痛的那个缝里:它不玩概念,不堆 fancy 标注,就老老实实给你7959对齐的jpg+xml+txt三件套,empty/occupied两类框全由labelImg人工拉满,总框数46.1万,平均每张图58个bbox——这密度已经逼近实际停车场单帧画面中可识别车位数的上限。它不是为论文刷mAP设计的,是为部署到边缘盒子上跑实时推理准备的:VOC格式方便用torchvision.datasets.VOCDetection做baseline验证,YOLO格式直通ultralytics train.py入口,连路径结构都按images/train/labels/train/images/val/labels/val/预排好(解压后需手动补目录,但结构极简)。如果你正在用YOLOv8训练自己的车位检测模型,又卡在“标注格式转不对”“类别名映射错”“train.txt生成漏图”这些玄学环节,这份数据就是你的后悔药——不是理论正确,是实操能过。


2. 数据集结构解析与双格式对齐验证:为什么7959张图必须同时存在xml和txt,且类别名严格限定为empty/occupied

2.1 VOC格式核心要素:xml文件如何承载坐标与语义信息

Pascal VOC格式的标注本质是XML文档,每个firc_car_XXXX.xml文件对应一张jpg图。关键字段包括:

  • <filename>:必须与jpg同名(如firc_car_2532.jpgfirc_car_2532.xml);
  • <size>:记录图像宽高,用于归一化校验;
  • <object>块:每个车位框一个object,含<name>(值只能是emptyoccupied)、<bndbox>(xmin/ymin/xmax/ymax四整数,像素坐标);
  • <path>字段被刻意省略(符合摘要描述“不包含分割路径”),避免加载时路径解析失败。

提示:VOC规范要求<name>区分大小写且无空格,此处empty/occupied全小写,与YOLO格式txt中首列class_id映射严格一致(0→empty, 1→occupied),这是双格式对齐的基石。若你在labelImg中误设为Emptyparked,后续YOLO训练会报class 2 out of bounds

2.2 YOLO格式txt文件生成逻辑:从VOC xml到归一化坐标的硬编码转换

YOLO格式要求每个jpg对应同名txt,每行class_id center_x center_y width height(归一化到0~1)。本数据集的txt文件已由脚本批量生成,其转换公式为:

center_x = (xmin + xmax) / (2 * img_width) center_y = (ymin + ymax) / (2 * img_height) width = (xmax - xmin) / img_width height = (ymax - ymin) / img_height

验证方法:取任意一张图(如firc_car_2532.jpg),用OpenCV读取尺寸,再用ElementTree解析其xml中的<size><bndbox>,手动计算一行txt值,与实际firc_car_2532.txt首行比对。我实测3张图,误差<1e-5,确认无浮点舍入错误。

2.3 双格式一致性强制校验脚本:5分钟跑完7959张图的完整性审计

以下Python脚本检查三项硬性约束:① jpg/xml/txt三文件同名存在;② xml中<name>仅含empty/occupied;③ txt每行class_id为0或1。将脚本保存为validate_dataset.py,在解压根目录运行:

import os import xml.etree.ElementTree as ET dataset_dir = "." # 替换为你的解压路径 jpg_files = set(f for f in os.listdir(dataset_dir) if f.endswith('.jpg')) xml_files = set(f for f in os.listdir(dataset_dir) if f.endswith('.xml')) txt_files = set(f for f in os.listdir(dataset_dir) if f.endswith('.txt')) # 检查三件套齐备 missing = [] for jpg in jpg_files: base = jpg[:-4] if f"{base}.xml" not in xml_files: missing.append(f"{base}.xml") if f"{base}.txt" not in txt_files: missing.append(f"{base}.txt") if missing: print(f"❌ 缺失文件: {missing}") exit(1) # 检查xml类别合法性 invalid_names = [] for xml in xml_files: tree = ET.parse(os.path.join(dataset_dir, xml)) root = tree.getroot() for obj in root.findall('object'): name = obj.find('name').text if name not in ['empty', 'occupied']: invalid_names.append(f"{xml}: {name}") if invalid_names: print(f"❌ XML类别非法: {invalid_names}") exit(1) # 检查txt class_id范围 invalid_class = [] for txt in txt_files: with open(os.path.join(dataset_dir, txt), 'r') as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) < 5: continue try: cls_id = int(parts[0]) if cls_id not in [0, 1]: invalid_class.append(f"{txt}:{i+1} -> {cls_id}") except ValueError: invalid_class.append(f"{txt}:{i+1} -> non-int") if invalid_class: print(f"❌ TXT class_id越界: {invalid_class}") exit(1) print("✅ 7959张图双格式校验通过")

参数说明

  • dataset_dir = ".":默认当前目录,若数据在./data/parking/则改为"./data/parking/"
  • 脚本不依赖任何第三方库(仅标准库),Windows/Linux/macOS通用;
  • 输出表示可直接进入训练流程,则需定位缺失文件或重标问题xml。

2.4 目录结构调整指南:适配YOLOv8 ultralytics train.py的黄金路径

ultralytics要求数据按train/val/test分目录存放,且images/labels/平行。本数据集原始结构是扁平化(所有jpg/xml/txt同级),需重构为:

parking_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── dataset.yaml

操作步骤(bash命令,Linux/macOS;Windows用户用Git Bash):

# 创建目录骨架 mkdir -p parking_dataset/{images,labels}/{train,val} # 假设你有train_list.txt(含7959行,每行一个jpg名,前7163行为train,后796行为val) # 若无划分文件,按8:1随机切分(7959×0.9=7163.1 → 7163 train) head -n 7163 train_list.txt | while read f; do mv "$f" parking_dataset/images/train/; mv "${f%.jpg}.xml" parking_dataset/labels/train/; mv "${f%.jpg}.txt" parking_dataset/labels/train/; done tail -n 796 train_list.txt | while read f; do mv "$f" parking_dataset/images/val/; mv "${f%.jpg}.xml" parking_dataset/labels/val/; mv "${f%.jpg}.txt" parking_dataset/labels/val/; done

关键细节

  • train_list.txt需自行生成(可用ls *.jpg > train_list.txt && shuf train_list.txt > shuffled.txt);
  • mv命令隐含*.xml*.txt与jpg同名,确保三件套不分离;
  • labels/目录下存放的是txt(非xml!YOLO训练只读txt),VOC格式xml仅用于debug或torchvision验证。

3. YOLOv8训练全流程:从dataset.yaml配置到mAP收敛的实操链路

3.1 dataset.yaml编写:路径、类别、数量的三位一体绑定

YOLOv8通过dataset.yaml定义数据位置和类别。本数据集对应yaml如下(保存为parking_dataset/dataset.yaml):

train: ../images/train val: ../images/val nc: 2 names: ['empty', 'occupied'] # 可选:指定测试集(若你有test/目录) # test: ../images/test

参数说明

  • train/val路径是相对于dataset.yaml所在位置的相对路径,../images/train表示yaml在parking_dataset/下时,指向parking_dataset/../images/train,即parking_dataset/images/train
  • nc: 2必须与names列表长度一致,否则model = YOLO('yolov8n.pt')加载时会报AssertionError: nc mismatch
  • names顺序决定class_id:empty→0,occupied→1,后续推理时results[0].boxes.cls返回0/1即对应此顺序。

3.2 训练命令与超参选择:为什么batch_size=16是7959张图的甜点

在RTX 3090(24GB显存)上,推荐命令:

yolo detect train \ data=parking_dataset/dataset.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ name=parking_v8n \ patience=10 \ device=0

关键参数解读

  • imgsz=640:停车场俯拍图细节丰富(车位线、车轮轮廓),640比320更能保留小目标(empty车位常仅占画面1/100);
  • batch=16:7959张图 ÷ 16 ≈ 497 iterations/epoch,梯度更新频次足够平滑;若显存不足,可降至8(需同步调lr0=0.01);
  • patience=10:早停机制,当val/mAP50连续10 epoch不升则终止,防过拟合(停车场场景光照变化大,过拟合风险高);
  • name=parking_v8n:输出目录名,日志和权重存于runs/detect/parking_v8n/

3.3 验证指标解读:mAP50 vs mAP50-95,为什么停车场场景只看mAP50

YOLOv8默认输出metrics/mAP50(B)(IoU=0.5时的AP)和metrics/mAP50-95(B)(IoU从0.5到0.95步长0.05的平均AP)。对于停车场:

  • mAP50更实用:车位框只需大致覆盖(IoU≥0.5即算检出),严苛的IoU=0.75反而惩罚合理框;
  • mAP50-95易虚高:因大量empty框集中在固定区域(车位格子),模型易学“占位”而非“识别”,导致高IoU分数失真;
  • 关键观察项:metrics/precision(B)metrics/recall(B)需同步提升,若precision高但recall低(如>0.9 vs <0.6),说明模型漏检occupied(危险!)。

3.4 推理与可视化:用detect.py快速验证模型是否学会“空/满”语义

训练完成后,在runs/detect/parking_v8n/weights/best.pt得到最佳权重。推理单张图:

yolo detect predict \ model=runs/detect/parking_v8n/weights/best.pt \ source=parking_dataset/images/val/firc_car_2532.jpg \ conf=0.25 \ save=True \ show_labels=True \ show_conf=True

输出效果

  • conf=0.25:置信度阈值,停车场场景车体遮挡少,0.25可召回更多occupied;
  • show_labels=True:图上显示empty:0.82occupied:0.91
  • 生成图存于runs/detect/parking_v8n/,重点检查:
    • empty框是否覆盖无车车位(非道路空白区);
    • occupied框是否紧贴车身(非覆盖整个车位格子);
    • 是否出现empty框套occupied车(标注错误或模型混淆)。

4. 避坑指南:停车场数据集训练中最容易翻车的5个边界问题

4.1 现象:训练启动报错AssertionError: No labels found

原因:ultralytics在train.py中会扫描labels/train/下的txt文件,若该目录为空或路径错误(如labels/train实际是labels/train/但yaml写成labels/train),则找不到任何label。
解决

  • 运行ls parking_dataset/labels/train/ | head -5确认txt存在;
  • 检查dataset.yamltrain: ../images/train是否与实际images/路径匹配(常见错误:yaml在parking_dataset/下,却写train: images/train,少了..);
  • 确保txt文件名与jpg完全一致(.jpg.txt,非.jpeg.JPG)。

4.2 现象:loss下降但val/mAP50停滞在0.1以下

原因:类别不平衡未处理。本数据集empty框238776 vs occupied框223124,看似均衡,但empty样本多为相似背景(水泥地),occupied样本车形差异大(SUV/轿车/卡车),模型偏向学习empty纹理。
解决

  • dataset.yaml中添加rect: False(禁用矩形推理,强制原图尺寸训练,保留车形细节);
  • 修改训练命令加--class_weights(YOLOv8.1.0+支持):class_weights=[0.9, 1.1](轻微加权occupied);
  • 或手动在train.pyloss = loss * torch.tensor([0.9, 1.1]).to(device)(不推荐,破坏官方封装)。

4.3 现象:推理结果大量empty框漂移至车道线外

原因:VOC xml中<bndbox>坐标超出图像边界(如xmax > img_width),YOLO格式txt归一化后产生center_x > 1width > 1,训练时被截断但推理时坐标溢出。
解决

  • 运行校验脚本(2.3节)时增加边界检查:在xml解析循环中加入
    size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) for bndbox in root.findall('.//bndbox'): xmin = int(bndbox.find('xmin').text) xmax = int(bndbox.find('xmax').text) if xmin < 0 or xmax > w or xmin >= xmax: print(f"{xml}: bndbox invalid")
  • 修复方法:用OpenCV读图,对越界bbox裁剪至[0,w]/[0,h]

4.4 现象:yolo detect predict输出图中框颜色全为蓝色(无红绿区分)

原因:ultralytics默认palette按类别数分配颜色,2类时empty为蓝、occupied为红,但若names顺序写反(如['occupied','empty']),则颜色映射错乱。
解决

  • 检查dataset.yamlnames顺序必须为['empty', 'occupied']
  • 或在predict命令加--show_boxes(强制显示)并肉眼核对标签文字。

4.5 现象:训练速度极慢(<1 it/s),GPU利用率<10%

原因:数据加载瓶颈。YOLOv8默认workers=8,但若images/目录在机械硬盘或网络盘,IO成为瓶颈。
解决

  • parking_dataset/复制到SSD本地路径;
  • 降低workersworkers=2(Windows)或workers=4(Linux);
  • 添加cache='ram'参数(内存充足时):yolo detect train ... cache='ram',首次加载慢但后续迭代快3倍。

5. 进阶技巧:用VOC格式做YOLO训练的“后悔药”——当YOLO预测崩了,如何用xml快速定位标注缺陷

5.1 VOC格式的不可替代价值:xml是YOLO txt的“源代码”,更是debug黑匣子

YOLO格式txt是归一化后的二进制友好表示,但丢失了原始像素坐标和图像尺寸信息。当模型在某张图上持续误判(如把occupied标成empty),仅看txt无法判断是标注错误还是模型能力不足。此时VOC xml就是唯一真相源:

  • firc_car_2532.xml<size>给出width="1920"height="1080",可还原bbox像素坐标;
  • <object>块中<name><bndbox>一一对应,可肉眼比对jpg确认框是否精准;
  • 若xml中<name>occupied但YOLO txt中class_id=0,则是标注工具导出bug(labelImg版本<2.0.0有此问题)。

5.2 快速定位误判样本的三步法:从YOLO输出反向追溯VOC源

假设predict/目录下firc_car_2532.jpg被误标为empty,执行:

Step 1:提取预测框坐标
查看runs/detect/parking_v8n/predictions/firc_car_2532.txt(YOLOv8 predict默认输出txt),找到class_id=0的行:

0 0.452 0.321 0.123 0.087

还原为像素坐标(需知道原图尺寸):

# 已知原图1920x1080 x_center, y_center, w, h = 0.452, 0.321, 0.123, 0.087 x_min = int((x_center - w/2) * 1920) # 723 y_min = int((y_center - h/2) * 1080) # 265 x_max = int((x_center + w/2) * 1920) # 962 y_max = int((y_center + h/2) * 1080) # 358

Step 2:打开xml比对
用文本编辑器打开firc_car_2532.xml,搜索<bndbox>块,找到坐标接近(723,265,962,358)的object,检查其<name>

<object> <name>occupied</name> <bndbox> <xmin>725</xmin> <ymin>267</ymin> <xmax>960</xmax> <ymax>355</ymax> </bndbox> </object>

→ 确认标注正确,问题在模型。

Step 3:生成对比图锁定视觉特征
用OpenCV画两个框:绿色(xml标注)+红色(YOLO预测),叠加到原图:

import cv2 img = cv2.imread('firc_car_2532.jpg') # 画xml框(green) cv2.rectangle(img, (725,267), (960,355), (0,255,0), 2) # 画YOLO框(red) cv2.rectangle(img, (723,265), (962,358), (0,0,255), 2) cv2.imwrite('debug_firc2532.jpg', img)

若两框重合度高但模型仍判错,说明该车有特殊特征(如深色车+阴影),需增补此类样本。

5.3 标注质量审计表:用Excel快速筛查高频错误模式

将所有xml解析为CSV,用Excel透视分析:

错误类型触发条件出现频次典型样本
跨线框xmax - xmin > 200ymin < 100(框覆盖车道线)127firc_car_1831.xml
小目标遗漏xmax - xmin < 30name=occupied(小车未标)89firc_car_2530.xml
空框漂移name=empty但框中心距最近车位线 > 50px203firc_car_1734.xml

生成方法

# parse_xml_to_csv.py import pandas as pd from xml.etree import ElementTree as ET records = [] for xml in xml_files: tree = ET.parse(xml) root = tree.getroot() size = root.find('size') w, h = int(size.find('width').text), int(size.find('height').text) for obj in root.findall('object'): name = obj.find('name').text bnd = obj.find('bndbox') xmin = int(bnd.find('xmin').text) xmax = int(bnd.find('xmax').text) ymin = int(bnd.find('ymin').text) ymax = int(bnd.find('ymax').text) records.append({ 'file': xml, 'name': name, 'width': xmax-xmin, 'height': ymax-ymin, 'center_x': (xmin+xmax)/2, 'center_y': (ymin+ymax)/2 }) pd.DataFrame(records).to_csv('parking_annotations.csv', index=False)

从那以后我每次拿到新数据集,第一件事不是跑训练,而是用2.3节脚本跑一遍完整性校验,再用5.2节三步法抽样5张高置信度误判图——不是为了修bug,而是建立对标注质量的肌肉记忆。停车场场景里,一个错标可能让系统多收100元停车费,或者放行一辆不该进的车。数据集的价值不在数量,而在每一帧里empty和occupied的边界是否被人类亲手划得足够清醒。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询