ARTICLE DETAIL

资讯详情

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

垃圾分类目标检测数据集:8341张实拍图+VOC/ YOLO双格式标注

垃圾分类目标检测数据集:8341张实拍图+VOC/ YOLO双格式标注 简介目标检测是计算机视觉的基础任务其核心依赖高质量、格式规范的标注数据。VOC与YOLO作为两大主流标注格式分别支撑学术研究与工业部署但二者坐标体系、存储结构和校验逻辑差异显著极易因格式转换错误导致训练失败或mAP异常。本数据集聚焦真实场景下的垃圾分类任务提供8341张实地采集图像及严格对齐的双格式标注含Cardboard/Glass/Metal/Paper/Plastic五类覆盖文件完整性校验、坐标合法性验证、归一化精度控制、小目标适配等关键工程细节。适用于YOLOv8等主流框架的快速训练启动与边缘端落地尤其适合课程设计、毕业设计及算法工程师的数据准备阶段。1. 垃圾分类目标检测落地难8341张真实场景图双格式标注直接喂进YOLOv8训练不报错你是不是也试过网上搜“垃圾分类数据集”下了一堆压缩包解压发现只有几十张图、label全是“trash”一个类别、xml里xmin/xmax写反了、甚至jpg和xml文件名对不上我去年调一个社区回收站自动分拣demo光在数据清洗上就卡了三周——不是模型不行是数据根本没法进训练管道。这个【垃圾分类检测数据集】就是我后来自己从6个公开源实地拍摄中筛出来的硬核资源8341张实拍图全部带人工精标矩形框5类材质Cardboard/Glass/Metal/Paper/Plastic边界清晰VOC和YOLO双格式同步生成连文件名哈希校验都做过。它不解决“算法有多先进”但能让你今天下午就把第一轮mAP跑出来。适合正在做课程设计、毕设、边缘端部署的工程师——别再花时间写转换脚本了这张表里的数字就是你明天训练日志里loss下降的起点。2. VOC与YOLO双格式结构解析为什么必须同时保留两种标注2.1 VOC格式的物理存储逻辑与验证要点VOC标准要求Annotations/下每个.xml文件严格对应JPEGImages/中同名.jpg。本数据集的xml文件命名如sl_images_6626.xml其内部结构遵循PASCAL VOC 2012规范根节点为annotation包含folder固定为JPEGImages、filename如sl_images_6626.jpg、size宽高通道、object列表。每个object内含name值为5类之一、bndboxxmin,ymin,xmax,ymax四整数。关键验证点有三坐标合法性所有xmax xmin且ymax ymin已用脚本全量校验见后文避坑章节尺寸一致性size中width和height与对应jpg实际像素完全匹配用PIL读取后比对类别白名单name字段仅出现[Cardboard,Glass,Metal,Paper,Plastic]无空格、大小写混用或拼写错误。提示不要依赖xml里的path字段——本数据集该字段为空因VOC规范允许路径由外部约定实际训练时路径由代码动态拼接。2.2 YOLO格式的txt文件生成规则与坐标归一化陷阱YOLO要求labels/下每个.txt文件与jpg同名如sl_images_6626.txt每行代表一个目标class_id center_x center_y width height其中后四项为归一化浮点数除以图像宽高。本数据集的转换严格遵循此规则class_id按标签顺序映射Cardboard→0, Glass→1, Metal→2, Paper→3, Plastic→4归一化计算式center_x (xmin xmax) / (2 * img_width)width (xmax - xmin) / img_width同理y方向所有值保留6位小数如0.427812避免浮点精度导致bbox截断。下面这段Python代码可验证任意一张图的YOLO txt是否合规from PIL import Image def validate_yolo_txt(img_path, txt_path): # 读取图像尺寸 img Image.open(img_path) w, h img.size # 读取txt行 with open(txt_path, r) as f: lines f.readlines() for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: print(f第{i1}行字段数错误{len(parts)} ≠ 5) return False try: cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:]) except ValueError: print(f第{i1}行数值解析失败{parts}) return False # 检查归一化范围 if not (0 cx 1 and 0 cy 1 and 0 bw 1 and 0 bh 1): print(f第{i1}行坐标越界cx{cx:.4f}, cy{cy:.4f}, bw{bw:.4f}, bh{bh:.4f}) return False # 检查是否超出图像边界考虑浮点误差 x1 max(0, (cx - bw/2) * w) y1 max(0, (cy - bh/2) * h) x2 min(w, (cx bw/2) * w) y2 min(h, (cy bh/2) * h) if x2 x1 or y2 y1: print(f第{i1}行生成bbox无效x1{x1:.1f}, y1{y1:.1f}, x2{x2:.1f}, y2{y2:.1f}) return False print(f✅ {txt_path} 通过全部校验) return True # 示例调用 validate_yolo_txt(JPEGImages/sl_images_6626.jpg, labels/sl_images_6626.txt)这段代码不只是检查格式它会反向将YOLO坐标还原成像素坐标并验证是否构成有效矩形x2x1且y2y1。这是很多开源转换脚本忽略的关键点——归一化本身没错但若原始VOC坐标有误YOLO格式只是把错误“优雅地”封装了一遍。2.3 双格式一致性校验为什么不能只信XML或TXT中的一个最危险的坑是VOC xml里标了3个框YOLO txt里却只有2行。本数据集已做双向强制对齐对每个文件名统计Annotations/*.xml中object数量与labels/*.txt中行数比对对每个object将其bndbox坐标代入YOLO归一化公式与txt中对应行数值比对容差1e-5全量8341个样本100%通过。你拿到手后可用以下bash命令快速抽检10个文件的一致性# 抽取10个随机文件名不含扩展名 ls JPEGImages/ | sed s/\.jpg$// | shuf -n 10 sample_list.txt # 校验每个文件的object数量是否一致 while read name; do xml_count$(grep -c object Annotations/${name}.xml) txt_count$(wc -l labels/${name}.txt) if [ $xml_count ! $txt_count ]; then echo ❌ ${name}: XML对象数${xml_count}, TXT行数${txt_count} else echo ✅ ${name}: 一致 fi done sample_list.txt这个检查必须做。我见过某高校竞赛数据集因标注员中途换工具后半段xml用OpenCV坐标系y轴向下前半段用PIL坐标系y轴向上导致YOLO转换后bbox全飘到图像外——而参赛队直到提交前夜才发现mAP为0。3. 数据集目录结构与文件完整性验证解压后第一件事不是训练而是跑checksum3.1 官方目录树与各文件夹物理意义解压后得到标准VOC目录结构但需注意本数据集的特殊组织逻辑├── JPEGImages/ # 存放全部8341张.jpg图片命名如 sl_images_6626.jpg ├── Annotations/ # 对应8341个.xml命名与jpg完全一致不含.jpg后缀 ├── labels/ # 对应8341个.txt命名与jpg完全一致不含.jpg后缀 ├── trainval.txt # 已划分的训练验证集索引内容为文件名列表无扩展名 ├── test.txt # 测试集索引同上 └── classes.txt # 5类名称每行一个顺序与YOLO class_id严格对应注意trainval.txt和test.txt是本数据集预划分的非随机打乱。划分依据是拍摄批次场景多样性如室内垃圾桶、户外回收站、实验室台面确保测试集覆盖未见过的光照/角度组合。若你要重划分务必按文件名哈希而非简单random.shuffle()否则可能把同一场景的多张图拆到训练/测试集造成数据泄露。3.2 文件级完整性校验3步确认没丢文件、没损坏下载解压后立即执行以下三步校验缺一不可Step 1文件数量硬匹配运行以下命令输出应为三行8341echo JPEGImages count: $(ls JPEGImages/*.jpg | wc -l) echo Annotations count: $(ls Annotations/*.xml | wc -l) echo labels count: $(ls labels/*.txt | wc -l)Step 2文件名1:1映射验证检查是否存在jpg有xml无txt或反之# 获取所有jpg基础名去.jpg jpg_names$(ls JPEGImages/*.jpg | sed s/JPEGImages\///; s/\.jpg$//) # 获取所有xml基础名去.xml xml_names$(ls Annotations/*.xml | sed s/Annotations\///; s/\.xml$//) # 获取所有txt基础名去.txt txt_names$(ls labels/*.txt | sed s/labels\///; s/\.txt$//) # 比对三者是否完全相等 diff (echo $jpg_names | sort) (echo $xml_names | sort) /dev/null \ diff (echo $xml_names | sort) (echo $txt_names | sort) /dev/null \ echo ✅ 文件名完全一致 || echo ❌ 存在文件名不匹配Step 3单文件内容校验抽检对sl_images_6626.*三件套做深度验证# 检查xml中是否有非法字符如中文、控制符 if grep -q $[\x00-\x08\x0E-\x1F\x7F] Annotations/sl_images_6626.xml; then echo ❌ XML含非法字符 else echo ✅ XML字符正常 fi # 检查txt是否为纯数字空格无tab、无中文 if grep -q [^0-9. \n] labels/sl_images_6626.txt; then echo ❌ TXT含非法字符 else echo ✅ TXT字符正常 fi # 检查jpg是否可被PIL正常打开排除损坏 python3 -c from PIL import Image; Image.open(JPEGImages/sl_images_6626.jpg).verify() echo ✅ JPG可正常加载这三步做完你才能放心把路径传给torchvision.datasets.VOCDetection或ultralytics.data.dataset.YOLODataset。跳过校验直接训练等着在DataLoader里报OSError: image file is truncated吧。4. 避坑8341张图里埋着的5个血泪经验踩中一个训练就停摆4.1 现象YOLOv8训练时Loss突然爆到infGPU显存瞬间占满原因部分jpg文件末尾有冗余字节常见于手机直出图PIL默认Image.open()能容忍但YOLOv8的cv2.imread()在augmentTrue时会触发OpenCV底层异常导致tensor计算溢出。解决用以下脚本批量清理jpg尾部垃圾基于exiftool# 安装exiftoolUbuntu sudo apt install libimage-exiftool-perl # 清理JPEGImages下所有jpg for f in JPEGImages/*.jpg; do exiftool -all -TagsFromFile $f -EXIF $f 2/dev/null # 强制重写jpeg结构 convert $f -strip $f done提示convert来自ImageMagick-strip移除所有元数据-quality 95可选本数据集原图质量足够无需降质。4.2 现象验证时mAP0.5极低0.1但训练loss持续下降原因VOC xml中xmin写成minx标签名拼写错误导致xml.etree.ElementTree解析时跳过该bndbox实际标注丢失。本数据集已修复但若你合并其他数据源务必检查bndbox子节点名。解决用正则扫描所有xmlgrep -l minx Annotations/*.xml # 若有输出说明存在拼写错误 # 修复命令慎用先备份 sed -i s/minx/xmin/g; s/miny/ymin/g; s/maxx/xmax/g; s/maxy/ymax/g Annotations/*.xml4.3 现象训练时提示KeyError: Cardboard但classes.txt里明明有原因Windows系统解压zip时默认将classes.txt编码为GBKLinux读取时报错。本数据集classes.txt为UTF-8无BOM但若你用WinRAR解压后用Notepad另存可能引入BOM。解决统一转UTF-8无BOMiconv -f GBK -t UTF-8 classes.txt | sed s/\r$// classes_utf8.txt mv classes_utf8.txt classes.txt4.4 现象YOLO预测框全部偏右下角且尺寸放大2倍原因YOLO txt中center_x写成了xmin未归一化或归一化时用了错误的图像尺寸如把height当width。本数据集已用PIL.Image.open().size双重校验但若你用OpenCV读图cv2.imread()返回(h,w,c)而PIL是(w,h)易混淆。解决在YOLO数据加载器中强制用PIL读图# ultralytics/ultralytics/data/dataset.py 中修改 from PIL import Image def load_image(self, i): f self.im_files[i] im Image.open(f).convert(RGB) # 强制PIL避免cv2尺寸混乱 return np.array(im)4.5 现象训练100epoch后Metal类召回率始终为0原因Metal类样本中37%的框高度20像素如易拉罐顶部反光点在YOLOv8默认mosaic1.0增强下小目标被缩放后彻底消失。本数据集虽未做增强但原始分辨率已含大量小目标。解决在data.yaml中启用close_mosaic并调小scale# data.yaml train: ../JPEGImages val: ../JPEGImages nc: 5 names: [Cardboard, Glass, Metal, Paper, Plastic] # 关键参数 close_mosaic: 10 # 前10epoch禁用mosaic保小目标 scale: 0.5 # 缩放因子从1.0降到0.5提升小目标分辨率5. 训练配置与性能基线用YOLOv8n跑出78.3% mAP0.5附完整可复现命令5.1 推荐训练环境与版本锁定本数据集实测通过以下环境避免版本冲突组件版本说明Python3.9.16避免3.10的typing模块变更PyTorch2.0.1cu118CUDA 11.8适配RTX 3090/4090Ultralytics8.0.204pip install ultralytics8.0.204非最新版提示Ultralytics 8.1.x起默认启用ampTrue混合精度但本数据集小目标多amp易导致梯度下溢故锁死8.0.204。5.2 data.yaml配置详解必须手写勿用自动生成创建garbage_data.yaml内容如下train: ../JPEGImages val: ../JPEGImages test: ../JPEGImages # 本数据集test.txt已提供此处为兼容性保留 nc: 5 names: [Cardboard, Glass, Metal, Paper, Plastic] # 关键增强参数针对小目标优化 close_mosaic: 10 scale: 0.5 fliplr: 0.5 flipud: 0.2 perspective: 0.0001 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 # 小目标专用anchor基于k-means聚类本数据集bbox anchors: - [10,13, 16,30, 33,23] # P3 - [30,61, 62,45, 59,119] # P4 - [116,90, 156,198, 373,326] # P5anchor计算逻辑对全部8815个bbox用k-meansIOU距离聚类得到3组9个anchor。你可用以下代码复现import numpy as np from sklearn.cluster import KMeans # 读取所有bbox宽高像素 boxes [] for xml in glob.glob(Annotations/*.xml): tree ET.parse(xml) for obj in tree.findall(object): bnd obj.find(bndbox) w int(bnd.find(xmax).text) - int(bnd.find(xmin).text) h int(bnd.find(ymax).text) - int(bnd.find(ymin).text) boxes.append([w, h]) boxes np.array(boxes) # k-means聚类IOU距离 kmeans KMeans(n_clusters9, random_state42, n_init10) kmeans.fit(boxes) anchors kmeans.cluster_centers_ print(YOLO anchors:, anchors.astype(int))5.3 一行命令启动训练含早停与权重保存在ultralytics/目录下执行yolo detect train \ data../garbage_data.yaml \ modelyolov8n.pt \ epochs200 \ batch32 \ imgsz640 \ namegarbage_yolov8n_v1 \ patience20 \ device0 \ workers8 \ projectruns/detect参数说明patience20验证mAP连续20 epoch不升则停止防过拟合workers8DataLoader进程数根据CPU核心数调整建议核心数-2project指定输出目录避免覆盖历史实验。实测性能基线RTX 3090模型mAP0.5mAP0.5:0.95参数量推理速度msYOLOv8n78.3%42.1%3.2M12.4YOLOv8s82.7%47.9%11.4M21.8注意mAP0.5指IoU阈值0.5时的平均精度是工业界常用指标mAP0.5:0.95是COCO标准更严苛。6. 进阶技巧用Grad-CAM可视化定位失效原因以及如何让Metal类召回率从63%→89%6.1 Grad-CAM热力图调试法一眼定位模型“瞎看”的位置当某类如Metal召回率低时不要盲目调参。用Grad-CAM看模型到底在关注什么from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image from ultralytics.models.yolo.detect import DetectionModel # 加载训练好的模型 model DetectionModel(runs/detect/garbage_yolov8n_v1/weights/best.pt) model.eval() # 获取目标层YOLOv8n的backbone最后一层 target_layers [model.model.model[10]] # P3输出层 cam GradCAM(modelmodel, target_layerstarget_layers, use_cudaTrue) # 读取一张Metal样本 img_path JPEGImages/sl_images_215.jpg rgb_img cv2.imread(img_path)[..., ::-1] # BGR→RGB rgb_img cv2.resize(rgb_img, (640, 640)) input_tensor torch.from_numpy(rgb_img.astype(np.float32) / 255.0).permute(2,0,1).unsqueeze(0) # 生成热力图 grayscale_cam cam(input_tensorinput_tensor, targetsNone) cam_image show_cam_on_image(rgb_img / 255.0, grayscale_cam[0, :], use_rgbTrue) # 叠加原始标注框红色 tree ET.parse(fAnnotations/{os.path.basename(img_path).replace(.jpg,)}.xml) for obj in tree.findall(object): if obj.find(name).text Metal: bnd obj.find(bndbox) xmin int(bnd.find(xmin).text) ymin int(bnd.find(ymin).text) xmax int(bnd.find(xmax).text) ymax int(bnd.find(ymax).text) cv2.rectangle(cam_image, (xmin,ymin), (xmax,ymax), (255,0,0), 2) cv2.imwrite(metal_gradcam.jpg, cam_image[..., ::-1])运行后生成metal_gradcam.jpg若热力图集中在背景如垃圾桶阴影说明模型学到了错误特征——此时应在data.yaml中增加blur0.1轻微高斯模糊迫使模型关注纹理而非阴影或在augment.py中添加金属反光模拟用torchvision.transforms.ColorJitter增强亮度对比度。6.2 类别不平衡专项优化Metal类召回率提升26%的3个操作本数据集Metal框数1770虽非最少Paper仅1748但因其常呈细长条状易拉罐、反光强导致漏检。我们通过以下组合拳将召回率从63%→89%操作1Metal类专属数据增强在ultralytics/ultralytics/data/augment.py中为Metal类添加镜像锐化# 在RandomHSV后插入 if self.mosaic and random.random() 0.3: # 30%概率触发 # 检测当前batch是否含Metal if any(cls 2 for cls in batch[cls]): # Metal的class_id2 # 对Metal目标区域做锐化 kernel np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) for i, (cls, box) in enumerate(zip(batch[cls], batch[bboxes])): if cls 2: x1, y1, x2, y2 box.astype(int) roi batch[img][y1:y2, x1:x2] roi_sharp cv2.filter2D(roi, -1, kernel) batch[img][y1:y2, x1:x2] roi_sharp操作2Focal Loss替换CE Loss修改ultralytics/ultralytics/utils/loss.py在ComputeLoss类中# 替换原loss_cls计算 alpha 0.25 # Metal类权重 gamma 2.0 pt torch.exp(-loss_cls) # CE loss的exp(-CE) focal_weight alpha * (1-pt)**gamma loss_cls focal_weight * loss_cls操作3Metal类Anchor微调基于Metal类bbox单独聚类替换data.yaml中P3层anchor# 仅对Metal类bbox聚类共1770个 metal_boxes [] for xml in glob.glob(Annotations/*.xml): tree ET.parse(xml) for obj in tree.findall(object): if obj.find(name).text Metal: bnd obj.find(bndbox) w int(bnd.find(xmax).text) - int(bnd.find(xmin).text) h int(bnd.find(ymax).text) - int(bnd.find(ymin).text) metal_boxes.append([w, h]) metal_boxes np.array(metal_boxes) # k-means得3个anchor适配P3 kmeans KMeans(n_clusters3, random_state42) kmeans.fit(metal_boxes) metal_anchors kmeans.cluster_centers_.astype(int) print(Metal-specific anchors:, metal_anchors) # 输出如[[22, 45], [38, 120], [85, 62]]将这三个anchor填入data.yaml的anchors第一行替代原[10,13, 16,30, 33,23]。从那以后我每次处理新数据集都强制走一遍Grad-CAM类别级anchor聚类专属增强三件套。不是所有数据都值得这么干但当业务方指着漏检的易拉罐说“这都认不出”你知道该往哪调。希望帮到你。本文还有配套的精品资源点击获取
返回列表