新闻详情

新闻详情

首页 / 资讯中心 / 详情

暗通道先验图像去雾:从原理到MATLAB实现与调参

发布时间:2026/9/16 1:51:46来源:尧图网络
暗通道先验图像去雾:从原理到MATLAB实现与调参
简介何凯明去雾算法的MATLAB实现包面向图像处理入门者、计算机视觉研究者及课程设计人员解决雾天图像清晰化与算法复现问题。资源基于大气散射模型覆盖核心去雾算法、引导滤波细化及盒滤波快速处理等关键环节并配有GUI交互界面与多张不同雾度测试图方便直观调整参数、对比恢复效果。压缩包共12个文件以4个M脚本含主程序、引导滤波、盒滤波、界面逻辑为主辅以JPG/PNG测试图、MATLAB FIG界面文件和PPT讲解文稿整体15.42MB结构清晰。已有763人学习适合作为理解何凯明算法、开展图像去雾实验或完成相关课程设计的参考工具。凭借这套完整程序包读者不仅能复现经典流程还能借助配套界面与演示文稿快速掌握思路修改与效果评估方法。1. 暗通道先验为什么是去雾算法的默认起点何凯明图像去雾的破局点在 matlab 图像处理大作业和各类视觉项目的预处理环节何凯明图像去雾算法几乎是绕不开的复现对象。它发布于 2009 年核心主张简单到反直觉无雾图像的局部暗通道亮度趋近于零而雾度会线性抬高这一数值。于是透射率不再需要多帧或深度信息直接从单张图就能估算出来。这个算法真正的贡献并不是提出了某个新滤波器而是把一个欠定物理模型用一条统计规律压出了可解性。这里按从业者验证算法的路径来写先梳理大气散射模型和暗通道先验的推导再给出可运行的 MATLAB 实现接着讨论参数调节与先验失效边界最后讲怎么验证效果。适合需要用 MATLAB 快速出结果、又要能说清原理的人。2. 大气散射模型与暗通道先验何凯明去雾公式怎么一步步落地2.1 大气散射模型一张雾图等于场景衰减加环境光去雾算法普遍建立的物理基础是大气散射模型$I(x) J(x) \cdot t(x) A(1 - t(x))$其中 $I(x)$ 是相机接收到的有雾像素$J(x)$ 是场景原本的辐射亮度$t(x)$ 是透射率$A$ 是全局大气光。直观理解光线从物体表面到相机一部分被空气中粒子散射掉强度衰减为 $J \cdot t$同时粒子散射的环境光叠加进来构成 $A(1-t)$。透射率与景深满足 $t(x)e^{-\beta d(x)}$$\beta$ 是散射系数$d$ 是深度所以远处场景通常更雾。把 $A$ 当成全局常量是一种简化严格说并不总是成立尤其在室内彩色光源场景但常量假设带来的求解稳定性明显超过误差所以几乎所有工程实现都保留这一假设。参数含义值得先固化下来调试时对照着看符号含义取值范围在图像上的表现$I$观测到的有雾图像$[0,1]$整体灰白、对比度低$J$无雾场景辐射$[0,1]$待恢复的清晰图$t$透射率$(0,1]$越小雾越浓$A$大气光常量决定雾的基础色板只要把 $J$、$t$、$A$ 求出来问题就算解决。但 $I$ 已知时每像素有 3 个通道方程、却要解 $J$ 的 3 个通道加 $t$ 共 4 个未知数天然欠定。所以算法真正的分水岭在于用什么额外约束把这个方程封闭。2.2 暗通道先验一句关于彩色图像的统计规律何凯明的破局点是暗通道先验。对任意无雾自然图像定义暗通道为最小颜色通道再做局部最小值滤波$J^{dark}(x) \min_{c \in {r,g,b}} \left( \min_{y \in \Omega(x)} J_c(y) \right)$统计结果显示对于非天空区域$J^{dark}(x)$ 几乎处处趋近于 0。原因很好理解彩色物体总存在某个通道反射率低阴影区域所有通道都暗暗色表面整体就暗。换句话说在每个局部窗口里RGB 三通道至少有一个出现极小值的概率非常高。用 MATLAB 很容易验证img im2double(imread(clear.png)); % 读一张无雾图 min_rgb min(img, [], 3); % 逐像素取RGB最小值 patch 15; dark_clear ordfilt2(min_rgb, 1, ones(patch)); % 局部最小值滤波 histogram(dark_clear(:)); % 检查数值分布代码说明min(img, [], 3)沿第三维取最小值得到单通道图ordfilt2做顺序统计滤波第 2 个参数设为 1 表示取窗口内最小值等价于滑动窗口最小值滤波。运行后如果直方图峰值压在 0 附近就说明先验对这张图成立。雾气会引入白光叠加直接抬高暗通道数值这正是后续估计透射率的依据。这个验证步骤建议保留在工程里换数据集时先跑一次确认先验是否成立。如果暗通道直方图峰值已经在 0.2 以上说明画面本身有大量灰白区域后续直接套用原公式很容易出事。注意暗通道先验的统计基础依赖彩色自然图像灰度图、红外图、水下偏色严重的图通道数量退化或响应失衡暗通道不再趋近零公式推导就塌了。在 matlab 图像处理实践里有人把监控灰度视频直接套暗通道结果要么过曝要么出现伪彩色第 4 章会专门谈失效边界。2.3 透射率推导把先验塞回模型假设局部窗口内透射率恒定对模型两侧同时取暗通道。因为 $A$ 是常量向量做逐通道除法后可得$\min_{c}\min_{\Omega} \frac{I_c}{A_c} 1 - t(x) \cdot \left(1 - \min_{c}\min_{\Omega} \frac{J_c}{A_c}\right)$在暗通道先验成立的区域右侧第二个因子趋近 1于是得到透射率估计$t(x) 1 - \min_{c}\min_{\Omega} \frac{I_c(x)}{A_c}$实际操作中会给 $t$ 乘一个系数 $\omega$论文默认 $\omega0.95$。原因是完全去除所有雾会让图像塑料感过重保留 5% 的薄雾能让景深感更自然。大气光 $A$ 的估计也依赖暗通道先取暗通道最亮的前 0.1% 像素再到原图这些位置里找亮度最大值作为 $A$避免直接取全图最亮像素时选中白色车或灯。到这里模型、先验和估计路径已经闭环下一步就是把这三个步骤翻译成 MATLAB 函数。3. 在MATLAB实现暗通道去雾从暗通道算到透射率恢复的五个函数3.1 主函数与参数结构写一个入口函数把流程串起来参数用结构体传入方便后续批量调参。各模块职责先列出来函数名输入输出职责dehaze_dark_channel雾图、prm去雾图主流程编排dark_channel图像、patch暗通道透射率与大气光的共同基础estimate_atmospheric_light图像、暗通道A(1×3)大气光估计guided_filter引导图、透射率、r、eps细化透射率边缘保持平滑recover_scene图像、t、A、t0去雾图模型反解function J dehaze_dark_channel(img, prm) if nargin 2 prm struct(patch, 15, omega, 0.95, t0, 0.1, ... r, 40, eps, 1e-3); end img im2double(img); % uint8转double[0,1] dark dark_channel(img, prm.patch); % 步骤1暗通道 A estimate_atmospheric_light(img, dark); % 步骤2大气光 A reshape(A, 1, 1, 3); % 变成1x1x3便于按通道除法 t 1 - prm.omega * dark_channel(img ./ A, prm.patch); % 步骤3透射率 t_refined guided_filter(rgb2gray(img), t, prm.r, prm.eps); % 步骤4细化 J recover_scene(img, t_refined, A, prm.t0); % 步骤5恢复 end代码说明im2double把 uint8 转到 double 的 $[0,1]$ 区间这一步不做后面的减法、除法会直接截断或被放大reshape(A,1,1,3)让矩阵除法沿通道广播rgb2gray是把灰度图作为导向滤波的引导图边缘结构与原始图像一致。3.2 暗通道最小值滤波是核心function dark dark_channel(img, patch) if mod(patch, 2) 0 patch patch 1; % 保证窗口中心像素存在 end min_rgb min(img, [], 3); % RGB三通道逐像素取最小 dark ordfilt2(min_rgb, 1, ones(patch)); % 滑动窗口最小值滤波 end代码说明ordfilt2第二个参数是顺序1 表示最小值。窗口默认补零暗通道在图像边缘会偏低一点但它影响的只是前几像素对其他区域的透射率排序影响很小如果要严格处理可先用padarray复制边缘再滤波。窗口大小patch建议取奇数默认 15 在 0.5K 到 2K 分辨率的图片上都比较稳。3.3 大气光估计暗通道前 0.1% 像素function A estimate_atmospheric_light(img, dark) [rows, cols] size(dark); n max(1, floor(rows * cols * 0.001)); % 最亮0.1%像素数 flat_dark dark(:); [~, idx] maxk(flat_dark, n); % 前n个最大值下标 [r_idx, c_idx] ind2sub([rows, cols], idx); A zeros(1, 3); for ch 1:3 values img(sub2ind([rows, cols], r_idx, c_idx) (ch-1)*rows*cols); A(ch) max(values); % 原图对应位置取最亮 end end代码说明这里取的是原图对应暗通道高亮位置的通道最大值而不是亮度的均值符合原始论文取最亮大气光的思路。maxk在较新的 MATLAB 版本中可用老版本可以先用sort(flat_dark, descend)截取前 n 个下标替换。如果大气光估出来偏红或偏蓝通常是因为暗通道最亮像素落在彩色光源上可以对前 0.1% 像素的通道值求均值鲁棒性更好。3.4 透射率估计与导向滤波透射率初估计带有窗口状方块直接恢复会在景深边缘形成光晕所以要做边缘保持平滑。原论文用导向滤波MATLAB 里一般自己实现一个简化版function q guided_filter(I, p, r, eps) win fspecial(average, [2*r1, 2*r1]); % 平均窗口 mean_I imfilter(I, win, replicate); mean_p imfilter(p, win, replicate); corr_I imfilter(I .* I, win, replicate); corr_Ip imfilter(I .* p, win, replicate); var_I corr_I - mean_I .* mean_I; cov_Ip corr_Ip - mean_I .* mean_p; a cov_Ip ./ (var_I eps); % 边缘保持系数 b mean_p - a .* mean_I; q imfilter(a, win, replicate) .* I imfilter(b, win, replicate); end代码说明这段代码把导向滤波化简为局部线性模型的均值滤波近似。假设在窗口内输出是引导图的线性变换a、b是线性系数取邻域均值后得到最终的q。严格版本会用积分图做盒式滤波这里的imfilterfspecial在效率上略低但一对 800×600 图像完全够用。如果你的 MATLAB 里 Image Processing Toolbox 版本较新也可以直接查imguidedfilter的文档参数语义对应这里的r和eps。透射率估计与细化这样串A reshape(A, 1, 1, 3); t 1 - prm.omega * dark_channel(img ./ A, prm.patch); % 初始透射率 t_refined guided_filter(rgb2gray(img), t, prm.r, prm.eps); % 细化代码说明把img逐通道除以大气光再做暗通道得到了透射率初值omega0.95时实际保留 5% 薄雾后续恢复结果不会显得过于生硬。用灰度原图做引导透射率图的边缘与物体轮廓对齐天空这类平坦区域则被平滑。3.5 恢复场景与完整调用function J recover_scene(img, t, A, t0) t max(t, t0); % 防止除零和噪声放大 J zeros(size(img)); for ch 1:3 J(:,:,ch) (img(:,:,ch) - A(ch)) ./ t A(ch); end J min(max(J, 0), 1); % 截断到[0,1] end模型反解就是J (I - A)/t A。t0论文里默认 0.1对应雾最浓、透射率最低的区域也不能完全信 0否则除以 0 会把噪声放大成雪花。最后min(max(J,0),1)把结果压回有效范围这行建议保留因为t下界虽然挡住了除零数值误差仍然可能让个别像素越界。调用示例I im2double(imread(haze.jpg)); prm struct(patch, 15, omega, 0.95, t0, 0.1, r, 40, eps, 1e-3); J dehaze_dark_channel(I, prm); imshow([I, J]);3.6 参数结构为什么用 struct所有中间量理论上都能写成嵌套脚本但用 struct 的好处是批量实验时可以在循环里改字段而不用改函数签名。比如针对一张低分辨率图prm.patch 9; prm.omega 0.97; J dehaze_dark_channel(I, prm);代码说明patch从 15 降到 9暗通道窗口变小透射率细节更锐利omega提高则去雾更彻底。这样改参不会破坏主函数接口第 4 章的参数表也能逐字段对应上。4. 参数怎么设、什么时候失效暗通道去雾的三个坑位与边界4.1 五个参数的对照表与调节方向去雾效果的好坏80% 集中在下面五个数字上参数默认值调大效果调小效果适用场景patch15透射率图更平滑边缘细节丢失细节更锐利容易出现块状halo高分辨率图可到21低分辨率用9omega0.95去雾更彻底灰白感更弱保留更多雾更自然薄雾0.85~0.95浓雾0.97~1.0t00.1雾浓处更暗更稳恢复值更亮但噪声放大天空区域过曝时提高到0.15~0.25r40透射率更平滑保留更多细节图像越大r 建议越大eps1e-3平滑更强边缘保持更强噪声图上调到1e-2实际调试建议先固定patch15, omega0.95, t00.1只动r和eps这两个参数组合决定透射率图是否干净。r调大后eps也要跟着调大否则平滑区域会出现亮度波纹。比如一张分辨率 1920×1080 的雾图可以用patch21, omega0.97, t00.12, r60, eps1e-3天空占比大时把t0改到0.18。而手机拍的 400×600 缩略图patch9就明显比 15 干净。另一个容易忽视的点是去雾不是把omega推到 1 就最强——许多城市雾霾图中完全去掉雾会把远处楼体边缘压出黑边保留 3% 到 5% 的雾反而让结果更像真景。4.2 天空区域过曝与先验失效暗通道先验的成立条件是局部窗口内至少一个通道有暗值。天空、白色墙面、白车身这类区域不满足先验暗通道偏亮导致透射率被低估恢复时补偿过度结果就是天空发灰、发暗甚至偏色。处理手段按顺序做优先把t0从 0.1 提到 0.15 以上强制限制最大增强幅度其次是修改大气光的选取避免 $A$ 落在霓虹灯或彩色反光面如果天空面积很大建议对天空区域单独做 mask透射率不再用暗通道推导而是直接平滑插值。提示判断是不是先验失效把透射率可视化出来看天空区域出现明显的凹坑或方块状突变基本就是暗通道高估了雾浓度。4.3 halo 边缘光晕的成因与三个缓解方向halo 来自窗口跨过深度突变边缘窗口内同时包含近处清晰物体和远处浓雾背景最小值滤波被背景低值污染透射率在边缘附近估计不准。减少 halo 的方向有三条一是缩小patch让窗口更难跨深度边界二是对透射率做强平滑代价是边缘细节损失三是调整 omega不完全恢复视觉上掩盖过度补偿。工程上通常先减patch再调r三步就能压掉大部分光晕。调试顺序建议先停到t_refined生成后看透射率图再停到恢复后看J。如果透射率正常但结果发灰问题在A如果透射率本身就是块状问题在patch或导向滤波如果透射率呈现整体偏移检查是否对img./A用错了矩阵除号。/和./在这里差别很大前者会尝试求解线性方程组而不是逐通道除法报错信息通常是维度不匹配。4.4 常见误用与排错清单对 uint8 数据直接做img - A负数被截断为零恢复图整体发黑。先im2double恢复完成后再转回 uint8 保存。大气光估计用了全图最亮像素而不是暗通道前 0.1% 像素白色车身会被当成大气光整体色温严重偏移。恢复时A是 1×3 向量直接和 H×W×3 矩阵做./会报维度不匹配必须先reshape(A,1,1,3)。忘记截断t的下界浓雾区域出现大量彩色噪点。透射率可视化时没做归一化输出一片白误以为是算法失败。这一套查下来九成报错集中在类型和维度上先看工作区变量大小再执行。5. 透射率可视化与无参考验证让去雾结果可解释的三个技巧5.1 把透射率画出来比盯结果图更容易发现问题t_show mat2gray(t_refined); % 映射到[0,1] figure; subplot(1,2,1); imshow(t_show); title(transmittance); colormap(jet); colorbar; subplot(1,2,2); imshow([img, J]);代码说明mat2gray会把透射率线性拉伸方便观察相对结构。合格的透射率图应该和景深结构一致近处亮、远处暗边缘过渡连续。如果图上出现方块边界说明patch太大或导向滤波没有真正生效先确认guided_filter的输出变量是否被后续代码使用。5.2 无参考质量指标梯度与熵的组合没有无雾真值图时用梯度均值和信息熵给结果做客观描述。有雾图细节被压缩去雾后梯度通常会上升但噪声放大也会拉高梯度所以要和熵一起看熵太低说明细节仍然不足或过度平滑太高说明噪声被一起放大了。gI imgradient(rgb2gray(img)); gJ imgradient(rgb2gray(J)); fprintf(grad before: %.3f / after: %.3f\n, mean(gI(:)), mean(gJ(:))); fprintf(entropy before: %.3f / after: %.3f\n, ... entropy(rgb2gray(img)), entropy(rgb2gray(J)));代码说明entropy是图像处理工具箱函数对 double 输入会先转 uint8 再算直方图所以两张图要同条件对比两张图来自同一曝光设置时结论才可靠自动曝光会改变直方图基线跨批次比较没有意义。5.3 批量回归测试固定参数跑完一组图再调参单张图调参容易过拟合到一张样本上。建议把去雾函数存成独立 m 文件然后批量跑目录下所有雾图files dir(fullfile(haze_set, *.jpg)); prm struct(patch, 15, omega, 0.95, t0, 0.1, r, 40, eps, 1e-3); for i 1:numel(files) I im2double(imread(fullfile(files(i).folder, files(i).name))); J dehaze_dark_channel(I, prm); imwrite(J, fullfile(output, files(i).name)); end跑完在缩略图模式下扫一遍重点看三个位置天空、远处物体边缘、暗部区域。如果大多数图透射率细节平滑、颜色偏移只出现在天空就继续调t0如果边缘光晕普遍存在先减小patch。这套流程同样适合把暗通道去雾作为预处理步骤接到深度学习 matlab 工作流里比如用结果透射率当伪标签训练一个轻量估计网络算是老算法衔接新方向比较自然的一种扩展。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

