当前位置: 首页 > news >正文

基于深度学习的可回收垃圾材质识别与分类研究毕业设计--整套 C/S 架构完整方案

架构组成:
• Server 服务端:Flask + 你训练好的 ResNet18 五分类模型(后端接口)
• Client 客户端:PyQt5 桌面界面(不本地加载模型,只调接口)
• 流程:PyQt 选图 → 传给 Flask 后端 → 后端 AI 识别 → 返回类别 + 置信度 → 界面展示

安装依赖

pip install flask torch torchvision pillow requests pyqt5

一、服务端 Flask 代码(新建 server.py)

放在和 garbage_resnet18_best.pth (用迁移学习resnet18跑出来的最优模型)同目录

from flask import Flask, request, jsonify import torch import torch.nn as nn from torchvision import models, transforms from PIL import Image import os import warnings warnings.filterwarnings("ignore") app = Flask(__name__) DEVICE = torch.device("cpu") # ====================== ✅ 你的官方正确类别顺序 ====================== CLASS_NAMES = ['塑料类', '玻璃类', '纸质类', '纺织类', '金属类'] # ==================================================================== BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MODEL_PATH = os.path.join(BASE_DIR, "garbage_resnet18_best.pth") transform = transforms.Compose([ transforms.Resize([224, 224]), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) def load_model(): model = models.resnet18(weights=None) num_ftrs = model.fc.in_features model.fc = nn.Sequential( nn.Dropout(0.3), nn.Linear(num_ftrs, 5) ) state_dict = torch.load(MODEL_PATH, map_location=DEVICE) model.load_state_dict(state_dict) model.to(DEVICE) model.eval() return model model = load_model() print("✅ 模型加载成功,后端服务启动") @app.route('/predict', methods=['POST']) def predict(): try: file = request.files['image'] img = Image.open(file.stream).convert("RGB") img_tensor = transform(img).unsqueeze(0).to(DEVICE) with torch.no_grad(): out = model(img_tensor) prob = torch.softmax(out, dim=1) conf, idx = torch.max(prob, dim=1) return jsonify({ "class": CLASS_NAMES[idx.item()], "confidence": round(conf.item() * 100, 2) }) except Exception as e: return jsonify({"error": str(e)}) if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=False)

二、客户端 PyQt5 代码(新建 client_ui.py)

不再本地加载模型,只调用后端接口,标准 C/S 架构

import sys import requests from PyQt5.QtWidgets import * from PyQt5.QtGui import QPixmap, QFont from PyQt5.QtCore import Qt # 后端接口地址 API_URL = "http://127.0.0.1:5000/predict" class GarbageUI(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("可回收垃圾智能识别系统【C/S架构】") self.setFixedSize(750, 650) self.img_path = None self.font = QFont("微软雅黑", 10) self.setFont(self.font) central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) main_layout.setSpacing(25) main_layout.setContentsMargins(50, 40, 50, 40) title = QLabel("♻️ 基于C/S架构的可回收垃圾智能识别系统") title.setFont(QFont("微软雅黑", 18, QFont.Bold)) title.setAlignment(Qt.AlignCenter) title.setStyleSheet("color: #1E9050;") self.img_label = QLabel() self.img_label.setFixedSize(600, 320) self.img_label.setStyleSheet(""" QLabel{ border: 2px dashed #888; border-radius: 12px; background-color: #ffffff; color: #666; font-size:14px; } """) self.img_label.setAlignment(Qt.AlignCenter) self.img_label.setText("🖼️ 请选择需要识别的垃圾图片") self.btn_choose = QPushButton("📁 选择图片") self.btn_predict = QPushButton("🔍 开始识别") btn_style = """ QPushButton{ font-size:14px; padding:12px 25px; border-radius:10px; background-color:#1E9050; color:white; font-weight:bold; min-width:180px; } QPushButton:hover{ background-color:#25A75F; } QPushButton:pressed{ background-color:#187A43; } """ self.btn_choose.setStyleSheet(btn_style) self.btn_predict.setStyleSheet(btn_style) btn_layout = QHBoxLayout() btn_layout.addStretch() btn_layout.addWidget(self.btn_choose) btn_layout.addSpacing(30) btn_layout.addWidget(self.btn_predict) btn_layout.addStretch() self.result_label = QLabel("识别结果:等待连接服务器") self.result_label.setAlignment(Qt.AlignCenter) self.result_label.setFont(QFont("微软雅黑", 14, QFont.Bold)) self.result_label.setStyleSheet("color:#2C3E50; margin-top:10px;") self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.status_bar.showMessage("客户端就绪,请先启动后端服务") main_layout.addWidget(title) main_layout.addWidget(self.img_label, alignment=Qt.AlignCenter) main_layout.addLayout(btn_layout) main_layout.addWidget(self.result_label) self.btn_choose.clicked.connect(self.choose_image) self.btn_predict.clicked.connect(self.run_predict) def choose_image(self): path, _ = QFileDialog.getOpenFileName(filter="图片 (*.jpg *.jpeg *.png *.bmp)") if path: self.img_path = path pix = QPixmap(path).scaled(580, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.img_label.setPixmap(pix) self.result_label.setText("✅ 已加载图片,点击开始识别") self.status_bar.showMessage(f"已加载图片:{path}") def run_predict(self): if not self.img_path: self.result_label.setText("❌ 请先选择图片!") return self.result_label.setText("🔍 正在请求服务器识别...") QApplication.processEvents() try: # 传图片给后端 files = {"image": open(self.img_path, "rb")} res = requests.post(API_URL, files=files, timeout=10) data = res.json() if "error" in data: self.result_label.setText(f"❌ 识别失败:{data['error']}") else: self.result_label.setText( f"🗑️ 识别类别:{data['class']} 置信度:{data['confidence']}%" ) self.status_bar.showMessage("识别完成,服务器返回结果成功") except Exception as e: self.result_label.setText("❌ 无法连接后端服务,请先启动 server.py") if __name__ == "__main__": app = QApplication(sys.argv) win = GarbageUI() win.show() sys.exit(app.exec_())

