Python智能垃圾分类系统源码解析:从毕业设计到Grad-CAM可视化
2026/9/24 18:15:09 网站建设 项目流程

简介:这份资源是一套基于Python开发的智能垃圾分类系统完整源码与部署指南,面向计算机相关专业的毕业设计、期末大作业及课程实践场景,适合具备一定Python基础、希望借助深度学习完成视觉识别项目的学生与开发者。压缩包共24个文件,约55.76MB,以py源码、zip数据集、ui界面文件为主,另含cpp示例、md说明文档及jpg、png效果图,覆盖模型推理、界面交互与数据组织等模块。系统采用卷积神经网络与迁移学习策略,可区分可回收物、厨余垃圾、有害垃圾和其他垃圾四大类别,并配有图像采集、预处理、推理引擎与可视化界面。包内附环境配置、依赖清单、模型训练与部署流程说明,代码模块化且关键算法注释详尽,便于二次开发与性能优化。目前已有57人学习,可作为教学研究和技术开发的参考案例。

1. 从一份毕业设计源码说起:这套垃圾分类系统到底能跑出什么结果

很多同学做毕业设计时,最头疼的不是写不出代码,而是找不到一套结构完整、能跑通、还带界面的参考工程。这份基于 Python 的智能垃圾分类系统源码,恰好卡在这个需求点上:它把图像采集、模型推理、结果可视化串成了一条完整链路,最终输出可回收物、厨余垃圾、有害垃圾、其他垃圾四个类别。拿到手第一件事不是急着点运行,而是先看清它的技术骨架——卷积神经网络做特征提取,迁移学习在小数据集上微调,PyQt 或 Tkinter 搭界面,OpenCV 负责图像预处理。适合谁?正在做毕业设计、期末大作业,或者想找一个带 UI 的深度学习落地案例练手的人。它不完美,但足够让你把“训练—推理—展示”这条线走通一遍。

2. 拆开压缩包:文件结构与模块职责先理清

2.1 从文件清单反推系统架构

拿到基于python的垃圾分类系统源码+使用说明.zip,别急着解压完就双击main_ui.py。先看目录里有什么:kid_ui.uia.uigarbage_ui.pymain_ui.pyui.py这些是界面层;classify.py是分类推理入口;garbage.py大概率是业务逻辑或数据加载;dataset文件夹下按cardboard.zipglass.zippaper.ziptrash.zipmeat.zipplastic.zip分门别类,说明数据是按类别打包的。还有test-1.pytest-4.py这种测试脚本,以及.zbak备份文件——这是原作者迭代时留下的痕迹,别删,有时候能找回被改坏的版本。

常见做法是:先确认README.md里的环境要求,再对照requirements.txt(如果有)装依赖。但这份资源里 README 可能写得比较简略,我一般会直接看classify.py的 import 段,反推需要哪些库。

2.2 界面文件与逻辑文件的对应关系

.ui文件是 Qt Designer 生成的界面描述,ui.pygarbage_ui.py是转换后的 Python 代码。main_ui.py通常是主入口,负责加载模型和绑定按钮事件。如果你发现运行main_ui.py报错说找不到kid_ui,大概率是.ui文件还没转成.py。转换命令如下:

# 把 .ui 文件转成 Python 代码,注意 pyuic5 的版本要和 PyQt5 匹配 pyuic5 kid_ui.ui -o kid_ui.py pyuic5 a.ui -o a.py

逻辑说明:pyuic5是 PyQt5 提供的命令行工具,把 XML 格式的界面描述转成可导入的 Python 类。参数-o指定输出文件名。如果你用的是 PySide2,命令换成pyside2-uic。转换后不要直接改生成的kid_ui.py,因为下次重新生成会覆盖,所有业务逻辑应该写在main_ui.py或单独的控制器文件里。

2.3 数据集的组织方式与标签映射

dataset下的 zip 包解压后,每个文件夹对应一个类别。但这里有个坑:文件夹名是英文,而界面显示需要中文。你需要在classify.pygarbage.py里找到标签映射字典,类似:

