news 2026/10/11 11:59:12

图像去雨Derain实战:从技术路线到PyTorch代码与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
图像去雨Derain实战:从技术路线到PyTorch代码与避坑指南

简介:Derain 是一份面向图像处理与计算机视觉学习者的 Python 去雨项目资源,聚焦于消除照片中的雨滴干扰,提升恶劣天气下图像的清晰度与可用性。项目综合运用图像预处理、特征提取、雨滴建模与背景恢复等思路,并涉及卷积神经网络、条件生成对抗网络等深度学习方法的实践,适合具备一定 Python 与图像处理基础、希望深入理解去雨算法的读者参考。压缩包共 4 个文件,包含 2 个 pdf 论文资料、1 个 py 主程序脚本和 1 个 txt 说明文档,整体约 3.72MB,其中论文可用于理解算法原理,脚本则提供可运行的实现入口。目前已有 199 人学习下载。通过这份资源,读者可以对照论文梳理去雨流程,借助主程序脚本动手复现算法,并参考说明文档快速了解项目结构与运行方式,为后续的图像恢复与增强研究提供可借鉴的实践素材。

1. Derain:从一张雨滴糊住的行车记录仪截图说起

前阵子帮一个做智慧工地巡检的朋友看数据,他手里有一批雨天拍的监控截图,雨滴挂在镜头上,画面像蒙了一层毛玻璃,钢筋绑扎间距、安全帽佩戴这些细节全糊成一团。他问我能不能用算法把雨去掉,我第一反应就是 Derain 这个方向——图像去雨。Derain 是 derain(de-rain)的缩写,在计算机视觉里专指从单张图像或视频序列中分离并去除雨滴、雨线、雨雾,还原干净背景的技术。它解决的不是"让照片更好看",而是让下游的检测、识别、测量任务在雨天不掉精度。适合谁?做安防监控、自动驾驶感知、户外巡检、遥感影像预处理的工程师,以及想入门底层视觉复原的研究生。这一章先把 Derain 到底在做什么、为什么难、值不值得投入讲清楚,后面几章直接上可复现的代码和参数。

2. Derain 的两条技术路线:雨线、雨滴和雨雾到底怎么分

2.1 先搞清楚你要去的是哪种"雨"

很多人一上来就说"我要去雨",结果拿到的数据里既有斜向雨线,又有镜头上的失焦雨滴,还有整体发灰的雨雾。这三类在物理成因和算法处理上完全不同,选错路线后面全白干。

雨线(rain streak)是雨滴下落时在曝光时间内形成的运动模糊,方向性强、长度和角度随快门和风速变化,通常叠加在背景上,属于加性噪声。雨滴(raindrop)是附着在镜头或玻璃上的球状水珠,它同时产生折射和模糊,会把背景局部扭曲,属于"透射+模糊"的复合退化。雨雾(rain fog / haze)是大量悬浮水滴对光的散射,导致对比度下降、颜色偏移,更接近大气散射模型。

我一般会先看数据:如果雨是斜线状、背景还能辨认,走雨线去除;如果是圆斑、背景被局部放大或扭曲,走雨滴去除;如果整幅发灰、远景消失,先做去雾再做去雨。这个判断决定了你后面选数据集、选网络结构、选损失函数。

2.2 从物理模型到深度学习:为什么现在主流是数据驱动

早期方法靠先验。雨线检测用形态学成分分析(MCA),把图像分解成低频背景和高频雨线,再对高频做稀疏约束。雨滴去除用层分离模型,假设观测图像是背景层和雨滴层的线性组合。这些方法在合成数据上指标还行,一到真实雨天就翻车,因为真实雨线的形状、密度、光照变化远超手工先验的覆盖范围。

现在主流是深度学习。核心思路是让网络学习一个从退化图像到干净图像的映射。早期用 CNN 做残差学习,预测雨线层再相减;后来引入注意力机制,让网络关注雨线区域;再后来用生成对抗网络(GAN)提升视觉质量,用循环一致性解决成对数据难获取的问题。近两年 Transformer 架构也被搬进来,因为自注意力对长条状雨线的全局建模更自然。

选型上我的建议很直接:如果你有成对数据(同一场景有雨和无雨),优先用监督学习,结构选带注意力或多尺度的 CNN/Transformer,损失用 L1 + 感知损失 + 对抗损失组合。如果没有成对数据,用半监督或零样本方法,但要有心理准备,指标和稳定性会差一截。不要迷信某个"SOTA 模型",先看你的数据分布和它训练集像不像。

