简介基于PythonPyQt5实现的语义分割系统源码包内置图形界面与完整模型权重面向计算机视觉方向学生毕业设计、课程设计、期末大作业等场景适合快速搭建图像分割演示系统或作为算法学习平台。压缩包共25个文件其中20个Python脚本负责数据处理、GUI控制、模型推理和分割结果可视化1个ui文件定义界面布局pth文件存放预训练权重md说明文档与txt依赖列表帮助快速完成环境配置和运行校验整体约44.7MB。模型部分整合了DFormer、DeepLabV3、UPerNet等主流分割网络结构代码按功能模块拆分便于阅读与替换适合在理解流程的基础上进行二次开发也可将预训练模型更换后用于不同场景的数据集。目前已有284人学习下载功能经实际验证可稳定运行。使用中注意解压路径和项目名称不要使用中文以免解析出错可在现有GUI基础上DIY交互功能满足入门学习或中期项目演示等需求。1. 语义分割系统源码一个真正跑得起来的 PyQt5 毕设项目做过语义分割的人都有个体会训练模型不难难的是把 .pth 权重变成答辩时能现场演示的完整系统。这套基于 Python PyQt5 的语义分割工程恰好补齐了从模型到桌面应用这段最容易被忽略的路。解压后是完整的 Qt Designer 界面、main.py 程序入口、带 50 个 epoch 训练好的权重文件以及 test.py 命令行推理脚本属于那种「拿过来就能跑、跑起来就能讲」的毕设项目。项目结构里最有价值的部分在 model 目录backbone 用的是 DFormer.py解码头一口气集成了 FCN、DeepLabV3、UPerNet、Non-Local、Hamburger、MLP 等七种主流结构通过配置切换就能换解码头不用动主流程。适合计科、人工智能、大数据、通信、物联网方向的在校生做课程设计或毕业设计也适合第一次接触语义分割工程、想完整走一遍「数据—模型—GUI」链路的人。源码带 GUI权重、脚本、示例输出 result.jpg 都齐完全可以基于此做二次开发。2. 环境搭建与首次运行从 conda 环境到加载 ckpt_epoch_50.pth2.1 先看依赖清单requirements.txt 定生死很多朋友拿到 Python 项目习惯性直接python main.py跑挂了才回头查依赖。这个工程解压后第一件事应该是打开根目录的 requirements.txt看它锁了哪些库。按这类语义分割 GUI 项目的常见配置里面大概率会包含 torch、torchvision、pyqt5、opencv-python、numpy、Pillow、tqdm 这几样。其中 torch 和 torchvision 是模型推理的地基版本必须和你本机 CUDA 环境匹配不然加载权重时极易报错pyqt5 管界面opencv-python 和 Pillow 负责图像读取与格式转换语义分割里 image 到 tensor 的转换、结果图的伪彩色叠加都依赖它们。先把这个文件的内容和你的 Python 版本对照一遍比蒙头装依赖省时间得多。cd 项目目录 pip install -r requirements.txt装的时候有两个常见替换策略一是 pyqt5 在部分 Python 版本上 pip 安装会卡在下载阶段此时可以用conda install pyqt5替代二是 torch 的安装包体积大如果机器没有 NVIDIA 显卡不要硬装 CUDA 版本直接装 CPU 版能省几个 G 的下载流量。依赖装完跑一句python -c import torch, PyQt5能正常返回就说明基础环境通了。2.2 conda 虚拟环境与 CPU/GPU 版 PyTorch 的选择我一般习惯用 conda 单独建一个虚拟环境跑这类项目原因很实际毕设机器上往往已经装了一套 Python 环境里面可能有 TensorFlow、可能有其他项目的旧版 torch直接 pip 装会互相污染。用 conda 隔离后这个工程的所有依赖都锁在一个环境里坏了就删掉重建成本极低。conda create -n seg python3.8 -y conda activate seg pip install -r requirements.txtPython 版本选择 3.8 或 3.9 比较稳妥PyQt5 和 torch 在这两个版本上的兼容性最成熟不会遇到编译型依赖装不上的尴尬。之后每次跑程序都要先conda activate seg不然 import 的是全局环境里的旧库GUI 起不来大概率就是这个原因。环境建好后建议顺手跑一段设备检测代码确认模型会被放在哪张卡上import torch print(CUDA available:, torch.cuda.is_available()) print(device count:, torch.cuda.device_count()) if torch.cuda.is_available(): print(current device:, torch.cuda.get_device_name(0))这段代码的输出直接决定了后面的推理体验。CUDA available 为 True说明能走 GPU 加速一张 512×512 的图基本一两秒出结果为 False 也别慌模型会自动落到 CPU 上跑速度慢一些但对毕设演示和功能验证完全够用。重点在于确认 torch 是不是 CPU 版——有些人装了 CUDA 版 torch 但机器没有驱动加载时会直接崩。2.3 首次跑通 test.py权重与模型结构对齐验证我不建议一上来就开 GUI先跑根目录的 test.py原因很简单GUI 涉及界面、线程、事件循环出了问题不好定位是模型的事还是界面的事而 test.py 是一条纯后端的推理链路读图、加载模型、前向、保存结果一气呵成。这条链路通了再开 GUI 就有底了。打开 test.py 会发现它的核心逻辑是构建模型 → 加载 save_model/ckpt_epoch_50.pth → 对输入图做预处理 → 前向推理 → 把类别 id 的掩膜转成可视化图 → 写出 result.jpg。如果你看不懂里面每个步骤可以先写一个权重结构检查脚本确认权重内容和模型结构是否对得上import torch ckpt torch.load(save_model/ckpt_epoch_50.pth, map_locationcpu) print(type(ckpt)) if isinstance(ckpt, dict): keys list(ckpt.keys()) print(顶层key:, keys[:5]) # 常见结构是 ckpt[state_dict] 或 ckpt[model] for name in [state_dict, model, net]: if name in ckpt: state ckpt[name] print(f找到 {name}, 参数层数: {len(state)}) break这段代码用map_locationcpu把权重先加载到内存避免 GPU 显存占用打印顶层 key 是为了看清楚这个 pth 里到底存的是裸的 state_dict 还是包了一层字典。很多报错「Missing key(s) in state_dict」都出在这一步——权重结构和模型定义对不上。确认没问题后运行python test.py如果正常项目目录下会出现或刷新 result.jpg和原图对比一下边缘贴合度就能直观看到 50 轮训练的效果。这条链路通了GUI 的事就只剩下界面交互了。3. 模型侧解剖DFormer 主干与七种解码头的选型逻辑3.1 解剖模型目录DFormer.py 与 EncoderDecoder.py 的组装逻辑整个 model 目录的设计思路是「主干与解码头解耦」。DFormer.py 是 backbone负责从原始图像中提取多尺度特征EncoderDecoder.py 是把 backbone、解码头、辅助损失拼装成完整分割模型的骨架DF_config.py 则是这个骨架的配置中心所有开关都集中在这里。这是目前开源语义分割工程里最常见的组织方式好处是换结构不用改主代码。从命名习惯看DFormer.py 属于 Transformer 家族的主干网络D 大概率代表 Downsample 之类的下采样设计具体每个 block 的参数要看代码注释。这类主干的核心特点是擅长建模全局上下文比纯 CNN 的 FCN 系列对长距离依赖更敏感代价是计算量偏大。EncoderDecoder.py 里做的组装可以理解为输入图像过 backbone得到多层特征选一层或几层喂给解码头解码头上采样回原分辨率最后过一个卷积层输出每个像素的类别概率。文件清单里还有一个 decode_head.py负责的是解码头的通用接口定义。之所以单拎出来是因为不同解码头需要的输入特征层级不一样有的只要最后一层有的要多层金字塔。接口统一后换解码头只需要保证它实现了同一个 forward 接口即可。所以你在看代码时优先读 DF_config.py再读 EncoderDecoder.py最后挑一个具体解码头看实现这条阅读路径最省力。3.2 七种解码头FCN、DeepLabV3、UPerNet、NL、Hamburger 与两个 MLP先看一张表把这七种解码头的定位摆清楚后续选型心里就有数了文件解码头类型核心思路典型适用fcnhead.pyFCN全卷积1×1 卷积加双线性上采样最简单的 baseline跑通链路用deeplabv3plus.pyDeepLabV3ASPP 空洞卷积金字塔 编解码结构街景、遥感图像等大目标场景UPernet.pyUPerNet多尺度特征金字塔逐级融合通用分割小目标与边界兼顾nl_head.pyNon-Local自注意力建模全局依赖大目标、长距离上下文敏感场景ham_head.pyHamburger矩阵分解做轻量上下文建模显存受限但对效果有要求MLPDecoder.pyMLP Decoder线性层直接聚合特征速度快、结构简单LMLPDecoder.py轻量 MLP降通道后的 MLP 聚合CPU 推理、实时性要求高其中 FCN 是最经典的纯卷积方案适合刚接触语义分割算法时理解「逐像素分类」这件事DeepLabV3 在遥感图像和街景分割里出场率很高ASPP 能捕获多尺度上下文UPerNet 对多尺度融合做得很扎实属于通用场景的稳健选择nl_head 和 ham_head 都看重全局信息但实现路径不同——前者是显式注意力后者是矩阵分解后者对显存更友好。这套工程的价值恰恰在于它不是只给你一个写死的模型而是把七种解码头都实现了你在毕设论文里可以名正言顺写「本系统在解码端采用可替换架构对比了 FCN、DeepLabV3、UPerNet 等结构的性能差异」。这句话写在系统设计章节里答辩老师一看就知道工作量是足的。3.3 关键参数与配置num_classes、输入尺寸与设备分配DF_config.py 是改模型行为的唯一入口。读这个文件时重点关注三个参数num_classes、输入尺寸、解码头名称。num_classes 决定最后分类卷积的输出通道数Pascal VOC 语义分割是 21 类20 个物体类 1 个背景如果你要换成自己的数据集这个值必须改否则模型加载会直接报形状不匹配。输入尺寸指的是模型前向时的固定分辨率。语义分割模型一般不吃任意尺寸的图工程里常见做法是把输入 resize 到 512×512 或 1024×1024推理完成后再把掩膜 resize 回原图尺寸做叠加。尺寸越大细节越好但对显存和耗时都更敏感本机是 CPU 的话建议用 512。设备分配一句话就能说清模型构建后调用model.to(device)device 根据torch.cuda.is_available()决定。注意权重加载时也要指定相同的 map_location不然模型在 GPU 上、权重却从 CPU 加载可能出现设备不匹配的报错。修改配置的常见做法是直接在 DF_config.py 里改默认值# 以 DF_config.py 为配置中心改这几个字段就能切换解码头 config dict( num_classes21, # VOC 类别数 背景换数据集必须改 input_size(512, 512), # 推理时统一缩放的尺寸 encoderDFormer, # backbone 选择 decoderdeeplabv3plus, # 可换成 fcnhead / upernet / nl_head / ham_head ckpt_pathsave_model/ckpt_epoch_50.pth, )具体的字段名以仓库里 DF_config.py 的实际写法为准但思路就是这个思路。解码头名称字段切到哪个实现EncoderDecoder.py 就会实例化对应类你不需要动主流程。这就是解耦设计的好处。4. PyQt5 GUI 驱动分割链路从 Qt_seg.ui 到 main.py 的完整闭环4.1 Qt_seg.ui 与 Qt_seg.pypyuic5 生成与二次绑定GUI 相关的两个文件是 Qt_seg.ui 和 Qt_seg.py。前者是 Qt Designer 保存的界面文件本质是 XML描述窗口上有哪些按钮、标签、布局后者是界面对应的 Python 代码。正常情况下Qt_seg.py 是由 Qt_seg.ui 通过 pyuic5 工具自动生成的命令如下pyuic5 Qt_seg.ui -o Qt_seg.py但这里有个坑pyuic5 生成的是纯粹的界面定义代码不包含业务逻辑如果你在 Qt Designer 里改了界面布局重新生成 Qt_seg.py 时手动加进去的绑定代码可能会被覆盖。所以我的习惯是先看 main.py 是怎么引用 Qt_seg.py 的如果 main.py 里用的是Qt_seg.Ui_MainWindow这类标准写法说明 Qt_seg.py 还是原始生成状态可以安全重新生成如果 main.py 里直接调用了 Qt_seg.py 里自定义的函数名那就不建议覆盖。Qt_seg.ui 即便你不懂 XML 也可以打开它左侧是控件树右侧是属性表按钮的 objectName、显示文字、信号槽连接关系都写在里面。改按钮文字这类小需求直接在 Designer 里做会比写代码直观得多。4.2 main.py 推理链路从 QPixmap 到 result.jpgmain.py 是这个系统的主入口它的职责可以用四句话概括创建 QApplication、加载 UI 界面、把按钮信号绑定到推理槽函数、把推理结果显示出来或保存。整个分割链路的核心槽函数逻辑结构大致是下面这样def run_seg(self): img_path, _ QFileDialog.getOpenFileName(self, 选择图片, , Images (*.png *.jpg *.bmp)) if not img_path: return # 1) QPixmap 读图 pixmap QPixmap(img_path) # 2) 转成 numpy 数组再走模型的预处理 image pixmap.toImage().convertToFormat(QImage.Format_RGB888) w, h, ptr image.width(), image.height(), image.bits() arr np.frombuffer(ptr, dtypenp.uint8).reshape(h, w, 3).copy() # 3) 模型前向得到 seg_map (H, W)值为类别 id seg_map self.model.predict(arr) # 4) 上色并叠加回原图写 result.jpg colorized label_to_color(seg_map) cv2.imwrite(result.jpg, colorized)理解这段链路最关键的是第三步到第四步的转换。模型输出的是每个像素属于各个类别的概率取 argmax 后得到类别 id 矩阵id 是 0 到 num_classes-1 的整数可视化时再把这些 id 映射成预设的 RGB 颜色。如果发现分割结果颜色错乱基本是 id 到颜色的映射表和你训练时的类别顺序没对齐。np.frombuffer(ptr, dtypenp.uint8)这一步一定要.copy()因为 QPixmap 在函数栈上很快会被释放直接引用其内存指针会造成悬空访问这是 PyQt5 图像处理里最典型的崩溃源。工程里已经写好的部分可以直接用但如果想扩展「保存分割结果」「导出叠加图」这类功能顺着这条链路在第四步前后加代码就行。4.3 界面卡顿问题的常规解法把分割丢进 QThreadPyQt5 的 GUI 程序是单线程事件循环模型按钮点击触发的槽函数跑在主线程里。如果你直接在主线程里执行模型前向一张图推理耗时几秒到几十秒期间主线程无法处理界面刷新窗口就会变成「未响应」的白屏状态。这不是程序挂死了而是事件循环被阻塞。解决思路很简单把耗时操作放到子线程主线程只负责接收结果并更新界面。class SegWorker(QThread): finished pyqtSignal(np.ndarray) def __init__(self, model, image): super().__init__() self.model model self.image image def run(self): seg_map self.model.predict(self.image) self.finished.emit(seg_map)按钮槽函数里只需要 new 一个 SegWorker把模型和图像传进去然后连接 finished 信号到界面更新函数即可。模型推理在 run() 方法里执行finished 信号把结果传回主线程界面保持流畅。如果你的机器是 CPU 推理这个改动几乎是必须的否则演示时每次点按钮都要等十秒白屏观感很差。5. 避坑与常见问题中文路径、state_dict 不匹配和界面卡死的五条记录5.1 双击 main.py 闪退提示找不到 Qt platform plugin windows现象命令行运行python main.py直接报错退出错误信息类似could not find or load the Qt platform plugin windows。原因PyQt5 的运行时插件目录没有暴露给程序。常见于没激活 conda 环境就运行或者 pyqt5 安装不完整、多个 Python 环境的 PyQt5 互相冲突。解决先conda activate seg确认在正确环境里如果重装 PyQt5 后仍报错手动设置环境变量QT_QPA_PLATFORM_PLUGIN_PATH指向 site-packages 里 PyQt5/Qt/plugins 目录。这个报错和项目代码无关属于环境配置问题。5.2 加载权重报错state_dict 的 key 对不上现象运行 test.py 或启动 GUI 时load_state_dict抛异常提示Missing key(s)或Unexpected key(s)。原因最常见的是权重文件在训练时用了多 GPUstate_dict 里所有 key 都带module.前缀而当前模型是单卡定义没有这个前缀还有一种可能是别人训练时 num_classes 和当前配置不一致导致最后一层卷积层形状不匹配。解决加载时先做一层 key 清洗把module.前缀剥掉再 load。ckpt torch.load(save_model/ckpt_epoch_50.pth, map_locationcpu) state ckpt.get(state_dict, ckpt) # 兼容裸字典和包了一层的情况 state {k.replace(module., ): v for k, v in state.items()} model.load_state_dict(state)如果清洗完还报形状不匹配基本可以断定是类别数不一致回 DF_config.py 把 num_classes 改成和原训练一致的值再试。5.3 中文路径下图片读不出来模型加载报编码错误现象图片明明存在cv2.imread却返回空对象或者模型路径带中文时加载失败报 UnicodeDecodeError。原因PyTorch 和 OpenCV 的底层 C 实现对中文路径支持不稳定这是老生常谈的路径解析玄学。项目压缩包的备注里也明确强调了解压后路径不要用中文。解决解压后把项目目录重命名为全英文路径比如D:\semantic_seg_gui\并且确保数据集路径、图片路径全部没有中文字符。这个坑一旦踩到改配置和代码都未必能绕开最省事的方案就是别让路径里出现中文。5.4 推理时显存爆掉或速度慢到无法接受现象GPU 推理直接 OOM 报错CPU 推理一张图要几十秒。原因没有把输入图 resize 到模型期望的尺寸直接把原图分辨率喂进去了。高分辨率图在 Transformer 结构的主干上计算量是平方级增长的显存和耗时都会爆炸。解决在预处理阶段统一 resize 到 config 里的 input_size通常是 512×512 或 1024×1024。显存小的机器用 512推理结果再上采样回原图叠加。CPU 推理时把 torch 换成 CPU 版能省掉 CUDA 初始化时间同时把输入尺寸设小一点交互体验会明显改善。5.5 GUI 点「开始分割」后窗口无响应现象按钮按下后界面白屏转圈系统提示「未响应」等模型跑完才恢复。原因模型前向推理直接写在按钮槽函数里运行在主线程阻塞了 Qt 的事件循环。这不是死锁是长时间占用了主线程。解决按 4.3 的 QThread 方案把推理放进子线程主线程只负责等待 finished 信号并更新界面。另外给按钮加一个禁用状态推理期间置灰防止用户重复点击触发并发推理。这个现象在 CPU 推理时尤其明显属于 PyQt5 交互开发的必修课。6. 进阶用 create_seg_map.py 和 mini_dataset.py 训练自己的语义分割模型6.1 三个文件的配合create_seg_map.py、mini_dataset.py 与 LDN_transforms.py工程目录里没有专门的 train.py训练部分的入口依赖三个数据相关脚本它们的分工如下文件职责输入输出create_seg_map.py生成语义分割标签图RGB 标注图像素值为类别 id 的单通道 PNGmini_dataset.py数据集加载器原图 标签图供训练循环取用的图片/掩膜对LDN_transforms.py数据增强图片 掩膜增强后的图片 掩膜create_seg_map.py 解决的是标签格式问题。人工标注时习惯用 RGB 颜色区分物体但模型训练需要每个像素一个整数 id。这个脚本做的事情就是把颜色值映射到对应的类别数字区域染色和像素换 id。mini_dataset.py 负责按路径读取原图和掩膜保证两者索引对齐。LDN_transforms.py 是训练专用的增强集合随机翻转、缩放、颜色抖动都在里面推理时不要启用。6.2 从数据准备到微调训练循环想训练自己的类别路径是清晰的准备一批原图和对应的标注图 → 跑 create_seg_map.py 生成 id 图 → 改 DF_config.py 里的 num_classes → 参考 mini_dataset.py 的结构补一个训练脚本循环里走前向、算损失、反向传播、保存检查点。训练循环的骨架长这样for epoch in range(50): for imgs, masks in train_loader: imgs, masks imgs.to(device), masks.to(device) logits model(imgs) # (B, num_classes, H, W) loss criterion(logits, masks) optimizer.zero_grad() loss.backward() optimizer.step()关键点是 masks 的格式必须是单通道的类别 id 矩阵不能是 one-hot且背景类要单独占一个 id。数据量不大时建议加载 ckpt_epoch_50.pth 做微调而不是从随机初始化开始训省时间且收敛更快。网上对带 GUI 的免费 Python 源码需求很杂但真正能落在自己数据集上的没几个——这套工程把数据映射和增强脚本都留好了是难得的可以直接接自己数据复用的结构。从那以后我每次接到语义分割工程都会先跑一遍 test.py 确认权重和模型结构对齐再开 GUI 调交互最后才考虑训练自己的数据。这个顺序能省半天调试时间希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?