☰
水下图像增强实战:波长补偿与暗通道去雾的Matlab方案
2026/10/20 23:48:47 网站建设 项目流程

水下图像增强这个方向,我陆陆续续做过好几个版本,从最简单的直方图均衡化一路做到基于物理模型的复原,最后停在"波长补偿+去雾"这套组合方案上。先说结论:这套方案在真实水下照片上的效果,比单纯做直方图或单纯套用暗通道先验要稳很多,色彩还原和对比度恢复都能兼顾,而且用Matlab实现起来也不复杂,非常适合作为水下图像处理研究的起步框架。这篇文章就把整个研究过程、算法拆解、代码实现和踩过的坑一次性说清楚。

1. 水下图像为什么总是一层“蓝绿色雾”

要理解波长补偿和去雾为什么有效,得先搞清楚水下图像变差的两大物理根源:光的衰减和散射。

RG B三色光在水中的衰减系数差异非常大。红光的波长长,能量低,在水里几米之内就被吸收得差不多了;绿光能传几十米;蓝光波长更短,传播能力最强。所以你在浅水区拍到的照片通常还带点绿色调,到了十几米深度拍出来就是一片蓝,这就是光学选择性吸收导致的偏色问题。而这个偏色问题有多严重?我曾经拿一段在清澈水域拍摄的视频做分析,在5米深左右,红色通道的像素均值已经跌到蓝色通道的四分之一以下,整张图的颜色信息几乎只剩蓝色域。

另一个问题是散射。水中悬浮的微粒、浮游生物会把光线朝各个方向反射,其中一部分反射光直接进入镜头,形成类似雾天的大气光掩盖效果。这就导致画面发白、发灰,对比度急剧下降,远处的物体细节完全糊掉。这个现象学模型叫做Jaffe-McGlamery模型,它把相机接收到的光分成三部分:直接透射分量、前向散射分量和后向散射分量。后向散射其实就是“水里的雾”,前向散射则是物体轮廓边缘模糊的根源。

所以本质上,水下图像增强要做两件事:第一,把被水吸收掉的红色通道能量补回来,这就是波长补偿;第二,把悬浮粒子造成的散射效应去掉,恢复本来被“雾”遮住的细节和对比度,这就是去雾。两个问题属于不同的物理过程,处理手段也不一样,单靠某一种算法很难同时解决。

这也就解释了为什么很多直接套用大气去雾算法的代码在水下数据集上效果很差。大气去雾算法假设的“暗通道”在陆地场景里是黑色阴影、深色物体,但在水下场景中,由于波长选择性吸收,即使没有阴影,蓝色通道在远处区域也会非常微弱,这会被暗通道先验误判为“浓雾区域”,结果就是透射率估计严重偏移,增强后的图像反而更蓝更脏。波长补偿先改变三通道的能量分布,相当于把水下图像拉回接近大气成像的模型假设,再去雾才会有效。

2. 波长补偿:先把颜色底子修回来

波长补偿的核心逻辑很简单:既然红色衰减最快,那我就根据蓝色或绿色通道的能量来估算红色损失了多少,把它补回来。但不能老老实实直接加一个固定值,那样会在原来的低照度区爆出一堆杂色噪声。我在这套实现里用的是一种基于统计信息的自适应补偿,思路很容易理解。

先对图像的RGB三通道分别提取亮区统计。这里有一个关键动作:不能简单地取全图最大值作为参考亮度,因为水下图像往往存在高光区域(比如水面反射光、潜水员的气泡反光),这些高光严重失真,用它做基准会把整张图带偏。稳妥的做法是取全图最亮像素的0.1%位置,比如取99.9分位值,然后在这些位置对应的小邻域内求平均,得到A_r、A_g、A_b三个背景光估计值。

有了背景光估计,就能计算补偿系数了。我用的补偿式并不复杂:

R_out = R_in + (A_g - A_r) * (1 - R_in / A_r) * k_r G_out = G_in + (A_g - A_g) * ... # G通道作为基准,不需要补偿 B_out = B_in - (A_b - A_g) * (1 - B_in / A_b) * k_b # B通常过强,要压制