AD9954 DDS芯片原理与GPIO模拟并行总线驱动实战 2026/9/16 2:27:49

AD9954 DDS芯片原理与GPIO模拟并行总线驱动实战

简介:本资源面向嵌入式系统开发工程师与高频信号发生器设计学习者,提供基于ADI AD9954芯片的32位直接数字频率合成(DDS)应用方案,聚焦硬件接口适配与底层驱动移植。压缩包共2个文件(1份PDF原理图、1个C语言…

阅读更多 →
微型光伏并网逆变器C/C++控制程序核心要点解析 2026/9/16 2:27:49

微型光伏并网逆变器C/C++控制程序核心要点解析

简介:微型光伏并网逆变器设计资源,面向光伏逆变器研发工程师、电力电子方向学生及嵌入式控制开发者,用于理解小型并网系统的硬件架构与控制逻辑。压缩包约2.8MB,涵盖逆变器原理图、PCB设计文件以及基于C/C的DSP控制程序&#xff1…

阅读更多 →
数据智能分析Agent实战:从Text-to-SQL到自动化图表生成 2026/9/16 2:27:49

数据智能分析Agent实战:从Text-to-SQL到自动化图表生成

先说个真实场景。业务线每个月问我最多的不是"做张报表",而是"我有个问题,你帮我看看数据"。问题看着小,背后是一连串动作:确认口径、写 SQL、跑数、核对、画图、再解释。过去这一套流程走下来,快…

阅读更多 →
TaoToken 统一通道下的 Token 调用爆发排查:把 Codex 的 Base URL 换过去 2026/9/16 2:27:49

TaoToken 统一通道下的 Token 调用爆发排查:把 Codex 的 Base URL 换过去

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
一次对话生成5分钟视频?vivago R1实测:AI视频的“单反时刻”来了 2026/9/16 2:27:49

一次对话生成5分钟视频?vivago R1实测:AI视频的“单反时刻”来了

1. 为什么“一次对话生成5分钟”会让我觉得AI视频真的要变天了说实话,我拿到vivago R1的测试资格后,第一时间并没有急着生成视频,而是先冷静了两天。做AI视频内容也算有一阵子了,从早期的Runway Gen-2、Pika,到后来的可…

阅读更多 →
直播高并发环境从0到1搭建:SRS、WebSocket与Redis实战 2026/9/16 2:24:48

直播高并发环境从0到1搭建:SRS、WebSocket与Redis实战

去年年底接了个带视频直播的业务项目,团队里没专人搞过流媒体,我自己平时也就是写写接口、做做增删改查的水平。硬着头皮从装流媒体服务器开始,到后面扛住一场在线几千人的直播活动,整个过程踩了不少坑。这篇文章就是我这套直播高…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