# 类别索引到中文名称的映射,顺序必须和训练时一致 CLASS_MAP = { 0: "可回收物", 1: "厨余垃圾", 2: "有害垃圾", 3: "其他垃圾" }

参数说明:这个字典的键是模型输出层的索引,值是对外展示的中文。如果你重新训练了模型,类别顺序变了,这里必须同步改,否则界面显示会张冠李戴。我见过有人训练时把cardboardpaper调换了顺序,结果推理全错,排查了一下午才发现是映射没更新。

3. 环境配置与依赖安装:把 Python 版本和库版本锁死

3.1 Python 版本选择与虚拟环境创建

这份源码大概率是在 Python 3.6 到 3.8 之间写的,因为 TensorFlow 1.x 或 PyTorch 1.x 对高版本 Python 支持不好。别用 Python 3.11 去硬跑,你会遇到一堆AttributeError。我一般会先建一个干净虚拟环境:

# 创建虚拟环境,指定 Python 3.8,避免污染全局环境 python3.8 -m venv venv_garbage # 激活环境,Windows 用 venv_garbage\Scripts\activate source venv_garbage/bin/activate # 升级 pip,老版本 pip 装某些库会报错 pip install --upgrade pip

逻辑说明:虚拟环境把依赖隔离在项目目录下,换机器时直接删掉重来,不会影响系统 Python。参数-m venv是标准库自带的模块,不需要额外装。激活后命令行前面会出现(venv_garbage)提示。

3.2 核心依赖库清单与安装顺序

根据classify.pymain_ui.py的 import 段,常见依赖包括:opencv-pythonnumpyPillowPyQt5torchtensorflowtorchvision。安装顺序有讲究:先装 numpy,再装 opencv,最后装深度学习框架。因为 opencv 编译时依赖 numpy 的 C 接口,顺序反了可能报numpy.core.multiarray failed to import

# 按依赖关系依次安装,torch 版本根据你的 CUDA 情况选 pip install numpy==1.19.5 pip install opencv-python==4.5.5.64 pip install Pillow==8.4.0 pip install PyQt5==5.15.4 # 如果没有 GPU,用 CPU 版 torch,体积小很多 pip install torch==1.10.0 torchvision==0.11.1

参数说明:numpy==1.19.5是较稳定的版本,兼容大多数老代码。opencv-python不要装 4.8 以上,因为某些 API 改名了。torch的版本要和torchvision对应,去 PyTorch 官网查兼容表。如果你用 TensorFlow,换成tensorflow==2.4.01.15.0,具体看源码里是import tensorflow as tf还是import keras

3.3 验证环境是否就绪

装完后别急着跑主程序,先写个三行脚本验证核心库能不能导入:

# 验证脚本,逐个导入关键库并打印版本 import cv2 import numpy as np import torch print("OpenCV:", cv2.__version__) print("NumPy:", np.__version__) print("PyTorch:", torch.__version__)

逻辑说明:如果这一步报ImportError,说明某个库没装好或版本冲突。常见问题是opencv-pythonopencv-contrib-python同时装了,导致cv2指向错误。解决方法是pip uninstall opencv-python opencv-contrib-python然后只装一个。

4. 模型推理与界面联调:让分类结果在窗口里显示出来

4.1 加载模型与图像预处理流程

classify.py里通常有一个predict函数,接收图像路径或 numpy 数组,返回类别标签和置信度。核心步骤是:读图 → 缩放 → 归一化 → 转 tensor → 前向传播 → softmax → 取最大值。常见代码结构如下:

