资讯动态

Python+YOLO垃圾分类目标检测系统:从数据标注到部署全流程

发布时间:2026/10/17 2:52:43 来源:尧图企业网站定制
简介面向高校计算机、人工智能等专业毕业设计场景这份完整源码工程提供基于YOLOv8的垃圾分类目标检测系统实现涵盖PyQt5可视化界面、MySQL数据库及配套论文文档。系统针对传统人工分类效率低、误差大等问题采用YOLOv8模型并结合迁移学习与超参数优化在测试集上取得mAP0.5达94.2%、单帧检测约60 FPS的精度与实时性平衡可识别可回收物、厨余垃圾、有害垃圾及其他垃圾四大类别。资源共2000个文件以txt数据标注文件为主另有yaml模型配置、md说明文档及docx配套论文压缩包整体约229.28MB目录层次清晰便于按模块迁移复用。当前已有106人学习下载适合需要快速搭建智能垃圾分类演示系统、完成毕业设计或开展目标检测实验的开发者参考。1. 垃圾分类检测系统YOLO方案是不是值得毕业设计死磕的路径拿到这个题目时我心里想的是垃圾检测听起来简单真正做起来光数据干净度就能劝退一半人。基于Python的YOLO垃圾分类目标检测系统说白了就是把YOLO的检测能力套到垃圾图片上训练出一个能识别“这是什么垃圾”的模型再配上Web界面和数据库记录。它适合两类人一类是要交python毕业设计的在校同学需要一套完整可跑的流程另一类是刚接触目标检测、想拿垃圾分类当练习场景的开发者。你不需要重新发明检测算法但要会处理数据、调参数、部署接口——这套资源覆盖的正是从标注到训练再到系统集成的完整链路。跟用开源检测框架跑个demo不同毕业设计的交付物除了模型还要有后台、有记录、有可视化。这套资源的核心价值是把这三件事串起来让模型不只是跑给自己看而是能演示给导师看。2. 数据准备先行原始图片怎么变成YOLO能吃的标注格式任何目标检测任务的第一步都不是写模型而是整理数据。YOLO的原生数据格式是一张图片配一个同名txt文件每行写“类别ID、中心点x、中心点y、宽、高”全部归一化到0-1。这个格式本身不复杂但实际拿到的数据往往是VOC XML或COCO JSON手里没有一套转换工具第一天就会被格式卡住。2.1 类别粒度取舍四分类还是细分类先定类别再谈训练。垃圾分类毕设常见的做法有两种一种是按垃圾桶的四种颜色分成“可回收物”“厨余垃圾”“有害垃圾”“其他垃圾”另一种是细化到具体的瓶罐纸盒等单品。四分类看起来省事但类别太抽象同一个大类里的物体外观差异巨大模型容易欠拟合细分类的问题是每个类别能凑到多少有效样本。这套资源里采用的是四分类为主、单品为辅的方案。类别映射如下class_id类别名说明0recyclable可回收物塑料瓶、纸箱、易拉罐1kitchen_waste厨余垃圾剩饭、果皮、菜叶2hazardous有害垃圾电池、灯管、过期药品3other_waste其他垃圾污损纸张、陶瓷碎片选择这种粒度的原因很实际公开数据集中这四个方向的样本量最充足标注时也容易划定边界不至于让标注员在“这个塑料瓶到底是可回收还是其他”这种问题上纠结半天。2.2 标注转换脚本从VOC的XML到YOLO的TXT如果你手里的数据是LabelImg标注生成的VOC XML第一件事就是写一个转换脚本。核心是把XML里的绝对坐标换算成归一化的几何信息下面这段是简化版的工具逻辑import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_txt_path, class_map): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue class_id class_map[name] box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) # 计算中心点坐标与宽高再除以图片尺寸做归一化 x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))逻辑说明脚本读取XML里的图片尺寸和每个目标的bounding box按YOLO格式生成txt。class_map是字典比如{recyclable: 0, kitchen_waste: 1}建议放在外部json里统一维护免得改类别时去动代码。参数说明中心坐标写((xmin xmax) / 2)而不是xmin (xmax - xmin) / 2两者数学等价但前者少一步减法逻辑更清晰{:.6f}保留六位小数足够太多精度位没有意义。这里有个隐藏坑XML里的坐标可能超出图片边界转换脚本不会报错但训练时Loss会异常具体排查见第5章。2.3 数据增强与样本均衡让模型不会只记住背景标注完几千张图直接开训的结果往往是过拟合。垃圾图片的背景很杂桌面、草地、垃圾桶内部各种场景都有所以增强策略要同时照顾亮度变化和尺度变化。常见的做法是借助albumentations做在线增强import albumentations as A from albumentations.pytorch import ToTensorV2 transform A.Compose( [ A.HorizontalFlip(p0.5), # 水平翻转 A.RandomBrightnessContrast(p0.3), # 亮度对比度扰动 A.HueSaturationValue(hue_shift_limit10, sat_shift_limit20, p0.2), A.RandomSizedBBoxSafeCrop(size(640, 640), p0.5), # 随机裁剪并保证bbox不越界 ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels], min_visibility0.3) )这个组合不是随便拼的。HorizontalFlip对瓶罐这类对称物体有效垃圾分类场景里基本没有语义反转的顾虑。RandomSizedBBoxSafeCrop是重点它随机裁剪局部区域再缩放到640x640让模型看到不同尺度的垃圾同时会保护标注框不落在画面外。参数说明min_visibility0.3表示增强后只剩30%以下的框会被丢弃。这个值设太大会大量丢样本设太小又起不到难度增强的效果实际跑下来0.3比较稳。增强做完还要看一眼类别分布。我训练时发现“其他垃圾”样本明显少于前三类最后的处理是给这一类别复制了部分难例而不是无脑重复全部样本一遍这样既不会破坏验证集独立性也让每个epoch里的正样本量更均衡。转换和增强都做完后建议先把标注可视化一遍再开训。用OpenCV把txt里的框画回原图只要看到个别框对不上物体就要立刻检查是不是jpg和xml配对错位。这一步看起来很土但能省掉后面好几个小时的排查时间。3. 模型训练把YOLO跑起来不是终点能收敛才是数据干净之后训练本身反而没那么玄。垃圾分类场景不需要魔改网络结构用标准的YOLO就能拿到能演示的结果重点是把环境、参数和收敛判断这三件事处理好。3.1 环境选型版本匹配与目录结构我一般会选YOLOv5作为基线因为它的文档和社区资料最全出问题容易搜到。环境安装其实只有两条命令pip install torch2.0.1 torchvision0.15.1 pip install -r requirements.txtrequirements.txt里主要是opencv-python、numpy、matplotlib、pyyaml这些。这里要留意torch和torchvision的版本必须配对跨大版本装容易在import时直接报undefined symbol错误。如果你只有CPU环境也能装但训练速度会慢到让人失去耐心。数据集目录结构按YOLO习惯组织在项目根目录建datasets/garbage/里面放images/train、images/val、labels/train、labels/val四个文件夹然后写一个data配置文件# data_rc.yaml train: datasets/garbage/images/train val: datasets/garbage/images/val nc: 4 names: [recyclable, kitchen_waste, hazardous, other_waste]逻辑说明train和val指向图片目录即可YOLO会按同名规则自动去labels/找txt。nc必须和类别数一致names的顺序要和class_map保持一致否则标签会错位。3.2 关键训练参数batch、img和epochs怎么取舍训练参数不用贪多真正影响结果的只有下面几个参数推荐值说明img640精度与速度的平衡点不建议低于512batch16或32看显存小于8则考虑缩小模型epochs100-200看loss曲线不是越大越好workers4-8数据加载线程Windows下别超过8这里有个经验显存不够时优先降batch而不是降img。img缩到480后小尺寸垃圾的检测能力会明显下滑改batch反而只是训练波动问题。另外如果batch调小学习率也要跟着调否则收敛会晃。3.3 训练命令与日志解读训练指令本身不复杂python train.py \ --data data_rc.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --project runs/train \ --name garbage_exp01 \ --cache参数说明--weights yolov5s.pt是预训练权重用小模型做迁移学习对垃圾这种类别间差异大的场景足够--cache把图片提前缓存在内存里能省掉训练过程中反复读盘的时间但数据集太大时注意内存占用。训练开始后不要傻等要看两个信号box_loss和cls_loss是否在稳步下降验证集的mAP是否还在爬升。如果loss前30轮就在0.05附近抖说明模型在死记训练集如果loss下降但mAP不涨多半是数据标注有噪声。训练中断也不用慌加上--resume runs/train/garbage_exp01/weights/last.pt就能继续。4. 系统集成从权重文件到能演示给导师看的完整系统模型训练完毕后只完成了一半。毕业设计答辩时导师不会只看你终端里的loss曲线他们要看到界面、要看到检测结果、要看到数据记录。所以这一章解决的是“怎么把权重文件变成能演示的系统”。4.1 封装一个干净的Detector推理类先把推理逻辑从训练代码里剥出来不要直接在Flask路由里写YOLO细节。常见做法是封装一个检测器类加载一次模型后面所有请求共用它# detector.py import torch import cv2 class GarbageDetector: def __init__(self, weights_path, devicecpu): self.model torch.hub.load(ultralytics/yolov5, custom, pathweights_path) self.model.conf 0.25 # 置信度阈值 self.model.iou 0.45 # NMS的IoU阈值 self.model.classes [0, 1, 2, 3] # 只保留这四个类别 def predict(self, img_path): results self.model(img_path) # 输入单张图片路径 df results.pandas().xyxy[0] # 转为DataFrame列出每个框 outputs [] for _, row in df.iterrows(): outputs.append({ class_id: int(row[class]), name: row[name], confidence: round(float(row[confidence]), 4), bbox: [int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax])] }) return outputs逻辑说明这个类把模型加载和推理封装在一起torch.hub.load(ultralytics/yolov5, custom, pathweights_path)加载你自己训练出的best.pt。predict返回一个JSON兼容的列表方便后续接口调用。参数说明conf是置信度阈值答辩演示时建议设0.25太低会飘出很多假框太高又可能漏检iou是NMS去重阈值垃圾分类场景里垃圾不会靠得特别近0.45够用。4.2 用Flask搭建图片上传检测接口后端框架选Flask因为它足够轻把模型加载和HTTP服务放一起也不会有太多心智负担# app.py from flask import Flask, request, jsonify from detector import GarbageDetector import os app Flask(__name__) detector GarbageDetector(weights/best.pt, devicecpu) app.route(/detect, methods[POST]) def detect(): file request.files.get(image) if file is None: return jsonify({code: 1, msg: no image uploaded}) save_path uploads/temp.jpg file.save(save_path) boxes detector.predict(save_path) # 这里只返回检测结果图片保存在本地供前端回显 return jsonify({ code: 0, count: len(boxes), boxes: boxes }) if __name__ __main__: app.run(host0.0.0.0, port5000, threadedFalse)逻辑说明接口接收POST上传的图片保存到临时目录调用predict得到检测框返回JSON。注意threadedFalse很重要如果Flask的多线程模式同时进来多个请求而Detector的模型推理不是线程安全的会出现串结果的问题。参数说明host0.0.0.0是为了方便答辩时同一局域网内的电脑也能访问演示页。如果需要展示检测后的图片建议在服务端画好框后再返回图片流而不是让前端去画框否则前端工作量会莫名变大。4.3 SQL脚本与入库逻辑给检测结果留个存档答辩时经常被问“数据怎么存储”所以这套资源里带着一份SQL脚本建一个检测记录表CREATE TABLE IF NOT EXISTS detection_record ( id INT AUTO_INCREMENT PRIMARY KEY, image_name VARCHAR(255) NOT NULL, detected_class VARCHAR(50) NOT NULL, confidence FLOAT NOT NULL, bbox VARCHAR(100) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明每次检测完成后把结果插入这张表后台就能按时间倒序查看识别历史。create_time用数据库默认时间避免程序端时区不一致问题。import pymysql def save_record(image_name, box, result): conn pymysql.connect( hostlocalhost, userroot, password123456, databasegarbage_db, charsetutf8mb4 ) sql INSERT INTO detection_record (image_name, detected_class, confidence, bbox) VALUES (%s, %s, %s, %s) cur conn.cursor() cur.execute(sql, (image_name, box[name], box[confidence], str(box[bbox]))) conn.commit() cur.close() conn.close()参数说明这里的插入用参数化查询而不是字符串拼接能避免SQL注入风险也是答辩时导师比较看重的细节。charsetutf8mb4必须和建表语句保持一致否则中文类别名会变成乱码。5. 避坑指南从训练到演示最常见的五个翻车点训练到部署这一路我踩的坑不少这里挑五个最典型的记录下来每条都按“现象-原因-解决”写希望能让你少走一点弯路。5.1 训练到一半loss突然变成NaN现象训练日志里box_loss和cls_loss在前20轮正常下降到第25轮突然打出nan之后所有指标全部变成无效值TensorBoard曲线直接断掉。原因最常见的是学习率偏大加上混合精度开关同时开启少数样本的梯度溢出另一种可能是个别标注框坐标越界导致loss计算出现无穷值。我第一次遇到时甚至怀疑是显卡问题换卡重装驱动折腾了一下午。解决如果是混合精度的原因在train.py里把--amp关掉或者把初始学习率从默认0.01降到0.005如果是坐标越界用2.2节的脚本全量扫描一遍txt把小于0或大于1的数值强制clamp到边界def clamp_yolo_label(line, img_w640, img_h640): parts line.strip().split() class_id parts[0] x_center min(max(float(parts[1]), 0.0), 1.0) y_center min(max(float(parts[2]), 0.0), 1.0) w min(max(float(parts[3]), 0.0), 1.0) h min(max(float(parts[4]), 0.0), 1.0) return f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}等训练稳定后再打开--amp提速不要一开始就开着省显存。5.2 CPU上跑推理一张图要好几秒现象用训练好的best.pt在本地做演示图片推理耗时3-5秒现场调接口就显得非常卡导师看完皱眉头。原因YOLOv5s虽然不大但在CPU上FP32推理仍然很慢更关键的是Flask的同步推理会阻塞进程下一张请求必须等前一张完成体验更差。解决答辩演示机若没有GPU先用torch.hub加载时指定devicecpu再开启ONNX导出加半精度FP16压缩。CPU场景下常见做法是导出成ONNX后用OpenVINO跑推理速度能提升两到三倍如果不想改架构至少做到启动时加载一次模型别在每个请求里重复加载权重。5.3 画框时目标框超出画面现象检测结果可视化后有些目标的检测框左边是负数坐标或者右下角超出图片宽度和高度画出来的框像被裁剪过一样。原因YOLO的txt坐标经过了归一化但在解码回像素坐标时没有对越界值做截断。模型预测的框可能在边缘略超出图像这在NMS后很常见。解决在后处理里对x_min,y_min,x_max,y_max做clamp同时考虑检测框太小的情况面积小于20x20像素的框直接丢弃。宁可漏一个边缘小物体也不要画出一个看起来不专业的破框。5.4 Flask的多线程导致检测结果串台现象两个浏览器标签页几乎同时上传图片返回的结果框却出现在另一张图上A图片的结果被B图片识别出来。原因Flask默认的threadedTrue开启多线程处理请求而我最初把Detector设成了全局对象多个线程并发调用同一个模型的forward推理结果互相污染。这个坑隐蔽性很强单张图片测试永远测不出来。解决要么启动时加threadedFalse要么给推理加一个全局锁每个时刻只处理一路请求。我后来选择加锁因为答辩现场投影仪和手机同时连服务器不至于让请求排队到没响应import threading lock threading.Lock() app.route(/detect, methods[POST]) def detect(): with lock: boxes detector.predict(save_path) return jsonify({code: 0, boxes: boxes})5.5 数据库中文类别名乱码现象detection_record表里插入“可回收物”后终端查询出来是å¯åæ¶ç©页面显示直接变成问号但英文类别名完全正常。原因建库时没有指定utf8mb4字符集或者数据库连接时charset参数没写。MySQL默认字符集在部分环境是latin1中文插进去就被截断。解决建库语句把字符集写清楚CREATE DATABASE IF NOT EXISTS garbage_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;再把Python连接的charsetutf8mb4加上确认两边一致后再插入数据。这类问题查起来很烦但修复往往就是一行字符串的事。6. 答辩验收级的自检mAP、混淆矩阵和一次玄学修复训练完模型、跑通系统之后别急着放松。答辩时导师大概率会问“你的模型mAP多少”“误判主要发生在哪两类”这些问题不提前准备现场容易卡壳。6.1 用一句话跑出mAP与PR曲线YOLO自带验证脚本不需要自己写评估逻辑python val.py \ --data data_rc.yaml \ --weights runs/train/garbage_exp01/weights/best.pt \ --img 640 \ --conf 0.25 \ --iou 0.45参数说明--conf和--iou要与推理时保持一致否则算出来的指标不能反映系统真实表现。跑完之后在runs/val/exp/下生成mAP_0.5、mAP_0.5:0.95以及PR曲线图答辩时可以截图放进PPT。经验值参考垃圾分类四分类场景下mAP0.5能到70%以上就足以支撑论文结论了如果低于60%先别优化模型结构回去检查标注框是不是有错标和漏标。6.2 混淆矩阵看模型在哪两类之间摇摆YOLO的val脚本同时生成confusion_matrix.png这张图整个答辩过程中最有说服力。以我的四分类为例真实类别\预测类别recyclablekitchen_wastehazardousother_wasterecyclable82%6%2%10%kitchen_waste5%88%1%6%hazardous3%2%90%5%other_waste12%7%4%77%我当时的坑主要集中在“other_waste”和“recyclable”之间因为污损纸张和纸箱在没有颜色特征时确实容易混。解决办法不是加数据而是把两张训练图上“瓶中带残余液体”的例子单独复制到other_waste里重新训了一版混淆矩阵里的交叉数值立刻降了四个点。这段经历后来成了我的习惯每次训练完先看混淆矩阵再决定要不要重训而不是只看总mAP。指标好看但矩阵里某一类错得离谱说明系统在真实场景里根本不能交付。从那以后我每跑完一轮实验都强制走一遍“验证脚本混淆矩阵抽样可视化”三步虽然有时候看起来像玄学但至少不会让答辩现场翻车。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