从629张小数据集到可部署模型:基于YOLOv8的停车位检测实战指南
发布时间:2026/9/12 21:47:46 作者:尧图编辑部 阅读量:1,286

简介面向YOLO系列目标检测学习者的停车位检测数据集包含629张带标注的实景图像覆盖不同角度与场景下的车位区域适合用于车位检测、智能停车等视觉任务的模型训练与效果验证。包内已按训练/验证需求完成数据集划分并附带可直接引用的yaml配置文件适配yolov5、yolov8、yolov9、yolov7、yolov10、yolo11等主流算法。压缩包共1888个文件主要包括629个jpg图像、629个txt标注文件、629个xml标注文件以及1个yaml配置txt采用YOLO格式xml采用VOC格式可满足不同训练框架与标注习惯省去格式转换环节。全部文件合计51.54MB体量适中便于快速下载与迭代实验。已有219人学习使用适合刚接触YOLO的初学者用于熟悉数据处理流程也适合有检测基础的研究者直接复用标注结果开展算法调优。1. 停车位检测为什么一张 629 张的小数据集也值得认真对待停车位检测在智慧停车、园区道闸和自动泊车系统里都是一线需求但它和常规目标检测有一个本质差异你检测的不是“车”这个物体本身而是“这个矩形区域里现在能不能停车”。车的姿态、光照、遮挡变化很大而车位线相对固定所以很多工程师会直接拿通用检测权重去微调结果发现小样本数据集很容易过拟合训练十几个 epoch 后 loss 还在降验证集 mAP 却纹丝不动。629 张图像带标签的数据集规模不大刚好卡在“能训练”和“容易翻车”的临界点上。这个量级跑通一次完整流程非常快适合用来验证算法选型、标注质量和训练参数也为后面扩展到几千张数据留好基线。本文就从数据格式、标注转换、训练配置到部署验证把一套可复现的停车位检测落地路径讲清楚重点是参数为什么这么定、失败时看什么指标而不只是给你一串能跑的命令。2. 先把数据吃透停车位数据集的标注结构、类别平衡与转换工具2.1 停车位检测的标签语义从“车”到“空位/占用位”的视角切换目标检测数据集的质量不只看数量更要看标签语义和任务目标是否对齐。通用检测数据集如 COCO 标的是 person、car、traffic light 这类具体物体而停车位检测数据集的标签语义是“区域状态”最常见的是 two classesempty表示空车位occupied表示被占用的车位。也有数据集只标一类parking_space把空/占的判断交给后续分类模型这种方案在工程里也常见但精度上限取决于车位框的质量。629 张图像带标签的数据集通常是俯视或斜视视角的停车场画面标注对象是画好线的车位框。标注框的贴合程度直接影响训练效果框太大把邻车或墙面圈进去框太小切掉车位线边缘这两种情况都会让模型学到错误的边界。拿到数据集后第一步不是直接开训而是把标签可视化画在原图上抽查 50 到 100 张确认标注框是否紧贴车位线、有没有漏标和错标。另一个需要确认的维度是类别分布。用下面这段脚本可以快速统计每个类别的框数量和框的尺寸分布一个合理的停车位数据集里empty和occupied的框数比例不应严重失调比如超过 10:1否则训练时模型会偏向多数类少类的召回率会很难看。import os from collections import Counter from pathlib import Path label_dir Path(labels) # YOLO txt 标签目录 class_counter Counter() box_areas [] for txt_path in label_dir.glob(*.txt): with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) w float(parts[3]) h float(parts[4]) class_counter[cls_id] 1 box_areas.append(w * h) print(类别统计:, dict(class_counter)) print(框面积均值:, sum(box_areas) / len(box_areas) if box_areas else 0)这段代码按行读取 YOLO 格式的标注文件每行是class x_center y_center width height归一化到 0 到 1。统计类别框数和框面积均值的意义是如果occupied的框数远大于empty后续训练必须调整类别权重或者用增强手段补齐少类样本框面积过小说明标注的是远端车位这类小目标在训练时容易学不出来。2.2 标注格式转换VOC 和 LabelMe 坐标怎么转成 YOLO 文本格式拿到手的 629 张带标签数据交付格式未必是 YOLO 的 txt常见的情况是 VOC 的 XML 文件或 LabelMe 的 JSON 文件。VOC 格式里每个目标的坐标是xmin ymin xmax ymax的绝对像素值转 YOLO 格式需要做两步把绝对坐标转成中心点加宽高再除以图像的宽和高做归一化。KITTI 标注转 YOLO 也是一样的逻辑只是字段排列顺序不同。下面是一个把 VOC XML 批量转换为 YOLO txt 的脚本转换时要注意三点类别编号从 0 开始按字母序映射、坐标做边界裁剪防止越界、跳过损坏的 XML 文件并输出日志。import xml.etree.ElementTree as ET from pathlib import Path class_names [empty, occupied] # 按字母序排列 def voc_to_yolo(xml_path, out_dir, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_names: print(f未知类别 {name} 在 {xml_path.name} 中) continue cls_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path Path(out_dir) / (Path(xml_path).stem .txt) out_path.write_text(\n.join(lines), encodingutf-8)这段转换逻辑的关键参数是img_w和img_h这两者必须和图像实际尺寸一致否则归一化坐标整体偏移训练时目标框会全部错位。另一个容易踩的坑是 XML 里存在difficult标签的样本转换代码里一般直接跳过这种目标但如果你训练场景里有大量遮挡严重的车位可以把difficult样本也保留让模型见过难例。转换完成后要随机抽样十张图把 YOLO txt 坐标画回图上人工核对这一步不能省。2.3 数据划分与路径组织训练集、验证集、测试集怎么分才不虚629 张图的数据集建议按 8:1:1 划分成训练、验证、测试三个集合注意划分时要按图像目录打乱而不是按标注文件打乱避免同一场景的连续帧同时落入训练集和验证集造成评估虚高。一个稳妥的做法是先把图片文件名全部洗牌再按比例切分最后生成对应的 train.txt、val.txt、test.txt 文件。在动手写划分脚本之前先把数据目录整理成下面这种结构方便后续直接喂给 YOLO 训练接口。images和labels保持同名同路径结构训练框架才能自动找到对应标注。parking_dataset/ ├── images/ │ ├── train/ # 约 500 张 │ ├── val/ # 约 63 张 │ └── test/ # 约 66 张 └── labels/ ├── train/ ├── val/ └── test/划分完成后检查labels/train里的 txt 文件是否和images/train里的图片一一对应缺少标注的图片要从训练集移出否则训练到一半会因为找不到标签文件报错。这个步骤看似琐碎但它是后续训练能否一次跑通的关键629 张的小数据集经不起一点点路径问题导致的返工。3. 用 YOLOv8 在本地跑通停车位检测的最小训练流程3.1 为什么停车场场景默认从 YOLOv8n 起步选模型权重是训练前最实际的决策。停车场场景通常是部署到边缘盒子或工控机上这些设备的算力有限实时性要求高这就是为什么建议从 YOLOv8n 而不是 YOLOv8x 起步。YOLOv8n 参数量只有 3.2M在 640x640 输入下推理速度可以达到几十毫秒级别而停车位检测的目标框普遍比较大不需要极高分辨率的特征图来捕捉细节。对比 YOLOv5 和 YOLOv7YOLOv8 在训练上最大的区别是内置了 Anchor-Free 检测头和任务对齐学习策略对小数据集来说收敛更稳定不用手动调 anchor 参数。YOLO11 作为较新的版本在 COCO 上的精度有提升但模型结构和训练超参变化不大如果只想快速跑通流程YOLOv8n 的上手成本最低社区文档和排错资料最全。如果你的场景需要检测极远距离的小车位可以后面再升级到 YOLOv8s 或 YOLO11s。3.2 数据配置文件 data.yaml 的字段说明训练前要写一个 YAML 配置文件把数据集路径、类别数量和类别名告诉训练程序。这里有一个经常被忽略的细节names列表的索引顺序必须和标注文件里的 class id 一一对应一旦写反empty会变成occupied训练出来的模型在部署时预测结果完全反了。path: /home/user/parking_dataset # 数据集根目录 train: images/train val: images/val test: images/test nc: 2 names: [empty, occupied]path字段建议写绝对路径避免因为终端工作目录不同导致数据加载失败。train和val字段是相对path的路径不要写../parking_dataset/images/train这种相对路径训练脚本的工作目录不是固定的。写完后可以用下面这条命令验证配置和标签是否匹配。python -c from ultralytics import YOLO; model YOLO(yolov8n.yaml); model.train(datadata.yaml, epochs1, batch4)这条命令只跑 1 个 epoch目的是提前暴露数据加载、标签格式、路径配置等问题。如果这一步顺利跑完说明数据链路是通的可以正式进入训练阶段。很多人跳过这一步直接跑完整训练结果等了两小时发现数据没加载对白白浪费时间。3.3 训练命令、batch size 与 imgsz 的参数取舍正式训练命令建议这样写参数不要照抄要环境确认后再用。yolo detect train \ modelyolov8n.pt \ datadata.yaml \ epochs100 \ batch16 \ imgsz640 \ patience20 \ projectparking_runs \ nameexp1 \ seed42 \ optimizerAdamW \ lr00.001 \ lrf0.01逐项说明关键参数的选择逻辑。epochs100在 629 张图的小数据集上是合理的上限配合patience20可以实现早停即验证集 mAP 连续 20 个 epoch 不提升就结束训练。batch16取决于显存大小以 24GB 显存为例imgsz640时 batch 16 是安全值如果显存只有 8GB退到batch8。imgsz的选择要看标注框的实际大小如果大多数车位宽高占图像的 1/8 以上640 就够用如果图像远端有很多小目标可以试imgsz960训练时间会增加约一倍。optimizerAdamW和lr00.001是 YOLOv8 官方预训练权重微调时常用的组合。如果你用的是 COCO 预训练权重yolov8n.pt初始学习率不建议设太高0.001到0.002之间比较安全。seed42固定随机种子保证两次训练结果可比这在调参时特别重要否则你很难判断精度变化是参数引起的还是随机波动。lrf0.01表示学习率衰减到初始值的 1%让末期训练步长变小有利于收敛到更稳的局部最优。3.4 训练日志的分析loss 收敛和验证指标如何判断正常训练启动后不要只看终端滚动日志用下面的命令实时监控两条信息总共训练多少 epoch、当前验证集 mAP 是否在增长。yolo detect train modelyolov8n.pt datadata.yaml epochs100 batch16 imgsz640 patience20训练过程中每个 epoch 结束会输出一个表格其中box_loss、cls_loss、dfl_loss是训练集上的损失函数值precision、recall、mAP50、mAP50-95是验证集指标。这里要格外注意训练损失持续下降但验证 mAP 不再提升同时 precision 和 recall 开始震荡是典型的过拟合信号。小数据集通常在第 40 到第 60 个 epoch 之间达到最佳验证精度之后训练损失下降但验证精度停滞此时训练脚本会自行判断是否提前停止。如果发现验证指标从第一个 epoch 开始就一直很低比如 mAP50 始终在 0.1 以下优先检查标注框是否画错了位置而不是急着调参。可以用下面这段代码把标注框画到图上人工确认数据质量。import cv2 from ultralytics.utils.plotting import Annotator from ultralytics import YOLO model YOLO(yolov8n.pt) # 仅为了使用内置标注工具 img cv2.imread(sample.jpg) with open(sample.txt) as f: lines f.readlines() for line in lines: cls_id, xc, yc, w, h map(float, line.split()) h_img, w_img img.shape[:2] x1 int((xc - w / 2) * w_img) y1 int((yc - h / 2) * h_img) x2 int((xc w / 2) * w_img) y2 int((yc h / 2) * h_img) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)这个可视化脚本把 YOLO 归一化坐标还原成像素坐标再绘制矩形框。停车位的标注框应该紧贴车位线边界如果发现大量框偏移或过松回到标注阶段修正不要在这种数据上继续训练。4. 小样本数据集的精度提升数据增强与损失调优4.1 629 张图的数据增强参数设置数据增强是小样本目标检测的最关键手段。YOLOv8 内置了丰富的增强选项在训练配置里可以用hsv_h、hsv_s、hsv_v、degrees、translate、scale、fliplr、mosaic等参数控制。下面是针对停车位场景的一套推荐配置核心原则是增强强度要匹配场景分布不引入现实中不可能出现的形态。hsv_h: 0.015 # 色调变化幅度停车位颜色比较单一不宜过大 hsv_s: 0.7 # 饱和度变化光照不同时饱和度差异大 hsv_v: 0.4 # 亮度变化白天/夜晚/逆光场景的模拟 degrees: 5.0 # 旋转角度车位线一般是规则的矩形旋转过大会切掉信息 translate: 0.1 # 平移比例模拟摄像机位置偏移 scale: 0.4 # 缩放比例模拟不同高度的摄像机视角 fliplr: 0.5 # 水平翻转车位左右对称这个增强非常有效 mosaic: 1.0 # 马赛克拼接四张图拼一起训练丰富上下文为什么degrees只有 5 度而不是 15 度因为停车位检测一个常见场景是道闸或高位摄像头视角基本固定车位线也不会大角度旋转过度旋转会让模型学到不真实的车位形态。hsv_h调低同理停车场的地面颜色通常是灰色或深色色调扰动太大会让模型把颜色当特征。fliplr在这里特别有效因为停车位的左右镜像形态完全合法等于免费把数据集翻了一倍。4.2 损失权重调整box 和 cls 的配比在车位检测里的实际意义YOLOv8 的损失函数由三部分组成box loss 负责回归预测框的位置cls loss 负责分类dfl loss 负责分布焦点损失。默认权重是box7.5、cls0.5、dfl1.5这个配比在 COCO 上表现好但停车位场景有自己的特殊性。停车位检测的类别区分难度不高———空位和占用位在视觉上差异明显难的是把框的位置回归准。如果一个框把旁边车位的车头也圈进来了虽然类别判断对但边界不贴合后续如果要计算车位占用坐标误差会很大。因此在实际调参中可以考虑按下面的思路修改box和cls的权重比。box: 10.0 # 提高位置回归的权重让框更贴合车位线 cls: 0.4 # 类别权重适当降低 dfl: 1.8 # DFL 权重略微提高帮助边界框分布更集中这个配置是在验证集 mAP50 已经稳定但预测框边界偏松的情况下使用的。判断是否需要调 box 权重的方法很简单把验证集的预测结果可视化如果看到框覆盖了车位线外侧的人行道或相邻车位的边缘说明框回归精度不够优先调盒框损失和 DFL而不是增加训练轮数。4.3 过拟合的判断与应对从特殊停车型到泛化能力的验证629 张图像最大的风险就是过拟合。模型可能记住了训练集里某个停车场的特殊颜色地面、特定光照条件或特定角度的车位线在测试集上表现尚可换一个停车场就失效。过拟合的应对手段按优先级排序加大数据增强、减少模型容量从 YOLOv8s 降到 YOLOv8n、增加验证集早停的严格度。实际训练中如果发现最佳权重出现在前 30 个 epoch 以内之后验证 mAP 持续下降或震荡可以把patience调小到 10同时增加mosaic到 1.0 和scale到 0.5迫使模型学到更通用的车位形态特征。测试集在这个阶段变得非常重要。训练完成后用测试集做一次评估专门看哪些样本被预测错误错误类型通常分为三类远端小目标车位漏检、逆光条件下错检、同色车辆与地面背景混淆。把这几类错误样本统计成表能直接指导下一步采集数据的方向———是补充低光照样本还是补充远端图像而不是盲目增加总数据量。错误类型可能原因应对策略远端小目标漏检车位框在 640 分辨率下小于 16 像素提高 imgsz 到 960或对远端区域做 ROI 裁剪逆光错检训练集缺少高对比度样本补充逆光图像或增强 hsv_v同类色混淆深色车与阴影区域相似增加对比度增强多看 SH 调色5. 验证、导出和部署落地时最容易忽略的几个细节5.1 测试集评估不要只看一张混淆矩阵图训练完成后用测试集做完整评估输出包含每类的 precision、recall 和 mAP。停车位检测要特别关注occupied类的 recall因为它直接决定系统会不会把被占用的车位显示为空位———这在真实业务里比把空位显示为占用更严重。用下面的命令导出测试集评估结果。yolo detect val \ modelparking_runs/exp1/weights/best.pt \ datadata.yaml \ splittest \ save_jsonTrue \ conf0.25 \ iou0.5conf0.25是置信度阈值实际部署时通常在 0.3 到 0.5 之间调iou0.5是 NMS 的 IoU 阈值设太低会让重叠的检测框保留过多设太高会合并相邻车位的各自预测框在车位密集的场景里可以用 0.45 起步。5.2 导出 ONNX 或 TensorRT 后分辨率一致性是最容易出问题的点训练时的 640x640 输入尺寸和导出模型时的输入尺寸如果不一致部署后会出现框全部偏移或目标漏检。导出命令如下。yolo export modelbest.pt formatonnx imgsz640 halfTruehalfTrue开启 FP16 精度一般能降低显存占用和推理延迟。导出后用 ONNXRuntime 加载模型做一次推理和 PyTorch 推理结果比对预测框坐标偏差在 1 像素以内是正常的如果偏差明显检查预处理阶段是否缺少letterbox操作或归一化系数是否正确。这是部署环节最常见的错误来自训练和推理图像预处理不一致。5.3 实战技巧重叠车位区域用一个后处理规则去重摄像头视角下相邻车位往往有部分重叠两个框的 IoU 可能超过 0.5NMS 之后偶尔还会保留两个高度重叠的预测框导致一个车位被计为两个空位。一个简单有效的技巧是部署代码里把分类为empty的框再做一次低阈值 NMSIoU 阈值设置为 0.3比默认值更严格。如果检测目标是计算空位数量而不是输出框坐标更推荐按车位中心点做去重以预测框中心点画一个半径 15 像素的邻域这个范围内只保留置信度最高的那个框。这个方法在工程里比单纯调 NMS 阈值更稳尤其适应摄像头安装角度造成的车位角度差异。加上这个后处理整个检测链路才是完整的部署后空位计数的误差才能控制在业务接受范围内。本文还有配套的精品资源点击获取