尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
分享一套锋哥原创的基于PyTorch的手写数字识别系统(深度学习+PyQt6+LetNet-5)
大家好我是Java1234_小锋老师分享一套锋哥原创的基于PyTorch的手写数字识别系统(深度学习PyQt6LetNet-5)项目介绍手写数字识别是模式识别与计算机视觉领域的经典问题在邮政分拣、票据录入、教育测评、表单数字化等场景中具有重要应用价值。随着深度学习技术的快速发展卷积神经网络在图像分类任务中展现出优异性能。本文围绕“基于PyTorch的手写数字识别系统”这一课题设计并实现了一套面向桌面端的可视化应用系统完成了从模型训练到实时识别的完整闭环。系统以 Python 为主要开发语言采用 PyTorch 深度学习框架构建改进版 LeNet-5 卷积神经网络并在经典 MNIST 手写数字数据集上完成训练与评估前端交互层基于 PyQt6 实现集成首页介绍、模型训练、数字识别与关于说明等功能模块。训练模块支持在图形界面中配置训练轮数、批次大小与学习率通过 QThread 在后台执行训练任务并利用 Matplotlib 实时绘制损失与准确率曲线识别模块支持鼠标手写与本地图片上传两种输入方式经过灰度化、反色、裁剪、缩放与归一化等预处理后送入模型推理输出预测数字及各类别置信度。在系统设计方面本文采用分层架构思想将用户交互、业务逻辑、模型计算与数据存储进行解耦在功能设计方面形成介绍、训练、识别、关于四大模块在实现方面重点解决了训练不阻塞界面、手写输入采集、上传图片自适应预处理、最优模型保存与结果概率可视化等问题。论文同步给出系统架构图、功能结构图、模块结构图、网络结构图、流程图、时序图、用例图等多类图示增强了设计表达的规范性与可读性。实验结果表明所设计的改进版 LeNet-5 模型在 MNIST 测试集上可取得较高识别准确率系统界面操作简便、流程清晰能够满足本科毕业设计对功能完整性、技术综合性与可演示性的要求。本文工作对理解卷积神经网络原理、掌握 PyTorch 训练流程以及运用 PyQt6 构建桌面应用具有较好的实践意义也可作为后续扩展多位数字识别、模型对比实验或 Web 化部署的基础平台。源码下载链接: https://pan.baidu.com/s/1EIy2zxFCAHBxNn89tPg8Uw?pwd1234提取码: 1234系统展示核心代码数字识别页面。 from io import BytesIO from pathlib import Path from PyQt6.QtCore import Qt, QByteArray from PyQt6.QtGui import QImage, QPixmap from PyQt6.QtWidgets import ( QFileDialog, QFrame, QGridLayout, QHBoxLayout, QLabel, QMessageBox, QPushButton, QVBoxLayout, QWidget, ) from PIL import Image from app.core.predictor import Predictor from app.widgets.draw_canvas import DrawCanvas class RecognizePage(QWidget): 数字识别页面支持手写画板与图片上传两种输入方式。 def __init__(self, project_root: Path, parentNone): 初始化识别页。 Args: project_root: 项目根目录 parent: 父控件 super().__init__(parent) self.project_root project_root self.model_path project_root / models / best_model.pth self.predictor Predictor(self.model_path) self._uploaded_image: Image.Image | None None self._init_ui() def _init_ui(self): 构建识别页界面。 layout QVBoxLayout(self) layout.setContentsMargins(32, 24, 32, 24) layout.setSpacing(16) title QLabel(数字识别) title.setObjectName(pageTitle) subtitle QLabel(在画板上手写数字或上传本地图片进行识别) subtitle.setObjectName(subtitle) layout.addWidget(title) layout.addWidget(subtitle) content_layout QHBoxLayout() content_layout.setSpacing(24) # 左侧输入区域 input_frame QFrame() input_frame.setObjectName(card) input_layout QVBoxLayout(input_frame) input_layout.setContentsMargins(20, 20, 20, 20) input_layout.setSpacing(12) input_title QLabel(输入区域) input_title.setStyleSheet(font-weight: bold; font-size: 15px;) input_layout.addWidget(input_title) # 手写画板 canvas_label QLabel(鼠标手写:) self.draw_canvas DrawCanvas(280, 280) input_layout.addWidget(canvas_label) input_layout.addWidget(self.draw_canvas, alignmentQt.AlignmentFlag.AlignCenter) canvas_btn_layout QHBoxLayout() self.clear_btn QPushButton(清空画板) self.clear_btn.setObjectName(secondaryButton) self.clear_btn.clicked.connect(self._clear_canvas) canvas_btn_layout.addWidget(self.clear_btn) input_layout.addLayout(canvas_btn_layout) # 分隔线 sep QFrame() sep.setFrameShape(QFrame.Shape.HLine) sep.setStyleSheet(color: #e2e8f0;) input_layout.addWidget(sep) # 上传图片 upload_label QLabel(或上传图片:) self.upload_preview QLabel(暂无图片) self.upload_preview.setFixedSize(140, 140) self.upload_preview.setAlignment(Qt.AlignmentFlag.AlignCenter) self.upload_preview.setStyleSheet( border: 1px dashed #cbd5e1; border-radius: 8px; background: #f8fafc; ) upload_btn_layout QHBoxLayout() self.upload_btn QPushButton(选择图片) self.upload_btn.setObjectName(secondaryButton) self.upload_btn.clicked.connect(self._upload_image) upload_btn_layout.addWidget(self.upload_btn) input_layout.addWidget(upload_label) input_layout.addWidget(self.upload_preview, alignmentQt.AlignmentFlag.AlignCenter) input_layout.addLayout(upload_btn_layout) # 识别按钮 self.recognize_btn QPushButton(开始识别) self.recognize_btn.setObjectName(primaryButton) self.recognize_btn.clicked.connect(self._recognize) input_layout.addWidget(self.recognize_btn) content_layout.addWidget(input_frame) # 右侧结果区域 result_frame QFrame() result_frame.setObjectName(card) result_layout QVBoxLayout(result_frame) result_layout.setContentsMargins(20, 20, 20, 20) result_layout.setSpacing(12) result_title QLabel(识别结果) result_title.setStyleSheet(font-weight: bold; font-size: 15px;) result_layout.addWidget(result_title) # 预测数字 self.digit_label QLabel(-) self.digit_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.digit_label.setStyleSheet( font-size: 96px; font-weight: bold; color: #2563eb; padding: 20px; ) result_layout.addWidget(self.digit_label) self.confidence_label QLabel(置信度: -) self.confidence_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.confidence_label.setStyleSheet(font-size: 16px; color: #64748b;) result_layout.addWidget(self.confidence_label) # 预处理后图像预览 pre_label QLabel(预处理后图像 (28×28):) self.preprocessed_preview QLabel() self.preprocessed_preview.setFixedSize(112, 112) self.preprocessed_preview.setAlignment(Qt.AlignmentFlag.AlignCenter) self.preprocessed_preview.setStyleSheet( border: 1px solid #e2e8f0; border-radius: 8px; background: #000000; ) result_layout.addWidget(pre_label) result_layout.addWidget( self.preprocessed_preview, alignmentQt.AlignmentFlag.AlignCenter ) # 概率条形图 prob_title QLabel(各类别概率:) prob_title.setStyleSheet(font-weight: bold; margin-top: 8px;) result_layout.addWidget(prob_title) self.prob_grid QGridLayout() self.prob_grid.setSpacing(4) self.prob_bars: list[QLabel] [] self.prob_labels: list[QLabel] [] for i in range(10): digit_lbl QLabel(str(i)) digit_lbl.setFixedWidth(20) digit_lbl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) bar_lbl QLabel() bar_lbl.setFixedHeight(18) bar_lbl.setStyleSheet(background: #e2e8f0; border-radius: 4px;) prob_lbl QLabel(0.0%) prob_lbl.setFixedWidth(55) prob_lbl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) self.prob_grid.addWidget(digit_lbl, i, 0) self.prob_grid.addWidget(bar_lbl, i, 1) self.prob_grid.addWidget(prob_lbl, i, 2) self.prob_bars.append(bar_lbl) self.prob_labels.append(prob_lbl) result_layout.addLayout(self.prob_grid) result_layout.addStretch() content_layout.addWidget(result_frame, stretch1) layout.addLayout(content_layout) self._update_model_status() def _update_model_status(self): 更新模型加载状态提示。 if self.predictor.is_loaded: self.recognize_btn.setEnabled(True) self.recognize_btn.setText(开始识别) else: self.recognize_btn.setEnabled(False) self.recognize_btn.setText(请先训练模型) def refresh_model(self): 刷新模型训练完成后调用。 self.predictor.load_model(self.model_path) self._update_model_status() def _clear_canvas(self): 清空画板。 self.draw_canvas.clear() self._uploaded_image None self.upload_preview.setText(暂无图片) self.upload_preview.setPixmap(QPixmap()) def _upload_image(self): 上传本地图片。 file_path, _ QFileDialog.getOpenFileName( self, 选择图片, , 图片文件 (*.png *.jpg *.jpeg *.bmp *.gif), ) if file_path: self._uploaded_image Image.open(file_path) pixmap QPixmap(file_path).scaled( 130, 130, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation, ) self.upload_preview.setPixmap(pixmap) self.upload_preview.setText() def _recognize(self): 执行数字识别。 if not self.predictor.is_loaded: QMessageBox.warning(self, 提示, 模型未加载请先在「模型训练」页面完成训练。) return # 优先使用上传图片否则使用画板内容 if self._uploaded_image is not None: input_image self._uploaded_image elif self.draw_canvas.has_content(): input_image self.draw_canvas.to_pil_image() else: QMessageBox.information(self, 提示, 请先在画板上手写数字或上传一张图片。) return try: digit, probs, processed self.predictor.predict(input_image) confidence probs[digit] * 100 self.digit_label.setText(str(digit)) self.confidence_label.setText(f置信度: {confidence:.1f}%) # 显示预处理后图像 buffer BytesIO() processed.save(buffer, formatPNG) qimage QImage.fromData(QByteArray(buffer.getvalue())) preview_pixmap QPixmap.fromImage(qimage).scaled( 100, 100, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation, ) self.preprocessed_preview.setPixmap(preview_pixmap) # 更新概率条 max_prob max(probs) if probs else 1.0 for i, (bar, lbl) in enumerate(zip(self.prob_bars, self.prob_labels)): ratio probs[i] / max_prob if max_prob 0 else 0 width max(1, int(ratio * 200)) color #2563eb if i digit else #94a3b8 bar.setStyleSheet( fbackground: {color}; border-radius: 4px; min-width: {width}px; ) bar.setFixedWidth(width) lbl.setText(f{probs[i]*100:.1f}%) except Exception as exc: QMessageBox.critical(self, 识别失败, f识别过程出错: {exc})基于PyTorch的手写数字识别系统 - 程序入口。 import sys from pathlib import Path from PyQt6.QtWidgets import QApplication from app.main_window import MainWindow def main(): 启动应用程序。 project_root Path(__file__).resolve().parent (project_root / data).mkdir(exist_okTrue) (project_root / models).mkdir(exist_okTrue) (project_root / assets).mkdir(exist_okTrue) app QApplication(sys.argv) app.setApplicationName(基于PyTorch的手写数字识别系统) window MainWindow(project_root) window.show() sys.exit(app.exec()) if __name__ __main__: main()
RELATED