三、运行顺序(必须按这个来)

1. 先运行 server.py 启动后端 Flask 服务
2. 再运行 client_ui.py 打开桌面客户端
3. 客户端选图 → 点开始识别 → 自动调用后端 AI 模型

四、毕设 C/S 架构说明(可直接复制论文)

基于C/S(客户端 / 服务器)架构设计可回收垃圾智能识别系统,
• 服务端(Server)采用 Flask 框架搭建接口服务,部署训练完成的 ResNet18 深度学习模型,负责垃圾图像的特征提取与智能分类推理;
• 客户端(Client)基于 PyQt5 开发桌面可视化界面,仅提供图片选择、结果展示等人机交互功能,不加载深度学习模型;
• 客户端通过 HTTP 协议将待识别图片上传至服务端,服务端完成推理后将分类类别与置信度返回客户端展示。
该架构实现业务界面与 AI 模型解耦,支持多客户端共用模型服务,易维护、易扩展,满足桌面智能识别系统设计要求。

四、把后端服务 + PyQt 客户端打包成独立 EXE

打包后:电脑不用装 Python、不用装任何库,双击直接运行,答辩插上电脑就能演示。
前置准备
1. 已经装好打包工具

pip install pyinstaller

2.把我刚给你的正确版 server.py保存好

3.把你的garbage_resnet18_best.pth模型文件放在同文件夹

4.两个文件路径都在:C:\本科毕业论文\garbage\

第一步:先打包后端 server.py

1. 进入项目文件夹

文件夹地址栏输入cmd回车,打开命令行。

2. 执行打包命令(后端专用)

pyinstaller -F -c server.py

参数说明:
• -F 打包成单个 exe
• -c 保留黑框控制台(能看启动日志、识别请求,答辩好用)

3. 打包完成后

进入文件夹:garbage\dist\里面会出现:server.exe

关键一步(必做)

garbage_resnet18_best.pth复制到 dist 文件夹里👉 exe 和模型必须在同一个文件夹,否则找不到模型!


第二步:打包 PyQt 客户端 client_ui.py

执行打包命令

pyinstaller -F -w client_ui.py
  • -w无黑框,纯桌面界面

打包好后dist里出现:client_ui.exe

客户端不用带模型,只调接口,直接能用。


第三步:答辩使用流程(以后每次都这么用)

  1. 打开dist文件夹
  2. 先双击运行 server.exe看到模型加载成功,Running on http://127.0.0.1:5000就别动黑框
  3. 再双击运行 client_ui.exe打开系统界面
  4. 选图片 → 开始识别,完全正常使用
  5. 关闭直接点右上角就行,不用任何环境配置

