E_bicycle2数据集:专治电动车乱停识别落地难
发布时间:2026/10/1 17:55:36来源:尧图网络
简介本资源是面向机器视觉算法工程师与智能交通项目开发者的YOLOv5专用非机动车违规停放识别数据集聚焦电动车细粒度分类与场景化检测任务。资源包含E_bicycle2子类共994张高质量JPG图像及配套PASCAL VOC格式XML标注文件共1976个文件总容量160.58MB标注覆盖绿源、台铃、小刀、雅迪、共享电动车等主流品牌支持模型训练、验证与部署全流程。已有211人学习下载适用于城市治理中非机动车违停AI巡检系统开发、YOLOv5模型迁移学习与多类别目标检测精度优化等实战场景。数据已按车型严格归类标注规范统一可直接用于训练脚本加载预览显示共享电动车样本丰富且角度多样具备良好泛化基础显著降低数据清洗与标注校验成本。1. 这不是“又一个YOLOv5数据集”它专治城市治理中电动车乱停的“视觉盲区”你见过城管队员在早高峰绕着地铁口拍了27张照片最后靠人工数出34辆共享电动车压在消防通道上吗这不是段子是真实巡检日志。而这份E_bicycle2_images_xmls数据集就是为这种场景量身打磨的——它不讲通用目标检测只聚焦「非机动车违规停放」这个具体业务断点车轮是否越过白线、车身是否斜跨人行道、是否紧贴商铺卷帘门、是否堵塞单元门入口。994张共享电动车实拍图对应PASCAL VOC格式XML标注全部来自一线城管巡查和社区监控截图光照变化大、遮挡多、角度刁钻连车筐里塞着的快递袋、后视镜挂的雨衣都标得清清楚楚。它不是学术玩具而是能直接喂进YOLOv5训练管道、跑出可落地上线模型的生产级燃料。如果你正被「识别率还行但一到真实路口就漏检」卡住或者反复调参却总在「压线判定」上翻车这份资源就是你缺的那块拼图——它把“电动车”从通用类别里拎出来用真实违规形态重新定义边界。2. 从XML标注到YOLOv5训练四步打通数据链路2.1 理解E_bicycle2的标注逻辑为什么VOC格式在这里是刚需这份数据集采用PASCAL VOC标准XML结构而非COCO或YOLO原生txt。这不是历史包袱而是业务倒逼的设计选择VOC的bndbox标签天然支持像素级坐标xmin, ymin, xmax, ymax这对判断“是否压线”至关重要——你需要精确计算车轮中心点到禁停线的距离而不是笼统的框中心。XML中每个object包含name固定为e_bicycle、pose记录拍摄角度用于后续数据增强策略、truncated标识是否被遮挡影响loss权重和difficult标记极难样本训练时可设为0跳过。特别注意所有XML文件名与对应JPG严格同名如共享电动车_777.jpg→共享电动车_777.xml且路径层级必须保持images/和labels/分离。我曾因把XML直接丢进images/目录导致train.py报错No labels found查了3小时才发现是路径解析逻辑硬编码了labels/前缀。2.2 VOC转YOLO格式用Python脚本批量生成txt标签YOLOv5要求每张图配一个同名.txt文件每行格式为class_id center_x center_y width height归一化到0~1。手动转换994次不存在的。以下脚本已实测兼容Windows/Linux/Mac自动处理中文路径和空格文件名# voc2yolo.py import os import xml.etree.ElementTree as ET from pathlib import Path def convert_voc_to_yolo(voc_dir, yolo_labels_dir, class_names[e_bicycle]): 将VOC XML批量转为YOLO格式txt # 创建输出目录 Path(yolo_labels_dir).mkdir(parentsTrue, exist_okTrue) # 遍历所有XML文件 for xml_file in Path(voc_dir).glob(*.xml): tree ET.parse(xml_file) root tree.getroot() # 获取图像尺寸关键YOLO需要归一化 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) # 输出txt路径与XML同名扩展名改为.txt txt_path Path(yolo_labels_dir) / f{xml_file.stem}.txt with open(txt_path, w) as f: for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in class_names: continue # 跳过非目标类别 # 获取bbox坐标 bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # YOLO格式归一化中心点宽高 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 写入txtclass_id0因只有e_bicycle一类 f.write(f0 {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n) if __name__ __main__: # 修改为你自己的路径注意路径中不要有中文空格 VOC_DIR r./E_bicycle2_images_xmls/annotations # XML所在目录 YOLO_LABELS_DIR r./E_bicycle2_images_xmls/labels # 输出txt目录 convert_voc_to_yolo(VOC_DIR, YOLO_LABELS_DIR) print(✅ VOC转YOLO完成共生成, len(list(Path(YOLO_LABELS_DIR).glob(*.txt))), 个标签文件)提示运行前务必检查VOC_DIR路径是否指向XML文件夹不是图片夹且YOLO_LABELS_DIR为空目录。脚本会自动创建目录但若已有同名txt文件会被覆盖。归一化计算必须用原始图像尺寸切勿用resize后的尺寸——这是新手最常踩的坑。2.3 构建YOLOv5训练目录结构避开官方文档没说的硬性约定YOLOv5的train.py对目录结构极其敏感稍错即报AssertionError: No images found。正确结构长这样以E_bicycle2为例E_bicycle2/ ├── images/ │ ├── train/ # 训练图建议700张 │ ├── val/ # 验证图建议200张 │ └── test/ # 测试图建议94张刚好994-700-200 ├── labels/ │ ├── train/ # 对应train/的txt标签 │ ├── val/ # 对应val/的txt标签 │ └── test/ # 对应test/的txt标签 └── data.yaml # 数据配置文件必须data.yaml内容必须严格如下注意缩进和冒号空格train: ../E_bicycle2/images/train val: ../E_bicycle2/images/val test: ../E_bicycle2/images/test nc: 1 # 类别数只有e_bicycle names: [e_bicycle] # 类别名顺序必须与class_id一致注意train/val/test路径是相对于data.yaml所在位置的相对路径。若你把data.yaml放在E_bicycle2/下则路径写../E_bicycle2/images/train若放在YOLOv5根目录下则写E_bicycle2/images/train。路径错误会导致训练时找不到图片报错信息极隐蔽只显示No images found不提示具体路径。2.4 启动训练用最小配置跑通第一轮验证别一上来就调超参数。先用YOLOv5s轻量版跑通基础流程验证数据链路是否通畅# 在YOLOv5根目录下执行确保已安装torch/torchvision python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data ./E_bicycle2/data.yaml \ --cfg ./models/yolov5s.yaml \ --weights \ --name e_bicycle2_exp1 \ --cache关键参数说明--img 640输入分辨率640是YOLOv5s默认过高会OOM显存不足--batch 16批大小根据GPU显存调整RTX3090可到32GTX1060建议8--weights 空字符串表示从头训练不加载预训练权重避免类别不匹配--cache启用内存缓存加速数据加载首次运行会慢后续快训练启动后实时查看runs/train/e_bicycle2_exp1/results.png中的Precision/Recall曲线。若10轮后mAP0.5仍低于0.3大概率是数据路径或标签格式问题——此时别调学习率先回溯2.2和2.3步。3. 非机动车违规停放的四大业务边界如何让模型懂“压线”而不是“画框”3.1 为什么通用YOLOv5检测结果不能直接用于执法——业务逻辑层的鸿沟YOLOv5输出的是[x,y,w,h]矩形框但城管系统需要的是“是否违规”的布尔值。这中间隔着三道坎空间关系判定框的底部y坐标是否低于禁停线像素位置朝向一致性车头方向是否与禁停线垂直斜停更易堵塞多车关联同一区域连续出现3辆以上e_bicycle是否构成“聚集停放”这份数据集的XML里埋了线索pose字段记录了Unspecified/Left/Right/Front/Rear正是为朝向判定准备的。但YOLOv5原生不输出朝向需在后处理中加逻辑——例如用OpenCV计算车轮连线与图像底边夹角再映射到pose标签。3.2 基于YOLOv5输出的违规判定代码模板import cv2 import numpy as np def is_violation(box, img_shape, stop_line_y420): 判定单个检测框是否违规停放 box: [x1,y1,x2,y2] 归一化坐标需乘img_shape还原 img_shape: (h,w,c) 图像尺寸 stop_line_y: 禁停线在图像中的y坐标像素值需现场标定 h, w img_shape[:2] x1, y1, x2, y2 [int(x * dim) for x, dim in zip(box, [w,h,w,h])] # 规则1车轮是否压线取框底部中点y坐标 bottom_y y2 if bottom_y stop_line_y: return True, 压线 # 规则2是否斜停计算框长宽比2.5视为斜停 width x2 - x1 height y2 - y1 if width / max(height, 1) 2.5: return True, 斜停 # 规则3是否紧贴障碍物距图像左/右/下边缘20像素 if x1 20 or x2 w-20 or y2 h-20: return True, 贴边 return False, 合规 # 使用示例接YOLOv5 detect.py输出 results model(img) # results.xyxy[0] 是tensor [[x1,y1,x2,y2,conf,cls]] for det in results.xyxy[0].cpu().numpy(): if det[5] 0: # 只处理e_bicycle类别 is_vio, reason is_violation(det[:4], img.shape) if is_vio: print(f⚠️ 检测到违规{reason}置信度{det[4]:.2f})血泪经验stop_line_y绝不能凭感觉设必须用标定板在真实场景拍摄用OpenCV手动标出禁停线像素位置。我曾用手机拍的图设stop_line_y300结果模型在监控画面里全判错——因为监控镜头有畸变实际线位置偏移到y480。3.3 多车聚集判定用DBSCAN聚类替代简单计数单纯统计“10米内3辆车”会误判并排停放的合规车辆。更鲁棒的做法是用空间聚类from sklearn.cluster import DBSCAN import numpy as np def cluster_violations(boxes, eps150, min_samples3): 对检测框中心点聚类识别聚集停放 boxes: list of [x1,y1,x2,y2] 归一化坐标 eps: 聚类半径像素150px≈监控画面中2米距离 min_samples: 最小聚类样本数 centers [] for box in boxes: x1, y1, x2, y2 [int(x * 1280) for x in box[:4]] # 假设图像宽1280 centers.append([(x1x2)//2, (y1y2)//2]) if len(centers) min_samples: return [] X np.array(centers) clustering DBSCAN(epseps, min_samplesmin_samples).fit(X) # 返回每个聚类的中心点和车辆数 labels clustering.labels_ clusters [] for label in set(labels): if label -1: # 噪声点跳过 continue mask labels label cluster_boxes [boxes[i] for i in range(len(boxes)) if mask[i]] clusters.append({ center: X[mask].mean(axis0).astype(int).tolist(), count: mask.sum(), boxes: cluster_boxes }) return clusters # 调用示例 clusters cluster_violations(all_boxes) for c in clusters: if c[count] 3: print(f发现聚集停放{c[count]}辆中心位置({c[center][0]}, {c[center][1]}))4. 避坑指南99%的人在E_bicycle2数据集上栽过的5个具体坑4.1 现象训练时mAP0.5始终卡在0.0loss不下降原因XML中的name标签含不可见字符如BOM头或全角空格导致class_names匹配失败所有标签被过滤实际训练的是空数据集。解决用Notepad打开任意XML选“编码→转为UTF-8无BOM”再用Python脚本检查obj.find(name).text.strip()长度是否为10e_bicycle共10字符。若为11说明有隐藏空格。4.2 现象验证时val_batch0.jpg显示检测框严重偏移原因data.yaml中train/val路径写成绝对路径如D:/data/E_bicycle2/images/train而YOLOv5强制用相对路径解析导致图片读取尺寸错误坐标映射失真。解决全部改用相对路径并确认data.yaml与train.py在同一工作目录下运行。4.3 现象模型在测试集上召回率高但实际视频流中漏检严重原因训练时用了--cache但测试时未用--cache导致数据增强如Mosaic在训练/测试时不一致。YOLOv5的Mosaic增强仅在训练时启用测试时自动关闭但若cache状态不同图像预处理流水线会微调。解决测试时也加--cache参数或统一禁用删掉--cache确保前后端处理一致。4.4 现象导出ONNX模型后推理结果bbox坐标全为负数原因YOLOv5导出时未指定--dynamic参数导致ONNX模型输入尺寸固定为训练时的--img 640而实际推理图像尺寸不同如1920x1080坐标计算溢出。解决导出命令加--dynamic并确保推理时输入尺寸是640的整数倍如1280x720python export.py --weights runs/train/e_bicycle2_exp1/weights/best.pt --include onnx --dynamic4.5 现象部署到树莓派5后FPS仅1.2帧远低于宣传的5FPS原因未启用TensorRT加速且PyTorch默认使用CPU推理即使有NPU。树莓派5的RP1 NPU需通过torch.compile()或专用SDK调用。解决放弃PyTorch原生推理改用onnxruntimeonnxruntime-genai树莓派5专用后端并设置providers[CPUExecutionProvider]NPU驱动尚未开放。5. 把“识别结果”变成“执法证据”三个落地必做动作5.1 证据固化给每张违规截图打上不可篡改的时间戳和GPS水印城管系统要求证据链完整单纯检测框不够。必须在保存截图时嵌入设备时间、经纬度来自树莓派5的GPS模块和数字签名from PIL import Image, ImageDraw, ImageFont import datetime import hashlib def add_evidence_watermark(img_path, gps_coord(31.2304, 121.4737), device_idRPi5-001): 为违规截图添加执法水印 img Image.open(img_path) draw ImageDraw.Draw(img) # 时间戳精确到秒 now datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) # GPS坐标保留6位小数 lat, lon gps_coord gps_str fGPS:{lat:.6f},{lon:.6f} # 设备ID 数字签名防篡改 signature hashlib.md5(f{now}{gps_str}{device_id}.encode()).hexdigest()[:8] # 字体需提前下载simhei.ttf到项目目录 try: font ImageFont.truetype(simhei.ttf, 20) except: font ImageFont.load_default() # 绘制水印右下角 text f{now} | {gps_str} | ID:{device_id} | SIG:{signature} draw.text((img.width-400, img.height-40), text, fill(255,0,0), fontfont) # 保存带水印图 new_path img_path.replace(.jpg, _evidence.jpg) img.save(new_path) print(f✅ 证据图已生成{new_path}) # 调用示例在检测到违规后立即执行 add_evidence_watermark(violation_20240520_083215.jpg)注意simhei.ttf字体文件必须随程序分发否则Linux系统会回退到默认字体中文显示为方块。树莓派5需提前安装中文字体sudo apt install fonts-wqy-zenhei。5.2 证据分级用置信度空间规则组合生成执法等级单纯conf0.5太粗暴。我们按违规严重性分三级每级触发不同处置流程等级判定条件处置动作证据要求一级提醒conf≥0.6且is_violationTrueAPP推送车主短信单张截图时间戳二级警告conf≥0.7且cluster_count≥2自动语音播报LED屏警示连续3帧截图GPS轨迹三级执法conf≥0.8且is_violationTrue且stop_line_dist10px生成PDF处罚单上传执法平台10秒视频片段水印截图实现核心是get_violation_level()函数def get_violation_level(conf, is_vio, cluster_count, stop_line_dist): if not is_vio: return 0 # 无违规 if conf 0.8 and stop_line_dist 10: return 3 elif conf 0.7 and cluster_count 2: return 2 elif conf 0.6: return 1 else: return 0 # 在检测循环中调用 level get_violation_level(det[4], is_vio, cluster_count, stop_line_dist) if level 0: trigger_action(level, det, img)5.3 模型迭代闭环用误检样本反哺数据集上线后必然有误检如把广告牌当电动车。关键不是修代码而是建闭环将误检截图自动存入./misclassified/目录每周用脚本扫描该目录提取特征颜色直方图、纹理熵找出高频误检模式如“红底白字广告牌”人工标注为false_positive类将新样本加入训练集但class_id设为-1YOLOv5支持忽略类# auto_label_fp.py自动为误检图生成ignore标签 def create_ignore_label(img_path): 为误检图生成全图ignore标签class_id-1 img cv2.imread(img_path) h, w img.shape[:2] # 全图标注为ignoreYOLOv5中class_id-1会被loss忽略 txt_path img_path.replace(.jpg, .txt) with open(txt_path, w) as f: f.write(f-1 0.5 0.5 1.0 1.0\n) # 全图中心全覆盖 print(f 已为{img_path}生成ignore标签)从那以后我每次部署新模型都强制走一遍misclassified/清理流程——不是为了追求100%准确率而是让模型学会“什么不该识别”。真实场景里拒绝错误比追求正确更难也更重要。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网