相关推荐

AI Coding 零基础实战教程|第三部分:Claude Code 深度使用与进阶技巧

AI Coding 零基础实战教程|第三部分:Claude Code 深度使用与进阶技巧

AI Coding 零基础实战教程 第三部分:Claude Code 深度使用与进阶技巧 学习目标:在掌握安装与基础配置后,深入掌握 Claude Code 的全部用法与进阶技巧,让 AI 真正成为你的高效编程搭档 完成标志:能熟练使用 Claude Code 完成完整开发流程,并掌握高阶配置、最佳实践与进阶…

📅 2026/9/6 0:00:13
亲测G3800,G3810,TS3380,TS6120,G2810,G4810,MG3180,G5080支持代码5B00,5B02,5B04,1700,1702,1704,P07,E08佳能清零软件。

亲测G3800,G3810,TS3380,TS6120,G2810,G4810,MG3180,G5080支持代码5B00,5B02,5B04,1700,1702,1704,P07,E08佳能清零软件。

蓝奏云:点这里下载 密码:00 百度云:点这里下载 备用:pan.baidu.com/s/1gls2G4rqWWP-Mw-z6tVjnQ?pwd0000 常见型号如下: G1000、G1100、G1200、G1400、G1500、G1800、G1900、G1010、G1110、G1120、G1410、G1420、G1411、G151…

