ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

DehazeNet图像去雾实战:PyTorch复现与预训练模型推理全流程

2026/9/1 2:16:17 拓冰建站 浏览量
DehazeNet图像去雾实战:PyTorch复现与预训练模型推理全流程 简介本资源是面向深度学习初学者与图像复原研究者的PyTorch版DehazeNet去雾实现方案聚焦单幅图像雾霾去除这一经典低层视觉任务适用于遥感、自动驾驶、监控视频增强等实际场景。压缩包共21个文件114KB包含9个核心Python模块如net.py定义网络结构、train_*.py支持室内外数据集训练、2个预训练.pth模型best_indoor.pth与best_outdoor.pth、3个MATLAB辅助函数guidedfilter.m等用于对比验证及完整readme与说明文档结构清晰、开箱即用。已有40人学习下载用户可直接加载预训练权重进行推理演示亦能基于现有代码快速开展消融实验、数据增强或网络改进。项目兼顾工程实用性与教学可读性所有模块解耦明确便于理解DehazeNet的特征提取、多尺度融合与端到端优化机制。 图像去雾这个方向很多年了一直没凉过。从早期的暗通道先验到后来的深度学习算法再到如今各种基于CNN和Transformer的端到端网络学术界和工业界都投入了大量的研究。不过真正常被拿来当基线、跑通就能用的经典网络并不多DehazeNet算一个。网上关于它的论文解读不少但大多只讲了原理真正把PyTorch复现、预训练模型加载、图像恢复全流程跑通的文章很少。这篇文章就基于我实际跑过的一个项目把DehazeNet的完整实现过程掰开揉碎讲清楚顺便把我踩过的坑和优化的细节一并整理出来希望看完这篇内容你也能顺利跑出满意的去雾效果。DehazeNet是2016年提出的一种基于CNN的单图像去雾网络核心思想是通过卷积神经网络直接估计雾图的透射率再结合大气散射模型恢复出清晰图像。相比传统方法它不需要手工设计特征端到端训练推理速度快而且效果稳定。用PyTorch实现这个网络配合训练好的预训练模型几行代码就能完成单张图片的去雾处理。这篇文章适合这几类读者刚入门图像去雾、想跑通一个经典算法练手的研究生需要在工程里快速集成去雾模块的开发者以及对PyTorch模型训练和推理流程还不熟悉想通过一个完整项目加深理解的同学。1. 项目背景与整体设计思路1.1 图像去雾的技术本质先来聊一个核心问题雾天图像到底是怎么形成的为什么看起来灰蒙蒙的其实在计算机视觉里有一个非常经典的大气散射模型它把雾图建模成清晰图像和大气光的线性组合。公式长这样I(x) J(x) * t(x) A * (1 - t(x))其中I(x)是观测到的雾图J(x)是我们要恢复的清晰图像A是全局大气光t(x)是透射率图。透射率反映了光线穿过雾气到达相机的衰减程度取值范围在0到1之间雾越浓透射率越低。这个公式是整个去雾任务的理论基础。给定一张雾图I如果我们能估计出大气光A和透射率t恢复清晰图像J就是一个简单的代数运算。所以几乎所有基于物理模型的去雾算法本质上都在解决同一个问题怎么把透射率图估准。DehazeNet的定位非常明确它就是不做任何手工先验直接训练一个CNN输入雾图输出透射率图。整个系统的关键点就在这个透射率估计网络上面。1.2 为什么选择DehazeNet去雾算法发展到现在大体经历了三个时代。第一代是传统的图像增强方法比如直方图均衡化、Retinex等简单但物理意义不足容易产生颜色失真。第二代是基于物理模型的先验方法最典型的就是何恺明提出的暗通道先验效果不错但处理天空区域容易偏色、耗时也高需要配合导向滤波等优化手段。第三代就是深度学习方法思路各不相同有的直接端到端生成清晰图如AOD-Net有的先估计透射率再恢复如DehazeNet、MSCNN。我为什么在这个项目里选了DehazeNet有几点考虑第一它的设计思路涉及的知识点非常完整。网络结构包含多尺度特征提取、拼接融合、局部极值约束、非线性激活等经典操作训练过程涉及合成数据生成、损失函数设计、模型评估指标很适合作为学习图像去雾的入门项目。第二它的规模适中。DehazeNet是一个很轻量的网络参数量只有几百万级别CPU上都能跑推理。不像现在很多SOTA模型动辄上亿参数需要多卡训练。用好DehazeNet能让你把整个训练和推理链路吃透而不是把时间浪费在调参和等训练上。第三效果上限不低。虽然DehazeNet不是目前效果最好的算法但它的输出结果经过适当的后处理比如配合导向滤波细化透射率能达到比较专业的去雾水平在很多实际场景下已经够用。1.3 系统总体架构设计我搭建这个系统的时候没有只做一个孤立的模型文件而是设计成了一个完整的处理流程。这个流程从输入一张雾图开始到输出清晰图结束中间主要包含四个模块雾图输入之后先做预处理包括尺寸调整和归一化然后进入DehazeNet模型得到透射率估计接着通过暗通道方法计算大气光最后把大气光和透射率代入大气散射模型的反演公式得到恢复后的清晰图像再做一次后处理输出。这个设计思路借鉴了很多去雾工具箱的做法但我在实现细节上做了不少调整比如归一化方式的选择、大气光计算时的稳健性处理、输出图像的范围裁剪等。这些细节在后面的章节里会逐一展开每一步改动都是有原因的。2. 环境准备与依赖安装2.1 PyTorch环境搭建既然是基于PyTorch的项目环境搭建是绕不开的第一步。这里我假设你已经在电脑上装好了Python和Anaconda如果还没有建议先装Anaconda用它来管理虚拟环境非常方便可以避免不同项目之间的依赖冲突。我的建议是用conda创建独立的环境不要把依赖安装到base环境里。具体命令如下conda create -n dehaze python3.9 -y conda activate dehaze然后安装PyTorch。这里要特别注意不同版本的PyTorch安装命令不一样需要根据你的CUDA版本和硬件来选。如果用的是NVIDIA显卡推荐安装GPU版本速度会快很多。如果只是学习或者没有独显安装CPU版本也完全能跑通这个项目只是训练和推理慢一些。# 有NVIDIA显卡安装CUDA 11.8版本对应的PyTorch pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 或者没有显卡安装CPU版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu这里有一个容易踩的坑如果你本机已经装了CUDA但安装PyTorch时版本不匹配运行时不会报错但你调用torch.cuda.is_available()会一直返回False然后你就只能用CPU慢慢跑。所以装完之后一定要先验证一下import torch print(torch.__version__) print(torch.cuda.is_available())如果显卡驱动是支持的CUDA版本但PyTorch识别不了多半是安装的PyTorch版本对应的CUDA比你本机驱动支持的CUDA更高。这时候换个低一点的CUDA版本重新装即可。这个排查思路在第六章会再讲。2.2 依赖清单与项目结构除了PyTorch之外这个项目还需要几个常用的Python库。我在requirements.txt里是这样写的torch1.13 torchvision0.14 numpy1.21 opencv-python4.5 pillow9.0 matplotlib3.5 scipy1.7scipy用来做导向滤波里的某些矩阵运算但其实opencv也有引导滤波的实现看个人习惯。OpenCV负责图像I/O和基本图像处理比如缩放、颜色空间转换。如果不想装OpenCV也可以用PillowNumPy替代但OpenCV更顺手处理速度也快。接下来是项目目录结构。一个清晰的结构能让你后续维护省很多心我采用的是这样一个布局dehazenet_pytorch/ ├── models/ │ ├── __init__.py │ └── dehazenet.py # DehazeNet网络定义 ├── utils/ │ ├── __init__.py │ ├── data_utils.py # 数据集加载与预处理 │ ├── image_utils.py # 图像I/O与后处理 │ └── haze_model.py # 大气散射模型辅助函数 ├── pretrained/ │ └── dehazenet.pth # 预训练权重文件 ├── test_images/ │ └── hazy_input.jpg # 测试图片 ├── run_inference.py # 单图/批量推理入口 ├── train.py # 训练脚本 └── requirements.txt这样的结构把网络定义、工具函数、模型权重、推理入口分开每一层职责清楚。后面要加新的功能比如视频去雾、批量处理文件夹只需要新写一个脚本调用已有的模块就行。2.3 预训练模型获取DehazeNet的官方权重是Caffe框架下训练的。如果直接拿Caffe的权重转到PyTorch流程比较麻烦得先把Caffe的模型和参数读出来再一层层映射到PyTorch的state_dict。我在项目里其实用的是一份从训练数据上重新训练得到的PyTorch权重这样避免了框架转换的坑效果也和原论文的指标基本一致。如何获取预训练模型通常有几个途径一是从论文作者的GitHub仓库找Caffe版本再转换二是网上有人分享的PyTorch基础权重三是自己用RESIDE数据集子集训练。我最推荐第三种因为训练一个DehazeNet在我的显卡上大概只需要几个钟头后面我会讲训练细节而且自己训练出来的模型对数据分布更有感觉出了问题也好排查。不过既然标题说了附预训练模型我也会在后续的小节里详细讲解权重文件的加载方法和验证方法包括PyTorch 2.6版本里torch.load的weights_only参数这个新坑。3. DehazeNet核心原理与网络结构解析3.1 大气散射模型的数学推导再回到那个公式I(x) J(x) * t(x) A * (1 - t(x))。我们要从I推测J就需要知道t和A。如果t和A都准确那J的计算就是J(x) (I(x) - A) / t(x) A这个公式里有一个非常隐蔽的坑当t趋近于0的时候除法会爆炸。所以在实际实现中不能直接用原公式需要加一个下界保护把t限制在[t0, 1]范围内t0通常取0.1。也就是说J(x) (I(x) - A) / max(t(x), 0.1) A这样一来即使某个区域的透射率被估计得接近0也不会出现无穷大的像素值。这个下界保护在图像恢复中是常规操作但如果你没做过第一次写恢复代码时多半会漏掉。还有一个关键点大气光A是一个标量还是非均匀的这里做了简化把A看作全局常数用同一个大A作用于所有像素。这样做的好处是计算简单坏处是遇到远景和近景光照差异大的场景时可能会出现局部偏色。不少论文做过自适应大气光的改进但对基础版DehazeNet来说全局A够用了。3.2 网络结构四段式设计DehazeNet的论文把网络分成了四个模块特征提取、多尺度映射、局部极值、非线性回归。我来逐一拆解。特征提取模块输入是三通道的雾图网络用3x3、5x5、7x7三种不同尺寸的卷积核并行提特征每种卷积核输出16个通道然后把三个分支的结果在通道方向上拼接得到48通道的特征图。为什么要用多种尺寸的卷积核因为雾的浓度在不同尺度上的表现是不一样的小卷积核感受野小适合捕捉细节纹理大卷积核感受野大适合捕捉整体的雾浓度变化。这种多尺度设计让网络能同时感知不同范围内的透射率变化。多尺度映射模块这个模块把48通道的特征图经过两层卷积降维到24通道再接一个24通道到24通道的卷积层。这一部分的作用是对特征进行非线性变换和融合让网络能表达更复杂的映射关系。原论文里的卷积层顺序和通道数我在这里按最常复现的版本给出稍后代码里能看到。局部极值模块对特征图做步长为1的7x7最大池化。这个操作借鉴了暗通道先验的思路——局部区域的透射率变化是平滑的取局部最大值可以让透射率图更平滑同时也能抑制噪声的影响。池化窗口大小是个超参数论文用的是7x7你也可以尝试5x5或9x9效果略有差别。非线性回归模块最后用一个1x1的卷积把24通道映射到1通道得到透射率图。在输出之前网络用了一个非常特殊的激活函数叫BReLU双边ReLU。ReLU是max(0, x)BReLU则是min(max(0, x), 1)把输出限制在0到1之间这和透射率的物理定义完美匹配。这也是DehazeNet一个非常有代表的细节。3.3 BReLU激活与损失函数BReLU在PyTorch里实现起来非常简单一个torch.clamp就搞定了。import torch import torch.nn as nn class BReLU(nn.Module): def __init__(self, upper1.0): super(BReLU, self).__init__() self.upper upper def forward(self, x): return torch.clamp(x, 0, self.upper)有的复现版本把BReLU做成可学习的上下界作为可训练参数但原论文用的是固定边界我也推荐固定边界简单稳定。训练时使用的损失函数是均方误差MSE直接计算模型输出的透射率图和真实透射率图的逐像素差异。为什么用MSE而不是其他损失因为透射率图的恢复是一个回归任务MSE对大的误差惩罚更重能让网络把注意力集中在估计偏差大的区域收敛也相对稳定。不过MSE也有个缺点它倾向于把结果平均化导致预测的透射率图偏平滑这在细节恢复上有一定影响。如果想进一步改善可以考虑在MSE基础上加一个结构相似性损失但那是改进方向了基础版用MSE就够。训练数据的生成也是一个值得讲的主题。通常会模拟大气散射模型用清晰图像和随机的大气光和透射率合成雾图。合成时透射率图一般不用常数而是用随机深度渐变或者随机斑点来模拟真实场景的雾浓度变化。数据生成这一步直接决定了模型在真实雾图上的泛化能力做得越好模型越鲁棒。4. 模型推理与图像恢复实现4.1 透射率估计流程推理阶段的第一个环节就是把输入图片喂给DehazeNet拿到透射率图。在实际代码里需要注意三点。第一输入图片要归一化到0到1之间因为网络训练时就是在0到1范围内进行的如果直接输入0到255的像素值模型输出的结果会偏移。第二PyTorch模型的输入维度是(B, C, H, W)所以要先转成张量扩张batch维度再交给模型。第三网络输出的是这个batch内的透射率图需要经过squeeze去掉batch维才能做后续处理。批处理的模板我放在后面的4.4节这里先看单张图片的预测import cv2 import torch import numpy as np from models.dehazenet import DehazeNet def estimate_t(model, image): # image: HWC uint8 范围的BGR图像 img image.astype(np.float32) / 255.0 img_tensor torch.from_numpy(img).permute(2, 0, 1).unsqueeze(0) model.eval() with torch.no_grad(): t model(img_tensor) t t.squeeze().cpu().numpy() return t这样得到的t就是一张和输入图像同样尺寸的透射率灰度图。它的值基本在0到1之间雾浓的地方值小雾淡的地方接近1。如果想可视化可以把t乘以255保存成灰度图看一眼能直观感受到网络对场景深度的估计能力。这里有一个隐藏的细节如果输入图像的尺寸不是4的倍数 or 奇数某些下采样层可能导致尺寸不匹配。DehazeNet没有传统意义上的下采样层池化用的都是步长1的池化所以理论上任何尺寸都能处理但为了统一和稳妥推理脚本里通常会先做一个尺寸归一化。我在项目里就统一把图片短边resize到512这样既保证了速度也避免了过小图片的边缘效应。4.2 大气光计算策略透射率有了还差大气光A。大气光表示的是无穷远处雾的亮度也就是天空区域的像素值。经典的做法是暗通道先验法先计算图像的暗通道然后在暗通道中取亮度最高的前0.1%像素把这些像素位置对应到原图上取它们的平均颜色作为大气光。这里的逻辑是暗通道中最亮的区域通常对应的是天空或者最远处的雾区这些区域几乎没有纹理亮度接近大气光。我写的计算函数如下def estimate_atmospheric_light(image, dark_channel, top_percent0.001): h, w dark_channel.shape num_pixels h * w top_count max(int(num_pixels * top_percent), 1) flat_dark dark_channel.flatten() indices np.argpartition(flat_dark, -top_count)[-top_count:] top_pixels image.reshape(-1, 3)[indices] A np.mean(top_pixels, axis0) return A这个函数里用到np.argpartition而不是直接argsort是因为argsort是全排序复杂度高而argpartition只需要找出前top_count个速度快很多。图像分辨率高时这个性能差距会非常明显。还有一点要提醒如果图像整体都是雾蒙蒙的没有明显的天空区域这种取最亮像素的做法可能把前景里的白色物体误判成大气光导致恢复结果偏暗。这个问题的鲁棒性改进方案有很多比如基于位置加权的天空检测但基础版就用经典暗通道法就行。4.3 图像恢复与后处理推理的最后一步把大气光和透射率代入恢复公式得到清晰图像。def recover_image(image, t, A, t00.1): # image: 归一化到0-1的HWC浮点图像 # t: 归一化到0-1的透射率图 t np.clip(t, t0, 1.0) t np.expand_dims(t, axis-1) # 扩展为H,W,1以便广播 A A.reshape(1, 1, 3).astype(np.float32) / 255.0 J (image.astype(np.float32) - A) / t A J np.clip(J, 0, 1) return J注意这里有几个细节t做了下界截断防止除零A除以255是因为前面归一化到了0-1范围最终结果clip到0-1。这样恢复出来的J就是0-1范围的浮点图像需要乘255转回uint8才能保存显示。其实做完这些一张图的基本去雾流程就闭环了。但有一个常见的改进步骤值得做——在恢复之前对透射率图做一次导向滤波。DehazeNet直接输出的透射率图虽然整体准确但在边缘处容易产生光晕伪影因为网络对精细边缘的预测不够锐利。导向滤波能在保留边缘信息的同时平滑透射率图显著改善恢复结果的自然度。我用的导向滤波实现来自opencv的ximgproc模块当然也可以用scipy自己写。核心思想是以原图的灰度图作为引导图对透射率图进行滤波。参数里radius和eps需要调我实测下来radius40, eps1e-3时效果比较均衡。4.4 批量推理模板实际项目中很少只处理一张图通常是把一个文件夹里的图片批量去雾。我写的批量推理脚本如下import os import cv2 import torch from models.dehazenet import DehazeNet from utils.image_utils import load_image, save_image, guided_filter def batch_dehaze(model, input_dir, output_dir): os.makedirs(output_dir, exist_okTrue) files [f for f in os.listdir(input_dir) if f.lower().endswith((.jpg, .jpeg, .png, .bmp))] for idx, fname in enumerate(files): img_path os.path.join(input_dir, fname) img cv2.imread(img_path) if img is None: print(f读取失败: {img_path}) continue # 预处理归一化 img_norm img.astype(np.float32) / 255.0 # 转张量推理 img_tensor torch.from_numpy(img_norm).permute(2, 0, 1).unsqueeze(0) with torch.no_grad(): t model(img_tensor).squeeze().cpu().numpy() # 大气光估计 A estimate_atmospheric_light(img.astype(np.float32), dark_channel(img_norm)) # 可选导向滤波 t_refined guided_filter(img_norm, t, radius40, eps1e-3) # 图像恢复 J recover_image(img_norm, t_refined, A) # 保存 out_path os.path.join(output_dir, fdehazed_{idx:03d}_{fname}) save_image(out_path, J) print(f处理完成: {fname} - {out_path})这里的dark_channel是一个计算暗通道的函数通常是取RGB三通道最小值然后用最小值滤波实现。批量处理时我习惯在循环外只加载一次模型而不是每张图都重新加载权重这样能大幅减少重复IO开销。如果图片数量很多可以考虑用torch.utils.data.DataLoader配合batch推理把多张图拼成一个batch喂给模型推理速度会成倍提升。我实测用batch8时GPU利用率显著提高整体吞吐量提升大约4倍。当然batch处理要处理不同尺寸图片的pad问题会稍微复杂一些这里就不展开了。5. 预训练模型使用指南5.1 权重加载与weights_only参数标题里写了附预训练模型所以这一节必须讲透。拿到权重文件之后加载方式决定了你能不能顺利跑起来。PyTorch的权重加载最常见的方式是import torch from models.dehazenet import DehazeNet model DehazeNet() state_dict torch.load(pretrained/dehazenet.pth, map_locationcpu) model.load_state_dict(state_dict) model.eval()这段代码在PyTorch 2.5及以下版本没问题。但如果你装的是PyTorch 2.6或者更高版本也就是最近一段时间开始普及的版本运行时会跳出一行长长的提示或者直接报错。原因是PyTorch 2.6改了torch.load的默认行为weights_only默认值从False变成了True。weights_onlyTrue意味着只能加载张量、字典、列表这类基础数据类型不允许加载任意的Python对象。这样做是为了安全因为旧版的torch.load在加载恶意构造的pth文件时可能执行任意代码。但从用户角度很多模型权重文件是用pickle序列化的里面会包含一些自定义对象加载时就容易出问题。如果你遇到pickle.UnpicklingError或者类似Weights only load failed的错误有两种解决办法。第一种加载时显式指定weights_onlyFalsestate_dict torch.load(pretrained/dehazenet.pth, map_locationcpu, weights_onlyFalse)第二种如果你确保权重文件里只包含张量可以先把文件转化为纯state_dict格式用weights_onlyTrue加载。我推荐第一种简单直接自己本地的模型文件安全性可以自己把控。还有一点用load_state_dict时如果遇到unexpected key或missing key的报错最常见的原因有几种一是网络的层名和权重文件里存的层名不一致二是model定义里多了或少了层三是保存时带上了module.前缀。第三个情况在用了DataParallel训练时最常见处理方式是对key做字符串替换state_dict {k.replace(module., ): v for k, v in state_dict.items()}5.2 自定义数据集评估加载好模型后最好先在公开数据集或者自己拍摄的雾图上看一下效果而不是直接扔到业务场景里。我常用的评估方式有两种一种是主观看图和客观指标结合另一种是只有雾图没有参考图的情况下用无参考指标。有参考图时常用的指标是PSNR峰值信噪比和SSIM结构相似性。PSNR越高表示像素级误差越小SSIM越接近1表示结构保真度越好。但这两个指标对去雾任务来说并不完美因为去雾本来就是一种风格迁移式的变换数值上非常接近的图可能视觉上不如某些数值略低的图自然。所以我的习惯是指标作为参考最终以目视效果为准。无参考图时可以用一些雾密度评估方法比如对比度、饱和度等简单统计特征。虽然不够严谨但至少能对比出处理前后的变化趋势。在自定义数据集上评估时还容易遇到一个适配问题训练时用的图像尺寸和预处理方式与你的测试图不一致。如果预训练模型是在固定尺寸或某一种数据分布上训练的直接扔一张不同分布的图片进去效果可能大打折扣。所以评估之前一定要确认数据的归一化方式、色彩空间BGR还是RGB和模型训练时完全一致。我在项目里就吃过这个亏用OpenCV读的图默认是BGR但训练时如果用RGB出来的透射率图会明显不对整个去雾结果是花的。5.3 微调与再训练建议预训练模型在通用场景下效果不错但如果你的图片场景很特殊比如全是水下图像、浓雾天的高速公路、或者监控摄像头视角那通用权重可能不够理想。这时候建议在你自己收集的数据上做微调。微调的流程和正常训练类似但有几个关键点。第一学习率要调低通常从预训练模型的最后收敛学习率往下调比如1e-4或者更小否则很容易破坏已经学好的底层特征。第二冻结前几层卷积只更新后面的层这种做法在小数据集上特别有效可以防止过拟合也能显著加速训练。第三数据扩充非常重要随机裁剪、水平翻转、颜色抖动这些操作能提升模型对场景的泛化能力。训练自己的DehazeNet是一个更完整的工程需要准备成对的雾图和清晰图。合成数据是常规做法从COCO、ADE20K这些数据集里选清晰图像然后随机生成大气光和深度图通过大气散射模型合成雾图。这一步要在数据加载时在线完成否则磁盘会被撑爆。如果你条件允许强烈建议自己训一次。即使最终效果不如预训练模型这个过程能让你对数据分布、收敛速度、损失曲线这些概念有切身体会。6. 常见问题与踩坑实录6.1 环境与安装问题排查把这段时间做项目时遇到的高频问题整理成了一张速查表希望能帮你少走弯路问题现象可能原因解决方案torch.cuda.is_available() 返回FalseCUDA版本不匹配 / 驱动过旧升级驱动重新安装对应CUDA版本的PyTorch安装PyTorch时提示找不到合适的版本pip源网络问题换国内镜像或从官网下载whl文件手动安装conda create速度极慢默认channel访问慢配置清华/阿里conda镜像源import torch报错缺少DLL缺少VS运行库安装Visual C Redistributable内存不足/显存溢出图像尺寸太大 / batch过大减小输入尺寸或降低batch size其中最常见也最让人烦躁的就是CUDA版本问题了。建议先运行nvidia-smi看驱动支持的CUDA版本然后安装的PyTorch CUDA版本只要不高于这个就能用。比如驱动支持CUDA 12.1那装cu118或cu121的PyTorch都没问题。6.2 模型加载与推理问题加载权重时最容易遇到的就是前面讲的weights_only报错以及state_dict的key不匹配。这里再补充几个我在实践中的其他问题一是模型输出全黑或全白。这通常意味着输入没有归一化。很多人直接用cv2.imread读进来的0到255的值喂给网络DehazeNet输出的透射率经过BReLU之后被clamp到0到1之间但中间的特征计算会爆炸导致输出极端值。解决办法就是老老实实把输入除以255。二是恢复出来的图色彩很怪。BGR和RGB通道顺序弄反是头号嫌疑。我在代码里统一用OpenCV的BGR顺序处理网上的某些实现用的是RGB如果你的模型是在某份RGB预处理下训练的直接用BGR推理就会偏色。稳妥的做法是训练和推理都保持一种约定并且在遇到色彩问题的时候第一个怀疑这个环节。三是恢复结果有明显的块状光晕。透射率图在边缘处不够锐利导致的。先用导向滤波细化透射率基本上能解决。如果还有光晕可以适当增大导向滤波的radius或者对透射率图做一次中值滤波。6.3 去雾效果不佳的原因分析如果模型部署好之后去雾效果不理想不要急着怀疑模型有问题先按下面这个顺序排查输入图片本身是否适合当前模型DehazeNet是在自然雾场景上训练的如果输入的是夜间图像、低照度图像、或者是带了大量人造光源的室内场景效果就会很差。这是物理模型本身的限制不是代码bug。透射率估计是否合理把模型输出的透射率图打印出来用灰度图形式查看。如果发现大部分区域透射率接近1说明模型认为这张图几乎没有雾那恢复效果当然不明显。如果透射率有高有低但恢复出来的图仍然偏灰就要检查大气光的估计是否准确。大气光取值是否太大或太小把估计出来的A打印出来一般应该接近图像中最亮区域的像素值。如果A偏大了恢复出的图会整体偏暗A偏小了则会出现灰蒙蒙的残留。后处理中是否做了色彩校正有些图片恢复之后对比度提升了但看起来不自然此时可以适当做一次色温平衡或者直方图裁剪这个不在DehazeNet的范围内但作为工程实践是常见的后续操作。6.4 性能优化与部署心得最后聊一下在实际部署中总结的几个优化经验。CPU推理优化DehazeNet本身很轻量CPU上处理一张512x512的图耗时大概在几十毫秒到几百毫秒之间取决于具体的硬件。如果还想提速可以尝试把模型导出成ONNX再用ONNX Runtime推理或者用OpenVINO在Intel平台上加速速度提升非常明显。GPU批量推理用DataLoader做batch推理吞吐量提升是接近线性的。瓶颈往往不在模型本身而在图像的预处理和IO。建议把解码、resize、归一化这些操作放到多进程的DataLoader里让GPU计算和CPU预取重叠起来。内存管理如果一次要处理整个文件夹的图片千万不要先把所有图片读进内存再统一处理。正确做法是逐张读取、逐张处理、逐张保存。我见过有人做视频去雾时把某几帧图片直接读成一个巨大的numpy数组结果内存直接爆掉。逐帧处理虽然慢一点但稳而且更容易在中间加可视化。模型导出部署如果只是离线处理图片用PyTorch原生的torch.no_grad()就够。如果要集成到Web服务里建议先把模型转成TorchScript或者ONNX这样可以减少运行时对Python版本的依赖也能享受到更高效的执行引擎。我在实际使用中还有一个体会就是把DehazeNet的输出结果t作为深度信息的近似值还能用来做其他后处理比如雾浓度检测和图像增强的权重图。这个思路虽然超出了DehazeNet原本的范畴但说明透射率图本身就是一个很有信息量的中间产物。如果你正在做安防监控、自动驾驶或者航拍图像处理不妨从这个角度尝试一下说不定会有意外的收获。本文还有配套的精品资源点击获取