首页 / 资讯中心 / 文章详情

Python毕业设计YOLO垃圾分类检测与SQL落库全链路

Python毕业设计YOLO垃圾分类检测与SQL落库全链路 ★ FEATURED ARTICLE
简介面向Python毕业设计、课程设计与计算机视觉入门者这套资料围绕基于YOLOv8的垃圾分类目标检测系统展开提供可运行的完整源码、MySQL数据库脚本及配套论文文档帮助读者完成从数据集标注、模型训练推理到PyQt5可视化界面展示的完整项目链路。压缩包共2000个文件约229.28MB以1996个txt数据标注/说明文件为主另含md说明、yaml配置与docx论文便于按模块查阅和复现实验。系统覆盖图像上传、摄像头实时检测、结果标注与分类统计等功能并采用数据预处理、YOLO推理、界面交互和结果输出等模块化结构可作为毕设答辩、项目实训与算法二次开发的参考。已有108人学习适合需要完整方案、代码骨架与排错思路的读者。1. 从一张误判的快递盒照片说起Python 毕业设计里的 YOLO 垃圾分类目标检测某开发者把沾了油渍的快递盒举到摄像头前系统却把它标成“其他垃圾”日志里置信度只有 0.31。这个场景几乎就是垃圾分类检测的日常类别边界模糊、背景干扰多、模型得分不上不下。这套资源围绕 Python 毕业设计、YOLO、垃圾分类检测和 SQL 落库展开包含可训练与推理的源码、建表脚本和完整流程适合课程设计、毕设复现以及想把目标检测跑成闭环的初学者。它不承诺一键高精度价值在于把数据、训练、推理、记录四段链路摊开给你改。2. 拆开源码包YOLO 垃圾分类系统的四层结构与 SQL 落点拿到这类“源码SQL 脚本”的包我一般不会先改界面也不会急着把epochs拉满。先看数据流确认一张图片从上传到写入数据库之间到底经过哪些文件。很多毕设项目表面是“垃圾分类检测”拆开以后其实是四层页面或接口层、推理封装层、模型与权重层、关系库记录层。四层里任意一层对不上最后都会表现成“模型不准”或者“数据库没数据”但根因可能完全不在模型。2.1 从上传图片到检测记录一次请求走完哪四层先把最小链路列出来再去找源码里的对应文件。下面这张表不是让你背而是拿到包后按行找入口找不到就说明目录被改过或者缺文件。层常见入口主要职责出问题先看接口层app.py、web/、api/接收图片、返回 JSON、跳转页面请求体字段名、文件保存路径推理层detect.py、predict.py、utils/读图、预处理、模型前向、NMS输入尺寸、颜色通道、置信度阈值模型层weights/、models/权重文件、类别数量、模型结构类别数与训练时是否一致记录层sql/、database.py建表、插入检测框、统计查询表字段、图片路径、一次请求多框接口层最常见的写法是 Flask 或同类轻量框架一个/api/detect接收multipart/form-data把上传文件落到static/uploads/再把绝对路径交给推理函数。推理层返回的通常不是一个类别字符串而是一组框class_id、class_name、confidence、x1,y1,x2,y2。记录层要决定一件事一张图里检出 3 个框是写 1 条记录还是 3 条记录。我的习惯是“一个检测框一条记录再用request_id把同一次上传串起来”这样后面做类别统计和置信度筛选都方便。2.2 环境与启动顺序先跑通单图推理再碰训练源码包里的requirements.txt优先级高于你本机环境。不要上来就pip install最新版尤其是深度学习项目版本漂移会把一个本来能跑的包变成一堆 import 报错。我一般先建独立环境再验证核心依赖能不能正常加载。python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install -r requirements.txt python -c import torch; print(torch.__version__); print(torch.cuda.is_available())这段命令做三件事建隔离环境、按锁文件安装依赖、检查深度学习框架是否可用。torch.cuda.is_available()返回False不代表项目坏了只说明当前机器走 CPU 推理如果源码里硬编码了.cuda()就要把设备选择改成device cuda if torch.cuda.is_available() else cpu。requirements.txt里如果同时出现多个深度学习框架版本先按文件里的顺序装不要自己合并升级。启动顺序我固定为先跑 SQL 脚本建表再确认权重文件存在然后用单张图片跑推理最后才启动 Web 服务。单图推理能过说明模型加载、类别映射、预处理基本对齐单图不过就开服务页面只会把错误藏得更深。2.3 SQL 脚本不是附属品检测记录表要按“框”设计这类资源里的 SQL 脚本通常包含建库、建表、初始类别和索引。下面给一个最小可用的检测记录表结构字段名可以按你包里的脚本调整但设计思路值得对照。CREATE TABLE detect_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, request_id VARCHAR(64) NOT NULL, image_path VARCHAR(255) NOT NULL, class_id INT NOT NULL, class_name VARCHAR(32) NOT NULL, confidence DECIMAL(5,4) NOT NULL, bbox_json JSON NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_request (request_id), INDEX idx_class_time (class_name, created_at) );request_id用来聚合同一次上传的多个检测框image_path建议存相对路径不要存/home/xxx/...这种绝对路径否则换一台机器图片全挂confidence用DECIMAL(5,4)能保留 0 到 1 之间的小数bbox_json存[x1,y1,x2,y2]比拆四个字段更灵活。idx_class_time是为“最近一周各类垃圾数量”这类统计查询准备的没有索引时数据量一小看不出问题演示时一翻页就慢。提示如果源码包里用的是 SQLiteAUTO_INCREMENT、JSON类型和DATETIME DEFAULT CURRENT_TIMESTAMP的写法要换先看 SQL 文件头部的数据库声明不要直接拿 MySQL 脚本往 SQLite 里灌。3. 数据集与标注把垃圾分类类别落成 YOLO 能吃的标签垃圾分类检测的精度上限很大一部分在标注阶段就定了。模型只能学你标出来的东西同一张图里纸箱一半在画面外你标了整框模型就会学到“猜”四个类别里“其他垃圾”塞进了太多无法归类的物体模型就会把这个类当成垃圾桶。YOLO 训练并不神秘真正费时间的是类别表、标注文件和数据划分。3.1 类别表怎么定四个中文名背后要有稳定的英文标签毕业设计里常见的四分类是“可回收物、厨余垃圾、有害垃圾、其他垃圾”。中文名可以出现在页面和论文里但训练标签必须用稳定的英文或拼音短名并且顺序固定。下面是一份对照表重点是class_id一旦训练就不要改。class_id中文名训练标签采集注意0可回收物recyclable纸箱、塑料瓶、易拉罐要分开拍避免只拍干净摆拍图1厨余垃圾kitchen果皮、菜叶、剩饭容易粘连框要贴住主体2有害垃圾hazardous电池、灯管、药品包装不要和普通垃圾混标3其他垃圾other纸巾、烟头、陶瓷碎片容易与可回收物混淆边界要收紧类别表要和data.yaml或源码里的classes列表完全一致。我见过一种翻车训练时names是[recyclable,kitchen,hazardous,other]推理端却按[可回收物,厨余垃圾,有害垃圾,其他垃圾]去取索引结果页面把“电池”显示成“厨余垃圾”。这不是模型错是映射表错。# data.yaml 的关键字段路径按你的数据集实际位置改 path: ./dataset train: images/train val: images/val nc: 4 names: 0: recyclable 1: kitchen 2: hazardous 3: otherpath是数据集根目录train和val是相对路径nc必须等于类别数names的键要从 0 连续排到nc-1。有些源码包把类别写在 Python 字典里而不是 YAML 里改的时候要全局搜一遍别只改训练配置。3.2 标注文件检查归一化坐标、空标签和越界框YOLO 标签是“一张图一个.txt一行一个框”每行格式是class_id x_center y_center width height后四个值都是相对图片宽高的 0 到 1 小数。标注工具导出后最常见的错误是坐标没归一化、类别 id 超范围、宽高为 0、框超出图片边界。下面这段检查脚本可以直接放进tools/check_labels.py。from pathlib import Path import cv2 IMG_DIR Path(dataset/images/train) LBL_DIR Path(dataset/labels/train) CLASS_NUM 4 bad [] for img_path in IMG_DIR.glob(*.jpg): label_path LBL_DIR / f{img_path.stem}.txt if not label_path.exists(): bad.append((img_path.name, 缺少标签文件)) continue img cv2.imread(str(img_path)) if img is None: bad.append((img_path.name, 图片无法读取)) continue h, w img.shape[:2] for line_no, line in enumerate(label_path.read_text().splitlines(), 1): parts line.strip().split() if len(parts) ! 5: bad.append((img_path.name, f第{line_no}行字段数{len(parts)})) continue cid, x, y, bw, bh parts cid int(float(cid)) x, y, bw, bh map(float, (x, y, bw, bh)) if cid 0 or cid CLASS_NUM: bad.append((img_path.name, f第{line_no}行类别越界:{cid})) if not (0 x 1 and 0 y 1 and 0 bw 1 and 0 bh 1): bad.append((img_path.name, f第{line_no}行坐标异常:{line})) if bw * w 2 or bh * h 2: bad.append((img_path.name, f第{line_no}行框过小)) for name, reason in bad[:30]: print(name, reason) print(异常总数:, len(bad))这段脚本先看图片和标签是否一一对应再逐行检查字段数、类别 id、归一化范围和最小像素尺寸。CLASS_NUM要和data.yaml的nc一致bw * w 2是过滤掉标注工具误点产生的极小框阈值可以按你的图片分辨率改。跑完不要只看“异常总数”前 30 条要人工翻一下有些是文件名对不上有些是图片本身损坏。3.3 数据划分与增强别让同一张图的增强副本跨进验证集随机按 8:2 切分听起来没问题但同一个视频抽帧、同一场景连拍、同一张图的旋转增强副本如果同时进了训练集和验证集验证集 mAP 会虚高。我的做法是按“原始采集组”划分文件名里带scene01_的整组进训练或验证不拆开。数据量少时增强放在训练配置里做不要提前把增强图落盘再随机切。from pathlib import Path import shutil, random random.seed(42) src_img Path(dataset/all_images) src_lbl Path(dataset/all_labels) train_img, val_img Path(dataset/images/train), Path(dataset/images/val) train_lbl, val_lbl Path(dataset/labels/train), Path(dataset/labels/val) for p in (train_img, val_img, train_lbl, val_lbl): p.mkdir(parentsTrue, exist_okTrue) # 按文件名前缀分组保证同一场景不跨集 groups {} for img in src_img.glob(*.jpg): key img.stem.split(_)[0] groups.setdefault(key, []).append(img) keys list(groups) random.shuffle(keys) val_keys set(keys[:max(1, int(len(keys) * 0.2))]) for key, imgs in groups.items(): dst_img val_img if key in val_keys else train_img dst_lbl val_lbl if key in val_keys else train_lbl for img in imgs: shutil.copy2(img, dst_img / img.name) lbl src_lbl / f{img.stem}.txt if lbl.exists(): shutil.copy2(lbl, dst_lbl / lbl.name)random.seed(42)保证每次划分可复现key img.stem.split(_)[0]是按第一个下划线前的场景号分组你的命名规则不同就改这一行。0.2是验证集比例小数据集不要低于 10%否则单类样本太少评估结果没有参考价值。增强参数不要一上来拉满颜色抖动、随机翻转、轻微缩放够用过度 mosaic 会让小目标更碎。4. 训练与评估从预训练权重到能写进论文的 mAP 表训练部分最容易被“一键脚本”掩盖。源码包里通常有一个train.py参数散在argparse里也可能直接写死。先不要改网络结构先把输入尺寸、batch、学习率和类别数这四个变量对齐。垃圾分类检测不是比赛刷榜毕设交付更看重可复现同一份数据、同一组参数跑两次结果不能差太多。4.1 训练参数怎么设imgsz、batch、workers 与学习率下面这组参数适合单卡显存 8GB 左右的常见环境不是万能值。imgsz决定输入分辨率垃圾袋、纸盒这类主体通常占画面比例较大640 够用如果小目标多比如烟头、瓶盖才考虑往上加。batch先给 8 或 16显存爆了再降。workers在 Windows 上不要开太大数据加载进程过多反而卡住。参数建议起点作用调整信号imgsz640训练与推理输入尺寸小目标漏检多再升显存不够再降batch8 或 16每次迭代样本数显存溢出就减半epochs80 到 150训练轮数验证集早停不要盲堆lr00.01 左右初始学习率loss 震荡大就降收敛慢就小步升patience20 到 30早停耐心值数据少时别设太小workers2 到 4数据加载进程卡在第一个 epoch 就降学习率不要和 batch 一起乱动。先固定lr0只调 batchbatch 翻倍后学习率可以适当上调但毕设数据量通常不大保守一点更稳。epochs不是越多越好垃圾分类四分类在几千张图上跑 100 轮左右就能看出趋势后面涨不动就是数据问题。4.2 训练命令与断点续训显存不够时的降级顺序源码包里的训练入口可能是train.py也可能是框架命令行。下面用常见的脚本参数写法示例具体参数名按你包里的argparse调整。python train.py \ --data data.yaml \ --weights weights/pretrained.pt \ --img 640 \ --batch 8 \ --epochs 100 \ --workers 2 \ --device 0 \ --project runs/train \ --name garbage_yolo--data指向类别配置--weights用预训练权重比从头训练收敛快--device 0表示第一块 GPUCPU 环境改成cpu--project和--name决定日志与权重输出目录。显存不够时按这个顺序降先把batch从 16 降到 8再把imgsz从 640 降到 512最后才关掉缓存或减少workers。不要一上来改网络宽度那会让预训练权重对不上。断点续训要找last.pt或resume参数。有些包把resume写死成False训练中断后只能重跑拿到源码后先搜resume确认断点文件路径。续训前看一眼上次的results.csv如果验证集指标已经连续多轮下降续训只是把过拟合延长。4.3 评估别只看 mAP混淆矩阵、PR 曲线和单类召回mAP0.5是一个汇总值答辩时好看但不能告诉你“有害垃圾是不是全被认成其他垃圾”。我一般会额外看三样混淆矩阵、每一类的召回率、置信度阈值变化时的精度召回曲线。下面这段评估脚本按检测结果统计单类召回字段名按你源码的输出改。from pathlib import Path from collections import defaultdict # 假设 val 推理后每行: image_id, pred_cls, conf, gt_cls # 实际项目里从 val 结果文件或推理接口读取这里用列表模拟结构 records [ (img1, recyclable, 0.91, recyclable), (img1, other, 0.55, recyclable), (img2, hazardous, 0.88, hazardous), (img3, kitchen, 0.72, other), ] hit defaultdict(int) gt_count defaultdict(int) for _, pred_cls, conf, gt_cls in records: if conf 0.25: continue gt_count[gt_cls] 1 if pred_cls gt_cls: hit[gt_cls] 1 for cls in sorted(gt_count): recall hit[cls] / gt_count[cls] print(f{cls}: recall{recall:.3f}, gt{gt_count[cls]})conf 0.25是评估时的低阈值过滤实际部署可能用 0.4 或 0.5评估阶段放低一点能看清模型的召回上限。gt_count是真实框数量hit是类别匹配且置信度达标的命中数。这个脚本只做类别级匹配不计算 IoU正式评估还是用框架自带的验证入口这里用来快速定位“哪个类拖后腿”。如果hazardous召回明显低先回去看这一类标注数量和场景覆盖不要急着调 NMS。5. 避坑与排查垃圾分类检测从训练到落库的五次翻车记录这一章不按“功能模块”讲按现场症状讲。垃圾分类检测系统的坑很集中数据标签、划分泄漏、预处理不一致、路径错位、一次请求多框写库。下面五条都是我在类似源码包里反复见到的每条按“现象、原因、解决”拆开方便你对照日志排查。5.1 数据与标注类的两次翻车现象一训练 loss 一直降验证集 mAP 不动页面检测结果类别全乱。原因通常不是模型容量不够而是标签类别 id 和推理端类别表不一致。训练时class_id2是有害垃圾推理端names[2]却写成了厨余垃圾或者标注文件里混进了class_id4而nc4只允许 0 到 3。YOLO 训练阶段不一定报错评估时按错误映射算指标自然难看。解决先跑 3.2 的标签检查脚本确认类别 id 范围再把data.yaml的names、源码里的CLASSES、SQL 初始化类别表三处对齐。我一般会写一个class_map.json作为唯一来源训练、推理、页面都从它读少一处手写就少一次翻车。现象二验证集 mAP 高得离谱换一批新图片就崩。原因多半是数据划分泄漏同一张原图的旋转、裁剪、亮度增强副本同时进了训练集和验证集模型在验证集上等于“见过答案”。还有人把同一段视频的连续帧随机切分帧间差异极小指标也会虚高。解决按原始采集组划分不按单张图随机切增强只在训练时在线做不提前落盘划分完用文件哈希或图片感知哈希查一遍跨集重复。数据量少的时候宁可验证集小一点也要保证干净。5.2 训练与推理类的两次误判现象三训练时验证图框得挺准Web 上传同一张图却框偏、框少、置信度低。这类问题十有八九是预处理不一致。训练框架默认可能是 RGB、letterbox 填充、归一化到 0 到 1Web 端用 OpenCV 读进来是 BGR直接 resize 又没保持长宽比送进模型前也没做同样的归一化。模型看到的分布变了输出自然飘。解决把推理预处理固定成和验证阶段同一套。下面这段是 letterbox 的常见写法重点是等比例缩放加灰边填充别直接拉伸。import cv2 import numpy as np def letterbox(img, new_size640, color(114, 114, 114)): h, w img.shape[:2] scale min(new_size / w, new_size / h) nw, nh int(round(w * scale)), int(round(h * scale)) resized cv2.resize(img, (nw, nh), interpolationcv2.INTER_LINEAR) canvas np.full((new_size, new_size, 3), color, dtypenp.uint8) top (new_size - nh) // 2 left (new_size - nw) // 2 canvas[top:top nh, left:left nw] resized return canvas, scale, left, topnew_size要和训练imgsz一致color是填充灰度常见是 114返回的scale,left,top用来把检测框映射回原图。如果源码里推理端直接cv2.resize(img, (640,640))而训练用 letterbox框偏就是必然的。颜色通道也要查训练用 PIL 读 RGB推理用 OpenCV 读 BGR就要在送模型前转一次。现象四小目标漏检严重大纸箱能检出瓶盖、电池基本没有。原因可能是训练和推理输入尺寸不一致也可能是数据里小目标标注太少。还有人把imgsz从 640 改成 1280 训练推理却仍然用 640模型学到的尺度信息和实际输入对不上。解决先固定训练与推理的imgsz再统计标注框的像素面积分布如果小框占比很低补拍近景或提高采集分辨率最后才考虑切片推理。切片推理能提升小目标召回但会把一张图拆成多块后处理合并框的逻辑要自己写毕设演示不一定需要。5.3 服务与 SQL 类的隐蔽问题现象五数据库里有记录页面图片打不开换一台机器部署后历史记录全变空图。原因是image_path存了绝对路径比如/home/student/project/static/uploads/a.jpg换环境后目录不存在。另一个变体是存了static/uploads/a.jpg但 Flask 的静态目录配置改了URL 拼接重复。解决数据库只存相对于上传根目录的路径例如uploads/2025/03/a.jpg读取时用配置项UPLOAD_ROOT拼绝对路径返回 URL 时再用STATIC_URL_PREFIX拼。路径拼接只留一个函数别在控制器、模板、SQL 查询里各写一遍。现象六一次上传检出多个框统计页面把同一张图重复计数。原因是记录表按“框”存统计查询却按image_path去重没做或者同一次请求被前端重试写了两次。前者会让“今日检测图片数”虚高后者会让类别数量翻倍。解决写入时生成唯一request_id同一次上传的多个框共用统计图片数用COUNT(DISTINCT request_id)统计框数才用COUNT(*)前端重试加幂等键或者后端对request_id建唯一约束的一部分。SQL 设计阶段把“请求”和“框”分开后面少很多后悔药。注意排查时不要只看 Web 页面。先跑单图推理再把推理输出打印成 JSON最后查 SQL 记录。三层分别验证才能判断是模型、预处理还是落库的问题。6. 用一条请求验收全链路从上传图片到 SQL 记录可追溯我验收这类系统不看首页多漂亮只做一条最小链路上传一张已知类别的图片记录返回的request_id、框数、类别和置信度再去 SQL 里按同一个request_id查记录。命令不用多能闭环就行。curl -s -X POST http://127.0.0.1:5000/api/detect \ -F file./samples/box_001.jpg | tee resp.json python -c import json;djson.load(open(resp.json));print(d[request_id], len(d[detections])) # MySQL 示例按上一步输出的 request_id 替换 mysql -uroot -p garbage_detection -e \ SELECT request_id,class_name,confidence,image_path FROM detect_record WHERE request_id替换成上一步的id;curl里的-F是表单文件上传字段名要和接口层一致tee把响应落盘方便后面对照len(d[detections])检查返回框数如果页面显示 3 个框但这里只有 1 个问题在接口序列化而不是模型。SQL 查询重点看三件事记录数是否等于框数、image_path是否能拼出可访问文件、confidence是否落在合理区间。SQLite 环境把最后一条换成sqlite3 garbage_detection.db SELECT ...即可。那条链路跑通以后我才会去改阈值、换权重、调页面。从那以后我每次拿到这类源码包都强制先跑一遍“上传、返回、落库、回查”不再先看训练曲线。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站