# 图像预处理与推理,输入是 OpenCV 读到的 BGR 图像 def predict_image(img_bgr, model, class_map): # 转换颜色空间,训练时用的是 RGB img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 缩放到模型输入尺寸,常见是 224x224 img_resized = cv2.resize(img_rgb, (224, 224)) # 归一化到 [0,1] 并转成 float32 img_array = img_resized.astype(np.float32) / 255.0 # 增加 batch 维度,形状从 (224,224,3) 变成 (1,224,224,3) img_tensor = np.expand_dims(img_array, axis=0) # 转成 torch tensor 并调整通道顺序为 (1,3,224,224) img_tensor = torch.from_numpy(img_tensor).permute(0, 3, 1, 2) # 推理时关闭梯度计算,节省显存 with torch.no_grad(): outputs = model(img_tensor) # 取最大概率的索引 _, pred_idx = torch.max(outputs, 1) return class_map[pred_idx.item()]

参数说明:cv2.resize的尺寸必须和训练时一致,否则精度掉得厉害。permute(0,3,1,2)是因为 PyTorch 要求通道在前。如果你用的是 TensorFlow/Keras,预处理可能是tf.image.resizepreprocess_input,不要混用。

4.2 界面按钮事件绑定与结果显示

main_ui.py里会有一个MainWindow类,继承自QMainWindowUi_MainWindow。按钮的clicked信号需要连接到一个槽函数,槽函数里调用predict_image,然后把结果写到QLabelQTextEdit上。

# 按钮点击槽函数,打开文件对话框并显示分类结果 def on_classify_clicked(self): # 打开文件选择对话框,过滤常见图片格式 file_path, _ = QFileDialog.getOpenFileName( self, "选择图片", "", "Images (*.png *.jpg *.jpeg)" ) if not file_path: return # 用 OpenCV 读图 img = cv2.imread(file_path) # 调用推理函数 result = predict_image(img, self.model, CLASS_MAP) # 把结果显示在界面的 label 上 self.result_label.setText(f"分类结果:{result}") # 同时把图片显示在界面上,注意转换颜色空间 img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w, ch = img_rgb.shape q_img = QImage(img_rgb.data, w, h, ch * w, QImage.Format_RGB888) self.image_label.setPixmap(QPixmap.fromImage(q_img))

逻辑说明:QFileDialog.getOpenFileName返回一个元组,第一个元素是路径。QImage构造时最后一个参数ch * w是每行字节数,不传会显示花屏。setPixmap之前最好先setScaledContents(True),让图片自适应 label 大小。

4.3 模型文件缺失时的降级处理

有些资源包里模型权重文件(.pth.h5)可能因为体积大被删了,只留了训练脚本。这时候你运行主程序会报FileNotFoundError。解决办法有两个:一是找原作者要权重,二是自己用dataset里的数据跑一遍训练。训练脚本通常在test-1.pytest-4.py里,或者需要你从classify.py里反推。如果实在找不到,可以先用一个预训练的 ResNet18 临时顶替,至少让界面能跑通。

# 临时用预训练模型替代,仅用于验证界面逻辑 import torchvision.models as models model = models.resnet18(pretrained=True) # 修改最后一层全连接,输出类别数改为 4 model.fc = torch.nn.Linear(512, 4) # 设为评估模式 model.eval()

注意:这只是权宜之计,预训练模型没有在你的数据集上微调过,分类结果基本是随机的。正式使用必须用训练好的权重。

5. 避坑与排查:那些让我熬夜的报错和玄学问题

5.1 现象:运行主程序报No module named 'PyQt5.sip'

原因:PyQt5 安装不完整,或者之前装过 PyQt5 又卸载了但残留了部分文件。解决:先pip uninstall PyQt5 PyQt5-sip PyQt5-Qt5,然后重新pip install PyQt5==5.15.4。如果还不行,检查虚拟环境是否激活,有时候系统 Python 和虚拟环境的包混用了。

5.2 现象:界面能打开,但点击分类按钮后程序卡死

原因:推理过程在主线程里执行,模型加载和计算耗时较长,阻塞了 Qt 的事件循环。解决:把推理放到QThread里,或者至少用QApplication.processEvents()让界面刷新。更简单的办法是加载模型时只加载一次,不要每次点击都重新加载。