📅 2026/9/16 11:56:20
TI AM64x异构多核SoC:工业边缘设备实时控制与网络服务一体化设计

TI AM64x异构多核SoC:工业边缘设备实时控制与网络服务一体化设计

1. 项目概述:为什么我们需要AM64x这样的处理器?在工业自动化领域干了十几年,我见过太多工程师为了一个项目,不得不在电路板上堆砌各种芯片:一颗主处理器跑Linux做网络服务,一颗MCU做实时控制,再…

📅 2026/8/24 1:28:28
MORE NEWS

更多资讯

📰

遥感知识体系与工程实践:从大气窗口到图像分类的完整链路

简介:《遥感原理与应用》完整版知识点PDF,适合遥感科学与技术、地理信息科学、测绘工程等专业的学生用于期末复习、考研备考或日常夯实基础,也适合在线自学人群。内容直接从课程高频考点切入,围绕电磁波及遥感物理基础、遥感平台及…

📰

Linux内核深度剖析:从系统调用到设备驱动的资源调度机制

简介:操作系统内核是现代计算系统的核心枢纽,它向下管理CPU、内存、磁盘等硬件资源,向上为应用程序提供稳定的执行环境。理解内核的工作原理,通常从用户态与内核态的切换机制入手,系统调用作为唯一合法入口&#xff0c…

