简介面向停车场车位检测的PKLot数据集YOLO格式版本适用于目标检测与车位占用识别任务。数据源自PKLot数据库共12416张监控摄像机帧覆盖晴天、阴天、雨天等不同天气场景每张图像中停车位均标注为有人或空闲。压缩包内共2000个文件以1994张JPG图像为主体另含3个TXT标注文件与3个JSON配置文件可配合常用深度学习框架直接进行训练与验证整体包体约842.49MB。已有680人学习下载。压缩包内图片按拍摄时间命名便于按场景检索对训练鲁棒的车位占用模型、对比不同天气下的检测表现以及开展相关学术研究都具有实用参考价值。1. 停车位识别数据集与 YOLO一张俯视图背后的关键问题“停车位识别数据集”后面缀上“Yolo版”意味着你已经默认走目标检测这条路把车位当目标框把占用状态当类别交给 YOLO 去学。这套方案能解决一个很具体的业务问题——给定一张停车场俯视图直接输出每个车位的占用状态用于停车诱导、余位统计或者占位监控。适合手里有高位摄像头样本、想把车位识别能力真正落到现场的人也适合拿公开数据做算法验证的初学者。难点从来不在模型结构而在标注规范和数据工程这两件事没做对换哪个 YOLO 版本都救不回来。2. 数据从哪来采集方式、类别定义与标注规范2.1 三种主流数据源与选型高位相机、无人机航拍、车载环视停车位识别的数据来源业内基本上就三类选错了后面全是返工。第一类是高位相机也就是停车场里常见的立杆或墙顶机位。单个摄像头覆盖 8 到 20 个车位视角固定背景变化小。这是工程落地最主流的数据源因为数据集构建时的视角和你上线运行时的视角完全一致模型学到的特征可以直接迁移。采集时只需要按不同时段、不同光照条件录制视频再抽帧成图片即可成本很低。第二类是无人机航拍。优点是单次采集能覆盖几百个车位适合做停车场宏观调度或大范围车位规划。但它有两个麻烦一是高度不固定导致车位尺度变化大二是太阳角度会造成阴影方向不一致。用作预训练数据可以直接拿去做线上车位状态识别召回率会差不少。而且多数停车场有顶棚无人机视角和落地相机视角差异太大我不建议作为唯一数据源。第三类是车载环视也就是停车辅助系统里那种 360 度拼接画面。这种数据的车位线和车辆相对位置时刻在变标注难度极高通常也不适合直接套用“Yolo版车位检测框”的做法更多是走“车辆检测 车位线语义分割”的组合。如果你做的是自动泊车别在这个数据集方向里深挖。选型建议第一优先级永远是高位相机自采固定机位、固定高度标注完就能直接训练。如果拿公开数据集练手要特别注意别人的“车位框判定标准”和你的现场是否一致公开集里常见的“贴着外线标”和“只标内框”两种风格混在一起会让模型无所适从。2.2 类别设计直接检测车位位置还是检测车辆再判断占用标注之前先定类别这里有三条路线。方案 A只检测车辆不检测车位。系统预先在画面里画好每个车位的坐标区域车辆检测框和车位区域做交并比判断重叠超过阈值就判定占用。优点是类别只有 car 一个训练非常简单公开的车辆检测模型都能直接拿来用。缺点是车位坐标是外挂的摄像头角度稍微震动或者移位整个区域映射就偏了维护成本全被挪到了部署端。方案 B直接检测车位矩形类别分成 occupied占用和 free空闲两类。模型在一帧画面里同时输出车位位置和占用状态不依赖外部坐标地图摄像头微调后依然能工作。这是“停车位识别数据集 Yolo版”最常见的标注路线也是我最推荐的一种。因为模型输出可以直接映射成业务字段省掉一层后处理坐标换算。方案 C检测车位的四个角点用关键点法还原车位位置。精度上限确实高但角点一旦被车辆或柱子遮挡整个车位就废了标注工作量也翻倍。除非要做精确的车位线引导否则普通停车场管理项目不建议碰。我见过的落地项目最终都收敛到方案 B。一个车位一个框框内特征本身就包含“地上有没有车”的信息模型训练目标与业务目标完全一致误检时也只是单个车位的问题不会像方案 A 那样出现全局偏移。2.3 标注规范内框优先、不跨障碍、遮挡只标可见部分类别定完最容易被忽视的是矩形标注规范。一条铁律只标车位内侧矩形。标准车位由内外两条线组成外侧白线或黄线经常被压线车辆挡住还容易磨损褪色模型跟着外侧线学等于白学。内框边缘和车辆底盘基本对齐特征最干净跨摄像头迁移也最可靠。几条我在实践中固化的规则一个车位一个框不允许柱子、栏杆、绿化带把框切成两半车辆骑在两个车位之间时按实际占用的框来标另一侧车位如果被挡住宁可跳过也不要标“感觉上应该有”完全被遮挡的车位直接跳过不要凭记忆补框补出来的都是标签噪声俯拍视角下框可以带角度普通 YOLO 处理的是轴对齐矩形框如果车位在画面里倾斜超过 30 度建议直接上 YOLOv8-OBB 旋转框版本而不是硬用正框去包标注工具方面常见做法是先选一个支持导出 COCO 或 VOC 格式的工具如 X-Anylabeling、LabelImg因为 YOLO 需要的 txt 格式不适合人工直接写。导出的 JSON 或 XML 再通过脚本转成 YOLO 标签下一步就落到格式转换上。3. 把标注转成 YOLO 标签目录组织、转换脚本与格式自检3.1 YOLO 训练的最小目录约定与 data.yaml从标注工具导出 COCO 或 VOC 格式后第一步是建标准目录。YOLO 的数据组织规则是图片和标签分开标签是 txt 文本文件名必须和图片名完全一致扩展名不同而已。图片放在 images 下标签放在 labels 下再按 train 和 val 分成两组。mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val这段命令建立了四个文件夹对应训练集和验证集的图片与标签。注意不要按类别建子目录那是分类任务的习惯检测任务里每张图可以同时出现多个类别所有类别都写在同一张图的同名 txt 里。做完这步再写一个 data.yaml 指向这些目录。path: /home/user/dataset train: images/train val: images/val names: 0: free 1: occupiedpath 字段我习惯写绝对路径因为 YOLOv8 训练时工作目录可能会变相对路径很容易在换机器后失效。names 的索引顺序必须和标注 txt 里的第一列数字严格对应这里 0 是 free、1 是 occupied转换脚本里类别的枚举顺序也要按这个写。3.2 把 COCO JSON 转成 YOLO TXT转换脚本与边界处理拿到 COCO 格式标注后最常见的是 json 里每张图的 bbox 是[x, y, width, height]原点在左上角而 YOLO 需要的是归一化后的中心点坐标和宽高。下面这段脚本是我平时最常用的转换模板。import json from pathlib import Path def coco_to_yolo(coco_json, output_dir, categories_map): coco_json: COCO 标注文件路径 output_dir: 输出 txt 的目录 categories_map: {car: 0, occupied: 1, free: 2} 之类的映射 with open(coco_json, encodingutf-8) as f: data json.load(f) img_info {img[id]: img for img in data[images]} cat_id_map {} for cat in data[categories]: # 根据类别名字找到 COCO 原始 id 映射到目标索引 if cat[name] in categories_map: cat_id_map[cat[id]] categories_map[cat[name]] for ann in data[annotations]: img img_info[ann[image_id]] w, h img[width], img[height] new_cat cat_id_map.get(ann[category_id]) if new_cat is None: continue x, y, bw, bh ann[bbox] # COCO 格式左上角 x, y, 宽, 高 # 转中心点坐标并归一化 cx (x bw / 2) / w cy (y bh / 2) / h nw bw / w nh bh / h # 越界保护把落在图外的框拉回 [0, 1] 区间 cx max(0.0, min(1.0, cx)) cy max(0.0, min(1.0, cy)) nw max(0.0, min(1.0, nw)) nh max(0.0, min(1.0, nh)) txt_path Path(output_dir) / (Path(img[file_name]).stem .txt) with open(txt_path, a, encodingutf-8) as f: f.write(f{new_cat} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}\n) print(转换完成)逻辑说明COCO 的 bbox 是左上角起点加宽高YOLO 需要中心点加宽高所以先算中心点(x bw/2, y bh/2)再各自除以图片宽高做归一化。这里除的是图片自己的宽高不是数据集中最大的宽高很多新手在这里翻车。参数说明categories_map这个字典把 COCO 的类别名映射到你的 YOLO 索引比如车位项目里{occupied: 0, free: 1}如果原始标注只有 car 一类后面打算用 2.2 说的方案 A那映射就是{car: 0}。脚本里的越界保护不是多余COCO 标注里压边框很常见中心点算出来可能是 0.97一旦出现负值或大于 1 的值训练时 anchor 匹配会算错梯度直接表现为 loss 变成 NaN。3.3 跑训练前先自检标签格式与配对的几个关键检查转换完标签别急着开训先跑一遍自检。这步能省掉后面一多半的排查时间。import os label_dir dataset/labels/train img_dir dataset/images/train allowed_ids {0, 1} # 必须和 data.yaml 的 names 对齐 for txt_name in os.listdir(label_dir): if not txt_name.endswith(.txt): continue # 检查图片是否存在 img_name txt_name.replace(.txt, .jpg) # 如果你的图是 png 就改成 png if not os.path.exists(os.path.join(img_dir, img_name)): print(f缺图片: {img_name}) continue with open(os.path.join(label_dir, txt_name)) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: print(f格式错误 {txt_name}: {line}) continue cat_id int(parts[0]) if cat_id not in allowed_ids: print(f非法类别ID {cat_id} 在 {txt_name}) for v in parts[1:]: if not (0.0 float(v) 1.0): print(f坐标越界 {txt_name}: {line})这段脚本会检查三类问题图片和标签是否配对、每行是否恰好五个字段类别 ID 加四个坐标、坐标是否都在 0 到 1 之间。还有一类问题是 COM 类别 ID 跳号比如 free 和 occupied 中间夹了一个你没注意到的 car导致 category_id 错位这类错误自检脚本能靠“非法类别ID”抓出来。把自检做成习惯后面每加一批新数据都跑一遍比训练到一半再看 loss 曲线猜问题要快得多。4. 用 YOLOv8 把数据集跑起来训练配置、损失函数与关键超参4.1 一条命令启动训练YOLOv8 最小训练写法数据集准备好之后训练阶段反而简单。YOLOv8 的命令行接口封装得比较完整一条命令就能跑通yolo train \ data/home/user/dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz1024 \ batch8 \ patience20 \ projectparking_lot \ nameexp_v1参数拆开看modelyolov8n.pt是 nano 版预训练权重车位识别目标不算复杂nano 或 small 足够imgsz1024是输入分辨率高位相机画面里一个车位大概占整图的 5% 到 15%用默认 640 容易丢失细节batch8取决于显存如果 8G 显存跑 1024 分辨率就先从 batch 4 开始patience20表示验证集指标连续 20 个 epoch 不涨就早停省时间。训练启动后不要干等打开日志看损失变化。YOLOv8 输出的损失有三项box_loss是边框回归损失YOLOv8 用 CIoU 计算理想情况下从 1.5 降到 0.1 左右cls_loss是分类损失车位只有两个类下降速度应该很快dfl_loss是分布聚焦损失负责边框精度的精细化这项降得慢是正常的。如果cls_loss一直不降就要回去检查类别标注是不是有大量错标。4.2 必调的四个超参分辨率、批次、早停与数据增强第一个是imgsz。俯视场景里车位尺度普遍偏小我的经验是 1024 起步车位特别密或摄像头特别高就上 1280。但分辨率翻倍显存占用接近翻四倍batch 必须同步下调这是最典型的取舍。第二个是batch。能用大 batch 就大一点但不要为了大 batch 把分辨率调小码题优先保持分辨率。显存不够时先减 batch再不行换小模型。第三个是patience。车位场景容易出现过拟合前的假高涨patience 设 20 左右比较稳。如果验证集指标在 80 到 100 epoch 还在持续涨说明数据量偏大或多样性够把 epochs 加到 200 也不亏。第四个是数据增强。YOLOv8 默认开了mosaic和fliplr对俯视车位场景这两个默认值经常帮倒忙。mosaic 会把四个角度的车位拼在一起训练出的模型在真实单一大画面上泛化一般fliplr 水平翻转对左右对称的车位没问题但如果你的停车场有单侧灯光、单侧墙面阴影翻转后会制造不存在的镜像特征。我一般这样设置yolo train \ data/home/user/dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz1024 \ batch8 \ mosaic0.2 \ fliplr0.0逻辑说明mosaic0.2保留少量拼接增强提高模型对车位局部特征的鲁棒性但不会让它过度依赖拼接纹理fliplr0.0直接关掉水平翻转避免光照方向的镜像特征干扰。如果你的数据集中日夜混合建议再加hsv_h0.02、hsv_s0.5、hsv_v0.4做轻微颜色扰动模拟不同时段的光线差异。4.3 训练日志怎么读从 loss 曲线到 mAP 指标对照训练完成后重点看验证集三组指标mAP50、mAP50-95和每类的 precision/recall。车位识别业务里mAP50比mAP50-95更贴合实际因为业务只需要知道车位大概位置不需要像素级精准通常mAP50到 0.85 以上模型就能上线验证了。但指标好看不代表现场能用。我遇到过mAP50到 0.9实际视频里空车位依然被误判成占用的情况。原因在于 mAP 是全局指标它把一个高置信度的正确检测和一个低置信度的错误检测混在一起算而业务关心的是每个车位的状态判断是否准确。所以训练完不要只看 mAP要单独看每类的混淆矩阵。YOLOv8 训练结束后会在runs/parking_lot/exp_v1/confusion_matrix.png生成混淆矩阵重点看 occupied 和 free 两类之间的误判比例。如果 free 被识别成 occupied 的比例超过 10%说明空位样本不足或特征不明显回到第 2 章的标注规范去核对。5. 停车位识别训练避坑五个典型问题与排查顺序5.1 空车位被识别成占用样本不均衡与后处理消抖现象模型在验证集 mAP 不低但录一段现场视频回放空车位经常闪红free 类召回率明显偏低。原因停车高峰时段采集的数据里占用车位数量远大于空位模型学到的先验是“这里大概率有车”。样本不均衡导致 free 类特征学习不足。解决先做数据重采样把 free 和 occupied 两类样本比例调整到接近 1:1空位少的时段单独补拍训练参数里加class_weights给 free 类提高损失权重。推理端再加一道滞后判定——连续两帧或三帧都是同一种状态才翻转车位状态这一招能消除单帧误检造成的闪烁。5.2 loss 正常下降但 mAP 卡死优先检查标签文件现象训练日志很漂亮box_loss 和 cls_loss 都在掉但 val 集 mAP50 一直在 0.3 到 0.5 之间匍匐。原因十次里有八次是标签文件出了问题。常见情况是 txt 里有坐标大于 1 或小于 0个别类别 ID 和 data.yaml 的 names 顺序对不上还有少量标签文件名和图片名不配对模型把 A 图的框当成了 B 图的目标去学。解决跑一遍 3.3 节的自检脚本重点检查越界和类别 ID。自检通过后还没解决把转换脚本的categories_map和 data.yaml 的 names 逐行打印出来人工核对这俩错位一套就这么错位一天。5.3 边缘车位反复漏检截断框标注标准不一致现象画面中央的车位检测得好越靠近画面边缘漏检率越高有时候整个边缘车位消失。原因标注阶段大家对“被图像边缘截断的车位”处理标准不统一有人把可见部分标完有人凭记忆补全被截断的部分。模型看到了同一位置两种完全相反的标注风格直接混乱。解决统一规则为“只标注可见部分截断处贴画面边界”一次把标准写在标注文档里。训练层面可以把边缘区域的车位样本单独多采样几轮或者训练时用更大的 imgsz让边缘车位在特征图上的占比不至于太小。5.4 白天能检、傍晚漏检光照域差与数据增强不足现象白天测试可靠傍晚和夜间召回率下降明显雨天地面反光时 free 类误检增多。原因数据集采集时间集中在上午光照分布太窄。傍晚低照度、地面积水反光、车灯直射这几种工况完全没覆盖到。解决分时段补数据是最直接的方案早中晚至少各采一批如果补拍条件受限训练时开启颜色扰动增强模拟不同光照。注意积水反光不能用强高斯模糊去模拟那会让车位线纹理丢失反而更糟糕。地面有积水时加一层轻微的色相偏移更接近真实效果。5.5 训练中途显存溢出参数配比与断点恢复现象训练跑到第 30 个 epoch 报torch.cuda.OutOfMemoryError直接中断。原因imgsz 设了 1280batch 还开着 8显存吃满了。YOLOv8 会在开始时报显存不足但有时前面能跑后面崩是因为中间 checkpoint 或验证阶段额外占用了内存。解决训练命令加--auto-batch或者手动把 batch 降到 4 或 2中断后用resumeTrue接着跑不需要从头开始。我习惯把project和name参数显式写好因为 resume 要靠这两个路径定位权重文件默认名很容易找错。6. 把模型接到现场导出 ONNX、置信度扫描与视频回放验证6.1 导出 ONNX 并在 OpenCV 上做推理训练完成先把 best.pt 导出成 ONNX部署端就不需要再装 PyTorch 全家桶了yolo export modelruns/parking_lot/exp_v1/weights/best.pt formatonnx opset12导出后用 onnxruntime 在 CPU 或 GPU 上跑推理代码很短import cv2 import numpy as np import onnxruntime as ort sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) img cv2.imread(test.jpg) resized cv2.resize(img, (1024, 1024)) blob resized.transpose(2, 0, 1)[None].astype(np.float32) / 255.0 outputs sess.run(None, {sess.get_inputs()[0].name: blob})[0] # outputs 形状: [1, 84, 8400]84 4个框坐标 80类 2个车位类注意outputs的通道数是 4 类别数车位识别两个类就是 84 维最后一个维度 8400 是所有尺度特征图锚点数量的总和具体数字随 imgsz 变化不要硬编码。解析框坐标时输出是 xywh 格式且基于 1024 分辨率要缩放回原图尺寸再画框。6.2 现场验证的三个实用技巧ONNX 推理跑通后真正决定能不能上线的是现场验证这里分享三个技巧。第一个是置信度阈值扫描。不要拍脑袋把 conf 阈值设为 0.5拿一段标注好的验证视频分别用 0.3、0.4、0.5、0.6 跑一遍统计每个阈值下的误检数和漏检数找误检和漏检的平衡点。大部分停车场场景最终阈值会落在 0.35 到 0.45 之间超过 0.5 的不是好模型是没调好数据。第二个是视频回放消抖验证。模型单帧检测会出现状态跳变——一个车位这一帧显示占用下一帧显示空闲。把检测结果叠加到视频上按 2 倍速回放目光盯着车位状态文字是否闪烁。闪烁严重的车位单独拉出来看多半是标注阶段没标好边缘截断框或遮挡框回到第 2 章补标即可。第三个是画注意力可视化图。YOLOv8 的explain模式能生成检测结果的热力图用它在现场画面上看模型到底关注哪里。如果 hot spot 集中在车位线上而不是车辆本体上说明模型学偏了需要重新检查标注是否贴近内框边缘。我之前有一次模型 mAP 很高上线后疯狂误检查了三天发现是标注时有一部分图片用了旧版规范框贴的是外线。那时我才意识到数据集规范不统一比模型选型错误更致命。现在每批数据进去之前都会先抽 20 张图人工复查一遍标注再跑自检脚本。希望这些避坑经验能帮你把“停车位识别数据集 Yolo版”这条路走得顺一点。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?