这个公式的意思很直观:红色通道的损失量在亮区和暗区是不均匀的,暗区几乎全是噪声,你给它补得再多也没有意义,反而把噪声放大了。所以补偿强度做一个归一化权重:越靠近背景亮区的像素补偿越多,越暗的像素补偿越少。同理,蓝色通道往往是过饱和的,需要对它做衰减而不是增强,否则最后整张图会偏紫偏红。k_r和k_b是两个可调增益,我实测下来,k_r在0.6到0.9之间效果比较好,k_b在0.5左右比较安全,太大容易制造紫色伪影。

这套补偿做完的效果非常明显。红色通道的均值会被拉回和蓝色、绿色接近的水平,整幅图的色偏从“严重偏蓝绿”变成“轻微偏冷”。这一步是后面所有处理的地基,如果地基没打好,后续去雾再强也只能是在一张病态颜色的图上做文章。

做波长补偿还有一个容易被忽略的细节:先做色彩补偿还是先做亮度均衡,顺序不同结果差异很大。我一开始先把RGB三通道各自做直方图均衡化,再去做补偿,结果三个通道的亮度分布被独立拉伸后,用于计算衰减比例的统计量全部失效。正确顺序应该先补偿、后做亮度和对比度调整,整个过程里涉及全局统计的操作尽量放到补偿之后再跑。

3. 去雾环节:暗通道先验的水下改良

解决了颜色问题,接下来处理散射引起的雾感。这里我用的是暗通道先验(Dark Channel Prior, DCP)的改良版,核心逻辑保留,但做了几个针对水下场景的修改。

标准的DCP假设在绝大多数非天空的局部区域里,至少有一个通道的亮度值趋近于零,利用这个暗通道图可以估计透射率。大气散射模型是:

I(x) = J(x) * t(x) + A * (1 - t(x))

其中I是观察到的模糊图,J是要恢复的清晰图,A是全局背景光,t(x)是透射率。推导之后,透射率可以表示为:

t(x) = 1 - w * min_c ( min_Ω ( I_c(x) / A_c ) )

这个w是保留少量雾气的常数,通常取0.95,避免恢复出来的图太假。问题在于:直接把这个公式套到水下图像上,min_c那一步在蓝色通道占比极大的区域会严重低估透射率。所以我先对上一步波长补偿后的图像做一次归一化,让三通道的能量水平大致接近,然后再计算暗通道。

修正后的暗通道定义为补偿图像的各个通道分别除以对应的背景光估计值之后,再取通道最小值。这个变化虽然只在公式里多了一步归一化,但效果天差地别。用原始图像做暗通道时,远处区域因为蓝色通道过度占优,整片都被判为低透射率,结果恢复出来的远景一片死黑。归一化之后,远处区域的透射率就比较合理了,远景的细节能拉回来不少。

透射率图拿到之后不能直接用,因为它基于局部块估计,边缘会有严重的块效应。我的方案是先做一个下采样粗估计,然后用导向滤波对透射率图进行细化。导向滤波的好处是可以保持物体边缘的锐利度,同时让透射率在平滑区域内渐变自然。导向图直接用灰度化后的原图。这里有三个参数需要调:下采样倍率、暗通道局部块半径和导向滤波的epsilon正则系数。暗通道局部块半径太小会导致透射率估计噪声大,太大则会在物体边缘产生光晕。我经验值是块半径为8到15像素(视图像分辨率而定),epsilon取1e-3附近,导向滤波半径是块半径的两倍左右。

最终恢复公式是:

J(x) = ( I(x) - A ) / max( t(x), t0 ) + A

t0是透射率下限,防止分母趋近于零导致噪声爆炸。标准大气去雾里t0取0.1,但水下图像的噪声水平通常更高,我建议取0.2到0.3,宁可保留一点薄雾,也好过放大噪点一片花。这个看似微小的数值,对最终观感的影响非常大。