2.3 一个最小可跑的 Derain 训练脚本长什么样

下面给一个基于 PyTorch 的最小训练框架,结构用简化的残差注意力网络,数据用合成雨线。这不是某个现成仓库的代码,是我自己搭验证流程时常用的骨架,你可以直接替换数据集和模型。

import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import Dataset, DataLoader # 简化残差块:两层卷积 + 跳跃连接 class ResidualBlock(nn.Module): def __init__(self, channels=64): super().__init__() self.conv1 = nn.Conv2d(channels, channels, 3, padding=1) self.relu = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(channels, channels, 3, padding=1) def forward(self, x): residual = x out = self.relu(self.conv1(x)) out = self.conv2(out) return out + residual # 残差学习,缓解梯度消失 # 去雨网络:输入3通道,输出3通道,中间堆残差块 class DerainNet(nn.Module): def __init__(self, num_blocks=8): super().__init__() self.head = nn.Conv2d(3, 64, 3, padding=1) self.blocks = nn.Sequential(*[ResidualBlock(64) for _ in range(num_blocks)]) self.tail = nn.Conv2d(64, 3, 3, padding=1) def forward(self, x): feat = self.head(x) feat = self.blocks(feat) rain = self.tail(feat) # 预测雨线层 return x - rain # 原图减雨线 = 干净图 # 合成数据集:真实干净图 + 随机方向雨线 class SyntheticRainDataset(Dataset): def __init__(self, clean_images): self.clean = clean_images def __len__(self): return len(self.clean) def __getitem__(self, idx): clean = self.clean[idx] # 生成随机角度和长度的雨线掩码,实际项目里用更真实的雨线渲染 rain = torch.randn_like(clean) * 0.15 rainy = torch.clamp(clean + rain, 0, 1) return rainy, clean # 训练循环 def train(model, loader, epochs=50, lr=1e-4): device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) optimizer = optim.Adam(model.parameters(), lr=lr) criterion = nn.L1Loss() # L1 比 L2 更不容易产生模糊 for epoch in range(epochs): model.train() total_loss = 0 for rainy, clean in loader: rainy, clean = rainy.to(device), clean.to(device) optimizer.zero_grad() output = model(rainy) loss = criterion(output, clean) loss.backward() optimizer.step() total_loss += loss.item() print(f"Epoch {epoch+1}, Loss: {total_loss/len(loader):.4f}") # 使用示例 # dataset = SyntheticRainDataset(clean_tensor_list) # loader = DataLoader(dataset, batch_size=8, shuffle=True) # model = DerainNet(num_blocks=8) # train(model, loader)

逻辑说明:网络不直接输出干净图,而是预测雨线层,再用输入减去雨线。这样做的好处是残差学习更容易训练,网络只需要关注雨线的高频部分,不用重建整个背景。num_blocks控制容量,8 到 16 是常见范围,太小欠拟合,太大在数据少时过拟合。L1Loss比 MSE 更保边缘,去雨任务里边缘清晰度很关键。

参数说明:学习率1e-4是 Adam 的稳妥起点,如果 loss 震荡降到5e-5;batch size 受显存限制,8 或 16 都行,但 batch 太小 BN 统计不稳,可以考虑 GroupNorm。训练轮数不是固定值,看验证集 PSNR 不再上升就停,通常 50 到 200 轮。合成雨线的强度0.15只是示意,真实项目要用雨线渲染引擎生成不同密度、角度、模糊程度的样本。

3. 数据集、评价指标和训练策略:别在第一步就埋雷

3.1 公开数据集怎么选,合成和真实的差距在哪

Derain 领域常用的公开数据集分两类:合成雨线和真实雨图。合成数据集通常用干净图加渲染雨线生成成对样本,优点是配对精确、指标可算,缺点是雨线分布和真实场景有域差。真实数据集靠采集同一场景的雨天和晴天图,或者用视频帧对齐,优点是真实,缺点是配对误差大、场景有限。

我一般这样用:先在合成数据上把流程跑通、把模型结构调好,再用真实数据做微调或域适应。如果你只有真实雨图没有干净图,考虑用循环一致性或零样本方法,但评估只能靠无参考指标加人眼判断。选数据集时重点看三点:雨的类型是否匹配你的场景、图像分辨率是否够、场景多样性是否覆盖你的部署环境。不要拿一个全是城市道路的数据集去训工地监控模型,域差会让你怀疑人生。

