ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

舌象识别毕设项目:ResNet50+PyQt5完整工程落地

舌象识别毕设项目:ResNet50+PyQt5完整工程落地 简介本资源是一套面向计算机专业本科生的毕业设计实战项目聚焦中医舌诊数字化场景基于深度学习实现舌苔图像的识别、检测与分类鉴定配套完整GUI交互界面适用于毕设开题、中期答辩及终期交付全流程。资源共109个文件包含26个核心Python源码含模型训练、推理、UI逻辑、6个预训练.pth模型文件、7张典型舌苔样本图、2个Qt Designer生成的.ui界面文件、2个Word格式论文文档含摘要、方法、实验与答辩PPT提纲以及日志文件和字体资源等整体压缩包大小为105.43MB。已有196人下载学习项目经导师指导并获99分高分评价代码结构清晰、注释完整、环境配置简易小白可直接运行调试特别适合缺乏项目经验的应届生快速上手亦可用于课程设计或AI视觉方向的实践拓展。1. 这不是又一个“AI看舌头”的Demo它真能跑通从数据采集→模型训练→GUI部署的完整闭环且已通过高校毕设答辩99分你搜“舌苔识别 毕业设计”十有八九点开的是PPT截图几行Keras代码一张舌图标注框——运行不了、改不了、交不了。而这份资源是某双非院校计算机专业大四学生真实交付的毕设项目导师签字页、答辩记录表、查重报告、99分评分表全在压缩包里。它不玩概念不做“伪端到端”原始舌象图来自公开中医舌诊数据集含正常/薄白苔/黄腻苔/灰黑苔四类用ResNet50微调完成分类TensorFlow 2.6 Keras实现训练日志events.out.tfevents.*全部保留可回溯最关键的是——它带一个真正能双击运行、支持拖拽图片/实时摄像头识别、结果带置信度中医术语解释的PyQt5 GUI不是Jupyter Notebook里敲两行predict()就完事。适合两类人一是正卡在毕设开题/中期/答辩前两周、急需可复现、可讲解、可演示的完整项目二是想用真实医疗影像小场景练手深度学习工程落地的新手——它把数据清洗、类别平衡、模型导出、GUI集成、打包exe这些“玄学环节”全摊开给你看连requirements.txt里每个包的版本都锁死了比如tensorflow2.6.0而非2.8避开了TF 2.7对Windows GPU驱动的坑。这不是玩具是踩过所有坑后焊死的流水线。2. 从舌象图到分类模型数据准备、网络选型与训练脚本实操2.1 数据集结构与预处理逻辑为什么必须重采样CLAHE增强项目采用自建公开数据混合集共1247张舌象图非公开商用仅限学术用途按./data/train/normal/、./data/train/yellow_greasy/等子目录组织。但原始图存在严重问题光照不均手机拍摄反光、分辨率混乱320×240到1920×1080、舌体占比差异大有的占图80%有的仅30%。直接喂给CNN会崩。所以预处理脚本preprocess.py做了三件事统一尺寸中心裁切先缩放至1024×768再用OpenCV找舌体最大连通域以该区域中心为锚点裁出512×512正方形CLAHE对比度受限自适应直方图均衡化针对舌苔纹理弱、色差小的特点参数设为clipLimit2.0, tileGridSize(8,8)比全局均衡更保细节类别重采样normal类321张gray_black仅187张用SMOTE生成合成样本注意仅对训练集做验证集保持原分布。# preprocess.py 关键片段 import cv2 import numpy as np from sklearn.preprocessing import StandardScaler from imblearn.over_sampling import SMOTE def clahe_enhance(img): clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8)) ycrcb cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) ycrcb[:,:,0] clahe.apply(ycrcb[:,:,0]) return cv2.cvtColor(ycrcb, cv2.COLOR_YCrCb2BGR) # 注意SMOTE只作用于训练集特征矩阵X_train标签y_train # X_train.shape (n_samples, 512*512*3) → 向量化后SMOTE smote SMOTE(random_state42, sampling_strategynot majority) X_train_res, y_train_res smote.fit_resample(X_train.reshape(-1, 512*512*3), y_train) X_train_res X_train_res.reshape(-1, 512, 512, 3) # 恢复图像维度提示CLAHE参数clipLimit不能设太高3.0否则舌苔边缘会过曝成白边tileGridSize太小如2×2会导致局部噪声放大。我一般先用cv2.imshow()预览10张图调参再批量处理。2.2 为什么选ResNet50微调而非YOLO或ViT医疗小样本下的务实选择项目没跟风ViT或Swin Transformer原因很实在你的GPU是GTX 16606GB显存数据量不到1500张ViT-base需要至少12GB显存2000样本才能收敛。ResNet50是平衡点——它在ImageNet上预训练的底层卷积核已学会提取边缘、纹理、颜色块对舌苔这种强纹理特征天然友好且include_topFalse后接GlobalAveragePooling2D参数量比全连接层少87%训练快、不易过拟合。关键改动有三处输入尺寸从224×224改为512×512适配舌象图高分辨率需求冻结前100层base_model.trainable True后手动设base_model.layers[i].trainable False for i in range(100)只微调顶层分类头用Dense(128, activationrelu) → Dropout(0.5) → Dense(4, activationsoftmax)4对应四类舌苔。# model_builder.py from tensorflow.keras.applications import ResNet50 from tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout from tensorflow.keras.models import Model base_model ResNet50( weightsimagenet, include_topFalse, input_shape(512, 512, 3) # 关键改输入尺寸 ) # 冻结前100层 for layer in base_model.layers[:100]: layer.trainable False x base_model.output x GlobalAveragePooling2D()(x) x Dense(128, activationrelu)(x) x Dropout(0.5)(x) predictions Dense(4, activationsoftmax)(x) # 四分类 model Model(inputsbase_model.input, outputspredictions) model.compile( optimizeradam, losscategorical_crossentropy, metrics[accuracy] )参数说明Dropout(0.5)是血泪经验——不加的话验证集准确率虚高92%但测试新舌图时抖动极大置信度标准差0.3加了之后训练慢15%但泛化稳定测试集准确率86.3%标准差0.08。2.3 训练日志解析如何用TensorBoard定位过拟合与学习率陷阱压缩包里的events.out.tfevents.*文件就是TensorBoard日志。别删它们能告诉你模型到底哪里不对。启动命令很简单tensorboard --logdir./logs --bind_all --port6006然后浏览器打开http://localhost:6006。重点看三块SCALARS → accuracy val_accuracy如果val_accuracy在第30轮后停滞甚至下降而accuracy继续涨就是过拟合项目中出现过解决方法是早停增加DropoutIMAGES → sample_images随机抽16张训练图确认预处理后的舌图是否清晰、无裁切错误曾发现23张图因CLAHE过度导致舌体发白手动剔除GRAPHS → model_1检查网络结构是否和代码一致尤其确认ResNet50的input_shape确实是(512,512,3)不是默认的(224,224,3)。注意TensorBoard日志文件名中的时间戳如1652188470对应Unix时间戳可用在线工具转为北京时间对照训练脚本里的datetime.now().strftime(%Y%m%d_%H%M%S)确保日志和代码版本匹配。3. GUI界面开发PyQt5如何承载深度学习模型并保证响应不卡顿3.1 主窗口架构QMainWindow 多线程推理避免GUI冻结PyQt5主线程负责UI渲染模型推理若在主线程跑点击“识别”按钮后整个界面会假死3~5秒ResNet50在CPU上推理单图约2.8s。解决方案是QThread信号槽机制InferenceWorker类继承QObject封装model.predict()调用主窗口创建QThread实例将InferenceWorkermoveToThread点击按钮触发worker.start()推理完成 emitresult_signal主窗口槽函数接收并更新UI。# gui/main_window.py from PyQt5.QtCore import QThread, QObject, pyqtSignal from PyQt5.QtWidgets import QMainWindow, QLabel, QPushButton class InferenceWorker(QObject): result_signal pyqtSignal(dict) # 发送 {label: yellow_greasy, confidence: 0.92} def __init__(self, model, img_array): super().__init__() self.model model self.img_array img_array def run(self): # 预处理归一化expand_dims img_norm self.img_array.astype(float32) / 255.0 img_batch np.expand_dims(img_norm, axis0) pred self.model.predict(img_batch)[0] label_idx np.argmax(pred) labels [normal, thin_white, yellow_greasy, gray_black] self.result_signal.emit({ label: labels[label_idx], confidence: float(pred[label_idx]) }) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.model load_model(./models/best_model.h5) # 加载已训练模型 self.thread QThread() self.worker None def on_recognize_clicked(self): # 从QLabel获取当前显示的舌图已转为numpy array img_array self.get_current_image_as_array() self.worker InferenceWorker(self.model, img_array) self.worker.moveToThread(self.thread) self.worker.result_signal.connect(self.update_result_display) self.thread.started.connect(self.worker.run) self.thread.start()关键细节img_array.astype(float32) / 255.0必须在子线程内做不能在主线程预处理好再传——因为PyQt5的QPixmap对象不能跨线程传递必须传原始numpy数组。3.2 中医术语解释模块硬编码规则表 vs 轻量级知识图谱GUI右侧的“中医解读”区域不是简单打印yellow_greasy而是映射到临床意义。项目采用轻量级规则表非大模型因为毕设不允许外网调用API。diagnosis_rules.py定义DIAGNOSIS_MAP { normal: { term: 正常舌象, description: 舌质淡红舌苔薄白干湿适中颗粒均匀。, clinical_significance: 脏腑功能协调气血充盈胃气充足。 }, yellow_greasy: { term: 黄腻苔, description: 舌苔色黄而黏腻颗粒细腻致密刮之难去。, clinical_significance: 湿热内蕴多见于脾胃湿热、肝胆湿热证。 }, # ... 其他两类 }UI中调用# 在update_result_display槽函数里 rule DIAGNOSIS_MAP.get(result[label], {}) self.label_term.setText(rule.get(term, 未知)) self.text_desc.setPlainText(rule.get(description, )) self.text_significance.setPlainText(rule.get(clinical_significance, ))为什么不用JSON或数据库因为毕设答辩要求“零依赖、一键运行”。硬编码Python字典打包进exe后无需额外文件且修改术语只需改.py文件比改JSON更易调试。3.3 打包为独立exePyInstaller的隐藏坑与必备参数用pyinstaller --onefile --windowed --add-data models;models --add-data data;data main.py打包后exe双击闪退常见原因有三TensorFlow DLL缺失PyInstaller无法自动抓取TF的C动态库需手动指定路径pyinstaller --onefile --windowed ^ --add-binary C:\Users\XXX\AppData\Roaming\Python\Python38\site-packages\tensorflow\libtensorflow_cc.dll;. ^ --add-binary C:\Users\XXX\AppData\Roaming\Python\Python38\site-packages\tensorflow\libtensorflow_framework.dll;. ^ --add-data models;models --add-data data;data main.pyPyQt5插件路径错误添加--add-binary指向PyQt5的plugins目录如C:\Python38\Lib\site-packages\PyQt5\plugins模型加载路径硬编码GUI代码里写load_model(./models/best_model.h5)但exe解压后工作目录是临时文件夹应改用import sys import os def resource_path(relative_path): if getattr(sys, frozen, False): base_path sys._MEIPASS else: base_path os.path.abspath(.) return os.path.join(base_path, relative_path) model load_model(resource_path(models/best_model.h5))血泪经验打包前务必用--debug参数生成log闪退时看log末尾报错——90%是DLL找不到或路径不对。我第一次打包失败log里ImportError: DLL load failed while importing _pywrap_tensorflow_internal折腾3小时才找到正确的DLL路径。4. 避坑指南99分毕设背后踩过的7个真实雷区4.1 现象训练时loss降得很快但val_accuracy卡在60%不上升原因数据集划分时未按类别分层stratify导致验证集里gray_black类只有5张图而训练集有180张模型根本没见过足够灰黑苔样本。解决用sklearn.model_selection.train_test_split时加stratifyy参数确保各类在训练/验证集中比例一致。4.2 现象GUI中摄像头实时识别帧率极低2fps原因OpenCV的cv2.VideoCapture(0)默认使用V4L2后端在Windows上性能差且每帧都做CLAHEresizeCPU扛不住。解决① 改用DirectShow后端cap cv2.VideoCapture(0, cv2.CAP_DSHOW)② 将CLAHE移至模型输入预处理即只对送入模型的单帧做摄像头预览用原始图③ 降低预览分辨率cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640); cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)。4.3 现象打包exe后点击“识别”按钮无反应控制台无报错原因PyInstaller未打包h5py依赖的zlib.dll和hdf5.dll导致load_model()静默失败。解决在spec文件中添加binaries[(path/to/zlib.dll, .), (path/to/hdf5.dll, .)]或用--add-binary命令行参数显式包含。4.4 现象论文里写的“准确率92.3%”但自己跑测试集只有84.1%原因原始论文用的是model.evaluate()在训练集上测的错误正确做法是用独立测试集test_data且关闭dropoutmodel.evaluate(test_data, verbose0)。项目已修正测试集准确率86.3%±0.7%5次随机划分均值。解决严格区分train/val/test三集合测试集绝对不可参与任何训练过程包括数据增强参数调整。4.5 现象舌图拖入GUI后显示变形拉伸成宽屏原因QLabel.setPixmap()未设置Qt.KeepAspectRatio图片被强制填满控件。解决在设置pixmap后加label.setScaledContents(False)并用pixmap.scaled(width, height, Qt.KeepAspectRatio)预缩放。5. 模型导出与跨平台部署如何让导师电脑上也顺利运行5.1 从HDF5到SavedModel兼容性升级的关键一步项目原始模型保存为best_model.h5Keras原生格式但它在不同TensorFlow版本间兼容性差TF 2.6保存的h5在TF 2.9可能load失败。毕设答辩时导师电脑装的是TF 2.8为保万无一失必须导出为SavedModel格式# export_model.py import tensorflow as tf from model_builder import build_model # 重建模型结构 model build_model() # 注意不加载权重只建结构 model.load_weights(./models/best_model.h5) # 单独加载权重 # 导出为SavedModel tf.saved_model.save( model, ./models/saved_model, # 输出目录 signatures{ serving_default: model.call.get_concrete_function( tf.TensorSpec(shape[None, 512, 512, 3], dtypetf.float32, nameinput_image) ) } )导出后./models/saved_model目录下会有assets/、variables/、saved_model.pb三个部分。GUI代码中加载方式改为# 替换原来的 load_model() model tf.keras.models.load_model(./models/saved_model)优势SavedModel是TensorFlow官方推荐格式跨版本兼容性极强且支持TensorRT加速虽毕设未用但留了接口。5.2 环境隔离方案conda vs venv为什么最终选venv项目最初用conda创建环境conda create -n tongue python3.8但打包时发现conda环境路径硬编码在.pth文件里PyInstaller无法正确解析。改用venv后问题消失# 推荐流程 python -m venv env_tongue env_tongue\Scripts\activate.bat # Windows pip install -r requirements.txt # 确保requirements.txt里明确写出版本号 tensorflow2.6.0 keras2.6.0 pyqt55.15.6 opencv-python4.5.5.64requirements.txt内容必须锁定版本尤其tensorflow和pyqt5——pyqt55.15在某些Windows系统上会安装6.x导致QThread信号槽失效PyQt6 API变更。5.3 导师电脑无GPUCPU推理优化三板斧导师电脑大概率是核显Intel UHD Graphics连CUDA都不装。此时CPU推理速度是瓶颈。项目已内置优化优化项实现方式效果模型量化使用tf.lite.TFLiteConverter转为int8模型推理速度提升2.3倍精度损失0.5%测试集85.8%→85.4%OpenMP线程数限制os.environ[TF_NUM_INTEROP_THREADS] 2os.environ[TF_NUM_INTRAOP_THREADS] 4防止多线程争抢CPU占用率从100%降至65%风扇不狂转预热推理GUI启动时自动执行一次model.predict(np.zeros((1,512,512,3)))首次识别延迟从3.2s降至1.1s避免JIT编译耗时# gui/app.py 开头 import os os.environ[TF_NUM_INTEROP_THREADS] 2 os.environ[TF_NUM_INTRAOP_THREADS] 4 # 启动时预热 dummy_input np.zeros((1, 512, 512, 3), dtypefloat32) _ model.predict(dummy_input) # 丢弃结果只为触发编译从那以后我每次给导师演示前都强制走一遍“预热→拍舌图→识别→看中医解读”全流程确保答辩现场不翻车。哪怕多花10秒也比答辩时卡住30秒强——那30秒够你重写整个毕设了。希望帮到你。本文还有配套的精品资源点击获取
返回列表