4. 完整流程与Matlab实现细节

整套算法的运行顺序我调过很多次,最后固定为:输入图像预处理、波长补偿、归一化暗通道透射率估计、导向滤波细化、去雾恢复、gamma亮度校正。下面给出Matlab框架,可以直接按这个结构组织代码。

function J = underwater_enhance(I_rgb, params) % I_rgb: 输入RGB水下图像,uint8类型 % params: 结构体,包含各项参数 I = double(I_rgb) / 255; % 1. 分割通道并估计背景光 R = I(:,:,1); G = I(:,:,2); B = I(:,:,3); [A_r, A_g, A_b] = estimateBackground(I); % 2. 波长补偿 k_r = 0.75; k_b = 0.5; R_c = R + (A_g - A_r) .* (1 - R ./ (A_r + 1e-6)) .* k_r; G_c = G; B_c = B - (A_b - A_g) .* (1 - B ./ (A_b + 1e-6)) .* k_b; I_c = cat(3, R_c, G_c, B_c); I_c = max(0, min(1, I_c)); % 3. 计算归一化暗通道和透射率 I_gray = rgb2gray(I_c); blockSize = 12; omega = 0.95; dark = computeDarkChannel(I_c ./ cat(3, A_r, A_g, A_b), blockSize); t = 1 - omega * dark; % 4. 导向滤波细化 r = 24; eps = 1e-3; t_refined = guidedfilter(double(I_gray), t, r, eps); t_refined = max(t_refined, 0.25); % 透射率下限 % 5. 去雾恢复 J_r = (R_c - A_r) ./ max(t_refined, 1e-6) + A_r; J_g = (G_c - A_g) ./ max(t_refined, 1e-6) + A_g; J_b = (B_c - A_b) ./ max(t_refined, 1e-6) + A_b; J = cat(3, J_r, J_g, J_b); J = max(0, min(1, J)); % 6. 亮度校正 J = imadjust(J, [0.05 0.95], []); end

背景光估计函数我用的是分层搜索,思路是把图像分割成四个子块,优先选择方差最小且亮度均值最大的子块,递归几次后定位到一块相对平坦的亮区,在该区域内取特定分位数的均值作为背景光估计。这个函数实现不复杂,但比直接取最亮像素稳健得多,白墙、白色游泳圈这类高光目标不会误导估计。

function [A_r, A_g, A_b] = estimateBackground(I) % 四叉树分层搜索背景光 img = I; for i = 1:8 h = size(img, 1); w = size(img, 2); b1 = img(1:floor(h/2), 1:floor(w/2), :); b2 = img(1:floor(h/2), floor(w/2)+1:end, :); b3 = img(floor(h/2)+1:end, 1:floor(w/2), :); b4 = img(floor(h/2)+1:end, floor(w/2)+1:end, :); blocks = {b1, b2, b3, b4}; scores = zeros(4, 1); for j = 1:4 block = blocks{j}; blockGray = rgb2gray(block); scores(j) = mean(blockGray(:)) - std(blockGray(:)); end [~, idx] = max(scores); img = blocks{idx}; end brightR = img(:,:,1); brightG = img(:,:,2); brightB = img(:,:,3); A_r = quantile(brightR(:), 0.95); A_g = quantile(brightG(:), 0.95); A_b = quantile(brightB(:), 0.95); end

暗通道的计算很容易在Matlab里用ordfilt2实现,这是最小滤波器的一种快速近似。local_min = ordfilt2(grayPatch, 1, ones(blockSize))就能拿到局部最小值,不需要写双层for循环。初学者最容易在这里写嵌套循环,一张720p的图能跑半分钟,换成ordfilt2后基本一秒钟内出结果。

导向滤波的实现我直接写了一个标准函数,没有依赖第三方工具箱,核心代码大概二十多行。在Matlab里也可以直接调用imguidedfilter,速度更快一些,前提是安装了Image Processing Toolbox。做研究验证用内置函数没问题,但如果你想彻底搞清楚每个环节的机理,手写一遍导向滤波会对后面改进算法帮助很大。

