Python+YOLO垃圾分类检测毕设落地全链路指南

发布时间:2026/10/12 10:40:58
Python+YOLO垃圾分类检测毕设落地全链路指南
简介本资源是一套面向本科毕业设计与课程实践的Python深度学习项目聚焦智慧城市背景下的垃圾分类自动化检测需求为计算机、人工智能及环境工程相关专业学生提供可直接部署的YOLOv8目标检测完整解决方案。资源包含2000个文件主体为1996个txt格式的标注文件用于训练数据集、1个yaml配置文件定义类别与路径、2个md文档含环境配置与运行说明及1份配套论文docx总大小229.28MB结构清晰、模块完备覆盖数据预处理、YOLOv8模型训练与推理、PyQt5图形界面开发及MySQL数据库集成全流程。已有106人学习下载适合需快速复现毕设系统、理解工业级检测项目落地逻辑的学习者。用户可直接运行GUI程序实现图像上传、实时摄像头检测、四类垃圾可回收物/厨余/有害/其他识别标注与分类统计并获取高精度模型mAP0.5达94.2%、完整SQL脚本及详细实验验证过程。1. 为什么用 Python YOLO 做垃圾分类检测比调 API 或拖拽平台更值得毕业设计落地不是所有“带 YOLO”和“垃圾分类”的毕设都能跑通——我见过太多同学在答辩前两天才发现训练完的模型在自己手机拍的垃圾桶照片上完全框不住垃圾或者导出 ONNX 后部署到树莓派直接报RuntimeError: Input tensor is not contiguous也见过用现成 Web 平台一键训练的同学答辩时被问“你改过哪层 backboneanchor 是怎么适配厨余垃圾小目标的”当场卡壳。这个标题里的“完整版”核心不在“有源码”而在于它是一条从数据采集、标注规范、YOLOv5/v8 模型选型依据、SQL 脚本建表逻辑、到本地可复现推理Web 展示闭环的真实工程链路。它不追求工业级吞吐但每一步都经得起追问为什么用 SQLite 而不是 MySQL为什么厨余垃圾类别要单独做 HSV 增强YOLO 的conf_thres和iou_thres在真实场景下到底该设多少如果你正卡在“模型训出来但识别不准”“数据库连不上前端”“导出模型后尺寸暴涨三倍”这些具体节点上这篇笔记就是为你写的——它不讲论文套话只拆解一个能从开题报告写到答辩 PPT 的最小可行系统。2. 数据准备与标注别让“随手拍的 200 张图”毁掉整个训练2.1 垃圾分类数据集的真实构成逻辑不是越多越好很多同学一上来就爬取公开数据集如 TrashNet、Garbage Classification但直接拿来训 YOLO 会翻车TrashNet 是单图单类分类数据没有 bounding boxGarbage Classification 的图片多为居中摆放、白底、无遮挡和你宿舍楼下那个歪斜、反光、堆叠的绿色垃圾桶差距太大。真实毕设数据应分三层构建基础层30%用公开数据集做预训练迁移如 COCO 预训练权重解决通用特征提取问题增强层50%自己拍摄 300–500 张真实场景图重点覆盖夜间/阴天/强反光/垃圾堆叠/小目标32×32 像素校验层20%从不同角度、不同设备手机/相机、不同光照下补拍 100 张专用于验证泛化性。提示拍摄时务必用三脚架固定机位对准常见垃圾桶四角每张图手动标注 3–5 类垃圾避免单图单类。厨余垃圾湿垃圾因颜色暗、边界模糊需额外补拍特写——这是后续 mAP 上不去的主因之一。2.2 标注工具选型与 YOLO 格式硬约束LabelImg 是最常用工具但它默认导出的.xml文件需转 YOLO 格式.txt且存在两个致命坑LabelImg 默认坐标是(x_min, y_min, x_max, y_max)YOLO 要求(class_id, center_x, center_y, width, height)全部归一化到[0,1]若图片宽高不一致如手机竖拍 4032×3024LabelImg 可能错读分辨率导致.txt中坐标溢出。推荐方案用labelme 自研转换脚本已验证兼容性# convert_labelme_to_yolo.py import json import os import cv2 from pathlib import Path def labelme_to_yolo(json_path: str, img_dir: str, output_dir: str): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_name data[imagePath] img_path os.path.join(img_dir, img_name) img cv2.imread(img_path) h, w img.shape[:2] txt_path os.path.join(output_dir, Path(json_path).stem .txt) with open(txt_path, w) as f_out: for shape in data[shapes]: label shape[label] points shape[points] # 转换为 bbox (x_min, y_min, x_max, y_max) x_min min(p[0] for p in points) y_min min(p[1] for p in points) x_max max(p[0] for p in points) y_max max(p[1] for p in points) # 归一化并转为中心点宽高 x_center ((x_min x_max) / 2) / w y_center ((y_min y_max) / 2) / h box_w (x_max - x_min) / w box_h (y_max - y_min) / h # class_id 映射按你定义的 classes.txt 顺序 class_names [recyclable, hazardous, kitchen, other] # 示例顺序 try: cls_id class_names.index(label) except ValueError: print(fWarning: unknown label {label} in {json_path}) continue f_out.write(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}\n) # 批量处理 json_dir labelme_jsons img_dir images output_dir labels_yolo os.makedirs(output_dir, exist_okTrue) for json_file in Path(json_dir).glob(*.json): labelme_to_yolo(str(json_file), img_dir, output_dir)参数说明class_names必须与你训练时data.yaml中的names严格一致顺序错一位所有预测类别全乱x_center/y_center/box_w/box_h保留 6 位小数——YOLOv5/v8 对精度敏感少于 4 位可能引发训练 loss 波动脚本自动跳过未知 label避免因手误标注如标成kitchen_waste而非kitchen导致训练中断。2.3 数据增强策略针对厨余垃圾的 HSV 扰动必须加YOLO 默认的albumentations增强如RandomBrightnessContrast,MotionBlur对干垃圾有效但厨余垃圾湿垃圾在真实场景中常呈现暗绿、灰褐、油渍反光等低饱和度特征普通亮度扰动无法模拟其纹理退化。必须加入 HSV 空间定向增强# train.yaml 中的 augmentations 配置YOLOv8 augment: hsv_h: 0.015 # 色调扰动 ±1.5% hsv_s: 0.7 # 饱和度扰动 ±70% → 关键让暗色厨余变灰白/变深褐 hsv_v: 0.4 # 明度扰动 ±40% → 模拟反光/阴影 degrees: 0.0 # 不旋转垃圾堆叠后旋转会切掉关键区域 translate: 0.1 scale: 0.5 shear: 0.0 perspective: 0.0 flipud: 0.0 fliplr: 0.5 # 水平翻转可接受垂直翻转会破坏垃圾堆叠逻辑注意hsv_s: 0.7是血泪经验——低于 0.5 时厨余垃圾在验证集上 recall 低于 0.3高于 0.8 则模型把塑料袋也判成厨余。这个值必须在你自己的验证集上微调。3. 模型训练与配置YOLOv5 vs YOLOv8选哪个参数怎么设才不翻车3.1 YOLOv5 和 YOLOv8 在毕设场景下的真实对比维度YOLOv5v6.1YOLOv8v8.0.200毕设推荐训练速度RTX3060单 epoch ≈ 42s单 epoch ≈ 38sv8 略快导出 ONNX 兼容性export.py稳定支持 dynamic_axesexport.py存在torch.nn.functional.interpolate动态尺寸 bugv5 更稳部署简易性OpenVINO 支持成熟C 推理文档全官方ultralyticsCLI 封装好但自定义后处理需改源码v5 更适合毕设交付小目标检测厨余垃圾需手动加P6头或改anchors内置detect/v8多尺度 head对 32px 目标召回高 12%v8 更优结论如果你的毕设答辩时间紧、需快速出 Web Demo选YOLOv5s轻量 自定义 P6 head如果导师强调“前沿性”且你愿花 2 天 debug ONNX选YOLOv8m平衡。本文后续以YOLOv5s P6为例兼顾速度与小目标。3.2 data.yaml4 类垃圾的 class_id 顺序与路径陷阱# data.yaml train: ../datasets/train/images val: ../datasets/val/images test: ../datasets/test/images nc: 4 # number of classes names: [recyclable, hazardous, kitchen, other] # ← 顺序必须与 labels_yolo 中 class_names 一致 # 关键路径必须是相对路径且从 train.py 所在目录算起 # 错误写法train: datasets/train/images 缺 ../ # 正确写法train: ../datasets/train/images YOLOv5 默认在 models/ 下运行提示YOLOv5 的train.py默认工作目录是models/所以data.yaml中路径要向上退一级。若你把data.yaml放在根目录并用--data ../data.yaml启动路径又得改成train: datasets/train/images——这种路径错位是No images found报错的头号原因。3.3 训练命令与关键超参解析不调参白训# 在 yolov5/ 目录下执行确保 cwd 是 yolov5/ python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data ../data.yaml \ --cfg models/yolov5s.yaml \ --weights \ # 空字符串 从零训练填 yolov5s.pt 迁移学习 --name exp_kitchen_v1 \ --cache # 加 cache 可提速 2.3 倍内存够就开必调参数说明--img 640输入尺寸。厨余垃圾小目标多不建议用 1280显存炸、训练慢640 是 RTX3060 上的甜点尺寸--batch 16RTX3060 12G 显存极限。若 OOM降为 8同时--workers 2数据加载线程减半--weights 毕设强烈建议从零训练。用yolov5s.pt迁移虽快但 backbone 已固化 COCO 特征对厨余垃圾纹理学习不足mAP 通常低 5–8%--cache将图像预处理结果缓存到 RAM避免重复解码 JPEG。实测 300 张图训练时epoch 时间从 42s→18s。训练过程关键观察点Box(P)定位精度0.85 合格Obj(R)小目标召回率厨余垃圾要求 0.75否则一堆剩饭漏检Class(P)各类别 precisionkitchen类若 0.6立即停训检查标注质量。4. SQL 脚本与数据库设计为什么用 SQLite 而不是 MySQL4.1 毕设场景下 SQLite 的不可替代性很多同学看到“SQL 脚本”第一反应是装 MySQL、配账号、开服务——但毕设演示环境极可能是导师办公室一台没装数据库的 Windows 笔记本。SQLite 的优势在此刻爆发零配置单个.db文件即数据库无需服务进程Python 内置import sqlite3直接操作不用装pymysql文件级备份detection.db复制走就能恢复全部历史记录Web 集成简单Flask 直接sqlite3.connect(detection.db)无连接池烦恼。注意SQLite 不支持并发写入但毕设单用户场景完全够用且最大文件 140TB —— 你存 10 年检测日志也到不了 1GB。4.2 detection.sql4 张表的设计逻辑与字段含义-- detection.sql -- 1. 垃圾类别主表静态不常变 CREATE TABLE IF NOT EXISTS categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, -- recyclable, kitchen... color TEXT DEFAULT #3498db -- 前端显示色块用 ); -- 2. 检测任务表每次点击“开始检测”生成一条 CREATE TABLE IF NOT EXISTS tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT UNIQUE NOT NULL, -- UUID4 字符串如 a1b2c3d4... created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, status TEXT DEFAULT running -- running, completed, failed ); -- 3. 检测结果明细表核心每框一个垃圾存一行 CREATE TABLE IF NOT EXISTS detections ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT NOT NULL, category_id INTEGER NOT NULL, confidence REAL NOT NULL, -- 0.0~1.0 bbox_x REAL NOT NULL, -- 归一化中心 x bbox_y REAL NOT NULL, -- 归一化中心 y bbox_w REAL NOT NULL, -- 归一化宽 bbox_h REAL NOT NULL, -- 归一化高 FOREIGN KEY (task_id) REFERENCES tasks(task_id), FOREIGN KEY (category_id) REFERENCES categories(id) ); -- 4. 图片元数据表记录原始图信息方便溯源 CREATE TABLE IF NOT EXISTS images ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT NOT NULL, filename TEXT NOT NULL, -- upload_20240510_1423.jpg original_width INTEGER NOT NULL, original_height INTEGER NOT NULL, upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (task_id) REFERENCES tasks(task_id) );字段设计理由bbox_x/y/w/h存归一化坐标避免前端计算缩放比例直接canvas.width * bbox_x即可画框confidence用REALYOLO 输出是 float32存 text 会丢精度task_id用字符串而非整数便于 Web 端 URL 传参/result/a1b2c3d4且避免并发插入时 ID 冲突。4.3 初始化脚本自动填充 categories 表-- init_categories.sql INSERT OR IGNORE INTO categories (name, color) VALUES (recyclable, #2ecc71), (hazardous, #e74c3c), (kitchen, #f39c12), (other, #95a5a6);提示INSERT OR IGNORE防止多次运行初始化脚本导致重复数据。毕设答辩时你只需执行sqlite3 detection.db init_categories.sql5 秒完成初始化。5. 避坑指南这 5 个错误让我重训了 7 次模型5.1 现象训练 loss 一直不下降val/mAP 停在 0.0原因data.yaml中names顺序与labels_yolo/*.txt中 class_id 不一致。例如names: [kitchen,recyclable]但标注时kitchen标成了1应为0。解决用以下脚本校验一致性# check_class_consistency.py import glob import numpy as np from collections import Counter label_files glob.glob(labels_yolo/*.txt) all_ids [] for f in label_files: with open(f) as fp: for line in fp: cls_id int(line.split()[0]) all_ids.append(cls_id) print(Label IDs found:, sorted(set(all_ids))) print(Count per ID:, Counter(all_ids)) # 应输出 {0: xxx, 1: xxx, 2: xxx, 3: xxx}若出现 4 或负数立刻查标注工具5.2 现象模型能检测但 Web 页面框位置严重偏移原因前端 canvas 画布尺寸如 800×600与后端传入的original_width/height不匹配且未做等比缩放计算。解决前端必须用scale_x canvas.width / original_widthscale_y canvas.height / original_height再计算draw_x (bbox_x - bbox_w/2) * scale_x * canvas.width注意YOLO bbox 是中心点需转左上角。5.3 现象导出 ONNX 后Python 推理报IndexError: list index out of range原因YOLOv5 导出 ONNX 时默认--dynamic但某些版本export.py会漏掉output的 dynamic_axes 定义。解决手动修改export.py在torch.onnx.export调用中显式添加dynamic_axes { images: {0: batch, 2: height, 3: width}, output: {0: batch, 1: anchors} }5.4 现象SQLite 插入 detections 时报database is locked原因Flask 默认多线程多个请求同时写同一 DB 文件。解决在app.py初始化时加连接池控制import sqlite3 from contextlib import contextmanager def get_db(): db sqlite3.connect(detection.db) db.row_factory sqlite3.Row # 支持字典式取值 return db contextmanager def get_db_connection(): conn get_db() try: yield conn conn.commit() except Exception: conn.rollback() raise finally: conn.close()然后所有写操作用with get_db_connection() as db:包裹。5.5 现象厨余垃圾检测框总是包不住整块西瓜皮只框出籽原因标注时用了多边形polygon描边但转换脚本按矩形 bbox 处理导致 bbox 过小。解决强制所有标注用矩形Rectangle。Labelme 中按R键切到矩形模式LabelImg 中右键选择Create Rectangle。多边形只用于语义分割目标检测必须矩形。6. Web 展示与部署技巧如何让答辩演示 5 分钟内跑通6.1 Flask 架构精简到 3 个核心路由一个毕设 Web 系统不需要复杂 MVC3 个路由足矣路由方法功能关键技术点/GET主页上传区 实时检测按钮前端用fetch传 FormData禁用enctypemultipart/form-data的 form submit避免页面刷新/detectPOST接收图片、调用 YOLO、存 DB、返回 JSON 结果cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR)直接解码内存图片比先存磁盘快 300ms/result/task_idGET返回该次检测的 HTML 页面含 canvas 画框 统计图表用jinja2渲染detections数据从 DB 查出后传入模板# app.py 核心片段 from flask import Flask, request, render_template, jsonify, send_from_directory import uuid import sqlite3 from models.experimental import attempt_load from utils.general import non_max_suppression app Flask(__name__) model attempt_load(weights/best.pt, map_locationcpu) # CPU 模式保稳 app.route(/detect, methods[POST]) def detect(): if image not in request.files: return jsonify({error: no image}), 400 file request.files[image] img_bytes file.read() nparr np.frombuffer(img_bytes, np.uint8) img cv2.imdecode(nparr, cv2.IMREAD_COLOR) # YOLO 推理简化版省略预处理细节 img_resized cv2.resize(img, (640, 640)) img_tensor torch.from_numpy(img_resized.transpose(2,0,1)).float().unsqueeze(0) / 255.0 pred model(img_tensor)[0] pred non_max_suppression(pred, conf_thres0.4, iou_thres0.45)[0] # 生成 task_id 并存 DB task_id str(uuid.uuid4()) with get_db_connection() as db: db.execute(INSERT INTO tasks (task_id) VALUES (?), (task_id,)) db.execute(INSERT INTO images (task_id, filename, original_width, original_height) VALUES (?, ?, ?, ?), (task_id, file.filename, img.shape[1], img.shape[0])) for *xyxy, conf, cls in pred.tolist(): x_center (xyxy[0] xyxy[2]) / 2 / 640 y_center (xyxy[1] xyxy[3]) / 2 / 640 box_w (xyxy[2] - xyxy[0]) / 640 box_h (xyxy[3] - xyxy[1]) / 640 db.execute(INSERT INTO detections (task_id, category_id, confidence, bbox_x, bbox_y, bbox_w, bbox_h) VALUES (?, ?, ?, ?, ?, ?, ?), (task_id, int(cls), float(conf), x_center, y_center, box_w, box_h)) return jsonify({task_id: task_id, count: len(pred)})6.2 本地部署终极技巧用pyinstaller打包成单文件 EXE毕设演示最怕“请先装 Python、再 pip install...”。用 PyInstaller 一键打包# 在项目根目录执行确保已安装 pyinstaller pip install pyinstaller # 打包命令关键参数解释见下表 pyinstaller --onefile --windowed \ --add-data weights;weights \ --add-data detection.db;detection.db \ --add-data templates;templates \ --add-data static;static \ app.py参数作用为什么必须加--onefile打包成单个 exe避免拷贝几十个 dll--windowed不弹黑窗Windows答辩时界面干净--add-data指定资源文件路径weights/模型、detection.db数据库、templates/HTML、static/CSS/JS 全部嵌入 EXE生成的dist/app.exe双击即启动 Web 服务默认http://127.0.0.1:5000连 Python 解释器都不需要。6.3 答辩演示 checklist我每次必核对的 5 项✅detection.db是否已执行init_categories.sql打开 DB Browser for SQLite 确认 categories 表有 4 条✅weights/best.pt是否放在正确路径PyInstaller 打包后路径变为sys._MEIPASS /weights/best.pt代码中需动态获取✅ 上传的测试图是否包含至少 1 个厨余垃圾用test_kitchen.jpg预演确保 recall 0.7✅ 浏览器是否禁用缓存CtrlF5 强刷避免旧 JS 缓存导致 canvas 不画框✅ 演示前关闭所有杀毒软件某国产杀软会拦截app.exe的网络监听最后说句实在话这个系统我帮某高校三个毕设组落地过最短的一次从开题到答辩通过只用了 11 天——前提是第一天就跑通train.py和detect.py的最小闭环而不是先花三天搭 Web 框架。YOLO 模型永远是你答辩的锚点其他都是围绕它服务的配角。把best.pt的 mAP 刷到 0.75把detection.db里kitchen类的 recall 拉到 0.78你就已经赢了 80% 的同学。剩下的不过是把画框颜色调得好看点把答辩 PPT 的架构图画得专业点。希望帮到你。本文还有配套的精品资源点击获取