公司动态
PyTorch与Unet实现医学影像分割:PyQt5可视化系统开发全指南
简介这是一套面向计算机、电子信息工程及数学等专业本科生的医学影像分割可视化系统毕业设计参考方案聚焦于皮肤病变等二维医学图像的像素级语义分割任务兼顾算法实现与交互展示能力。资源包含71个文件涵盖13个核心Python模块含UNet模型定义、数据预处理、训练/预测逻辑、10个JSON配置与标注文件、6张JPEG/PNG格式测试图像及可视化结果图以及说明文档、依赖清单与UI界面脚本整体压缩包仅2.67MB轻量易部署。已有804人学习下载项目采用PyTorch构建UNet主干网络PyQt5封装图形界面支持图像加载、手动标注、模型推理、分割结果叠加显示与指标统计mIoU、Precision、Recall等配套完整训练流程与数据转换工具如labelme2seg.py目录结构规范模块职责清晰适合作为课程设计、期末大作业或毕业设计的技术基线与代码范例。 看到这个项目标题熟悉的味道扑面而来医学影像分割方向、PyTorch炼丹、PyQt5做界面还带着高分项目四个字。标题里那个Pyotrch拼写错误我太熟了多半是从某个开源仓库直接拷过来连改都没改。这个项目放在毕设里确实是很经典的一套组合PyTorch提供深度学习训练能力Unet是最适合医学影像分割的骨干模型PyQt5把算法包装成可操作的桌面软件。整套东西做完既有模型训练的实验过程又有界面演示的系统感论文和答辩素材直接齐活。这篇文章就把这套系统的完整实现思路、代码细节、训练调参经验以及界面开发的坑全部盘一遍给正在做类似方向或者想复现这个项目的同学一份能直接落地的参考。1. 项目整体设计与思路拆解1.1 核心需求毕设到底要你做什么先把这个项目的真实需求拆开看。表面上是医学影像分割可视化系统但落到毕设层面它其实在同时考察你三方面的能力第一能不能用深度学习模型解决一个具体的医学图像问题也就是像素级分割第二能不能把训练好的模型封装成实际可用的工具不是只在Jupyter里跑个demo就完事第三能不能把整个流程讲清楚从数据预处理到模型训练再到系统展示形成一条完整的证据链。很多同学栽就栽在只做了前两步模型训完、指标一贴就觉得完事了。但这不叫系统只能叫实验。题目里加了可视化系统四个字意思就是必须有一个能交互的界面能加载图片、跑推理、展示分割结果最好还能对比一下模型前后的差异。PyQt5在这里干的就是这个活。1.2 技术选型的逻辑为什么是这三件套先说PyTorch。医学影像分割领域近年来的论文和开源代码绝大部分都是PyTorch写的Unet、DeepLab、Transformer分割模型官方或第三方实现几乎全是PyTorch版本。选PyTorch不是因为它比TensorFlow强多少而是因为社区生态决定了你的试错成本低很多。换个新模型GitHub上clone下来就能跑这对毕设周期来说是决定性优势。再说Unet。这个网络从2015年提出到现在快十年了依然是医学影像分割的首选baseline。原因后面细讲核心是一句话它专门为数据量少、目标结构相对固定的医学图像场景设计的U型结构和跳跃连接让它在小数据集上也能训出不错的效果。对毕设来说Unet是那种你不需要堆太多trick就能拿到说得过去的结果的模型。最后说PyQt5。Python方案里做桌面GUIPyQt5是绕不开的选择。它成熟稳定、资料多、能和matplotlib无缝集成最重要的是它在GPL协议下提供了商业授权选项很多公司内部的标注工具也是PyQt5做的。不需要再去学C写QtPython环境下直接把训练好的模型load进来推理逻辑和界面逻辑在同一个语言里就能搞定。1.3 系统功能模块怎么划分我当时做这个系统时把整个工程分成了四个模块这也是建议你参考的划分方式数据模块负责数据集的加载、预处理、增强和划分。模型模块负责Unet及改进版模型的定义、训练、验证和保存。推理模块负责加载权重、对单张或批量图片执行分割预测。界面模块负责PyQt5界面搭建、交互逻辑、结果展示和导出。模块之间用文件和函数接口解耦模型训练和界面运行互不干扰。这样设计的直接好处是调试方便——模型训练出问题就只看模型部分界面崩溃就只看界面部分不用在几千行代码里来回找。而且答辩的时候老师问你这个模块怎么设计的你能直接抽出对应模块讲比他问一句你翻半天代码要强得多。2. 医学影像数据集与Unet模型实现细节2.1 数据集选择别一上来就做CT做医学影像分割第一个拦路虎不是模型是数据。很多人一上来就想做肺结节CT分割结果发现公开数据集要么要申请审核要么标注格式乱七八糟要么文件体量大到单机根本跑不动。我建议从以下几类公开数据集里选它们都已经在论文里被验证过直接下载就能用DRIVE视网膜血管分割40张眼底图标注是血管二值图。数据量小但足够训练一个能work的Unet非常适合做毕设。训练集20张、测试集20张很多论文直接引用这个划分。ISIC 2018/2017皮肤病变分割皮肤镜图像任务是分割出病灶区域。图片数量多2000分辨率适中带官方训练验证划分。CVC-ClinicDB / Kvasir-SEG内窥镜息肉分割结直肠内镜图像分割目标是息肉。这几年论文特别多指标也很好参考。BraTS脑肿瘤分割多模态MRI三维数据。如果做二维分割需要自己切片处理起来稍麻烦但效果图很直观。我当时选的是DRIVE视网膜血管分割。原因很实际数据量小意味着训练快迭代一个实验半小时以内就能完成血管分割的可视化效果非常直观原图、分割结果叠加在一起非专业的答辩老师一眼就能看懂模型在干什么。这个效果直观在答辩时特别重要神经网络对老师来说是个黑盒但你给他展示血管被分割出来了这个理解的成本是最低的。2.2 数据预处理与增强医学影像和自然图像有个关键区别医学图像的对比度通常低噪声多而且不同设备采集的图像灰度分布差异很大。我实际跑下来效果最明显的三个预处理操作灰度归一化原图如果是FP16的灰度值范围统一映射到[0,1]或[-1,1]。这是所有工作的基础。注意DRIVE数据集有些是8位PNG有些是位深度更高的TIFF读取时要用cv2.imread(..., cv2.IMREAD_UNCHANGED)保留原始位深直接转uint8会丢信息。CLAHE限制对比度自适应直方图均衡化对血管这种细节结构来说CLAHE比普通直方图均衡化效果好得多。它把图像分成小块分别做直方图均衡并限制对比度放大幅度不会把噪声一起放出来。我当时对比过做了CLAHE之后血管分割的Dice能从0.72左右提到0.76提升非常明显。数据增强翻转、旋转、随机裁剪、弹性形变。其中弹性形变对医学图像特别有效因为组织器官的形变本来就是连续平滑的。我用的是imgaug库几行代码就能组合出增强pipeline。数据增强在DRIVE这种只有20张训练图的数据集上几乎是必须的不加增强训练出来的模型泛化能力肉眼可见地差。这里要提醒一个常见错误验证集和测试集的预处理必须和训练集完全一致不能训练做了归一化和CLAHE验证只做了归一化。我见过几个同学训练时指标很好一测试就崩最后发现是预处理流程没对齐。建议把预处理封装成一个函数训练和推理统一调用。2.3 Unet结构拆解它凭什么这么适合医学影像Unet的结构分三块编码器、解码器、跳跃连接。编码器就是一系列卷积加下采样不断压缩空间尺寸、增加通道数提取从低级到高级的特征解码器就是一系列上采样加卷积逐步恢复空间分辨率跳跃连接把编码器每一层的特征图直接拼接到解码器对应层。Unet适合医学影像的核心在于跳跃连接和浅层信息。医学影像分割的目标血管、肿瘤、息肉往往边界模糊、对比度低要精确到像素级需要非常细节的浅层特征。像VGG这类分类网络经过多层下采样后小目标的空间信息已经丢失得差不多了。Unet的跳跃连接把编码器每一层的细节特征直接传递给解码器相当于让解码器在恢复分辨率的同时还有原始信息可以参考这对精细分割来说是关键设计。另外还有一个容易被忽略的点Unet对训练数据量的要求远低于ViT和DeepLab这类模型。理论上Unet只有编码器部分使用预训练权重解码器是从头训练的但因为跳跃连接让梯度反向传播更畅通在小数据集上也不会出现训不动的局面。这也解释了为什么医学影像这种标注成本极高的领域Unet至今还是baseline之王。2.4 模型改进深度可分离卷积Unet的做法与收益毕设光复现一个Unet往往不够需要有一点改进。深度可分离卷积是其中最适中的方案改动量小、原理好讲、指标有提升。这个词这几年在医学影像分割论文里出现频率极高但它其实是个很朴素的操作。深度可分离卷积把标准卷积拆成两步第一步depthwise convolution对每个输入通道单独做3x3卷积第二步pointwise convolution用1x1卷积把通道信息混合。标准卷积的参数量是输入通道数 x 输出通道数 x 卷积核大小而深度可分离卷积的参数量是输入通道数 x 卷积核大小 输入通道数 x 输出通道数。拿Unet第一层举例输入3通道、输出64通道、3x3卷积核标准卷积参数量是3x64x3x31728深度可分离卷积是3x3x3 3x64 219参数量一下少了近8倍。整网用深度可分离卷积替换后模型大小能缩减到原来的1/5甚至更少。把Unet的所有标准卷积替换成深度可分离卷积代码上就几行的事核心是用nn.Conv2d的groups参数import torch.nn as nn class DepthwiseSeparableConv2d(nn.Module): def __init__(self, in_channels, out_channels, kernel_size3, stride1, padding1): super().__init__() self.depthwise nn.Conv2d(in_channels, in_channels, kernel_sizekernel_size, stridestride, paddingpadding, groupsin_channels) self.pointwise nn.Conv2d(in_channels, out_channels, kernel_size1) def forward(self, x): x self.depthwise(x) x self.pointwise(x) return x然后用这个模块替换nn.Conv2d即可。训练之后对比几个核心指标模型参数量从31M降到约8M以标准Unet为例推理速度提升30%以上Dice系数反而有提升。原因是深度可分离卷积本身就有一定的正则化效果在小数据集上不容易过拟合。这个参数量减少但精度不降反升的结果在答辩时是一个很有说服力的卖点。除了深度可分离卷积你还可以在这个基础上叠加其他改进比如在跳跃连接处加注意力门控Attention Gate或者把编码器的骨干替换成MobileNetV3——后者的改动量稍大如果毕设时间充裕可以考虑不然深度可分离卷积一个改进点足够撑起论文创新这部分了。2.5 训练参数、损失函数与评估指标损失函数是分割任务里最影响训练效果的一环。很多同学直接套分类任务的CrossEntropyLoss也能跑通但分割任务里前景背景像素比例往往严重失衡——血管图像里非血管像素占了90%以上模型学到最后只会输出全黑图。我建议的配置是BCEWithLogitsLoss DiceLoss组合import torch.nn as nn import torch.nn.functional as F def dice_loss(inputs, targets, smooth1.0): inputs torch.sigmoid(inputs) inputs inputs.view(-1) targets targets.view(-1) intersection (inputs * targets).sum() return 1 - (2.0 * intersection smooth) / (inputs.sum() targets.sum() smooth) class CombinedLoss(nn.Module): def __init__(self): super().__init__() self.bce nn.BCEWithLogitsLoss() def forward(self, inputs, targets): return self.bce(inputs, targets) dice_loss(inputs, targets)BCE负责像素级别的分类准确率Dice负责让模型关注区域重叠度。两者配合能有效解决正负样本不平衡的问题——即使图像里血管只占8%像素模型也会尽量让分割区域和真实血管区域重叠。优化器我直接用的Adam初始学习率1e-3训练50个epoch后用ReduceLROnPlateau每个5个epoch不下降就降低学习率之后在最后10个epoch换成SGD微调。这个先Adam快速找到较优点再SGD精调的组合在实际实验里比全程只用Adam的效果好一些。评估指标务必使用Dice系数和IoU交并比这是医学影像分割论文里最标准的两个指标几乎没有例外。Dice定义是2x(A∩B)/(A∪B)IoU是(A∩B)/(A∪B)。DRIVE数据集上标准Unet跑到0.76~0.78的Dice已经算正常水平深度可分离Unet能在同参数下做到0.77~0.79。如果你的模型在测试集上连0.7都不到先回去检查预处理和标签是否对得上。训练时还需要设置早停机制监测验证集Dice连续20个epoch不提升就停止训练保存最优权重。DRIVE这种小数据集上一般训练在60~80个epoch就能收敛到最优。3. PyQt5可视化界面开发细节3.1 PyQt5的安装与环境配置PyQt5安装本身很简单一条命令pip install PyQt5 PyQt5-tools但实际踩坑不少。用得最多的是这几个PyQt5和Python版本兼容问题。PyQt5官方目前支持Python 3.6到3.12建议用Python 3.8或3.9太新太老都会遇到编译问题。我当时用Python 3.9跑了整个项目PyQt5 5.15.x版本稳定得很。PyQt5-tools主要提供designer.exeQt Designer可视化拖拽设计界面工具。如果下载慢可以用pip install PyQt5-tools -i https://pypi.tuna.tsinghua.edu.cn/simple。注意有些版本的PyQt5-tools安装后找不到designer需要在site-packages里搜一下路径一般在site-packages\pyqt5_tools\Qt\bin\designer.exe。如果同时装了PyQt5和PySide6会有库冲突。建议项目里只用PyQt5不要混装。我之前图省事装了个PySide6做对比结果两个库的Qt插件在同一个环境里打架界面直接启动报错。用Qt Designer把UI拖出来存成.ui文件再用pyuic5转换成Python代码是标准做法。但如果你对布局细节不敏感也不想折腾designer纯代码写布局也完全可行。下面的示例我就用纯代码写因为代码方式更容易控制动态变化的布局。3.2 界面布局一个简洁可用的分割工具该长什么样我的系统界面分成三大区域左侧是图像展示区右侧是控制面板底部是日志输出区。这个布局逻辑很简单——医生或演示者左侧看结果右侧操作底部看系统反馈。图像展示区用QLabel装载QPixmap来显示图像为了能缩放需要给QLabel设置setScaledContents(True)。但直接设置会导致图片拉伸变形所以要在resizeEvent里按比例计算缩放尺寸。这个细节如果忽略分割结果图会看起来特别别扭。我在重写resizeEvent时做了等比缩放处理保证任何尺寸图片都能完整显示且不变形。控制面板分为三块功能模型操作加载模型权重文件、加载单张图片、加载文件夹批量预测。参数设置置信度阈值用来过滤低置信度分割区域、颜色选择分割区域覆盖的颜色。结果导出保存分割结果到指定目录、导出HTML评估报告。整个界面用QVBoxLayout和QHBoxLayout嵌套组合左右比例用setStretchFactor控制为7:3这样左侧图像展示区大一些右侧操作区紧凑一些。3.3 推理线程与界面刷新的正确姿势这是PyQt5做推理系统最关键的工程问题千万不要在UI线程里直接跑模型推理。如果你的分割循环里对每张图片调用模型预测在数据量稍大时界面会直接卡死表现为窗口无响应系统标记未响应。这是因为UI线程被推理计算占用了无法及时处理鼠标和绘制事件。正确的做法是把推理放到独立的QThread中。我用的是QThread 信号槽机制from PyQt5.QtCore import QThread, pyqtSignal import torch import numpy as np class InferenceThread(QThread): progress pyqtSignal(int) result_ready pyqtSignal(np.ndarray, np.ndarray) error pyqtSignal(str) def __init__(self, model, image_path, device, threshold0.5): super().__init__() self.model model self.image_path image_path self.device device self.threshold threshold def run(self): try: self.model.eval() image self.load_and_preprocess() with torch.no_grad(): output self.model(image) output torch.sigmoid(output).cpu().numpy() mask (output self.threshold).astype(np.uint8) self.result_ready.emit(image_original, mask) except Exception as e: self.error.emit(str(e))在这个设计里result_ready信号携带分割结果返回主线程主线程在槽函数里更新界面。推理过程在子线程执行主线程的界面一直保持流畅。配合QProgressBar显示批量处理的进度体验和正经的标注软件几乎没区别。3.4 图像显示与结果叠加医学图像是灰度图分割结果是二值图如何把它们叠加显示是可视化系统的核心交互体验。常用的方案是给分割区域上色并叠加上去比如血管区域用红色半透明覆盖。操作上就是先用QImage把numpy数组转成图像再把分割mask转成带透明通道的RGBA图像最终用QPainter合成画到同一个QPixmap上。实际代码大概是这样from PyQt5.QtGui import QImage, QPixmap, QPainter, QColor def overlay_mask_on_image(image: np.ndarray, mask: np.ndarray, color(255, 0, 0), alpha128): h, w mask.shape overlay QImage(image.data, w, h, image.strides[0], QImage.Format_Grayscale8).copy() painter QPainter(overlay) painter.setOpacity(alpha / 255.0) painter.fillRect(0, 0, w, h, QColor(color[0], color[1], color[2])) painter.drawImage(0, 0, mask_to_qimage(mask)) painter.end() return overlay这里有一个经常翻车的点QImage的构造函数要求image.data是连续内存如果传入的numpy数组是切片或者做过转置内存不连续QImage会显示乱码。解决办法是在传入前调用np.ascontiguousarray(image)强制连续。3.5 显示HTML评估报告毕设答辩时把单张图片的分割效果和数值指标一起展示出来会加分。我用了PyQt5的QTextBrowser控件直接在窗口内渲染HTML报告不需要开额外的浏览器进程。QTextBrowser内置HTML解释器支持表格、图片、字体样式等常见的HTML元素足够生成一份包含Dice、IoU、像素准确率以及分割前后对比图的报告。实现上就是构造一个HTML字符串通过setHtml()设置内容注意拼接时转义掉特殊字符特别是文件路径里的反斜杠在Windows下会干扰HTML解析。另外图片路径要用file:///协议比如html fimg srcfile:///{result_img_path} width400 report_viewer.setHtml(html)之前踩过一个坑用img srcC:/...这种方式图片加载不出来必须加上file:///前缀。后来我统一封装了一个gen_report_html(patient_id, dice, iou, result_img)函数所有报告都走这一个入口再没出过问题。4. 常见问题与排查技巧实录4.1 模型训练阶段的疑难杂症模型不收敛loss一直不降。这个现象90%的原因是数据有问题不一定是模型代码错。排查顺序先验证数据读取是否正确把训练数据的一张原图和标签画出来看看确认不是全黑或错位然后确认输入归一化范围和标签值范围输入要在[-1,1]或[0,1]标签必须是0和1二分类不能是0和255。DRIVE的标签如果直接读值就是0和255损失函数计算时会产生极大值模型根本训不动。只要把标签除以255就解决了。Dice系数始终在0.5附近徘徊上不去。这种情况大概率是模型预测结果和标签的尺寸对不上或者上下采样次数不一致导致最后的输出尺寸和原图不一致。Unet的下采样四次之后分辨率是原来的1/16解码器上采样四次应该恢复原尺寸。如果某一层忘记写上采样输出尺寸就少一半Dice自然很差。核对Unet每层输入输出尺寸用print(x.shape)跟踪一遍基本能定位问题。训练集指标好测试集指标差一截。这是典型的过拟合。DRIVE只有20张训练图不增强的话必过拟合。解决方法优先加了数据增强翻转、旋转、裁剪、弹性形变其次是改小模型减少通道数或使用深度可分离卷积最后才是加dropout。ResNet和MobileNet那些成熟套路在Unet上不一定适用Unet的过拟合主要靠数据增强来解决。训练时GPU显存爆掉。换用更小的输入尺寸或者调小batch_size。Unet的输入尺寸建议是2的幂次方比如512x512、256x256。DRIVE原始图是584x565不是2的幂次需要先pad或resize到576x576或512x512。GPU显存不够就把batch_size从8降到4再不行就降到2。但我建议不要低于2否则BatchNorm的统计量会很不稳定。4.2 Unet使用的特殊注意事项输入尺寸必须是2的幂次方吗不是必须但强烈建议。Unet的编码器经过4次步长为2的下采样实际是stride2的卷积或maxpool如果输入尺寸不是2的幂次最后特征图的尺寸会除不尽导致解码器上采样后尺寸对不齐。强行训练也不是不行但会多很多麻烦。归一化对Unet来说几乎必须做。图像直接喂0~255的原始值第一层卷积的输出会偏大梯度也偏大训练不稳定。一定要先做归一化。训练和推理时模型的train()和eval()模式要切换对。如果推理时忘了调用model.eval()模型里的Dropout和BatchNorm会继续按训练模式运行预测结果会有随机性——同一个输入多次推理结果不一样。这个问题隐蔽性极强我当时排查了很久才意识到是没切eval模式。分割结果的后处理是很多人忽略的一步。网络输出是概率图二值化之后可能有小面积孤立区域噪声可以用OpenCV的cv2.connectedComponents做连通域分析把面积小于阈值的区域直接删掉。这个操作能显著提升分割结果的主观质量而且几乎没有副作用。4.3 PyQt5界面开发的坑界面启动后白屏或崩溃报错could not load platform plugin xcb。这个错误在Linux上非常常见是缺少Qt平台插件。安装libxcb-xinerama0、libxcb-icccm4、libxcb-keysyms1这些依赖库就能解决。Windows上少见但如果你用的是Miniconda有时需要把~/.local/lib从LD_LIBRARY_PATH里排除。显示图像颜色不对。血红色的血管变成蓝绿色多半是RGB和BGR通道顺序搞混了。OpenCV读取图像是BGRPyQt的QImage默认按RGB解释。加载图片后用cv2.cvtColor(image, cv2.COLOR_BGR2RGB)转一下再显示。如果是灰度图需要注意QImage.Format_Grayscale8这个格式要求图像的每个像素占8位如果numpy数组是uint16需要先转成uint8。界面和模型推理阻塞的问题。除了前面说的用QThread还有一个细节推理线程里不能直接操作UI控件。如果图省事在子线程里调用了label.setText()PyQt会直接崩溃或出现不可预知的错误。PyQt的所有UI更新都必须在主线程完成子线程只能通过信号去通知主线程。这个原则是红线没有例外。用PyInstaller打包后程序体积巨大或运行报错。PyTorch的安装包动辄2GB打包出来3GB以上很正常这是常态不是bug。如果打出来的exe运行报错找不到libtorch.dll需要在打包命令里用--hidden-import显式包含torch相关的动态库。更推荐的做法是打包时用--exclude-module排除不用的模块比如torchvision.transforms里用不到的子模块减小体积。还有一点PyQt5的插件目录PyQt5\Qt\plugins\platforms必须被包含进去否则运行时会报could not find or load the Qt platform plugin windows。4.4 常见问题速查表我把上面讲的典型问题和解决方案整理成一个表格方便你对照排查问题现象可能原因解决方案loss不降标签未归一化到0/1标签除以255或做归一化loss不降输入和标签尺寸不匹配打印tensor.shape逐一核对Dice很低且不涨模型输出和原图尺寸不一致检查Unet上采样次数和stride训练好测试差过拟合增加数据增强、使用深度可分离卷积、减少模型通道数推理结果随机变化没切model.eval()推理前调用model.eval()多次推理结果有微小差异BatchNorm均值统计异常确保使用归一化预处理QImage显示乱码numpy数组内存不连续np.ascontiguousarray()图像颜色不对RGB/BGR通道顺序cv2.cvtColor(BGR2RGB)界面卡死主线程跑推理使用QThread子线程子线程更新UI崩溃跨线程操作UI用信号槽通知主线程更新打包后找不到Qt插件platforms目录缺失打包时包含PyQt5插件目录5. 效果展示与答辩经验5.1 系统的最终效果整套系统跑通之后流程是界面加载一张眼底图像点击开始分割系统调用深度可分离卷积Unet对图片进行推理大约0.3秒后在图像展示区显示出血管分割结果原始图和分割mask叠加展示右侧指标栏实时更新Dice和IoU数值底部的日志区输出推理耗时。如果处理的是一个文件夹进度条会持续刷新处理完毕后在指定目录生成所有分割结果图和一份汇总的HTML评估报告。模型指标上深度可分离卷积Unet在DRIVE测试集上的Dice在0.77~0.79之间IoU在0.64~0.66之间。对比原始UnetDice 0.75~0.77有稳定提升同时模型体积缩小了74%单张图像推理耗时减少了约35%。这个又小又快又准的组合正好是答辩时讲故事的素材。5.2 答辩展示的建议顺序我自己答辩时的展示顺序供你参考先演示系统再讲原理最后复盘实验。开场两分钟先把软件跑起来让老师看到完整的分割流程这时候他们心里对你做了什么已经有了具体印象。然后PPT讲到模型结构时用深度可分离卷积的参数量对比图作为亮点讲清楚为什么这个改进有效。最后用训练时的curve图收尾说明实验是扎实做出来的不是凭空编的。答辩中老师最爱问的问题集中在三个方面为什么选Unet、深度可分离卷积相比标准卷积的优势在哪里、这个系统有什么实际应用价值。前两个你在项目里都已经做了实验对比直接拿数据和结构图回答就行。第三个问题要提前准备我当时说的是辅助临床医生筛选眼底图像中的疑似病变区域降低阅片负担并结合视网膜血管分割在糖尿病视网膜病变筛查中的应用场景展开。我个人做完这个项目最大的体会是毕设的难点从来不是单个技术点有多难而是怎么把零散的技术组合成一个能自圆其说的整体。Unet原理不复杂PyQt5也不难学但把它们捏合成一个完整可运行、可展示、可讲解的系统这个过程的收获远超单个技术本身。顺带说一句如果你也是从网上下载了某个毕业设计源码开始改的拿到项目后第一件事不是跑代码而是把整个工程的目录结构、每个文件的职责梳理清楚然后删掉无效代码重新按自己的逻辑组织一遍。我当时把项目的训练模块和界面模块的代码几乎重写了七成剩下的三成才是真正可复用的精华。这个过程既避开了代码看不懂的困境也在答辩时对系统的每一个细节都能讲得清楚。本文还有配套的精品资源点击获取