从零跑通!纯 Python 手写汉字识别项目:PyTorch 训练 + Qt5 桌面端 UI(附万字报告框架)
一、前言:毕设痛点
很多同学一到毕设季就头大:
- 环境难配:论文里写的是 TensorFlow 1.x,GitHub 上是 PyTorch 1.x,自己电脑装的是 2.x,一路报错。
- 代码到处报错:依赖缺、路径错、GPU/CPU 不兼容,改了一堆还是跑不通 Demo。
- 没有完整思路:只有一个简单的分类脚本,没有数据预处理、可视化、UI 展示,很难写出一篇像样的设计与实现章节。
本项目就是专门为这类“想做深度学习毕设,但不想被环境折磨”的同学准备的:
- 纯 Python 开发:项目核心采用 Python + PyTorch + OpenCV + PyQt5,Windows 下可直接运行。
- 一键运行主程序:pycharm上一键运行主程序。
- 完整 UI 界面:使用 Qt5 设计精美的手写汉字识别界面,包含单张检测、批量检测、结果导出等功能,非常适合课堂展示和答辩 Demo。
- 配套大规模数据集:
all_data文件夹中已经准备好了大量手写汉字样本(数万张图片),可以按需裁剪成 毕设数据集。 - 训练 + 推理一条龙:从数据划分、训练、可视化、到最终 GUI 调用模型预测,完整闭环,非常适合写论文“系统设计与实现”章节。
二、系统演示(视觉冲击)
-
主界面效果图
![[请在此处插入项目主界面截图]](https://i-blog.csdnimg.cn/direct/5b4145555f784ed79c6f47f5b94d153e.png)
主界面采用深色背景 + 卡片式布局,左边是识别结果和置信度,中间展示当前输入图像,右侧支持单张检测和批量检测操作,并可将识别结果一键导出为
CSV文件。 -
识别效果图
![[请在此处插入识别成功效果图]](https://i-blog.csdnimg.cn/direct/a8aab5629e6a415ab63812ae086270be.png)
在实测环境(普通笔记本 + CPU / 简单独显)下:
- 分类准确率:在验证集上可稳定在 95% 左右(具体视数据划分与训练轮数而定)。
- 识别速度:单张图像预测延迟在几十毫秒量级,FPS > 30 完全可满足实时交互的体验需求。
- 批量检测:支持选中文件夹后自动逐张识别,表格实时更新图片路径、类别和置信度。
三、核心技术栈解析
-
Python 3.8+(实测 3.11 亦可)
- 优势:生态成熟,第三方库齐全,遇到问题基本都能在社区搜到答案,非常适合毕设和课程设计。
-
PyTorch(深度学习框架)
- 用途:实现 MobileNet 网络结构、训练、验证、模型推理等。
- 优势:动态图机制友好,上手快,官方和中文社区教程丰富。
-
OpenCV(
cv2)- 用途:对手写汉字图片做基本预处理,例如灰度化、二值化、轮廓提取等。
- 优势:经典图像处理库,和深度学习框架搭配非常常见,适合课程报告中介绍“传统+深度”的结合。
-
PyQt5 / Qt5(桌面 UI)
- 用途:构建完整的可视化操作界面,包括图片显示、按钮交互、表格展示、结果导出等。
- 优势:界面美观、交互友好,老师和答辩评委更容易直观理解你的系统功能。
-
Matplotlib / TensorBoard
- 用途:绘制精度曲线、损失曲线,并支持用 TensorBoard 查看训练过程。
- 优势:非常适合写“实验结果与分析”章节,报告里可以直接放训练曲线截图。
四、项目目录结构(Markdown Tree)
下面是精简后的项目结构(去掉大量图片文件,只保留关键目录和脚本),方便大家整体把握项目组织方式:
.
├── all_data/ # 手写汉字原始数据集(按类别子文件夹划分)
├── models/
│ └── mobilenet.py # MobileNet 网络结构定义(特征提取 backbone)
├── process/
│ └── yuchuli.py # 图像预处理相关脚本(辅助处理/可选)
├── ui/
│ ├── background.png # 界面背景图
│ ├── 图像.png # 单张图片选择图标
│ ├── 文件夹.png # 文件夹选择图标
│ └── main.qrc / 等资源 # Qt 资源文件
├── font/
│ └── simsun.ttc / ... # 中文字体文件,用于绘制中文可视化
├── weights/
│ ├── handwrite-best-epoch.pth # 训练好的最佳模型权重(推理时加载)
│ ├── loss.txt # 最优 epoch 指标记录
│ └── log.txt # 训练过程日志(loss / acc)
├── 先看我-项目使用说明/
│ ├── 使用说明.txt # 图文版项目使用说明
│ ├── 毕设文档示例.pdf # 论文模板/示例(用于撰写毕业论文)
│ └── ... # 其他辅助说明与截图
├── 主界面.py # 项目主入口(PyQt5 图形界面 + 模型推理)
├── ui.py # 由 Qt Designer 生成的 UI 布局 Python 文件
├── predict.py # 命令行版本单张图片预测脚本(无 UI)
├── train.py # 模型训练主程序(支持多 epoch 训练与最优模型保存)
├── my_dataset.py # 自定义数据集类(用于 DataLoader)
├── utils.py # 工具函数:数据划分、训练/验证循环、可视化等
├── class_indices.json # 类别索引与汉字标签对应关系
├── data_name.txt # 类别编码与中文名称映射(用于界面展示)
├── dataset.png # 各类别样本数统计柱状图(自动生成)
├── 精确度曲线图.png # 训练/验证精度曲线(train.py 自动生成)
├── 损失值曲线图.png # 训练/验证损失曲线(train.py 自动生成)
└── confusion_matrix.py / .png # 混淆矩阵绘制脚本及结果
从目录可以看出,训练、数据处理、推理、界面展示、结果分析都被清晰拆分成独立模块,结构非常适合直接写进毕设报告的“系统结构设计”小节。
五、核心代码展示
1. 主窗口 UI 初始化与模型加载
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self) # 加载 Qt Designer 生成的界面布局
# ===== 日期与星期显示 =====
today = str(datetime.datetime.now().date())
weekday = int(datetime.datetime.now().weekday()) # 0~6 对应周一到周日
self.label_13.setText(today.split('-')[0] + '/' + today.split('-')[1]+ '/' + today.split('-')[2])
if weekday == 0:
self.label_14.setText('星期一')
if weekday == 1:
self.label_14.setText('星期二')
if weekday == 2:
self.label_14.setText('星期三')
if weekday == 3:
self.label_14.setText('星期四')
if weekday == 4:
self.label_14.setText('星期五')
if weekday == 5:
self.label_14.setText('星期六')
if weekday == 6:
self.label_14.setText('星期日')
# ===== 表格组件初始化 =====
self.tableWidget.resizeColumnsToContents()
self.tableWidget.resizeRowsToContents()
self.tableWidget.setRowCount(4)
self.tableWidget.setColumnCount(4)
self.tableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch) # 列自适应
self.tableWidget.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch) # 行自适应
self.tableWidget.verticalHeader().hide() # 隐藏行号
self.tableWidget.setHorizontalHeaderLabels(['序号', '图像路径', '识别类型','置信度'])
self.tableWidget.setShowGrid(True) # 显示网格线
# ===== 结果标签置空 =====
self.label_res.setText('')
self.label_pro.setText('')
self.lineEdit_pic.setPlaceholderText(' 点击选择图像')
self.lineEdit_folder.setPlaceholderText(' 点击选择文件夹')
# ===== 模型加载部分 =====
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 读取类别索引映射,后续用于把模型输出转换为汉字标签
json_path = './class_indices.json'
assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)
json_file = open(json_path, "r")
self.class_indict = json.load(json_file)
# 创建网络结构并加载权重(核心结构在 models/mobilenet.py 中定义)
self.model = create_model().to(self.device)
model_weight_path = "weights/handwrite-best-epoch.pth"
self.model.load_state_dict(torch.load(model_weight_path, map_location=self.device))
self.model.eval() # 推理模式,不进行反向传播
# ===== 信号与槽绑定(按钮交互)=====
self.tableWidget.cellPressed.connect(self.getPosContent) # 点击表格中的一行,展示对应图片结果
self.pushButton_pic.clicked.connect(self.select_img) # 选择单张图像
self.pushButton_pic_begin.clicked.connect(self.img_detect) # 对当前图像进行检测
self.pushButton_folder.clicked.connect(self.choose_folder) # 选择批量检测文件夹
self.pushButton_folder_begin.clicked.connect(self.start_detect_folder) # 启动/停止批量检测
self.timer_folder = QtCore.QTimer()
self.timer_folder.timeout.connect(self.detect_folder_timer) # 定时器驱动逐张处理文件夹图片
self.pushButton_data.clicked.connect(self.save_data) # 导出识别结果到 CSV
# ===== 读取类别中文名称,用于界面显示 =====
with open('data_name.txt', 'r', encoding='utf-8')as fb:
name_data = fb.readlines()
self.name_dict = {}
for i in name_data:
self.name_dict[i.strip().split('+')[0]] = i.strip().split('+')[1]
2. 图像预处理 + 模型推理入口(界面版)
def img_detect(self):
# 清空表格内容,只保留一行用于当前图片的结果
self.tableWidget.clearContents()
self.tableWidget.setRowCount(1)
self.folder_res = []
# ===== 与训练阶段保持一致的预处理 =====
img_size = 224
data_transform = transforms.Compose(
[transforms.Resize(int(img_size * 1.143)), # 先按比例缩放
transforms.CenterCrop(img_size), # 再从中心裁剪为 224x224
transforms.ToTensor(), # 转为张量 [C,H,W]
transforms.Normalize([0.485, 0.456, 0.406], # 归一化(与 ImageNet 一致)
[0.229, 0.224, 0.225])])
# ===== 读取并预处理图像 =====
assert os.path.exists(self.img_path), "file: '{}' dose not exist.".format(self.img_path)
# 先做一次传统图像处理(如二值化+轮廓填充),增强手写汉字的结构信息
self.process_image('test.png') # 这里封装了 OpenCV 相关预处理逻辑
img = Image.open('processed_image.jpg')
img = data_transform(img) # 套用和训练阶段完全一致的 transform
img = torch.unsqueeze(img, dim=0) # 增加 batch 维度 -> [1,C,H,W]
# ===== 调用训练好的模型进行前向推理 =====
with torch.no_grad(): # 不计算梯度,更快更省显存
output = torch.squeeze(self.model(img.to(self.device))).cpu()
predict = torch.softmax(output, dim=0) # 转为概率分布
predict_cla = torch.argmax(predict).numpy() # 获取概率最大的类别索引
# 打印每个类别的概率,可以作为 Debug 或命令行版本输出
for i in range(len(predict)):
print("class: {:10} prob: {:.3}".format(self.class_indict[str(i)],
predict[i].numpy()))
# 取出最大概率对应的类别编码与置信度
res = self.class_indict[str(list(predict.numpy()).index(max(predict.numpy())))]
num = "%.2f" % (max(predict.numpy()) * 100) + "%"
# 将结果显示到界面:大号汉字 + 置信度
self.label_res.setText(self.name_dict[res]) # 将编码转成真正汉字,如 "你"、"我"
self.label_pro.setText(num)
# 同步写入表格(序号 / 路径 / 类型 / 置信度)
for column, data in enumerate(['1', self.img_path, self.name_dict[res], num]):
self.tableWidget.setItem(0, column, QtWidgets.QTableWidgetItem(str(data)))
3. 命令行预测脚本入口(无界面版本)
如果你想在pycharm上或命令行下快速测试模型效果,可以直接调用 predict.py 中的 main 函数,这段代码结构清晰,非常适合作为“模型推理模块设计”的代码示例。
def main(img_path):
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
img_size = 224
data_transform = transforms.Compose(
[transforms.Resize(int(img_size * 1.143)),
transforms.CenterCrop(img_size),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
# ===== 1. 加载待预测图片 =====
assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
img = Image.open(img_path)
plt.imshow(img) # 这里可以直接 matplotlib 显示原图(调试/可视化)
img = data_transform(img)
img = torch.unsqueeze(img, dim=0) # [1,C,H,W]
# ===== 2. 读取类别索引映射 =====
json_path = './class_indices.json'
assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)
json_file = open(json_path, "r")
class_indict = json.load(json_file)
# ===== 3. 构建模型并加载权重 =====
model = create_model(class_num=200).to(device)
model_weight_path = "weights/best-epoch.pth"
model.load_state_dict(torch.load(model_weight_path, map_location=device))
model.eval()
# ===== 4. 前向推理,输出概率与标签 =====
with torch.no_grad():
output = torch.squeeze(model(img.to(device))).cpu()
predict = torch.softmax(output, dim=0)
predict_cla = torch.argmax(predict).numpy()
for i in range(len(predict)):
print("class: {:10} prob: {:.3}".format(class_indict[str(i)],
predict[i].numpy()))
# ===== 5. 将类别编码转为可读中文名称 =====
with open('data_name.txt','r',encoding='utf-8')as fb:
name_data=fb.readlines()
name_dict={}
for i in name_data:
name_dict[i.strip().split('+')[0]]=i.strip().split('+')[1]
res = class_indict[str(list(predict.numpy()).index(max(predict.numpy())))]
num= "%.2f" % (max(predict.numpy()) * 100) + "%"
print(name_dict[res],num)
return name_dict[res],num
六、如何运行本项目(保姆级步骤)
下面以 Windows + Python 环境为例,给出一套从零跑通项目的流程。假设你已经拿到了完整项目压缩包。
步骤 1:创建虚拟环境并安装依赖
建议使用 conda 或 venv 创建隔离环境,例如:
# 进入项目根目录
cd mobile_net_handchinese_02
# (可选)创建并激活虚拟环境
conda create -n handchinese python=3.11 -y
conda activate handchinese
# 安装依赖(项目配套资源中提供了 requirements.txt)
pip install -r requirements.txt
主要依赖包括:
torch,torchvision,PyQt5,opencv-python,matplotlib,tqdm,tensorboard,Pillow等。
步骤 2:一键打开图形界面
项目主入口在 主界面.py,直接运行即可打开完整的手写汉字识别界面:
python 主界面.py
运行成功后,你会看到:
- 窗口顶部显示“基于深度学习的手写汉字识别系统”标题;
- 左侧大号汉字区域用于显示识别结果;
- 中间为当前待识别图片预览;
- 右侧为“单张检测”“批量检测”“结果导出”等按钮。
步骤 3:在界面中进行识别操作
-
单张图片识别:
- 点击右侧小图片按钮(或“点击选择图像”输入框右侧图标),选择一张手写汉字图片;
- 点击“开始检测”按钮;
- 界面中部会显示该图片,左侧大号汉字显示识别结果,下方显示置信度,表格中新增一行记录。
-
批量图片识别:
- 点击文件夹按钮,选择包含多张手写汉字图片的文件夹;
- 点击“批量检测”,系统会自动逐张识别,表格实时刷新每一张图像的类别与置信度;
- 检测完成后,点击“结果导出”,所有识别结果会被保存到
output.csv文件中,方便后续统计或写论文中的实验结果表格。
-
模型训练(可选,进阶):
-
想要自己从零训练模型,可以直接运行:
python train.py -
训练完成后,脚本会在
weights/下自动保存最优模型权重,并生成精度曲线图和损失曲线图,非常适合作为论文中的“实验结果可视化”。
-
七、获取方式
由于篇幅有限,本文只展示了项目的整体结构、部分核心调用代码和运行流程:
- 完整的 Python 源码工程(含训练、预测、GUI、可视化等模块);
- 已训练好的 MobileNet 手写汉字识别模型权重文件;
- 用于毕设/课程设计的 8500字项目报告(含系统分析、设计、实现与实验部分的写作模板);
- 以及配套的 大规模手写汉字数据集与可视化图表文件,
均已整理打包好。如果你希望直接上手跑通、按此项目写出一篇完整的毕业设计 / 课程设计报告,可以点击下方链接获取完整资源包。
项目获取链接:https://my.feishu.cn/wiki/NAnUwOrBBifpCfkfn3pcT9gsnVe?from=from_copylink
更多推荐
所有评论(0)