基于波形扩散模型的低光照图像增强算法Python实现源码拆解
简介本资源为基于波形扩散模型的低光照图像增强算法Python实现源码面向计算机、人工智能、电子信息等相关专业的在校学生、教师及企业研发人员可用于课程设计、毕业设计、大作业或初期项目立项演示帮助解决低光照图像质量提升的算法实现与复现问题。压缩包共34个文件约21.1MB以15个py源码文件为核心辅以14个pyc编译缓存、2张png效果对比图、1个yml配置文件及txt、md说明文档涵盖模型、数据集、工具与训练评估等模块。项目提供预训练模型和多个公开数据集包含训练与评估脚本并实现波形扩散、U-Net、图像恢复等关键组件便于读者直接运行、理解算法流程并在此基础上修改扩展。目前已有491人学习关注适合希望入门扩散模型与图像增强方向的学习者借鉴参考。1. 低光照增强还能这么玩波形扩散模型源码包拆解夜里十一点监控画面糊成一团人脸只剩轮廓车牌全是噪点——这是低光照图像增强最典型的战场。传统做法无非两条路Retinex 系分解光照或者 GAN 硬怼亮度前者容易过曝、颜色发灰后者训练玄学、细节靠猜。这份「基于波形扩散模型的低光照图像增强算法 python 实现源码.zip」走的是第三条路把扩散模型里的加噪-去噪过程搬到小波域让模型在频域里逐步恢复光照和纹理而不是在像素空间里盲猜。包里带了train.py、evaluate.py、configs/LOLv1.yml、models/下的wavelet.py、ddm.py、unet.py、restoration.py还有datasets/和utils/的采样、优化、日志模块属于能直接跑训练和评估的完整工程不是只丢一个模型定义的半成品。适合谁做图像复原方向的学生、要交毕设或课程设计的同学以及想拿扩散模型练手但不想从零搭训练框架的工程师。下面按「它怎么工作 → 怎么跑起来 → 坑在哪 → 怎么改」的顺序拆。2. 波形扩散模型到底在做什么从像素扩散到小波域扩散2.1 为什么不在像素空间直接扩散标准 DDPM 在像素空间加高斯噪声低光照增强任务里有两个硬伤。第一低光图像本身信噪比低像素级噪声和真实纹理混在一起扩散过程容易把暗部细节当成噪声抹掉。第二像素空间维度高LOLv1 这种 600×400 的图扩散步数动辄上千显存和推理时间都吃不消。波形扩散的思路是先把图像做小波变换拆成 LL、LH、HL、HH 四个子带LL 保留低频光照结构高频子带承载边缘和纹理。扩散过程只在高频子带上做低频部分用 restoration 模块单独回归。这样模型要学的分布更集中步数也能压下来。常见做法是用 Haar 小波计算量小、可逆models/wavelet.py里大概率就是 DWT/IDWT 的实现。2.2 源码里几个核心文件的分工拿到包先别急着python train.py花十分钟把文件关系理清楚后面调参才不会抓瞎。按目录结构核心链路是这样的文件职责你改它的频率models/wavelet.py小波正变换与逆变换低除非换小波基models/ddm.py扩散调度加噪、去噪、采样中调步数和噪声表models/unet.py去噪网络主干高改容量和注意力models/restoration.py低频光照恢复分支中改损失权重时看models/mods.py基础模块卷积、归一化等低utils/sampling.py采样策略DDIM 之类中推理加速靠它utils/optimize.py优化器与学习率调度中configs/LOLv1.yml数据集与训练超参高每次实验都动datasets/dataset.py数据加载与配对中换数据集要改datasets/data_augment.py增强策略低train.py是入口读 config、建模型、跑循环evaluate.py加载权重算 PSNR/SSIM。理解这个分工你才知道改一个参数该去哪个文件。2.3 扩散前向与反向在小波域的形式前向过程还是标准的高斯加噪只是作用对象换成了高频子带系数# 伪代码对应 models/ddm.py 里的加噪逻辑 import torch def q_sample(x_high, t, noise, alphas_cumprod): x_high: 高频子带系数, shape [B, C, H, W] t: 时间步, shape [B] noise: 同 shape 的高斯噪声 alphas_cumprod: 累积 alpha 表, shape [T] sqrt_alpha alphas_cumprod[t] ** 0.5 sqrt_one_minus (1 - alphas_cumprod[t]) ** 0.5 # 按 batch 维度对齐形状 sqrt_alpha sqrt_alpha.view(-1, 1, 1, 1) sqrt_one_minus sqrt_one_minus.view(-1, 1, 1, 1) return sqrt_alpha * x_high sqrt_one_minus * noise逻辑说明alphas_cumprod是预先算好的噪声调度表t越大sqrt_alpha越小信号被噪声淹没得越彻底。参数上T一般设 1000但小波域扩散可以降到 250500configs/LOLv1.yml里应该有对应字段。反向过程由 UNet 预测噪声再用采样器逐步还原utils/sampling.py里如果实现了 DDIM可以把推理步数从 1000 压到 50 步以内这是实际部署时最值得动的地方。3. 把源码跑起来环境、数据、训练与评估的完整链路3.1 环境依赖与安装顺序requirements.txt在根目录先看它再动手。PyTorch 版本是第一个坑扩散模型对版本敏感建议按下面顺序来# 1. 建虚拟环境别用系统 python conda create -n wddm python3.7 -y conda activate wddm # 2. 先装 PyTorch按 CUDA 版本选这里以 cu11.3 为例 pip install torch1.10.0cu113 torchvision0.11.0cu113 \ -f https://download.pytorch.org/whl/torch_stable.html # 3. 再装其余依赖 pip install -r requirements.txt逻辑说明先装 torch 再装 requirements是因为 requirements 里可能写了torch1.8这类宽松约束pip 会去拉最新版和 CUDA 驱动对不上就报CUDA error: no kernel image is available。参数上python 3.7 是包里__pycache__里.cpython-37.pyc透露的版本用 3.8 以上可能遇到collections.Iterable这类废弃警告能跑但烦。装完用python -c import torch; print(torch.cuda.is_available())验证返回 True 再往下走。3.2 数据集准备与 config 对齐LOLv1 是低光照增强的经典配对数据集结构是our485/low、our485/high、eval15/low、eval15/high。下载后按这个结构放然后改configs/LOLv1.yml# configs/LOLv1.yml 关键字段示意 data: root: ./data/LOLv1 # 数据集根目录 train_split: our485 # 训练子集 val_split: eval15 # 验证子集 patch_size: 256 # 训练裁剪尺寸 batch_size: 8 # 显存 8G 以下建议降到 4 model: base_channels: 64 # UNet 基础通道 diffusion_steps: 500 # 小波域扩散步数 wavelet: haar # 小波基 train: lr: 1e-4 epochs: 200 save_interval: 10逻辑说明patch_size和batch_size是显存杀手256×256 配 batch 8 大约吃 10G 显存不够就砍 batch 或降到 128。diffusion_steps设 500 是精度和速度的折中设 1000 训练慢一倍设 250 暗部细节会糊。wavelet字段要和models/wavelet.py里支持的基一致改之前先看代码里if wavelet haar这类分支。3.3 启动训练与日志观察python train.py --config configs/LOLv1.yml --gpu 0逻辑说明--config指定配置文件--gpu选卡。训练开始后看utils/logging.py输出的日志重点盯三个量loss是否稳定下降、lr是否按调度衰减、psnr在验证集上是否爬升。如果 loss 在前 100 步就炸成 NaN八成是学习率太大或数据没归一化到 [-1,1]去datasets/dataset.py里确认ToTensor之后有没有Normalize。如果 loss 不动检查models/restoration.py的损失权重低频分支权重过高会压制高频扩散的学习。3.4 评估与指标复现python evaluate.py --config configs/LOLv1.yml \ --checkpoint checkpoints/epoch_200.pth逻辑说明evaluate.py会加载权重在eval15上算 PSNR 和 SSIM。LOLv1 上这类方法的 PSNR 通常在 2024 dB 区间SSIM 0.8 上下具体看训练轮数和采样步数。如果指标远低于预期先确认评估时用的采样步数和训练一致DDIM 步数设太少会掉点。comparison.png是作者给的对比图可以拿来对照自己的输出看颜色偏移和细节恢复是否在同一水平。4. 避坑与排查跑不通、指标低、显存爆的常见原因4.1 现象训练启动就报ModuleNotFoundError: No module named models原因train.py里用了相对导入或包导入但你在错误的目录下执行或者models/下缺__init__.py。包里models/__init__.py是有的但如果你解压后手动挪过文件可能丢了。解决确认在项目根目录执行且models/、utils/、datasets/下都有__init__.py。缺的话手动建空文件或者用python -m train代替python train.py。4.2 现象loss 正常下降但验证集 PSNR 一直卡在 15 dB 以下原因低频和高频分支的损失权重失衡或者数据配对错位。低光增强是配对任务low和high文件名必须一一对应dataset.py里如果按排序配对文件名不一致就会错位。解决检查datasets/dataset.py的配对逻辑打印几条样本的路径确认。损失权重在configs/LOLv1.yml或train.py里把高频扩散损失权重调高低频重建损失调低常见比例是 1:0.5 到 1:0.2。4.3 现象CUDA out of memorybatch 已经降到 1 还爆原因UNet 通道数太大或者patch_size没降。base_channels: 64在 256×256 下对 8G 卡就吃力如果代码里还有中间特征缓存没释放更紧张。解决先把patch_size降到 128再把base_channels降到 32。另外在train.py里确认有没有torch.cuda.empty_cache()没有的话在每个 epoch 结束后加一句。混合精度训练也能省一半显存用torch.cuda.amp包一下前向。4.4 现象推理时输出全黑或全白原因采样过程的alphas_cumprod和设备不匹配或者逆小波变换前没把子带拼回去。utils/sampling.py里如果用了torch.tensor建表但没.to(device)在 GPU 上会静默出错。解决检查采样代码里所有张量的 device确保和输入一致。逆变换前打印一下四个子带的均值和方差正常应该在合理范围如果某个子带全是 0 或极大值说明前向或反向某步断了。4.5 现象evaluate.py报KeyError或权重加载不上原因checkpoint 保存的是state_dict还是整个模型对象加载方式不一样。train.py里如果torch.save(model.state_dict(), ...)评估时就要model.load_state_dict(torch.load(...))多一层[model]或[state_dict]都会报错。解决用torch.load先打印 keys看是OrderedDict还是嵌套字典再决定取哪一层。常见做法是保存时统一存{model: model.state_dict(), optimizer: ...}加载时按 key 取。5. 进阶玩法换小波基、压采样步数与自定义数据微调跑通默认配置只是起点这份源码真正的价值在于结构清晰方便你按自己的任务改。下面三个方向是我实际会优先动的。第一个是换小波基。models/wavelet.py里如果只实现了 Haar可以换成 Daubechies 系列db2、db4高频子带保留的纹理更丰富代价是计算量上升。改的时候注意正变换和逆变换必须成对滤波器系数别抄错否则重建图像会有网格伪影。换完在configs/LOLv1.yml里同步wavelet字段跑 20 个 epoch 对比 PSNR通常 db2 比 Haar 高 0.20.5 dB但训练时间多 15% 左右。第二个是压采样步数。训练用 500 步推理用 DDIM 可以压到 50 步甚至 20 步。utils/sampling.py里找ddim_sample这类函数把eta设为 0 就是确定性采样步数用torch.linspace(0, T-1, num_steps)生成子序列。我一般会做一张步数-指标表采样步数PSNR (dB)单张推理耗时 (ms)50023.182010022.81905022.41052021.648从表里选拐点50 步通常是性价比最高的位置再往下掉点明显。第三个是自定义数据微调。如果你手上有自己的低光-正常光配对数据不用改模型结构只改datasets/dataset.py的路径和configs里的root然后把lr降到 1e-5冻结models/wavelet.py和models/restoration.py的前几层只微调 UNet 后半段。这样 50 个 epoch 就能适配新场景比从头训练省一半时间。微调时记得把data_augment.py里的随机裁剪和翻转打开小数据集上能防过拟合。最后说个血泪经验每次改完configs或模型结构先跑python train.py --config ... --dry-run这类短跑模式如果代码支持或者手动把epochs设成 1确认前向反向能走通、显存不爆、loss 不是 NaN再开完整训练。我早期图省事直接上 200 epoch结果第 3 个 epoch 才报错一晚上白等。从那以后我每次动配置都强制走一遍单步验证这个习惯帮我省了太多后悔药。希望帮到你。本文还有配套的精品资源点击获取