简介:这份停车位识别数据集面向从事计算机视觉与深度学习的学习者和开发者,尤其适合正在实践目标检测、智能停车管理或智慧交通课题的人群。数据源自PKLot停车场监控视频帧,覆盖晴天、阴天与雨天等多种光照条件,停车位被标注为已占用或空置,原始旋转矩形标注已统一转换为标准目标检测边界框格式,可直接用于YOLOv5等模型的训练与验证。压缩包共24834个文件,包含12416张jpg图像、12417个txt标注文件及1个yaml配置文件,整体约866.09MB,图像与标注一一对应,yaml文件便于快速配置数据路径与类别。目前已有2590人学习下载,说明该数据集在停车位检测任务中具备较好的实用参考价值。借助这份数据,读者可完成从数据加载、模型训练到占用状态识别的完整流程,并对比不同天气条件下的检测表现,为算法调优与场景落地提供扎实的数据支撑。
1. 停车位识别数据集到底长什么样:从 zip 包到可训练目录
拿到「停车位识别yolov5数据集.zip」这类压缩包,第一反应往往不是解压,而是先确认它能不能直接喂给 YOLOv5。停车位识别和通用目标检测有个明显差别:车位在俯视或斜视角下是密集排列的矩形区域,标注框之间经常只隔一条白线,模型很容易把相邻车位粘成一个框。所以这个数据集的价值不在于图片多少,而在于标注是否按「单车位」粒度切分、是否覆盖了遮挡和光照变化。
这个方向适合三类人:做智慧停车场落地的算法工程师、想用 YOLOv5 跑通自己第一个垂直场景的学生、以及需要评估「车位检测能不能替代地磁/超声波」的产品侧同学。它解决的核心问题是把车位从「人工划线 + 地磁传感器」变成「摄像头 + 检测模型」,成本结构完全不同。下面按数据检查、格式转换、训练配置、部署验证的顺序拆开讲,每一步都给出可复现的命令和参数。
2. 解压后先别急着训练:停车位数据集的目录结构与标注检查
2.1 典型 zip 包解压后的三种目录形态
停车位识别数据集常见的组织方式有三种,先tree或find看一眼再决定怎么处理。第一种是已经切分好的images/train、images/val、labels/train、labels/val,这种最省事,直接改data.yaml路径即可。第二种是全部图片堆在一个目录,标注是 COCO 格式的单个annotations.json,需要自己转 YOLO txt。第三种是 VOC 格式的 XML,每个图片对应一个 XML,需要写脚本转换。
# 先看目录层级,不要盲目解压覆盖 unzip -l 停车位识别yolov5数据集.zip | head -50 # 解压到独立目录,避免污染当前工作区 mkdir -p ~/datasets/parking && unzip 停车位识别yolov5数据集.zip -d ~/datasets/parking # 统计图片数量和标注数量是否对得上 find ~/datasets/parking -name "*.jpg" -o -name "*.png" | wc -l find ~/datasets/parking -name "*.txt" -o -name "*.xml" -o -name "*.json" | wc -l逻辑说明:unzip -l先列内容,避免解压出一堆嵌套目录。图片数和标注数不一致是常见问题,比如 1200 张图只有 1150 个标注文件,说明有 50 张是负样本或漏标。参数上,-d指定解压目录,建议放在数据盘而不是系统盘,后续训练读图 IO 更稳。
2.2 用脚本抽查标注框是否越界和重叠
停车位数据集最容易翻车的地方是标注框越界(x 或 y 超出 0~1)和相邻车位框高度重叠。写一个检查脚本,在训练前跑一遍,比训练到一半发现 loss 不降要省时间。
import os import glob def check_yolo_labels(label_dir, img_dir): issues = [] for txt in glob.glob(os.path.join(label_dir, "*.txt")): with open(txt) as f: lines = f.readlines() boxes = [] for i, line in enumerate(lines): parts = line.strip().split() if len(parts) != 5: issues.append(f"{txt} 第{i}行字段数不对: {line.strip()}") continue cls, x, y, w, h = map(float, parts) if not (0 <= x <= 1 and 0 <= y <= 1 and 0 < w <= 1 and 0 < h <= 1): issues.append(f"{txt} 第{i}行越界: {line.strip()}") boxes.append((x, y, w, h)) # 检查两两 IoU 是否过高,停车位相邻框 IoU 超过 0.5 要警惕 for a in range(len(boxes)): for b in range(a + 1, len(boxes)): iou = compute_iou(boxes[a], boxes[b]) if iou > 0.5: issues.append(f"{txt} 框{a}和框{b} IoU={iou:.2f} 可能粘连") return issues def compute_iou(box1, box2): x1, y1, w1, h1 = box1 x2, y2, w2, h2 = box2 inter_w = max(0, min(x1 + w1/2, x2 + w2/2) - max(x1 - w1/2, x2 - w2/2)) inter_h = max(0, min(y1 + h1/2, y2 + h2/2) - max(y1 - h1/2, y2 - h2/2)) inter = inter_w * inter_h union = w1 * h1 + w2 * h2 - inter return inter / union if union > 0 else 0 if __name__ == "__main__": problems = check_yolo_labels("labels/train", "images/train") for p in problems[:30]: print(p) print(f"共发现 {len(problems)} 个问题")逻辑说明:YOLO 格式是class x_center y_center width height,全部归一化到 0~1。越界框会导致训练时坐标回归目标异常,表现为 loss 突然飙高或 NaN。IoU 检查针对停车位场景特别重要,因为相邻车位框如果 IoU 超过 0.5,模型学到的就是「一个大框」,推理时会把两个车位合并。参数上,IoU 阈值设 0.5 是经验值,实际停车位如果画得规范,正常相邻框 IoU 应该在 0.1~0.3。
2.3 划分训练集和验证集时别按随机种子一刀切
停车位数据集如果来自同一个停车场、同一时段,随机划分会导致验证集和训练集高度相似,mAP 虚高。常见做法是按摄像头或按时间段划分:比如 1 号摄像头的数据做验证,2~5 号做训练;或者白天做训练,傍晚和夜间做验证。这样验证集才能反映真实泛化能力。
import os import shutil import random def split_by_camera(src_img_dir, src_label_dir, dst_root, val_cams=("cam1",)): for split in ["train", "val"]: os.makedirs(os.path.join(dst_root, "images", split), exist_ok=True) os.makedirs(os.path.join(dst_root, "labels", split), exist_ok=True) for img_name in os.listdir(src_img_dir): cam = img_name.split("_")[0] # 假设文件名以摄像头编号开头 split = "val" if cam in val_cams else "train" shutil.copy(os.path.join(src_img_dir, img_name), os.path.join(dst_root, "images", split, img_name)) label_name = os.path.splitext(img_name)[0] + ".txt" shutil.copy(os.path.join(src_label_dir, label_name), os.path.join(dst_root, "labels", split, label_name))逻辑说明:按摄像头划分能避免同一视角的图片同时出现在训练和验证集。参数上,val_cams选一个摄像头即可,如果数据量少,可以按 8:2 比例在摄像头内部再分。注意文件名解析规则要根据实际命名调整,不要硬套。
3. 把停车位数据集喂给 YOLOv5:data.yaml 与超参数怎么设
3.1 data.yaml 的四个必填字段和路径坑
YOLOv5 训练入口是train.py,它读data.yaml找图片和标签。停车位识别通常只有一个类别parking_space,但有些数据集会把「空闲车位」和「占用车位」分成两类,这要看标注定义。
# data/parking.yaml path: /home/user/datasets/parking # 数据集根目录,绝对路径最稳 train: images/train val: images/val nc: 1 names: ["parking_space"]逻辑说明:path是根目录,train和val是相对路径。常见坑是path写了相对路径,训练时工作目录一变就找不到图。nc是类别数,必须和 names 长度一致。如果数据集把占用/空闲分开标,nc: 2,names 写["free", "occupied"]。参数上,路径里不要有中文和空格,YOLOv5 底层用 OpenCV 读图,中文路径在某些版本会静默失败。
3.2 停车位场景下 anchor 和输入尺寸的调整
YOLOv5 默认 anchor 是在 COCO 上聚类得到的,停车位框通常更扁更宽,直接套用会导致召回率偏低。常见做法是用kmeans在自己的标注上重新聚类 anchor。
# 用 YOLOv5 自带脚本聚类 anchor,输入是训练集 labels python utils/autanchor.py --data data/parking.yaml --img-size 640 --thr 4.0 --n 9逻辑说明:--img-size要和训练输入一致,--n 9是 anchor 数量,YOLOv5 默认 9 个。聚类结果会打印 9 组宽高,替换模型 yaml 里的 anchors。参数上,--thr是聚类阈值,停车位框密集时可以调到 4.0 以上,让 anchor 更贴合扁宽形状。输入尺寸方面,停车位在画面中占比小,640 可能不够,建议试 960 或 1280,但显存占用会明显上升。
3.3 训练命令与关键超参数:lr、batch、epochs
python train.py \ --data data/parking.yaml \ --weights yolov5s.pt \ --img 960 \ --batch 16 \ --epochs 100 \ --hyp data/hyps/hyp.scratch-low.yaml \ --name parking_yolov5s逻辑说明:--weights用预训练权重,停车位数据量通常几千张,从头训容易过拟合。--img 960比默认 640 大,适合小目标。--batch 16是 8G 显存下的保守值,显存够可以上 32。--hyp选hyp.scratch-low.yaml,学习率低一些,停车位标注噪声相对大,低学习率更稳。参数上,--epochs 100配合早停,如果 50 轮后 mAP 不涨就可以停。训练日志里重点看mAP@0.5和mAP@0.5:0.95,停车位场景 mAP@0.5 到 0.9 以上算可用,0.95 通常偏低,因为框的边界模糊。
4. 停车位识别训练中的避坑与排查记录
4.1 现象:loss 正常下降但 mAP 一直是 0
原因:验证集路径写错,YOLOv5 读不到验证图,或者验证集 labels 目录为空。解决:训练前用find val/images -type f | wc -l和find val/labels -type f | wc -l确认数量一致,且 labels 里不是空文件。
4.2 现象:推理时相邻车位被合并成一个大框
原因:训练标注里相邻车位框 IoU 过高,或者 NMS 的 IoU 阈值设太大。解决:先跑 2.2 的检查脚本,把 IoU 超过 0.5 的标注拆开;推理时把--iou-thres从默认 0.45 降到 0.3,让重叠框更容易被保留。
4.3 现象:傍晚和夜间图片漏检严重
原因:训练集里夜间样本太少,模型没学到低照度特征。解决:按时间段划分验证集,确认夜间 mAP 单独统计;如果夜间样本少于 10%,要么补数据,要么在训练时加 HSV 增强,把hsv_v调高到 0.5 以上模拟暗光。
4.4 现象:训练到一半 loss 变成 NaN
原因:标注框有越界或宽高为 0,导致坐标回归梯度爆炸。解决:跑 2.2 的越界检查,把w或h为 0 的行删掉或修正;同时把学习率从 0.01 降到 0.001 再试。
4.5 现象:模型在验证集上很好,换一个停车场就崩
原因:过拟合到特定摄像头角度和车位线颜色。解决:训练时加degrees旋转增强和translate平移增强,让模型不依赖固定视角;验证集必须包含不同停车场的数据,哪怕只有几十张。
5. 从训练到落地:停车位识别模型的验证与树莓派部署技巧
训练完拿到best.pt只是第一步,真正落地要看推理速度和误检率。停车位识别有个特殊指标:车位占用状态会随时间变化,模型检测的是「车位框」而不是「有没有车」,所以验证时要分两步——先看框准不准,再看框内是否有车。常见做法是用detect.py跑一批图,人工核对框的位置,再用一个二分类小模型或简单像素统计判断占用。
# 批量推理并保存结果,conf-thres 调低看召回 python detect.py \ --weights runs/train/parking_yolov5s/weights/best.pt \ --source ~/datasets/parking/images/val \ --img 960 \ --conf-thres 0.25 \ --iou-thres 0.3 \ --save-txt \ --project runs/detect_parking逻辑说明:--conf-thres 0.25比默认 0.25 略低,停车位场景宁可多检也别漏检,后续可以用占用判断过滤。--iou-thres 0.3防止相邻框被 NMS 误删。--save-txt保存检测结果,方便和标注对比算指标。参数上,--img必须和训练一致,否则精度会掉。
如果要在树莓派 5 上部署,YOLOv5s 直接跑 ONNX 大概 5~8 FPS,够用但不算流畅。我一般会先导出 ONNX,再用onnxruntime跑,比 PyTorch 轻量。
import onnxruntime as ort import cv2 import numpy as np sess = ort.InferenceSession("best.onnx", providers=["CPUExecutionProvider"]) img = cv2.imread("test.jpg") img = cv2.resize(img, (960, 960)) img = img[:, :, ::-1].transpose(2, 0, 1) # BGR->RGB, HWC->CHW img = np.expand_dims(img, 0).astype(np.float32) / 255.0 outputs = sess.run(None, {sess.get_inputs()[0].name: img}) # outputs[0] 形状 [1, 25200, 5+nc],后续做 NMS逻辑说明:ONNX 推理要手动做预处理和后处理,providers在树莓派上只能用 CPU。参数上,输入尺寸和训练一致,归一化用/255.0。后处理 NMS 可以用 OpenCV 的cv2.dnn.NMSBoxes,比手写快。
最后说一个我踩过的坑:停车位数据集里的「空车位」和「占用车位」如果标注时混在一起,模型会把有车的车位也框出来,但框的位置会偏。我的习惯是训练前先统一标注定义——只框车位本身,不管有没有车,占用判断交给下游逻辑。这样模型只学一个任务,泛化更稳。希望帮到你。
本文还有配套的精品资源,点击获取