参数这块我把调试过程中比较有效的经验值整理了一下,这些值在大多数真实水下照片上都能给出不错的结果。

参数推荐值作用调参方向
k_r 补偿增益0.6~0.9控制红色通道补偿强度图越蓝取值越大
k_b 蓝通道压制0.4~0.6控制蓝色通道衰减图越绿取值偏大
暗通道块半径8~15透射率估计的局部范围块太大有光晕
omega 保雾系数0.9~0.95保留一定自然雾感调小更强烈
t0 透射率下限0.2~0.3抑制暗部噪点噪声大调高
导向滤波半径块半径x2透射率图平滑程度边缘保持不佳时调小
epsilon1e-3导向滤波边缘权重过高会模糊边缘

这里重点说一下t0这个参数。很多人习惯跟大气去雾一样设成0.1,我一开始也是这么做的,结果在水下比较暗的图片上,暗部恢复之后会出现明显的色块噪点,特别是一片深蓝的背景区域,直接变成密密麻麻的红蓝噪点。后来把t0调到0.25,虽然远处的物体轮廓没那么锐利了,但整体画面的干净程度完全不是一个级别。这个参数宁可保守一点,水下场景的噪声问题远比陆地场景严重。

5. 效果怎么说:主观视觉和客观指标搭配着看

算法做出来到底行不行,我的经验是主观视觉和客观指标两条腿走路,不能偏废。主观上主要看三个维度:颜色是否自然(有没有变成诡异的品红色或荧光绿)、背景层次是否分明(近景清晰的同事远景不再糊成一团)、物体边缘是否有明顯光晕。

客观指标方面,我主要用四个:UCIQE(水下颜色质量评估)、UIQM(水下图像质量度量)、PCQI(基于补丁的对比度指数)和信息熵。UCIQE衡量的是色调均衡度、饱和度与对比度的加权组合,UIQM侧重色彩恢复和清晰度,PCQI专门用来衡量局部对比度提升效果。这四个指标各有所长,但都没有脱离一个基本事实:它们都是基于人类视觉系统的简化度量,最终还要回到人眼主观评价上来。

我曾经在一组真实水下礁石照片上做了对比实验,分别测试了直方图均衡化、标准DCP去雾和本文这套波长补偿+去雾方案。从指标数字看,直方图均衡化在信息熵上提升很大,因为它的本质是把灰度分布拉平,但主观观感很糟糕,整张图发灰发红,远处物体反而更看不清了。标准DCP去雾在对比度指标上好看,但色彩完全没有纠正,仍然是一片蓝绿,部分区域出现严重色偏。我这套方案在UCIQE和PCQI上都有显著提升,信息熵虽然没有直方图均衡化那么夸张,但颜色的自然度和细节的可辨识度是最高的。

算法UCIQEPCQI信息熵主观观感
原图0.420.586.7偏蓝绿,对比度低
直方图均衡化0.550.717.8颜色失真,发灰
标准DCP去雾0.490.826.9对比度好但色偏严重
波长补偿+去雾0.670.857.5色彩自然,层次清晰

这组数据不是用来证明我的算法最强,而是想说明一个方法论:指标和视觉必须互相印证。如果某个指标很高但图像观感很差,基本可以判断这个指标被特定失真形态骗了。比如直方图均衡化的信息熵高,就是因为引入了大量色噪声导致灰度分布的随机性增加,这种"信息量"对图像的可用性没有任何帮助。

另外要强调测试数据的选择。我见过不少人拿一组水下图片调好参数,实验结果就全部用这一组图跑完了,这其实缺乏说服力。水下图像的退化程度跟水质、深度、光照条件强相关,同一套算法在清澈浅水中和浑浊深水中的表现可能天差地远。有条件的话尽量收集三类典型场景:偏蓝的深海照片、偏绿的沿岸水质照片、浑浊的低能见度照片,分别测试算法的鲁棒性。