3.2 PSNR、SSIM 和 LPIPS:指标好看不等于能用

去雨任务常用三个指标。PSNR 衡量像素级误差,值越高越好,但它对模糊不敏感,一个糊掉的图也可能有高 PSNR。SSIM 衡量结构相似性,比 PSNR 更贴近人眼,但对纹理丰富的区域仍然不够。LPIPS 用深度特征算感知距离,值越低越好,和主观质量相关性更高。

我的经验是:三个指标一起看,再配合下游任务指标。比如你做安全帽检测,去雨后的图送进检测器,看 mAP 涨了多少,这比 PSNR 涨 0.5dB 有意义得多。如果只能选一个,优先看 LPIPS 和下游指标,PSNR 作为参考。很多论文里 PSNR 刷得很高,实际图一看还是糊的,这就是过拟合指标的典型翻车。

3.3 损失函数组合和训练技巧

单一 L1 损失会让结果偏平滑,细节丢失。常见组合是 L1 + 感知损失(VGG 特征距离)+ 对抗损失。感知损失保纹理,对抗损失保真实感,但对抗训练不稳定,权重给大了会出现伪影。我一般先用 L1 训到收敛,再加感知损失微调,最后视情况加对抗损失,权重从 0.01 起调。

训练技巧上,数据增强很关键:随机裁剪、水平翻转、颜色抖动都能提升泛化。学习率用余弦退火或阶梯下降,别一直固定。验证集要按场景划分,不能随机划分,否则同一场景的图同时出现在训练和验证里,指标虚高。还有一点,去雨网络的输入最好归一化到 [0,1] 或 [-1,1],和预训练权重保持一致,不然收敛慢。

4. 避坑与排查:那些让我返工三次的问题

4.1 去雨后画面发灰、对比度下降

现象:输出去雨图整体比干净图暗或灰,像蒙了一层。原因通常是网络学到了平均背景,把雨线和部分背景一起减掉了,或者损失函数对低频约束不足。解决:在损失里加一个低频一致性项,约束输出和输入的低频分量接近;或者改用预测干净图而不是预测雨线层,让网络直接对背景负责。另外检查数据归一化,反归一化时别把范围截断错了。

4.2 雨线去不干净,残留细长条纹

现象:粗雨线去掉了,细密雨丝还在。原因是感受野不够,网络看不到长条状雨线的全局结构。解决:加多尺度分支或空洞卷积扩大感受野;用注意力机制让网络关注雨线区域;训练数据里增加细雨线的比例。如果用的是 CNN,可以堆更多残差块,但注意过拟合。Transformer 在这类问题上天然有优势,但数据量要够。

4.3 真实场景效果远差于合成数据

现象:合成验证集 PSNR 35dB,真实雨图一跑全是伪影。原因是域差,合成雨线的分布和真实雨线不一样。解决:做域适应,用真实雨图做无监督微调;或者在合成时用更真实的雨线渲染,考虑雨滴的折射、运动模糊、光照变化。还有一个实用做法:拿少量真实成对数据做微调,哪怕只有几十对,也能明显改善。

4.4 训练 loss 不下降或震荡

现象:loss 卡住不动,或者上下跳。原因可能是学习率太大、batch 太小、数据有问题、网络初始化不当。解决:先用一个极小数据集过拟合,如果 loss 能降到接近零,说明网络和数据管道没问题,再调学习率和 batch。检查数据里有没有全黑或全白的异常样本。初始化用 Kaiming 或默认,别自己乱设。Adam 的默认 betas 一般够用,不要随便改。

4.5 推理速度太慢,达不到实时

现象:模型在服务器上跑一张图要几百毫秒,部署到边缘设备更慢。原因是网络太深或分辨率太高。解决:用轻量结构,减少通道数和残差块数量;用深度可分离卷积;推理前把图缩到合适分辨率,去雨后再上采样;用 TensorRT 或 ONNX Runtime 加速。如果场景允许,用视频去雨利用时序信息,比单帧去雨更稳也更快。

5. 进阶技巧:把 Derain 嵌进真实 pipeline 的几个习惯

先说一个验证方法。去雨模型训完之后,别只看指标,做一次下游任务验证:拿你的检测或分割模型,分别在雨天原图和去雨图上跑,对比 mAP 或 IoU。如果去雨后下游指标没涨甚至降了,说明去雨过程引入了伪影或丢失了关键信息,这时候要回头调损失权重,而不是继续刷 PSNR。我一般会保留一个"下游验证集",专门用来做这个判断。

