ARTICLE DETAIL

资讯详情

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

手把手教你构建停车位识别YOLO数据集:标注、转换与训练实战

手把手教你构建停车位识别YOLO数据集:标注、转换与训练实战 简介面向停车场车位检测与深度学习目标检测任务这份Yolo版PKLot停车位识别数据集源自经典PKLot数据库包含从监控摄像机帧中提取的12,416张停车场图像覆盖晴天、阴天和雨天等多种光照条件每一停车位均已标注为有人或空着。资源共2000个文件其中1994张jpg图片构成主体3个txt与3个json文件分别承担标注格式与类别/配置信息的补充整体压缩包约842.49MB适合直接用于YOLO系列模型的训练与评估。目前已吸引677人学习下载附带的源码和训练好的车位占用模型可帮助初学者快速上手免去标注与训练的时间成本进阶研究者也可基于PKLot原始论文的引用规范将本数据集用于车位状态识别、智慧停车等场景的实验验证。 停车位识别一直是智慧停车、自动驾驶、安防监控里绕不开的场景。很多人上来就问“用YOLO做停车位检测用哪个模型”但真正动手之后才发现模型反而是最简单的一环最难的是数据停车位怎么标、正负样本怎么平衡、不同视角和光照怎么覆盖。这篇文章我直接把我整理“Parking Lot停车位识别数据集Yolo版”的完整过程拆开讲从数据来源、标注格式转换、训练配置到踩坑记录一次性说清楚。如果你正准备用YOLO做停车位检测或者手里有一批自己的停车位图片不知道怎么转成YOLO格式这篇文章应该能帮你省不少时间。1. 项目思路与数据选型1.1 为什么停车位识别值得单独做一套YOLO数据集停车位检测和通用目标检测不太一样。通用检测关注的是“物体是什么”而停车位检测更关注“这个区域能不能停车、现在有没有被占用”本质上是一个“区域状态判断”问题。这也是为什么很多团队不直接拿COCO数据集去硬套而是单独构建停车位数据集的原因。另外停车位在图像里的形态很固定——一般是矩形或平行四边形颜色以白色、黄色线条为主背景是路面、地砖或水泥地。这种目标恰好是YOLO擅长的中规中矩的检测任务不需要太多trick就能取得不错的精度。但前提是数据集要贴合真实场景否则模型换个停车场就废掉。我建议把停车位检测拆成两个子任务来考虑停车位框检测检测出画面里每个停车位的边界框。停车位状态分类判断每个框内是否已有车辆占用。这两个任务数据标注方式不同。前者只需要画框后者需要标“空”和“占”两类甚至还可以加“障碍物”类别。我这套数据集在整理时就同时考虑了两个场景方便后面做扩展。1.2 数据集来源选型公开数据与自采数据做数据集无非两条路用公开数据集或自己采集。我两条路都走过各有优劣。先说公开数据。停车位识别领域最经典的是CNRPark-EXT和PKLot这两个数据集。PKLot有超过12万张图片覆盖晴天、阴天、雨天多种天气状况分辨率不高但场景真实适合做预训练。CNRPark-EXT的特点是图像从高处俯拍视角接近真实监控包含大量连续帧适合测试模型的时序稳定性。不过公开数据集有个明显问题国内停车场的划线标准、车位尺寸、车道布局和国外差异很大直接用公开数据训练出来的模型搬到国内地下车库或园区停车场时掉点比较明显。所以我更推荐自采数据公开数据混合的方案。自采时注意几个要点覆盖不同时间段早晨逆光、中午强光、夜间低照度。覆盖不同天气雨天路面反光对检测影响很大。覆盖不同拍摄角度最好有俯拍、侧拍、斜拍的混合样本。如果你手头没有条件实地采集也可以在合规的前提下从公开的街景数据、监控模拟场景中截取停车位画面作为补充。关键是控制类别均衡不要让“空位”比“占位”多出好几倍。1.3 标注规范与类别设计标注是数据集质量的核心。YOLO对标注的要求很直接每个目标对应一行文本内容是“类别ID 中心点x 中心点y 宽度 高度”所有值都归一化到0到1之间。我在设计这套数据集的类别时没有一开始就分“空”“占”“障碍物”三类而是先只做单类——统一标“停车位”。原因是单类标注的标注一致性更容易保证模型先学“哪里是停车位”后期再通过分类头去判断空/占准确率反而更高。如果你第一次做不要贪多先把单类检测做扎实。标注工具方面我推荐LabelImg或X-AnyLabeling。前者轻量简单适合批量画框后者支持YOLO格式直接导出内置的自动标注模型能辅助加速。注意无论用什么工具标注完成后一定要做“文件级校验”。最简单的方式是写一个脚本检查每张图片对应的txt文件是否存在、坐标是否越界、类别ID是否超出类别数。这一步能拦截80%以上的低级错误。2. 数据预处理与YOLO格式转换2.1 图片整理与样本清理数据采集回来之后第一步不是标注而是清理。原始图片往往包含大量重复帧、模糊帧和无关帧。比如监控视频按帧导出时相邻帧画面几乎一样如果不做去重直接全量标注训练出来的模型会对重复场景过拟合泛化能力反而差。我常用的去重方式是计算图像的感知哈希perceptual hash把相似度超过95%的帧只保留一张。这一步用OpenCV几行代码就能搞定import cv2 import numpy as np def dhash(image, hash_size8): gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) resized cv2.resize(gray, (hash_size 1, hash_size)) diff resized[:, 1:] resized[:, :-1] return sum([2 ** i for i, v in enumerate(diff.flatten()) if v]) def hamming(h1, h2): return bin(h1 ^ h2).count(1)两两计算汉明距离距离小于10的就视为重复帧删除其中一张。这样操作之后我通常能去掉30%以上的冗余数据标注压力也小了很多。清理完重复帧还要过滤两类图片一类是完全没有停车位的空背景图一类是车位线被严重遮挡例如被大车完全挡住的图。前者对检测没有帮助后者会让模型学到错误的特征。保留少量难例没问题但比例不要超过总样本的10%。2.2 标签格式换算原理YOLO标签格式看起来简单但很多人在转换时翻车根本原因是没搞懂坐标换算原理。VOC格式xml保存的是绝对坐标xmin、ymin、xmax、ymax分别表示左上角和右下角的像素坐标。YOLO格式需要的是归一化后的中心点坐标和宽高。公式如下x_center ((xmin xmax) / 2) / image_width y_center ((ymin ymax) / 2) / image_height box_width (xmax - xmin) / image_width box_height (ymax - ymin) / image_height这组公式的关键在于所有值都除以了图片宽高所以YOLO标签里永远不会出现大于1的坐标值。如果你在txt文件里看到某个值大于1基本可以断定是转换脚本写错了。COCO格式json的换算也类似只是COCO的bbox定义是“左上角x、左上角y、宽度、高度”转换时注意别把宽高和右下角坐标搞混。2.3 批量转换脚本实现我一般会把VOC和COCO统一转成YOLO格式方便后续用Ultralytics YOLO直接训练。核心脚本大致长这样import os import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_path, img_path, out_txt_path, class_names): tree ET.parse(xml_path) root tree.getroot() img_w, img_h Image.open(img_path).size lines [] for obj in root.findall(object): class_name obj.find(name).text if class_name not in class_names: continue class_id class_names.index(class_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) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))转换完成后随机抽10%的图片把标签画回图片上人工目检。这一步虽然土但比任何自动化校验都有效。画框脚本用OpenCV的rectangle方法就行注意YOLO输出的是归一化坐标要先乘回图片宽高再画。实操心得批量转换不是一次性的事。我在实际项目中每新增一批标注数据就会重新跑一遍转换抽检流程。宁可多花十分钟校验也不要等训练了十几个小时之后才发现标签错位。3. 模型训练实战3.1 数据集目录与配置文件Ultralytics YOLO对数据集目录结构有严格要求训练前必须把图片和标签按固定目录放好parking_lot/ images/ train/ img001.jpg val/ img010.jpg labels/ train/ img001.txt val/ img010.txt配套的data.yaml文件长这样path: /your/absolute/path/parking_lot train: images/train val: images/val nc: 1 names: [parking_space]这里要特别提醒path字段建议写绝对路径。一旦写成相对路径在命令行指定workdir参数时出了任何偏差训练器就会报“image not found”排查起来特别费劲。3.2 训练命令与参数调整完成目录和配置文件后直接跑训练命令即可。以YOLOv8s为例yolo detect train dataparking_lot.yaml modelyolov8s.pt epochs200 imgsz640 batch8我推荐的起步配置是yolov8s不是n。n参数量小、训练快但停车位线在图像里属于细长目标太轻量的模型容易丢掉细节。s在精度和速度之间比较平衡适合绝大多数停车场场景。参数方面几个关键点imgsz取640停车位框一般不会太小640足够除非你的图片里有很多远距离车位再考虑1024。batch按显存调8GB显存跑yolov8s大约支持batch8到16之间。batch太小会导致BN层统计不稳定。epochs至少100停车位目标简单收敛快但100轮以下往往还没有充分发挥模型潜力。patience设20到30早停策略开启避免无效训练时间。3.3 硬件适配AMD显卡与CPU训练很多人在硬件这一步卡住。特别是用AMD RX 580这类老显卡总担心跑不起来YOLO。我的经验是RX 580装不了CUDA但可以通过ROCm在Linux环境下跑YOLO前提是安装好ROCm版本的PyTorch。如果你不想折腾ROCm最省事的方案是直接用CPU训练。停车位数据集本身不大几千张图用CPU训练yolov8s每个epoch可能在10到20分钟一次训练几十个小时也能跑完完全在可接受范围内。前提是数据量别太夸张。如果你手头有几万张图建议先在GPU上用小batch快速跑通再用CPU加上分布式训练或换更轻量的模型。注意如果选CPU训练记得把线程数调大。在代码开头加一句torch.set_num_threads(16)能明显缩短单epoch时间。4. 评估方法与效果调优4.1 训练日志与mAP指标解读训练结束后YOLO会输出一组验证集指标核心关注mAP50和mAP50-95。对于停车位这种目标形态固定、类别单一的任务mAP50应该能轻松达到95以上才算正常。如果mAP50一直在90以下先别急着换模型回看标签和数据才是正路。我踩过最深的坑是mAP50很高、但实际推视频时漏检一堆框。原因在于验证集存在“标签泄漏”——训练和验证集图片来自同一段监控视频连续帧高度相似模型其实就是把验证集背了下来。解决办法是把来自同一段视频的图片按时间顺序分批前段进train、后段进val绝不能随机分割。另外类别分布也值得关注。如果空位样本占80%模型训练完之后会倾向于把所有相似区域都预测成“空”导致漏检占位场景。建议在标注阶段做好平衡或者用loss权重缓解。4.2 停车位场景的常见调优手段如果指标已经不错但实际场景中仍然有漏检可以从三个方向调一是数据增强。YOLO自带的mosaic、random_flip等增强对停车位场景已经比较友好不需要额外修改。有一点值得注意垂直翻转对俯拍监控图像通常影响不大但对于带道路立体感的斜拍图像翻转后语义可能改变建议关掉flipud参数。二是置信度阈值调整。停车位线条在光照差时会变得模糊模型预测的置信度整体偏低。推理时把conf从默认0.25降到0.15能有效减少漏检代价是会产生少量误检需要实际试一下。三是检测框过滤。很多场景下一个停车位会被预测出重叠的多个框。此时可以调高NMS的IoU阈值从0.45调至0.5保留置信度最高且几何位置最合理的框。这对窄长形状的停车位尤其重要默认参数容易把长框切成多个小框。5. 常见问题与排查技巧实录5.1 标注与转换环节的坑标注阶段最常见的问题是画框不贴边。YOLO对边界框的精度要求很高车位线是细长目标如果你画框时留了太多背景模型学到的特征是“路面线”而不仅仅是“线”导致推理时把有路缘石的地方也误判成停车位。我的经验是画框尽量贴合车位线内侧宁可紧一点也不要松。转换环节还有个高频坑图片有EXIF旋转信息时PIL直接读图会自动旋转但OpenCV的imread不会。如果你混用了两种读图方式图片方向和标签坐标就会错位。解决办法是统一用同一套库读取并且在转换时打印任意一张图片的尺寸确认expected与shape一致再批量跑。5.2 训练过程中的典型问题训练loss一开始就不下降十有八九是标签文件指向了错误的图片。检查train和val的txt文件用脚本对比每个txt的行数与图片中实际目标的数量是否一致。另一种常见情况是训练正常但loss曲线震荡剧烈。停车位目标大小相近、背景简单理论上不应该震荡。如果震荡明显先检查batch大小显存不够时batch设成2或4会导致BN统计不稳定调回8以上通常能缓解。还有人在验证集上AP很高但导出ONNX后在手机上跑结果掉点严重。这个问题一般出在输入尺寸上。训练时imgsz640推理时即使图像尺寸远大于640模型也会先resize到640再推理。但ONNX导出的动态输入尺寸如果设置不当推理时可能不走640路径导致结果不一致。建议导出时固定尺寸yolo export modelbest.pt formatonnx imgsz640 opset12 simplifyTrue5.3 部署阶段的注意事项检测模型跑通之后接业务逻辑时最容易忽略的问题是怎么定义“空/占”。很多团队的做法是检测到停车位框之后再对框内区域做二分类空/占而不是让YOLO直接输出状态。这种两段式方案比端到端多分类更稳定因为停车位检测和车辆占用判断的干扰因素不同拆开之后各自优化维护成本也低。在部署到边缘设备时我建议把推理逻辑封装成两个独立的Python类一个负责检测停车位一个负责判断状态。这样即使后续换了轻量模型或升级了检测框架业务层代码也不用动。最后说一点我个人心得。停车位识别项目看起来像个玩具级任务但真正落地时对数据质量的要求一点不比复杂目标检测低。我在做这个数据集的过程中最深的体会是“把数据做干净比反复换模型更有效”。YOLO系列模型已经很成熟了能在低成本硬件上取得不错的效果但它的效果上限是由数据质量决定的。如果你也要做类似的项目花70%的时间在数据处理上剩下的30%给训练和调参出结果的速度反而更快。后面如果你们在做停车场相关项目时遇到具体问题欢迎一起交流。我自己也还在持续扩充这个数据集下一步计划加上夜间红外场景和雨天后视镜反光的hard example让模型在极端条件下更能扛。本文还有配套的精品资源点击获取
返回列表