📰

SpringBoot宠物商城系统技术文档规范

简介:本资源是一份面向计算机专业本科生的毕业设计参考论文,聚焦Spring Boot技术栈在电商场景中的落地实践,专为宠物商城类毕设选题提供完整理论支撑与技术方案。文档以规范学术格式呈现,涵盖摘要、目录、绪论、关键技术分析&…

📰

Flower 联邦学习差分隐私与安全聚合实战:基于 MNIST 的 fl-dp-sa 示例源码级解析

Flower 联邦学习差分隐私与安全聚合实战:基于 MNIST 的 fl-dp-sa 示例源码级解析 【免费下载链接】flower Flower: A Friendly Federated AI Framework 项目地址: https://gitcode.com/GitHub_Trending/flo/flower 本文以 Flower 仓库中的 fl-dp-sa 示例&…

📰

低轨卫星分布式路由:基于时间快照的Dijkstra算法仿真实现

简介:一份聚焦低轨卫星通信网络的高效分布式路由算法研究文档,面向通信工程、卫星互联网及人工智能相关方向的研究者与学习者,系统梳理了低轨卫星系统的特点与路由优化需求。全文以“问题—设计—仿真—拓展”为线索,从分布式路由…

📰

基于SSM框架的大学生兼职系统设计与实现解析

如果你是在校生,或者刚接触Java Web开发,那“SSM222的大学生兼职系统”这个题目你大概率不会陌生。这几乎是国内软件工程、计算机科学与技术专业毕业设计里出场率最高的选题之一,每年都有大量学生拿它练手、交作业,甚至很多项目实…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