再说一个具体技巧:分块推理加重叠融合。高分辨率图直接送网络显存吃不消,切成小块又会在边界产生接缝。做法是切块时保留重叠区域,推理后对重叠部分做加权平均,权重用高斯窗,这样接缝基本看不出来。代码如下:

import torch import torch.nn.functional as F def tile_inference(model, image, tile_size=256, overlap=32): # image: [1, 3, H, W] _, _, H, W = image.shape output = torch.zeros_like(image) weight = torch.zeros_like(image) step = tile_size - overlap for i in range(0, H, step): for j in range(0, W, step): i_end = min(i + tile_size, H) j_end = min(j + tile_size, W) i_start = max(i_end - tile_size, 0) j_start = max(j_end - tile_size, 0) tile = image[:, :, i_start:i_end, j_start:j_end] with torch.no_grad(): pred = model(tile) # 高斯窗加权,边界权重低,中心权重高 h, w = pred.shape[2], pred.shape[3] gauss = torch.outer( torch.hann_window(h, device=pred.device), torch.hann_window(w, device=pred.device) ).unsqueeze(0).unsqueeze(0) output[:, :, i_start:i_end, j_start:j_end] += pred * gauss weight[:, :, i_start:i_end, j_start:j_end] += gauss return output / (weight + 1e-8)

逻辑说明:用 Hann 窗做加权,中心区域贡献大,边界贡献小,重叠区域自然融合。tile_size根据显存选,256 或 512 都常见,overlap至少覆盖网络感受野,32 到 64 比较稳。这个技巧在遥感大图和监控全景图上特别有用,直接整图推理要么爆显存要么边缘效果差。

最后说一个习惯:每次实验都固定随机种子,记录数据版本、模型配置、学习率和指标。去雨这方向复现性是个大坑,同一个模型不同次训练结果能差 1dB 以上。我吃过亏,后来养成用配置文件管理实验的习惯,省了很多后悔药。另外,别迷信排行榜,你的场景数据才是最终裁判。如果这个方向对你的业务有价值,先花两天把数据管道和评估流程搭好,再动模型,顺序反了会浪费几周。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 11:58:38

多域特征融合与GAN的旋转机械故障诊断方法详解

简介:面向旋转机械故障诊断研究者的完整工程包,围绕多域特征融合与生成对抗网络数据增强技术,实现复杂工况下的故障识别与剩余寿命预测。针对传统方法仅依赖单一时域或频域特征、信息不完整且泛化能力弱的问题,包内方案同时引入并…

作者头像 李华
网站建设 2026/10/11 11:56:36

OMNeT++ 4.3 Windows源码包编译实战:环境配置到Tictoc示例

简介:Omnet 4.3 源码压缩包(omnetpp-4.3-src-windows.zip)面向网络仿真研究者与 OMNeT 初学者,解决复杂网络系统建模与仿真环境搭建问题。该版本与 mixim-2.3 完全兼容,可用于无线传感器网络和自组织网络开发&#xff…

作者头像 李华
网站建设 2026/10/11 11:56:20

交换机工作原理全解析:MAC地址表、泛洪转发与网络排障

先说个我自己的感受:搞网络这行,很多人一开始都栽在“交换机和路由器到底有啥区别”这个问题上。有人画了一堆拓扑图,背了一堆命令,但真到了排查故障的时候,反而不知道从哪下手。其实根源就在于对交换机转发数据这件事…

作者头像 李华
网站建设 2026/10/11 11:56:08

R-Linux实战:ext4误删与格式化后的数据恢复指南

简介:面向Linux环境的数据恢复场景,R-Linux是一款能应对误删除、误格式化、分区损坏等常见问题的专业工具,适合个人用户与运维人员。资源为英文原版安装包,压缩包共两个文件,包含可直接运行的exe程序与htm格式的说明文…

作者头像 李华
网站建设 2026/10/11 11:55:02

ReactOS 0.3.15源码解析:编译虚拟机测试Windows兼容性

简介:ReactOS 0.3.15 源码包适合系统内核开发者、安全研究人员及对Windows兼容机制感兴趣的进阶学习者。该版本经实测可在Visual Studio 2012下编译生成ntoskrnl.exe与ntoskrnl.pdb,实现有限度的源码级内核调试,便于分析启动流程、内存管理、…

作者头像 李华