
简介基于暗通道先验与导向滤波的图像去雾方案提供完整C工程源码面向具备图像处理基础知识、希望掌握去雾算法工程化实现的开发者与视觉方向学习者。资源包为zip格式共6个文件约287KB包含基于OpenCV的核心算法cpp、头文件h、Visual Studio工程配置vcxproj以及png示例图片并附有工程辅助配置文件下载后可在VS环境中直接编译验证。算法代码覆盖暗通道提取、透射率估计、导向滤波细化、大气光校正和图像恢复等关键环节结构紧凑、注释清晰有助于读者对照理论公式理解每一步实现。已有2893人学习通过自带示例图片可以直观比较去雾前后的图像细节进一步体会暗通道先验与导向滤波如何协同作用该实现也适合迁移到实时视频、车载影像、户外监控等能见度较低的场景中。1. 图像去雾的工程实现暗通道先验与导向滤波的 C 落地雾天拍出来的照片总是灰蒙蒙一片直接拉对比度会把噪声一起放大真正要解决的是场景透射率的估计问题。这个项目用 C 和 OpenCV 完整实现了基于暗通道先验与导向滤波的图像去雾算法从暗通道计算、大气光估计、透射率估计、导向滤波细化到最终图像恢复全部是自写实现没有调用现成的高层去雾接口。工程里包含一个可编译的 CMake 工程和完整源码输入一张雾图就能得到透射率图和去雾结果适合想深入理解图像复原算法、或者需要一套可二次开发的 C 去雾管线的开发者。2. 算法原理与整体架构大气散射模型如何变成 C 代码去雾算法不是凭空提亮画面它背后有一个物理模型。图像去雾领域最常用的基础是大气散射模型这个模型把相机收到的光分成两部分一部分是场景物体反射的光经过雾气衰减后到达相机的另一部分是大气光直接散射进相机的。数学形式写成 I(x) J(x) * t(x) A * (1 - t(x))其中 I 是观测到的雾图J 是我们要恢复的无雾图A 是全局大气光t(x) 是透射率代表光线穿过雾气到达相机的比例。只要估计出 A 和 tJ 就可以直接反解出来。整个项目做的事就是分别解决“A 怎么估”和“t 怎么估”这两个问题再加上一个细化步骤让透射率图更干净。2.1 大气散射模型一张雾图的数学拆解先把模型拆开看。当 t(x) 接近 1 时说明这里几乎没有雾I(x) 约等于 J(x)图像保持原样当 t(x) 接近 0 时说明光线基本被雾气挡住了相机看到的绝大部分是大气光场景细节完全丢失。所以去雾的核心就是准确估计每个像素的 t(x)远处景物的透射率小近处景物的透射率大。这个模型本身很简单难点在于 t 和 J 都是未知的单张图像只有一个方程却有两个未知数属于典型的病态反问题。暗通道先验的出现给这个问题提供了一个很强的约束条件让单张图像去雾成为可能。后面所有 C 代码都是围绕这个模型在写先估 A再估 t最后用模型反解 J。2.2 暗通道先验一个观察结论如何变成可计算的公式暗通道先验的发现来自一个统计观察在绝大多数自然图像中任意一个局部窗口内至少有一个颜色通道的像素值非常低趋近于零。比如绿色植被的某个窗口里红色和蓝色通道值很低彩色物体的表面总有一个通道不反射光。把这个规律形式化就定义了暗通道J_dark(x) min_{c in {r,g,b}}( min_{y in Ω(x)} J_c(y) )。也就是先对每个像素取 RGB 三通道的最小值再在一个窗口内取最小值。无雾图像的暗通道值趋近于 0但有雾图像的暗通道会被大气光抬高。因为雾相当于一个白色的散射层它把所有通道的值都往上推。这个差异正是估计透射率的基础。代入大气散射模型后可以推导出透射率的估计公式t(x) 1 - ω * min_c( min_{y in Ω(x)} (I_c(y) / A_c) )其中 ω 是保留少量雾感的常数一般取 0.95 左右。这个先验也不是万能的它有一个众所周知的失效场景天空区域。天空本身的亮度很高暗通道不再趋近于零按公式算出来的透射率会偏小导致恢复结果出现颜色失真和光晕。工程实现里对这个问题的处理方式通常是给透射率设一个下限 t0比如 0.1防止除零和过度增强这也是本项目代码里的默认处理。2.3 整体流程与工程结构整个去雾流程在代码里按顺序是计算暗通道图、从暗通道估计大气光、计算初始透射率、用导向滤波细化透射率、按散射模型恢复无雾图像。这个顺序不是随意定的每一步的输出都是下一步的输入暗通道同时服务于大气光估计和透射率估计细化步骤则直接作用于透射率图。工程目录结构如下dehaze/ ├── CMakeLists.txt ├── src/ │ └── main.cpp ├── images/ │ ├── input/ │ └── output/ └── README.mdsrc/main.cpp 里包含了全部算法实现没有拆多个文件方便初学者一次看懂完整链路。CMakeLists.txt 负责查找 OpenCV 并生成可执行文件。技术选型方面C 的优势在于性能和内存控制去雾涉及大量窗口遍历C 的循环和矩阵操作更直接OpenCV 则提供了 erode、boxFilter、convertTo 这些高度优化的基础算子暗通道的最小值滤波用腐蚀实现导向滤波的均值计算用 boxFilter 实现都不需要自己造轮子。选择这个组合的另一个原因是可移植性好只要有 OpenCV 的 CMake 配置就能在不同平台编译运行。3. 核心模块拆解去雾链路上每个函数的实现细节这一章直接落到代码。整个去雾链路拆成六个函数每个函数解决一个独立的任务最后用一个 dehaze 入口函数把它们串起来。我一般会把每个函数都写得足够纯粹输入输出清晰这样后期调试或者换成别的算法模块时只需要替换其中一段不会牵一发动全身。3.1 计算暗通道最小值滤波与腐蚀操作暗通道计算分两步先对 RGB 三通道取逐元素最小值得到一个单通道灰度图再对这个灰度图做窗口内的最小值滤波。OpenCV 里的腐蚀操作正好等价于最小值滤波所以直接用 getStructuringElement 生成矩形核再调用 erode 即可。// 计算暗通道三通道最小值 窗口内最小值滤波 cv::Mat calcDarkChannel(const cv::Mat src, int blockSize 15) { std::vectorcv::Mat channels(3); cv::split(src, channels); // 逐像素取 RGB 最小值 cv::Mat dark cv::min(cv::min(channels[0], channels[1]), channels[2]); // 矩形核腐蚀操作等价于窗口内最小值滤波 cv::Mat kernel cv::getStructuringElement(cv::MORPH_RECT, cv::Size(blockSize, blockSize)); cv::erode(dark, dark, kernel); return dark; }这段代码里 blockSize 是暗通道窗口的边长默认 15也就是每个像素会参考周围 15×15 区域内的最小值。blockSize 越大暗通道越平滑但边缘信息丢失也越多太小则暗通道噪声大后续透射率图会不干净。cv::min 在三通道间取最小值时要求输入输出都是单通道 8Usplit 之后再操作就是为了满足这个条件。用腐蚀代替手写双重循环性能提升非常明显OpenCV 对腐蚀有高效的滑动窗口实现这比用 at 逐像素遍历快一个数量级。3.2 估计大气光取暗通道最亮区域的像素值暗通道里像素值越亮的地方说明雾气越浓。大气光的经典估计方法是取暗通道中亮度最高的前 0.1% 像素然后回到原图用这些位置的像素值来估计全局大气光 A。工程实现上取这批像素在三个通道的均值比直接取最亮点更稳定因为单点容易受到噪声干扰。// 估计大气光选取暗通道最亮的前 0.1% 像素在原图对应位置求均值 cv::Scalar estimateAtmosphericLight(const cv::Mat src, const cv::Mat dark) { std::vectorstd::pairfloat, cv::Point pixels; pixels.reserve(dark.rows * dark.cols); for (int y 0; y dark.rows; y) { for (int x 0; x dark.cols; x) { pixels.emplace_back(dark.atuchar(y, x), cv::Point(x, y)); } } int topN std::max(1, static_castint(pixels.size()) / 1000); // 部分排序只把前 topN 个最亮的值放到前面 std::partial_sort(pixels.begin(), pixels.begin() topN, pixels.end(), [](const auto a, const auto b) { return a.first b.first; }); cv::Scalar sum(0, 0, 0); for (int i 0; i topN; i) { cv::Vec3b p src.atcv::Vec3b(pixels[i].second); sum[0] p[0]; sum[1] p[1]; sum[2] p[2]; } return sum / topN; }这里用 partial_sort 而不是 sort是因为只需要找出最亮的前 0.1% 像素不需要把全图排好序复杂度从 O(n log n) 降到了 O(n log topN)。topN 是参与统计的像素个数全图 100 万像素时取 1000 个足够覆盖雾最浓的区域又不会引入过多随机噪声。大气光 A 是一个包含三个通道值的 Scalar后面透射率估计和图像恢复都要用到它。如果图像里存在大面积高亮的白色物体这个估计可能会偏差这是暗通道去雾的固有问题通常靠透射率下限来兜底。3.3 估计初始透射率按通道归一化后取最小值透射率的计算要回到公式 t(x) 1 - ω * min_c( min_{y in Ω(x)} (I_c(y) / A_c) )。注意这里每个通道要先除以对应的大气光分量再在窗口内取最小值最后跨通道取最小值而不是直接用暗通道除以 A。// 估计初始透射率逐通道归一化 - 窗口最小值 - 跨通道最小值 cv::Mat estimateTransmission(const cv::Mat src, const cv::Scalar A, int blockSize 15, float omega 0.95f, float t0 0.1f) { std::vectorcv::Mat channels(3); cv::split(src, channels); cv::Mat kernel cv::getStructuringElement(cv::MORPH_RECT, cv::Size(blockSize, blockSize)); cv::Mat minRatio; for (int c 0; c 3; c) { cv::Mat ratio; channels[c].convertTo(ratio, CV_32F); ratio ratio / A[c]; // 逐通道除以大气光 cv::erode(ratio, ratio, kernel); // 窗口内最小值滤波 if (c 0) { minRatio ratio.clone(); } else { cv::min(minRatio, ratio, minRatio); // 跨通道取最小值 } } cv::Mat t 1.0 - omega * minRatio; cv::max(t, t0, t); // 透射率下限防止除零和过度增强 return t; }这段代码把图像转成 CV_32F 再做除法是为了避免 uint8 的截断误差。omega 是保留雾感的系数论文中建议 0.95也就是保留约 5% 的雾这样恢复结果更自然不会显得假。t0 是透射率下限默认 0.1这行在下一次图像恢复时非常重要当透射率趋近于 0 时公式里的除法会导致像素值爆炸。设置下限后远处景物的透射率不会低于 0.1既防止了过分增强也避免了除零崩溃。这里的 blockSize 和暗通道计算用的窗口保持一致保证空间尺度统一。3.4 导向滤波细化透射率边缘保持的平滑直接从公式估计出来的透射率图带有明显的块状伪影因为暗通道窗口本身是矩形的。如果直接拿它恢复图像会在物体边缘出现光晕。业界常用的优化方案有两个soft matting 和导向滤波前者质量更高但求解线性系统很慢后者是一个可分离的局部线性模型速度快得多也是本项目采用的方法。// 导向滤波用灰度图作引导对透射率图做边缘保持平滑 cv::Mat guidedFilter(const cv::Mat guide, const cv::Mat src, int radius 40, double eps 0.01) { cv::Mat g, p; guide.convertTo(g, CV_32F, 1.0 / 255.0); // 引导图归一化到 [0, 1] src.convertTo(p, CV_32F); // 透射率本身在 [0, 1] cv::Size win(2 * radius 1, 2 * radius 1); cv::Mat meanI, meanP, meanIp, meanII; cv::boxFilter(g, meanI, CV_32F, win); cv::boxFilter(p, meanP, CV_32F, win); cv::boxFilter(g.mul(p), meanIp, CV_32F, win); cv::boxFilter(g.mul(g), meanII, CV_32F, win); cv::Mat varI meanII - meanI.mul(meanI); cv::Mat covIp meanIp - meanI.mul(meanP); cv::max(varI, 0, varI); // 防止浮点误差导致负方差 cv::Mat a covIp / (varI eps); // 局部线性系数 cv::Mat b meanP - a.mul(meanI); cv::Mat meanA, meanB; cv::boxFilter(a, meanA, CV_32F, win); cv::boxFilter(b, meanB, CV_32F, win); return meanA.mul(g) meanB; // q meanA * I meanB }导向滤波的核心假设是输出 q 和引导图 I 之间存在局部线性关系 q a * I b。在窗口内用最小二乘求解 a 和 ba 越大说明输出越依赖引导图的边缘结构b 则是基准偏移。代码里先用 boxFilter 计算窗口内的均值再通过均值组合出方差 varI 和协方差 covIp最后得到每个像素的线性系数。radius 控制窗口半径默认 40对应暗通道窗口的 2 倍以上这样才能把块状伪影完全抹平。eps 是正则化参数引导图归一化到 [0,1] 后eps 取 0.01 是经验值它抑制了平坦区域的系数抖动。整个函数没有任何循环全部是矩阵运算对大图非常友好。3.5 恢复无雾图像与主流程串接有了大气光 A 和细化后的透射率 t最后一步就是把大气散射模型反过来用。对每个通道执行 J (I - A) / t A然后转回 8U 输出。// 恢复无雾图像反解大气散射模型输出 8U 三通道图 cv::Mat recoverScene(const cv::Mat src, const cv::Mat t, const cv::Scalar A) { std::vectorcv::Mat channels(3); cv::split(src, channels); std::vectorcv::Mat result; for (int c 0; c 3; c) { cv::Mat f; channels[c].convertTo(f, CV_32F); cv::Mat j (f - A[c]) / t A[c]; // 反解模型 cv::Mat out; j.convertTo(out, CV_8U); // 自动饱和截断到 [0, 255] result.push_back(out); } cv::Mat dst; cv::merge(result, dst); return dst; }convertTo 在这里起到了两个作用把浮点结果截断到 [0, 255] 范围内同时转回 8U 格式。这比手动 clamp 再转要简洁OpenCV 的 convertTo 默认就是饱和转换不会出现数据溢出的环绕现象。t 在前一步已经被限制了最小值 t0所以这里不会出现除零。恢复结果在雾浓的区域会明显更清晰但也会放大该区域的噪声这是暗通道方法的固有代价。把这些步骤串起来的主入口函数// 去雾主流程 cv::Mat dehaze(const cv::Mat src, int blockSize 15, float omega 0.95f, float t0 0.1f, int guideRadius 40, double eps 0.01) { cv::Mat dark calcDarkChannel(src, blockSize); cv::Scalar A estimateAtmosphericLight(src, dark); cv::Mat t estimateTransmission(src, A, blockSize, omega, t0); cv::Mat guide; cv::cvtColor(src, guide, cv::COLOR_BGR2GRAY); cv::Mat tRefined guidedFilter(guide, t, guideRadius, eps); return recoverScene(src, tRefined, A); }主流程把六个参数全部暴露在外层这样调参时不需要改函数内部逻辑非常方便。guide 用的是原图的灰度图因为灰度图保留了物体的边缘结构导向滤波会把这些边缘信息传给透射率图让透射率在物体边缘处有清晰的跳变避免光晕。4. 常见问题与避坑记录跑通去雾项目时踩过的五个坑这个项目代码量不大但跑起来之后会遇到不少视觉上的问题很多问题不是语法错误而是参数和算法假设导致的。我把实际调试中遇到的高频问题整理成几条避坑记录每条都按现象、原因、解决的思路写。4.1 画面整体偏暗与颜色失真的问题现象去雾后的图像比原图还暗阴影区域直接变成死黑或者整体偏蓝、偏黄颜色明显不对。原因最常见的是透射率 t 没有设下限导致 t 接近 0 的像素点在恢复公式里被过度放大暗区饱和。另一个原因是大气光 A 估计得太低把局部亮像素当成了全局大气光导致整体亮度被压低。解决先检查 estimateTransmission 里是否加了 cv::max(t, t0, t)t0 建议不低于 0.1。再检查 estimateAtmosphericLight 是否取的是 topN 均值而不是单点最亮值单点最亮容易被噪点带偏。如果偏色严重可以输出 A 的三个通道值对比一下正常雾天场景下 A 的三个通道应该比较接近如果某个通道明显偏低说明 A 估计到了彩色物体上需要调大 topN 或者换输入图验证。4.2 透射率图块状伪影与边缘光晕的问题现象输出的透射率图有明显的矩形色块恢复出的图像在物体轮廓周围有一圈白色或者暗色的光晕看起来像没对齐的浮雕。原因初始透射率的窗口最小值滤波本身就是方块状的这是暗通道方法的固有产物。如果导向滤波的窗口半径太小比如和暗通道窗口一样大就没法把方块抹平eps 太小则导向滤波退化为纯边缘保持滤波平滑作用失效。解决把 guidedFilter 的 radius 调到暗通道窗口的 2 到 3 倍我用 blockSize15 时 radius 取 40 基本能抹平伪影。eps 在引导图归一化到 [0,1] 的情况下取 0.01 是稳妥值如果透射率图还是毛躁可以适当加大到 0.02。另外确认透射率图在导向滤波输入前是 CV_32F如果传了 8U 图进去透射率的值域会被截断到 0 到 255线性模型完全错乱。4.3 天空区域颜色失真与白色噪点的问题现象天空区域恢复后变成灰色或者出现彩色斑块云层边缘有刺眼的白色噪点。原因暗通道先验在天空区域不成立天空的暗通道不接近零按公式估计的透射率会严重偏小恢复时把大气光残差放大了。解决这是暗通道方法的经典痛点工程上常用的处理是加大透射率下限 t0让天空区域的透射率不要掉得太低。实测把 t0 从 0.1 提高到 0.2 能明显抑制天空区域的过度增强代价是远景细节会稍微雾一点。另一个做法是把 omega 从 0.95 下调到 0.9保留更多雾感天空区域会自然很多。如果场景里天空占比很大建议直接用亮度阈值检测天空区域对该区域单独设置更高的透射率下限。4.4 大图处理极慢与内存暴涨的问题现象输入超过 2000×1500 的图片程序跑了十几秒还没出结果内存占用飙升甚至直接卡死。原因暗通道的 erode 和导向滤波的 boxFilter 虽然都是优化过的滑动窗口算法但计算量和窗口面积与像素数成线性关系大图的总耗时依然可观。更隐蔽的是把整张图都转成 CV_32F 后每个像素占 4 字节一张三通道大图光浮点副本就占几十 MB。解决我一般会在主流程前加一个降采样预处理把长边压到 800 像素左右去雾恢复完成后再用 resize 拉回原尺寸。这样耗时从十几秒降到 2 秒以内视觉效果几乎不变。如果要处理批量图片可以配合 OpenCV 的 UMat 开启 TBB 并行或者把 erode 换成品字形的快速最小值算法。一定要用 Release 模式编译Debug 模式下 OpenCV 内部会有大量边界检查性能差距很大。4.5 读取图片时崩溃与通道数不匹配的问题现象程序一启动就崩溃或者 split 之后 channels.size() 不是 3cvtColor 报断言错误。原因输入图是灰度图或者带 Alpha 通道的 PNG代码里所有地方都假设输入是三通道 BGR。cv::imread 默认行为并不总是返回三通道图PNG 带透明通道时可能读出 4 通道。解决在 imread 时强制指定 cv::IMREAD_COLOR让 OpenCV 统一转成三通道 BGR并在 dehaze 入口加一个断言。实际使用中我还会检查 src.empty()防止读入不存在的路径。这类问题排查起来不难但每次换个测试图集都会遇到所以在主函数里做好防御性处理是值得的。5. 编译运行与参数调优把项目跑起来并调出理想效果代码逻辑看完了接下来让它在本地跑起来。这个工程依赖只有一个 OpenCV版本 3.4 以上都行用 CMake 构建整个过程比较简单。5.1 CMake 工程配置与编译CMakeLists.txt 的核心内容如下cmake_minimum_required(VERSION 3.10) project(dehaze) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) add_executable(dehaze src/main.cpp) target_link_libraries(dehaze ${OpenCV_LIBS})find_package(OpenCV REQUIRED) 会读取系统里安装的 OpenCV 路径OpenCV_LIBS 包含了 core、imgproc 等所有需要的库不需要手动逐个链接。编译命令如下mkdir build cd build cmake .. make -j4如果 cmake 找不到 OpenCV大概率是 OpenCV 没装到默认路径。常见做法是把 OpenCV_DIR 指到构建目录比如 OpenCV_DIR/usr/local/opencv/build 再执行 cmake。5.2 主程序调用与命令行参数主函数负责读图、调 dehaze 接口、写输出文件同时做了一个降采样优化。int main(int argc, char** argv) { if (argc 3) { std::cout Usage: argv[0] input_image output_image std::endl; return -1; } cv::Mat src cv::imread(argv[1], cv::IMREAD_COLOR); if (src.empty()) { std::cerr Failed to load image: argv[1] std::endl; return -1; } // 长边超过 800 时先缩小去雾再放大回原尺寸 cv::Mat processed; float scale 1.0f; if (src.cols 800) { scale 800.0f / src.cols; cv::resize(src, processed, cv::Size(), scale, scale, cv::INTER_AREA); } else { processed src.clone(); } cv::Mat dst dehaze(processed, 15, 0.95f, 0.1f, 40, 0.01); if (scale ! 1.0f) { cv::Mat dstFull; cv::resize(dst, dstFull, src.size(), 0, 0, cv::INTER_LINEAR); dst dstFull; } cv::imwrite(argv[2], dst); return 0; }编译完成后运行./dehaze ../images/input/haze.jpg ../images/output/result.jpg程序会打印一行说明输出路径然后在 output 目录下生成去雾结果。inter_area 在缩小图时保留更多纹理细节inter_linear 在放大时更平滑两者配合基本不会引入明显变形。如果想调试透射率图可以在 dehaze 函数里临时加一行 imwrite 把 tRefined 存出来方便对比参数变化。5.3 参数对输出效果的影响六个参数中真正影响视觉效果的是下面五个调参时先全部用默认值跑一遍再按需调整参数默认值作用调大效果调小效果blockSize15暗通道窗口边长透射率更平滑光晕变大细节保留多噪声增多omega0.95去雾强度增强更明显噪声和偏色风险增加效果更接近原图t00.1透射率下限远景保留更多雾感天空更自然远景增强更强可能偏色guideRadius40导向滤波窗口半径透射率更平滑边缘细节更清晰eps0.01导向滤波正则化更平滑更多细节可能引入噪声一条实用的调参路径是先用默认值跑看整体亮度是否合适明暗合适但颜色失真就调 omega 和 t0边缘光晕明显就加大 guideRadius画面噪点多就加大 blockSize。这几个参数是相互牵制的比如把 blockSize 调大后guideRadius 也要相应加大否则透射率图会出现内方外圆的伪迹。注意eps 的取值依赖引导图的值域。本项目的 guidedFilter 里把引导图归一化到了 [0,1]所以 eps 取 0.01 合理。如果后续修改代码不再归一化直接用 0~255 灰度图eps 必须同步放大 100 倍左右否则导向滤波会失效。6. 二次开发改造思路从固定参数到自适应与实时化这个项目的代码结构把每一步都拆成了独立函数二次开发基本不需要重写改某个环节的输入输出就能接上新算法。第一个改造方向是自适应 omega。现在 omega 是固定值 0.95但雾浓和雾薄的场景用同一个强度显然不合理。常见做法是先用暗通道图的均值估计雾浓度再映射到 omega。暗通道均值约等于归一化的雾浓度雾越浓值越大cv::Scalar meanDark cv::mean(dark); float omega 0.8f 0.15f * (meanDark[0] / 255.0);第二个方向是做视频或批量图像去雾。单张去雾没问题但视频帧之间如果各自独立估计 A 和 t会出现明显的亮度闪烁。我一般会把上一帧的 A 和透射率缓存下来对当前帧的估计结果做指数平滑只有场景切换时重新快速收敛。这样改动只在 dehaze 入口层算法内部不用动。第三个方向是替换透射率估计模块。现在初始透射率来自暗通道先验如果你想接入深度学习模型可以让模型输出一个透射率图替代 estimateTransmission 的返回值后面的导向滤波和 recoverScene 全部可以复用。导向滤波在这里变成了一个纯后处理模块它能把粗糙的模型输出磨得干净同时保住边缘这个组合在很多实际项目里都能跑通。我自己也有过把 omega 拉到接近 1.0 的时候觉得去雾越狠越好结果高清大图里远景边缘一直在抖动天空区域泛白。从那以后我每次改完算法都会拿同一批测试图在不同参数下跑一遍把结果拼成对比图逐张看确认没有偏色和噪点再合入。希望帮到你。这份工程源码里已经写好了上面全部函数和可直接编译的 CMakeLists下载后用任意一张雾图替换路径就能跑通改参数和改模块都有清晰的落点。本文还有配套的精品资源点击获取