第四步:我给你规避打包必踩的坑(已经帮你做好)

  1. 我改的server.py自动读取 exe 所在路径,不会报找不到模型
  2. 类别顺序和你训练集完全一致,识别不会错乱
  3. 打包后不依赖任何 Python 环境,任何 Windows 电脑都能跑

第五步:我把最终可直接打包的两份完整代码发给你

1. 打包专用最终版 server.py(直接覆盖旧文件)

from flask import Flask, request, jsonify import torch import torch.nn as nn from torchvision import models, transforms from PIL import Image import os import warnings warnings.filterwarnings("ignore") app = Flask(__name__) DEVICE = torch.device("cpu") # 和你训练集严格对齐的类别映射 CLASS_NAMES = ['塑料类', '玻璃类', '纸质类', '纺织类', '金属类'] # 自动获取exe所在目录,打包不会找不到模型 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MODEL_PATH = os.path.join(BASE_DIR, "garbage_resnet18_best.pth") # 与训练完全一致的预处理 transform = transforms.Compose([ transforms.Resize([224, 224]), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) def load_model(): model = models.resnet18(weights=None) num_ftrs = model.fc.in_features model.fc = nn.Sequential( nn.Dropout(0.3), nn.Linear(num_ftrs, 5) ) state_dict = torch.load(MODEL_PATH, map_location=DEVICE) model.load_state_dict(state_dict) model.to(DEVICE) model.eval() return model model = load_model() print("✅ 模型加载成功,后端服务启动") @app.route('/predict', methods=['POST']) def predict(): try: file = request.files['image'] img = Image.open(file.stream).convert("RGB") img_tensor = transform(img).unsqueeze(0).to(DEVICE) with torch.no_grad(): out = model(img_tensor) prob = torch.softmax(out, dim=1) conf, idx = torch.max(prob, dim=1) return jsonify({ "class": CLASS_NAMES[idx.item()], "confidence": round(conf.item() * 100, 2) }) except Exception as e: return jsonify({"error": str(e)}) if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=False)

2. 打包专用最终版 client_ui.py

import sys import requests from PyQt5.QtWidgets import * from PyQt5.QtGui import QPixmap, QFont from PyQt5.QtCore import Qt # 后端固定接口地址 API_URL = "http://127.0.0.1:5000/predict" class GarbageUI(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("可回收垃圾智能识别系统【C/S架构】") self.setFixedSize(750, 650) self.img_path = None self.font = QFont("微软雅黑", 10) self.setFont(self.font) central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) main_layout.setSpacing(25) main_layout.setContentsMargins(50, 40, 50, 40) title = QLabel("♻️ 基于C/S架构的可回收垃圾智能识别系统") title.setFont(QFont("微软雅黑", 18, QFont.Bold)) title.setAlignment(Qt.AlignCenter) title.setStyleSheet("color: #1E9050;") self.img_label = QLabel() self.img_label.setFixedSize(600, 320) self.img_label.setStyleSheet(""" QLabel{ border: 2px dashed #888; border-radius: 12px; background-color: #ffffff; color: #666; font-size:14px; } """) self.img_label.setAlignment(Qt.AlignCenter) self.img_label.setText("🖼️ 请选择需要识别的垃圾图片") self.btn_choose = QPushButton("📁 选择图片") self.btn_predict = QPushButton("🔍 开始识别") btn_style = """ QPushButton{ font-size:14px; padding:12px 25px; border-radius:10px; background-color:#1E9050; color:white; font-weight:bold; min-width:180px; } QPushButton:hover{ background-color:#25A75F; } QPushButton:pressed{ background-color:#187A43; } """ self.btn_choose.setStyleSheet(btn_style) self.btn_predict.setStyleSheet(btn_style) btn_layout = QHBoxLayout() btn_layout.addStretch() btn_layout.addWidget(self.btn_choose) btn_layout.addSpacing(30) btn_layout.addWidget(self.btn_predict) btn_layout.addStretch() self.result_label = QLabel("识别结果:等待连接服务器") self.result_label.setAlignment(Qt.AlignCenter) self.result_label.setFont(QFont("微软雅黑", 14, QFont.Bold)) self.result_label.setStyleSheet("color:#2C3E50; margin-top:10px;") self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.status_bar.showMessage("客户端就绪,请先启动后端服务") main_layout.addWidget(title) main_layout.addWidget(self.img_label, alignment=Qt.AlignCenter) main_layout.addLayout(btn_layout) main_layout.addWidget(self.result_label) self.btn_choose.clicked.connect(self.choose_image) self.btn_predict.clicked.connect(self.run_predict) def choose_image(self): path, _ = QFileDialog.getOpenFileName(filter="图片 (*.jpg *.jpeg *.png *.bmp)") if path: self.img_path = path pix = QPixmap(path).scaled(580, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.img_label.setPixmap(pix) self.result_label.setText("✅ 已加载图片,点击开始识别") self.status_bar.showMessage(f"已加载图片:{path}") def run_predict(self): if not self.img_path: self.result_label.setText("❌ 请先选择图片!") return self.result_label.setText("🔍 正在请求服务器识别...") QApplication.processEvents() try: files = {"image": open(self.img_path, "rb")} res = requests.post(API_URL, files=files, timeout=10) data = res.json() if "error" in data: self.result_label.setText(f"❌ 识别失败:{data['error']}") else: self.result_label.setText( f"🗑️ 识别类别:{data['class']} 置信度:{data['confidence']}%" ) self.status_bar.showMessage("识别完成,服务器返回结果成功") except Exception as e: self.result_label.setText("❌ 无法连接后端服务,请先启动 server.exe") if __name__ == "__main__": app = QApplication(sys.argv) win = GarbageUI() win.show() sys.exit(app.exec_())