5.3 现象:分类结果全是同一类,置信度接近 1.0

原因:图像预处理时归一化参数不对,或者模型输入尺寸和训练时不一致。检查classify.py里的resize尺寸和mean/std归一化值。常见错误是训练时用了transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),推理时却只除了 255。

5.4 现象:cv2.imread返回None,后续操作报NoneType错误

原因:图片路径包含中文或空格,OpenCV 在 Windows 下对中文路径支持不好。解决:用cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), cv2.IMREAD_COLOR)替代cv2.imread。或者先把文件复制到纯英文路径下再读。

5.5 现象:训练时 loss 不下降,准确率卡在 25%

原因:数据集类别不平衡,或者学习率设得太大。检查dataset下每个类别的图片数量,如果trash有 500 张而meat只有 50 张,模型会偏向多数类。解决:用WeightedRandomSampler做重采样,或者对少数类做数据增强。学习率从 0.001 开始试,不要一上来就 0.1。

6. 进阶技巧:用 Grad-CAM 看模型到底在关注哪里

模型跑通之后,你可能会好奇:它到底是根据什么判断这是“可回收物”的?是看到了塑料瓶的轮廓,还是背景里的桌子?这时候可以用 Grad-CAM 生成热力图,把模型注意力可视化出来。这对毕业设计答辩非常有用,老师一看就知道你不仅会调包,还理解模型行为。

实现思路是:取模型最后一个卷积层的输出,对目标类别的分数求梯度,把梯度做全局平均池化得到权重,再和特征图加权求和。PyTorch 有现成的pytorch-grad-cam库,但为了理解原理,我建议手写一遍简化版。

# 简化版 Grad-CAM,针对 ResNet 最后一个卷积层 import torch import torch.nn.functional as F import cv2 import numpy as np def grad_cam(model, img_tensor, target_layer, class_idx): # 保存特征图和梯度 features = [] grads = [] def forward_hook(module, input, output): features.append(output) def backward_hook(module, grad_input, grad_output): grads.append(grad_output[0]) # 注册钩子 handle_f = target_layer.register_forward_hook(forward_hook) handle_b = target_layer.register_backward_hook(backward_hook) # 前向传播 model.zero_grad() output = model(img_tensor) # 取目标类别的分数 score = output[0, class_idx] # 反向传播 score.backward() # 取出特征图和梯度 fmap = features[0].detach().cpu().numpy()[0] # (C, H, W) grad = grads[0].detach().cpu().numpy()[0] # (C, H, W) # 对梯度做全局平均池化,得到每个通道的权重 weights = np.mean(grad, axis=(1, 2)) # (C,) # 加权求和 cam = np.zeros(fmap.shape[1:], dtype=np.float32) for i, w in enumerate(weights): cam += w * fmap[i] # ReLU 激活,只保留正贡献 cam = np.maximum(cam, 0) # 归一化到 [0,1] cam = cam / (cam.max() + 1e-8) # 缩放到原图大小 cam = cv2.resize(cam, (224, 224)) # 移除钩子 handle_f.remove() handle_b.remove() return cam

逻辑说明:register_forward_hook在前向传播时抓取特征图,register_backward_hook在反向传播时抓取梯度。weights是每个通道的重要性,cam是加权后的热力图。最后用cv2.applyColorMap叠加到原图上,就能看到模型关注的区域。

参数说明:target_layer对于 ResNet18 是model.layer4[-1],对于 VGG 是model.features[-1]class_idx是你要解释的类别索引,和CLASS_MAP的键对应。如果热力图全黑,检查score.backward()之前有没有model.zero_grad(),以及target_layer是否真的参与了前向计算。

我一般会在答辩前跑三张测试图:一张塑料瓶、一张剩饭、一张电池,把热力图贴在 PPT 里。老师看到模型确实在关注瓶身和食物残渣,而不是背景,信任度会高很多。从那以后我每次交付模型相关的项目,都强制走一遍 Grad-CAM 验证,不然心里没底。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询