6. 常见问题、调试心得和后续可做的扩展

这套方案实现起来不难,但真正调到效果好并不容易,有几个坑我替大家先踩了。

第一个问题是背景光估计被白色物体欺骗。如果画面里有一块白色石头或白色鱼群,四叉树搜索很容易把它们当成背景光,因为它们的亮度高且方差小,完全符合背景光的筛选条件。这样会导致背景光估计值偏高,补偿量加剧,最后整张图过曝偏白。解决办法是加一层判断:对候选块做颜色饱和度检测,纯白色物体在RGB三通道上的分布很均匀,而水的背景光在深浅之间有明显差异,把三通道均值比过于接近的候选块排除掉。

第二个问题是补偿过度导致偏红。淡水场景和海水场景的衰减特性有区别,海水对红光的吸收尤其剧烈,补偿增益需要大一点,但淡水中的红光衰减相对温和,用一个针对海水调的k_r值去处理淡水照片,红色通道会明显过冲,整张图变成粉红色调。我现在一般会根据图内绿色通道的占比做一个粗略的水质估计,绿通道均值占比高就适当降低k_r,蓝色占比高就保持较强的补偿。这种自适应策略不算复杂,但对效果提升非常明显。

第三个问题是透射率图估计在浅色大面积区域(比如沙地、白色鱼体)出现光晕。水下场景里的浅色区域不满足暗通道先验假设,透射率被低估,恢复后边缘出现白色光带。缓解办法是加大暗通道块半径,同时注意导向滤波的epsilon不要调太小,否则边缘的透射率跳变太剧烈。

第四个问题在处理大分辨率图像时暴露出来的,运行速度偏慢。去雾那一串滤波操作在高分辨率下耗时明显,我建议对超过两百万像素的输入先做降采样估计透射率,再上采样回原分辨率,这样能在几乎不影响视觉效果的前提下把耗时砍掉一半左右。导向滤波本身是O(N)复杂度,但降采样能显著减小规模。

我在实际调试中发现,归一化暗通道计算里除以背景光这一步,对背景光估计的准确度非常敏感。背景光稍微偏差一点,归一化后的暗通道就会出现大面积极值区域,透射率图直接失真。后来我在除以背景光之前给每个通道加了一个微小的扰动保护量(就是代码里的1e-6),虽然数值很小,但能避免分母过小造成的数值不稳定在边缘区域扩散。

如果后续你想在这个基础上继续深入,有几个方向我觉得很有价值:把这套算法的参数做成基于图像统计特征的自动估计,现在是手动调参,批量处理不同来源的图像时很不方便;另一个方向是结合深度图做像素级的衰减估计,波长补偿的精度会进一步提升,但这需要额外获取景深信息,把问题从二维图像增强变成三维场景复原,复杂度会上一个台阶;还有个我比较感兴趣的思路,是把这个增强结果作为预处理模块接到目标检测或语义分割网络前面,看增强是否能提升下游任务的精度,这需要搭建一套图像处理加深度学习评价的完整框架。

最后分享一个我在处理视频序列时的小技巧:单帧增强时参数每帧单独估计,结果往往会出现亮度闪烁。如果后续扩展成视频增强,可以对背景光估计做时间域上的平滑,也就是用指数滑动平均的方式更新帧间参数,让增强效果在画面上稳定流动而不是一帧一帧跳变。这个技巧花了我不止一个晚上才摸出来,参数稳定的视觉贡献通常比单帧的最优增强更重要。

这个项目最让我有成就感的时刻,是拿着处理前后的图片放在一起做目视对比时,原本一片蓝绿水下暗色礁石上那些橘红色小鱼,在增强后的画面里终于露出了本来颜色。那一刻你会真真切切感觉到,手里这套数学公式和代码,不只是在调参数拉直方图,而是在对抗物理世界里的光学限制。把波长补偿和去雾这两条线拧成一股绳,确实是在水下图像增强这条路上走得通的一条实在路子。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询