你直接:

  1. 替换这两个代码文件
  2. 按我上面的命令打包
  3. 把 pth 模型放进 dist 文件夹
http://www.jsqmd.com/news/774036/

相关文章:

  • 基于MCP协议的文档解析服务器:统一处理PDF与Office文档的AI应用利器
  • Scipy优化踩坑实录:trust-constr和SLSQP约束定义到底差在哪?
  • 【四方杰芯】FSW7222A ——Dual 2:1 USB2 .0 Mux/De-Mux
  • 2025最权威的AI辅助论文助手横评
  • 别让工具成为摆设:写给新手的 Kali Linux 命令“破壁”指南
  • 非均匀网格Poisson求解器:原理、优化与应用
  • NVIDIA Profile Inspector深度解析:解锁隐藏显卡设置的技术指南
  • Konteks-Skill框架:快速构建与部署标准化AI功能模块的实践指南
  • valgrind的使用
  • Three.js开发调试革命:AI助手通过MCP协议实现零侵入实时调试
  • 为什么你还在用“感觉”管技术债务?AISMM模型强制引入可审计、可回溯、可量化的债务治理SLA
  • 2026年数据治理平台综合竞争力排行:五家主流厂商技术路线全景解读
  • 设计制作哪家好
  • 基于Spring Boot与Vue的全栈AI应用HugAi:集成GPT对话与AI绘画
  • 逆向工程助手:自动化与智能化工具链整合实践
  • 借助Taotoken快速切换不同模型以应对API服务波动
  • .NET+AI | Harness | MAF 1.4 发布,Harness Engineering 如约而至,智能体工程化更进一步
  • 自托管AI记忆系统Mnemonic:为智能体构建本地化记忆中枢
  • RA8P1 Titan开发板:高性能Cortex-M85 AIoT解决方案
  • 经典的Java双重检查锁代码
  • 一文带你搞懂现在爆火的RAG,究竟是什么
  • 专业的企业官网搭建怎么选?别再踩坑了!从技术底层拆解微加AI如何保底护航
  • POD 定制耗时费力?凌风工具箱批量操作,高效搞定全套定制设置
  • ORA富集分析避坑指南:为什么你的通路结果总是不显著?可能是这4个参数没设对
  • 【国家级AI安全强制标准前哨】:AISMM如何定义“可验证智能”?3大不可绕过的技术红线与2025Q4企业自检清单
  • Turnitin升级后查AI率更严!英文论文AI率 88%降到*%,5个降AI方法亲测有效
  • 工业自动化控制板上,隔离RS-485收发器如何护航总线通信?
  • 工业级高密度电力配置预算与可靠性平衡路径解析
  • 基于Unity的虚拟人开发:从架构解析到实战部署
  • 渗透测试实战(一):文件传输全技法与深度解析