
简介基于视觉Transformer的图像去雾算法研究与实现资料包属于Python高分项目包含完整源码与文档说明。资源面向深度学习方向的学生、科研人员及毕业设计开发者尤其适合正在开展图像去雾课题、需要算法复现或二次开发的研究者。压缩包共338个文件整体约156.36MB以Python脚本为核心提供204个py文件同时包含模型配置文件yaml、训练与评估记录csv、项目说明文档md/ipynb以及png/gif可视化图表覆盖从数据预处理、模型搭建到训练验证的完整流程。资源内置将训练图像切分为256×256小图的预处理脚本、My_train.py训练入口支持Uformer等视觉Transformer架构并附带基于NH-HAZE数据集的操作说明、训练日志与损失曲线方便复现实验、对照调参和结果分析。已有291人学习浏览适合需要快速跑通去雾流程、撰写毕业设计或复现论文实验的开发者。1. 基于 Vision Transformer 的图像去雾从 Uformer 训练到损失景观分析这套源码包里有什么图像去雾在监控和自动驾驶场景里一直是硬需求但暗通道先验这类传统去雾算法遇到浓雾、非均匀雾和颜色偏移时经常翻车。近年基于 Vision Transformer 的去雾算法把这个问题拉高了一个档次Uformer 就是其中典型——它用 U 型结构加窗口自注意力做图像复原在 NH-HAZE 这类真实雾图数据集上表现明显好过纯 CNN 方案。这套资源包的核心是两件事一是从 256×256 切图到 270 轮训练完整跑通 Uformer 去雾的代码链路二是附带了一批 CIFAR-10/100 上 ResNet、ViT-Ti、AlexNet 的 loss landscape 和 corrupted 数据 CSV用来对比不同架构的优化曲面和噪声鲁棒性。适合两类人做图像复原方向毕业设计、课程设计的学生以及想直观看到 Transformer 和卷积网络在优化行为上差异的研究者。下面按我实际跑通这套东西的顺序来拆。2. 数据预处理把 NH-HAZE 切成 256×256 小图切图脚本参数与目录对接2.1 NH-HAZE 数据集为什么必须先切图NH-HAZE 是 NTIRE 2020 去雾挑战里的真实雾图数据集每个场景都是成对的有雾图和干净参考图。真实雾图有个共同特点单张分辨率高、样本总数少。Uformer 这类 U 型 Transformer 的窗口自注意力需要把特征图切成固定大小的块输入分辨率一变后续的窗口划分和还原逻辑全部要跟着调所以项目里所有模型都约定输入一个固定尺寸。直接拿整张大图训练显存先撑不住而且样本量太少一个 epoch 看不了几张图模型根本学不出泛化能力。把图切成 256×256 小图之后样本数量按几十倍增长同时显存压力大幅下降。切图的另一个隐藏好处是数据增强——同一张大图的不同区域被当成独立样本模型被迫去学不同位置的雾气分布。这就是摘要里“把训练数据图像切分成大小为 256×256 的小图”这一步的全部理由。2.2 generate_patches_SIDD.py 切图命令与参数说明脚本名里的 SIDD 是手机图像去噪数据集这个切图工具显然是复用过来的——切图逻辑是通用的不管去噪还是去雾都是滑窗裁块并保证成对输出。原始 train 目录下每个场景一个子文件夹里面放对应场景的有雾图和干净图。执行命令如下python3 generate_patches_SIDD.py \ --src_dir /home/dell/桌面/TPAMI2022/Dehazing/#dataset/NH_haze/train \ --tar_dir /home/dell/桌面/2022毕业设计/Datasets/NH-HAZE/train_patches--src_dir指向 NH_haze 下的 train 原始目录脚本从这里读所有场景的原图--tar_dir是切图后的输出目录脚本一般会在它下面按hazy和gt两个子目录存放成对的 256×256 切片。第一次跑之前先确认两件事--src_dir里有没有混进.DS_Store、缩略图这类非图像文件以及输出盘剩余空间够不够——NH-HAZE 切完通常是好几 GB。跑的时候如果切图速度很慢优先怀疑是单片读入没走缓存常见做法是先把原图一次性 load 进内存再滑窗但这个脚本本身的实现不用改能跑通就行。2.3 切图结果校验顺手把验证集划出来切图脚本结束通常不打印汇总我一般自己写个三行校验避免后面训练读到一个空目录或残缺配对from pathlib import Path from PIL import Image base Path(/home/dell/桌面/2022毕业设计/Datasets/NH-HAZE/train_patches) for sub in [hazy, gt]: files sorted((base / sub).glob(*.png)) print(sub, 数量:, len(files)) img Image.open(files[0]) print(尺寸:, img.size, 通道:, img.mode)这段逻辑是按hazy/gt两个子目录去数文件并打印第一张图的尺寸和通道模式。如果脚本输出的是.jpg或.npy把glob里的后缀换掉即可。校验通过后再做最后一步从 train_patches 里按场景抽 10%~20% 出来当验证集别去动 NTIRE 官方 test 集——那个 test 是盲测没有公开参考图后面测评环节会说明原因。提示规划的tar_dir路径如果和src_dir在同一块盘上切图过程会同时读写同一块磁盘速度慢且伤盘。我一般会把tar_dir放到另一块机械盘或 SSD 上。3. 训练主流程Uformer 架构选型与 My_train.py 的七个参数3.1 为什么选 Uformer 而不是普通 ViT普通 Vision Transformer 把整张图拉成一串 patch 做全局自注意力特征图分辨率一高计算量就是二次方上涨直接用在去雾这种需要保持像素级细节的任务上不现实。Uformer 的思路是U 型编码器-解码器结构保留多尺度特征窗口自注意力把计算复杂度限制在窗口大小内再配合 LeWin 层做局部与全局的信息交换。去雾和去噪、去雨同属图像复原核心矛盾都是“保细节”和“去退化”之间的平衡。卷积网络局部感受野强但雾气是全局退化单靠局部卷积容易在浓雾区域留下雾斑Uformer 的窗口注意力既能感知局部纹理又能跨窗口传递全局雾气分布信息在 NH-HAZE 这种非均匀雾图上不容易出现颜色偏移。这个包里训练脚本默认--arch Uformer就是基于这个选型理由。3.2 My_train.py 训练命令与参数矩阵训练入口是My_train.py摘要里给出的原始命令如下python3 ./My_train.py \ --arch Uformer \ --nepoch 270 \ --batch_size 32 \ --env My_Infor_CR \ --gpu 1 \ --train_ps 128 \ --train_dir /media/dell/fd6f6662-7e3...--train_dir在原始命令里被截断了完整路径以你解压后 README 里写的为准。每个参数的具体含义和我的调整习惯见下表参数示例值含义我的调整建议--archUformer网络结构显存紧张先用 Uformer-S 验证链路--nepoch270总训练轮数先跑 5 轮确认 loss 下降再放长--batch_size32每步批量大小11G 显存配 Uformer-B 先降到 8--envMy_Infor_CR实验标识决定日志目录名每次实验改名避免覆盖旧日志--gpu1用第几张卡从 0 数先nvidia-smi确认空闲卡再填--train_ps128训练时随机裁剪尺寸数据是 256×256裁 128 等于在线增强--train_dir/media/...训练数据根目录必须指向切图后的 train_patches这里容易误解的是--train_ps 128和切图尺寸 256 的关系切图是离线的把原图裁成 256×256 的大块存盘训练时每次迭代再从 256 的大块里随机裁 128×128等于多了一层裁剪增强同时把实际输入分辨率压下来省显存。两者不冲突反而是这套代码里比较讲究的一个设计。3.3 训练过程中看什么loss 曲线与 checkpointUformer 系列训练脚本的常见配置是 AdamW 优化器加 cosine 学习率衰减My_train.py大概率沿用这个默认。前 10 轮训练 loss 从 0.5 量级快速降到 0.1 以下是正常节奏如果 20 轮还在 0.3 以上震荡优先怀疑数据配对读反或学习率设得过大别急着加轮数。checkpoint 一般会按固定 epoch 间隔保存一份同时额外保存验证集上 PSNR 最高的 best 模型。续训时脚本里通常有--resume或--pretrained之类的参数具体名字打开My_train.py的argparse段看一眼就知道。我跑长实验的习惯是每 30 轮打印一次验证集 PSNR 和 SSIM 到日志而不是只看训练 loss——训练 loss 低只能说明拟合了训练分布去雾效果好不好得看验证集的感知质量。4. 损失景观与鲁棒性 CSV三个模型对比实验的复现与画图4.1 losslandscape CSV 是怎么来的命名规则怎么读包里这批 loss landscape 数据来自损失景观可视化工具思路是先用 filter normalization 把网络参数归一化到一个超球面上再选两个随机方向作为坐标轴沿网格采样计算 loss最终得到一个三维曲面。CSV 命名规则是{数据集}_{模型}_{模型文件hash}_x1_losslandscape.csv拆开看就很清楚cifar100_vit_ti_9857b21357_x1_losslandscape.csv里的cifar100是数据集vit_ti是 ViT-Tiny9857b21357是生成该数据所用 checkpoint 文件名的 hash 段x1表示方向向量做了单位范数归一化。这个 hash 必须和原始模型文件名严格对应你把模型重命名后再画图坐标系就对不上了。cifar100_resnet_dnn_50_losslandscape.csv里的50可能是 ResNet 的层数也可能是某个训练轮数的 checkpoint 标记具体以包内说明为准。重点在于这批 CSV 是按统一坐标规范生成的不同模型之间可以放在同一坐标系里对比。4.2 corrupted 数据在对比实验里的作用cifar10_alexnet_dnn_corrupted.csv、cifar100_alexnet_dnn_corrupted.csv、cifar100_resnet_dnn_corrupted.csv这三份是鲁棒性实验数据。“corrupted”在图像分类实验里通常指标签噪声或输入扰动两种可能具体是哪一种要看包内配套脚本。这类实验的常见结论方向是CNN 在部分噪声标签下会倾向于记住简单模式而 ViT 因为全局注意力更依赖整体结构一致性对噪声的响应曲线和 CNN 明显不同。这批 CSV 的价值在于省掉你重新跑一遍完整对比训练的时间——直接加载画图就能作为论文或报告里“ViT 与 CNN 优化行为差异”的佐证材料。4.3 用 CSV 复现损失景观图画图脚本不用重写pandas 加 matplotlib 就够了。核心是先把 x、y 坐标轴转成网格矩阵import pandas as pd import matplotlib.pyplot as plt import numpy as np df pd.read_csv(cifar100_vit_ti_9857b21357_x1_losslandscape.csv) x np.unique(df[x].values) y np.unique(df[y].values) Z df.pivot(indexy, columnsx, valuesloss).values plt.figure(figsize(8, 6)) cf plt.contourf(x, y, Z, levels50, cmapviridis) plt.colorbar(cf, labelloss) plt.xlabel(x direction step) plt.ylabel(y direction step) plt.title(cifar100_vit_ti loss landscape) plt.savefig(vit_ti_landscape.png, dpi200)这段代码先把 x、y 去重成坐标轴再用pivot把 loss 值重排成二维矩阵contourf画等值面。注意坐标不是真实参数值而是沿随机方向走的归一化步长值域一般在 [-1, 1]。对比不同模型时要在同一坐标系下画才有意义——直接拿两个 CSV 叠在同一张图里如果坐标范围不一致结论就是错的。5. 避坑与排查五个把新手卡住的真实案例5.1 路径带 “#” 和中文导致找不到目录现象--src_dir路径明明存在运行切图脚本却报 FileNotFoundError 或目录为空。原因#dataset里的#在部分 shell 拼接和 glob 处理中被当成注释符或正则特殊字符“桌面”是中文路径脚本里如果直接用字符串拼接而没有做编码处理也会在读取时出错。解决把数据集拷贝到纯 ASCII、无空格的路径比如/data/NH_haze。如果非要用原始路径命令整体加引号并且在脚本入口用os.path.abspath展开一遍确认路径解析结果没问题再往下走。5.2 训练一启动就 CUDA out of memory现象Uformer-B 配batch_size 32在 11G 显存的卡上直接 OOM进程被杀。原因128×128 输入下窗口自注意力的激活值加上反向传播的中间变量峰值显存远超 11G默认 batch 对这个配置来说太大了。解决先把batch_size降到 8 跑通一次完整迭代确认显存占用再逐步往上加或者把--arch换成 Uformer-S 这种轻量配置。有条件就开启 AMP 混合精度torch.cuda.amp在这类复原训练任务里兼容性普遍不错显存能省接近一半。5.3 loss 不降或前几十个 iteration 就出 NaN现象训练 loss 从第 1 个 iteration 开始就是 nan或者一直在 0.3 以上剧烈震荡20 轮不收敛。原因最常见是输入和目标读反了——模型拿干净图去预测有雾图loss 自然降不下去其次是学习率设置过大且没有 warmup前几步就把参数冲飞了。解决写一个可视化脚本把每个 batch 的 input 和 target 并排保存成一张图肉眼确认 input 是有雾图、target 是干净参考图。学习率按 Uformer 官方默认的 1e-4 到 2e-4 起步别一上来就 5e-3。5.4 loss landscape 图画出来全是平的或大片空洞现象contourf画出来中间一大块没有数据或者 loss 值全是一个常数曲面完全没有起伏。原因采样网格超出了 checkpoint 实际的参数坐标范围更隐蔽的是模型文件被重命名导致 CSV 里的 hash 段和 checkpoint 对不上画图脚本按错误坐标系解读数据。解决画图前先df.describe()看一眼 x、y、loss 三列的数值范围确认不是全常数。如果数据本身没问题把levels从 50 调成 30避免采样点太少时出现大片空洞。模型文件保持原始命名重新生成 CSV 时输入正确的 checkpoint 路径。5.5 在 NH-HAZE test 目录上测出反直觉的低分现象直接拿官方 test 集当验证集PSNR 比论文结果低一大截甚至出现负值。原因NTIRE 2020 去的雾挑战赛的 test 集是盲测没有公开参考图官方不让你拿它算指标或者脚本读到了错误的配对文件把不同场景的图当成一对算 PSNR。解决从 train 切图里按场景抽 10%~20% 自建验证集。算指标时用skimage.metrics.peak_signal_noise_ratio输入要先转成 float 且值域统一到 [0, 1]用 uint8 直接算会出现数值偏差这就是那些“反直觉低分”最常见的来源。6. 验证去雾效果PSNR/SSIM 三行代码加上损失景观的量化进阶去雾模型好不好最终落到两个指标PSNR 衡量像素级重建误差SSIM 衡量结构相似度。我从这个包里学到的验证姿势是写一个统一评测脚本from skimage.metrics import peak_signal_noise_ratio, structural_similarity psnr peak_signal_noise_ratio(gt, pred, data_range1.0) ssim structural_similarity(gt, pred, data_range1.0, channel_axis-1) print(fPSNR: {psnr:.2f} dB SSIM: {ssim:.4f})data_range1.0的前提是输入已经是 float 且值域 [0, 1]如果手里是 uint8 的图先除 255 再传进来否则算出来的 PSNR 会整体虚高。channel_axis-1指示通道在最后一维RGB 图必须带上这个参数SSIM 才会逐通道算再取均值。进阶一点的用法是把损失景观数据从“画图”升级成“量化”对比cifar100_vit_ti和cifar100_resnet_dnn_50两张曲面时除了肉眼比平缓程度可以计算曲面 loss 值的方差或者中心区域的最大最小差值。Loss 曲面越平缓、中心谷越宽通常说明优化过程对学习率和初始化越不敏感——这是 ViT 相比 CNN 在优化行为上最常被讨论的一个点。如果你手上有 corrupted 数据还可以对比噪声标签下两类模型的损失景观变化幅度这个结论写进课程设计报告里非常有说服力。我跑这类对比实验踩过几次数据命名不一致的亏从那以后每次做对比都强制把三件套锁死模型 checkpoint 保持原始命名、随机种子固定、CSV 生成脚本和画图脚本放在同一目录。哪怕只改了一个参数也重新出一份完整数据绝不复用旧 CSV。这套流程走完你得到的就不只是能跑通的代码而是一套可复现、可对比、写进报告里站得住的完整实验。希望帮到你。本文还有配套的精品资源点击获取