水下图像增强实战:颜色平衡与多尺度融合的Matlab实现

发布时间:2026/10/19 6:35:33
水下图像增强实战:颜色平衡与多尺度融合的Matlab实现 水下增强这块我断断续续做了大半年中间踩了不少坑。最早是被一个水下巡检项目逼着做图像预处理拍回来的素材要么偏蓝偏绿要么对比度低到几乎看不清目标。试过单通道直方图均衡、暗通道先验、各种滤镜效果都不尽如人意直到把颜色平衡和多尺度融合这两块组合起来才算是找到了一条稳定好用的路径。这篇就把这套流程掰开揉碎讲清楚附上可跑的Matlab代码适合刚接触水下图像处理、或者想在传统方法上快速出效果的读者参考。1. 项目概述与整体方案设计思路1.1 水下图像为什么非增强不可水下图像的退化原因说起来就是两个字吸收和散射。水对光线的吸收是波长相关的红光在几米深的水里就衰减得差不多了橙光和黄光次之绿光和蓝光穿透力最强。这就是为什么你看到的水下照片普遍偏蓝或偏绿本质上是水体把长波段的颜色吃掉了。这个衰减过程可以用简单的指数模型描述到达相机的光强度等于表面光强乘以衰减系数随距离的指数衰减波长越短衰减越慢。更直白地说你在水下五米拍一张照片红色通道的信息可能已经损失了大半。而散射则是悬浮颗粒和微小气泡导致的。光线打到这些颗粒上会发生散射一部分散射光直接进入镜头形成背景雾另一部分从目标反射回来的光线被散射削弱结果就是整体对比度下降、边缘模糊远处物体发灰。所以水下图像增强要解决的核心问题有两个一是把被水染掉的色彩补回来这叫颜色平衡二是把雾气感和模糊感去掉把对比度提上去。很多刚接触这个方向的人会问为什么不直接套用普通图像的增强算法原因很简单普通算法假设的是大气散射模型而水下环境除了散射之外还有严重的波长相关吸收两者耦合在一起直接用大气去雾的方法经常会把颜色修正得过度、出现偏色伪影。这就是为什么水下增强需要用单独设计的颜色平衡步骤而不是一把梭用通用方法。1.2 这套方案的构成与选型理由我在这个项目里选的路线是颜色平衡 多尺度融合不是单靠某一个滤波器而是先修复色彩再生成多份互补的增强版本最后按像素重要性把它们融合起来。这个思路的好处在于不依赖训练数据不需要成对的清晰水下参考图部署起来很直接而且每一步都能可视化、可解释。具体来说流程分四步对原始水下图像做颜色平衡恢复通道间的比例关系基于颜色平衡结果生成对比度增强版本比如Gamma校正或自适应直方图均衡对每个版本分别计算权重图代表该版本在哪些区域更可信用拉普拉斯金字塔做多尺度融合把各版本的优点合成到一张图里。至于实验环境我用的是Matlab。不是因为它比Python强而是这个阶段我需要频繁调滤波器参数、看中间结果、验证金字塔每一层Matlab的图像处理工具箱在这类交互式调参场景下确实顺手。后面如果想部署到工程端再把算法翻译成C或Python也不难核心逻辑都是通用的。提示这套方案的核心是理解为什么要融合以及权重图该怎么设计。如果只抄代码不理解原理遇到换一批测试图效果崩掉的情况就会无从下手。2. 颜色平衡与融合技术原理解析2.1 颜色平衡的物理基础与灰度世界假设颜色平衡这一步我用的是经典的灰度世界假设Gray World Assumption。它的逻辑很朴素一幅自然图像里所有像素的各通道平均值应该趋向于一个中性的灰色也就是三个通道的均值大致相等。如果某通道的均值明显低于另外两个通道说明该通道的光线在传播过程中被衰减得更多我们就通过乘上一个增益系数把它补回来。放在水下场景这套逻辑正好对症。水下图像的红色通道均值通常远低于蓝绿通道灰度世界假设会把红色通道的增益拉高从而把丢失的红色信息大致恢复回来。当然它也有失效的情况比如场景本身以大面积的绿色水草或蓝色海水为主通道均值并不能代表中性灰这时候直接套灰度世界就可能把整体色调拽到一个不自然的方向。所以我实际的实现里做了两个改进。第一在计算通道均值之前先对每个通道做一个基于百分位数的截断拉伸把5%和95%分位点之外的极端亮暗像素去掉避免个别过曝或过暗的噪点干扰均值统计。第二增益计算加上一个极小值保护防止某个通道均值接近零时增益爆炸。这两个细节看着小但对结果的稳定性影响非常大。2.2 灰度世界与白平衡的差异和取舍这里要说明一下水下图像处理里常说的白平衡和相机里的白平衡不完全是一回事。相机白平衡是针对陆地光照条件下的色温偏移设计的目标是让中性灰还原成中性灰。而水下图像需要的是颜色平衡也就是在通道间做补偿恢复被水体选择性吸收掉的能量。两者目标函数不同参数也不通用。我在实验里也试过直接用相机白平衡接口效果很一般。原因就是水下图像不仅有色偏还有严重的能量损失直接把色偏纠正过来并不能把暗掉的红色通道信息无中生有地补出来。相比之下灰度世界假设配合截断拉伸至少能从统计意义上把通道比例调整到合理范围。当然如果条件允许更准确的做法是结合深度信息估计各通道的衰减系数再逐像素补偿但那需要额外的深度输入不适合作为通用预处理方案。注意颜色平衡只负责色彩比例不负责对比度。这也是为什么融合方案里一定需要第二个版本——对比度增强后的图像——来补充纹理和细节信息。2.3 为什么融合框架能进一步提升效果光做颜色平衡图片看起来还是发灰发雾细节不够锐利。这时候就需要融合框架出场。融合的思路可以这么理解同一个场景手上有两份各有所长的中间结果一份色彩还原更准但细节平淡另一份对比度够强但可能色彩过度饱和聪明的做法是让每份结果在它表现好的区域说了算。这个想法借鉴了图像处理领域里成熟的多曝光融合思想把多曝光换成多增强版本对每个输入版本计算一组权重图权重高的区域代表这个版本在该处更可信再做归一化最后按权重逐像素叠加。权重图的设计是整条链路里的灵魂我用的是三个权重的乘积对比度权重、饱和度权重和曝光度权重。对比度权重对灰度图做拉普拉斯滤波取绝对值。拉普拉斯值大意味着局部边缘和纹理清晰这个权重能引导融合结果保留细节丰富的区域。饱和度权重计算RGB三个通道的标准差标准差大代表色彩饱和、内容丰富倾向于把鲜艳区域保留下来。曝光度权重用高斯函数衡量像素亮度离中间调0.5的距离太暗或太亮的区域权重都低防止融合结果出现过曝欠曝。直接把三个权重相乘再归一化其实就是一张引导融合的信任地图。我实测下来这个组合在水下场景效果稳定而且每个权重都有明确的物理意义调参时有据可依。2.4 多尺度融合为什么能避免伪影权重图直接叠加到图像上做加权平均会出现一个很经典的伪影问题光泽或晕轮Halo效应。原因是权重图的边缘不够光滑直接逐像素加权会在强边缘附近产生不自然的过渡带。解决思路是多尺度融合。具体做法是把每份输入图像构造成拉普拉斯金字塔把对应的权重图构造成高斯金字塔然后在每一层按权重做融合最后从融合后的金字塔重建图像。这个过程可以理解为在不同空间频率上分别做融合大尺度的整体色调在低分辨率层融合细小的边缘纹理在高分辨率层融合每层上的权重都经过了对应尺度的平滑天然避免了边缘处的突变。我用生活类比来解释这就像两个厨子合作做一道菜一个擅长调味一个擅长摆盘。如果让两个人同时在整个盘子上操作难免互相干扰。但如果把工序分层——先在锅底定味道最后再细致摆盘——两边的优势就都能发挥出来。拉普拉斯金字塔做的事情本质上就是把图像拆成锅底到摆盘的各个层次分别取各家之长。3. Matlab核心代码与实操步骤3.1 主流程脚本先看整体骨架项目代码整体结构很清晰我用一个主脚本串联四个函数模块方便逐步调试。主脚本的流程如下%% 水下图像增强主脚本 clc; clear; close all; % 读取图像并归一化到[0,1] img imread(test_underwater.jpg); img im2double(img); % 第一步颜色平衡 img_cb colorBalance(img); % 第二步对比度增强基于颜色平衡结果 img_ce contrastEnhance(img_cb); % 第三步计算两个版本的权重图 w1 computeWeightMap(img_cb); w2 computeWeightMap(img_ce); % 归一化权重防止分母为零 w_sum w1 w2 1e-6; w1 w1 ./ w_sum; w2 w2 ./ w_sum; % 第四步多尺度拉普拉斯金字塔融合 levels 5; img_fused pyramidFusion(img_cb, img_ce, w1, w2, levels); % 显示结果 figure; subplot(2,2,1); imshow(img); title(原始图像); subplot(2,2,2); imshow(img_cb); title(颜色平衡); subplot(2,2,3); imshow(img_ce); title(对比度增强); subplot(2,2,4); imshow(img_fused); title(融合增强结果);这个结构有个好处每个阶段的结果都可以单独输出检查。我调试时习惯先把每一行停住分别看颜色平衡、对比度增强的中间图是否正确再去做融合。如果中间图就出了问题后面融合结果肯定不对没必要直接跳到结尾找原因。3.2 颜色平衡模块截断拉伸加灰度世界颜色平衡函数我做了两个层面的处理。先是分通道做百分位截断拉伸把动态范围拉开再计算裁剪后的通道增益让三通道均值趋向一致。代码如下function img_cb colorBalance(img) % 颜色平衡百分位截断 灰度世界增益 % 输入输出均为double类型取值范围[0,1] img_cb zeros(size(img)); % 第一步各通道百分位截断拉伸 for c 1:3 ch img(:,:,c); q_low prctile(ch(:), 5); q_high prctile(ch(:), 95); % 拉伸并在两端截断防止异常值影响 ch_stretch (ch - q_low) / (q_high - q_low 1e-6); img_cb(:,:,c) max(0, min(1, ch_stretch)); end % 第二步灰度世界增益补偿 mean_r mean2(img_cb(:,:,1)); mean_g mean2(img_cb(:,:,2)); mean_b mean2(img_cb(:,:,3)); mean_all (mean_r mean_g mean_b) / 3; % 加极小值防止除零 gain_r mean_all / (mean_r 1e-6); gain_g mean_all / (mean_g 1e-6); gain_b mean_all / (mean_b 1e-6); img_cb(:,:,1) img_cb(:,:,1) * gain_r; img_cb(:,:,2) img_cb(:,:,2) * gain_g; img_cb(:,:,3) img_cb(:,:,3) * gain_b; % 最终截断 img_cb max(0, min(1, img_cb)); end这里的分位点选择要说明一下。我默认用5%和95%但如果你测试的图像过曝区域很大可以把下分位点提到10%把上分位点降到90%压缩更狠。反过来如果图像本来就是低对比度且没有极端像素可以改用1%和99%来保留更多动态范围。这种参数没有绝对正确关键是要看一眼直方图再定。3.3 对比度增强模块HSV空间的组合策略对比度增强这一步我放在HSV空间操作只改亮度通道V不动色相和饱和度这样不会把颜色平衡的成果破坏掉。代码如下function img_ce contrastEnhance(img) % 对比度增强HSV空间V通道Gamma校正 CLAHE hsv rgb2hsv(img); v hsv(:,:,3); % Gamma校正提亮暗部 v_gamma v.^0.7; % CLAHE限制对比度自适应直方图均衡 v_clahe adapthisteq(v_gamma, ... NumTiles, [8 8], ... ClipLimit, 0.02, ... Range, full); hsv(:,:,3) v_clahe; img_ce hsv2rgb(hsv); end为什么先做Gamma再做CLAHE直接对原V通道做CLAHE暗部往往会被过度放大导致噪声跟着一起被强化。先做一次Gamma提亮让暗部细节先露出来再做CLAHE限制对比度增强整体会均衡很多。ClipLimit参数我设成0.02也就是限制每个直方图桶的最大高度不超过总像素数的2%这个值对水下图像比较友好。如果你发现结果有噪点感把ClipLimit降到0.01如果觉得对比度不足再往0.03调。3.4 权重图计算三因子相乘权重图函数是实现融合质量的关键。我把之前讲到的三个权重都用代码实现了function W computeWeightMap(img) % 计算融合权重图对比度 x 饱和度 x 曝光度 % 输入rgb图像输出权重图取值非负 % 对比度权重拉普拉斯绝对值 gray rgb2gray(img); lap fspecial(laplacian, 0.2); contrast abs(imfilter(gray, lap, replicate)); % 饱和度权重通道间标准差 mean_rgb mean(img, 3); diff img - cat(3, mean_rgb, mean_rgb, mean_rgb); saturation sqrt((diff(:,:,1).^2 diff(:,:,2).^2 diff(:,:,3).^2) / 3); % 曝光度权重高斯型中间调偏好 exposure exp(-(mean_rgb - 0.5).^2 / (2 * 0.25^2)); % 三因子相乘 W contrast .* saturation .* exposure; % 高斯平滑抑制权重图中的细小噪动 W imgaussfilt(W, 2); end关于权重图最后的高斯平滑这里有一个容易被忽略的细节如果不平滑权重图里细碎的纹理会导致融合结果出现局部忽明忽暗的闪烁感特别是在拉普拉斯金字塔的深层。平滑半径我取2个像素整体已经够稳。你要是发现融合结果边缘处还有微光可以把平滑半径加到3或4。3.5 金字塔融合从分解到重建融合的核心就是金字塔的构建和重建。这部分我封装了三个辅助函数逻辑上相互嵌套function img_out pyramidFusion(img1, img2, w1, w2, levels) % 多尺度融合主函数 % 构建拉普拉斯金字塔 L1 laplacianPyramid(img1, levels); L2 laplacianPyramid(img2, levels); % 构建权重的高斯金字塔 G1 gaussianPyramid(w1, levels); G2 gaussianPyramid(w2, levels); % 逐层加权融合 fused_pyr cell(1, levels); for k 1:levels w_sum G1{k} G2{k} 1e-6; fused_pyr{k} (L1{k} .* G1{k} L2{k} .* G2{k}) ./ w_sum; end % 重建图像 img_out pyramidReconstruct(fused_pyr); img_out max(0, min(1, img_out)); end function pyr gaussianPyramid(img, levels) % 高斯金字塔逐层平滑后下采样 pyr cell(1, levels); pyr{1} img; gauss_filter fspecial(gaussian, [5 5], 1.5); for k 2:levels smoothed imfilter(pyr{k-1}, gauss_filter, replicate); pyr{k} smoothed(1:2:end, 1:2:end); end end function pyr laplacianPyramid(img, levels) % 拉普拉斯金字塔当前层减去上一层上采样结果 pyr cell(1, levels); current img; gauss_filter fspecial(gaussian, [5 5], 1.5); for k 1:levels - 1 smoothed imfilter(current, gauss_filter, replicate); down smoothed(1:2:end, 1:2:end); up imresize(down, [size(current,1), size(current,2)], bilinear); pyr{k} current - up; current down; end pyr{levels} current; end function img pyramidReconstruct(pyr) % 从拉普拉斯金字塔重建图像 n length(pyr); current pyr{n}; for k n-1:-1:1 up imresize(current, [size(pyr{k},1), size(pyr{k},2)], bilinear); current up pyr{k}; end img current; end关于金字塔层数levels取5是平衡点。层数太少融合只在小尺度进行大尺度的色调过渡仍然会暴露加权痕迹层数太多最低分辨率层可能已经小于8x8这时权重归一化没有意义计算量还白白增加。对于常见的一两百万像素图片5层足够四万像素以下的小图取3到4层更稳妥。注意金字塔融合里每个上采样步骤都要严格匹配上一层的尺寸否则重建阶段会报错。如果图像尺寸不是2的整数倍最稳妥的办法是在主流程里先对图像做一次偶数尺寸裁剪。4. 实验效果对比与参数调优实践4.1 典型场景测试效果分析我用不同深度和不同水质下拍的测试图做了验证。在水体相对清澈、深度两三米的场景原始图像偏蓝远处岩石几乎看不清经过颜色平衡后整体色调明显回暖岩石区域出现了原本被淹没的暖色信息但雾感还在再经过融合远景轮廓清晰了近处纹理也保留得比较好。而在水质浑浊、漂浮物较多的场景里这套方案的优势更明显单纯颜色平衡会把蓝绿色调纠正过来但对比度依旧差而融合版本由于有饱和度权重和对比度权重的共同作用能够在压制背景雾的同时保留前景目标的边缘。不过说实话浑浊水体里如果目标物体距离超过七八米任何后处理算法都救不回来因为信息确实已经丢失了这时候我更建议配合物理补光来拍摄而不是指望后期算法。拿一些公开测试集中的水下图像做批量验证我发现这套方法对绿色主色调的图片修复效果最好蓝色调居中而红色调或人工照明强烈的场景容易出现色偏矫正过头的情况。原因也很直观灰度世界假设天然偏向把整体颜色拉向灰色当场景本身就有强烈的红色光源时计算得到的红色增益会被错误压低。4.2 各关键参数的调试心得参数调优这块我做了一张速查表方便大家对照调参参数位置参数含义推荐默认值调大时的效果调小时的效果colorBalance分位点截断范围上下限5% / 95%对比度更强但易偏色更柔和但可能发灰Gamma指数暗部提亮强度0.7暗部更亮但噪点变多效果减弱ClipLimitCLAHE对比度限制0.02对比度更强易过曝更平滑但偏平淡权重平滑半径imgaussfilt的标准差2融合更顺滑但细节损失更锐利但易有伪影金字塔层数多尺度分解层数5全局过渡更自然计算更快但融合粗糙我个人的经验是先固定金字塔层数和权重平滑半径专心调颜色平衡和CLAHE的参数等输出结果整体稳定了再回头微调权重平滑。因为前两个参数直接影响内容表达后两个更多影响融合的做工精不精细把它们解耦调试会节省很多时间。4.3 定量评价怎么判断增强到底有没有效除了肉眼对比我还习惯用两个无参考评价指标做辅助判断UCIQE和UIQM。UCIQE主要衡量色度、饱和度和对比度的综合质量UIQM则更贴近水下成像特点综合色彩、清晰度和对比度三项。Matlab里没有现成函数网上有大量第三方实现直接调用即可我通常比较增强前后这两项指标的变化量。但这里要提醒一句这类无参考指标的数值提升并不绝对等于视觉质量提升。我见过UCIQE涨了几个点、肉眼却很不自然的例子原因是算法把饱和度拉得过高人眼看着发假。所以我的习惯是指标准作辅证最终判断永远以肉眼观察为准重点看三类区域是否存在色偏、边缘是否有伪影、暗部是否出现过曝。5. 常见问题与排查实录5.1 高频踩坑记录这个项目从初版到稳定我记录了不少问题挑几个高频的分享出来。第一个坑是除零问题。灰度世界增益的计算里如果某个通道均值非常小比如接近0.001增益一下子放大上千倍图像立刻爆红。我第一次跑通时就是没做保护输出全红找了半天才发现是红通道均值太小导致增益爆炸。解决办法就是加一个极小值同时最终截断到[0,1]。第二个坑是金字塔尺寸不匹配。原始图像的宽高如果不是2的倍数下采样到第5层再做上采样回来尺寸对不上重建时图像边缘会出现错位或黑边。我后来在主脚本里加了两行预处理先把尺寸裁剪到偶数问题立刻消失。第三个坑是权重图中的负值问题。拉普拉斯滤波输出有正有负我一开始直接在原值上做归一化结果一些负权重区域在融合后产生了奇怪的暗斑。正确答案是先取绝对值再参与后续计算这也是我在代码里写abs的原因。这类小问题不会让程序崩溃但会非常隐蔽地影响输出质量排查时最费神。5.2 常见问题速查表现象可能原因处理方法输出图像整体偏红红色通道增益过大检查原图红通道均值是否过低调高截断下分位点输出图像过曝发白CLAHE ClipLimit偏大将ClipLimit降到0.01并适当提高Gamma指数边缘出现光晕伪影权重图不平滑或层数太少增大权重平滑半径或把金字塔层数从5提到6色彩饱和度不足颜色平衡后蓝色残留改用90%百分位截断或者增加饱和度权重系数程序报尺寸不匹配图像宽高非偶数用imresize或裁剪将宽高调整到偶数暗部噪点明显Gamma系数过小CLAHE放大噪声把Gamma指数提高到0.8并先做一次去噪预处理5.3 结合这个方案还能怎么扩展这套框架的可扩展性很强因为融合框架本身不限制输入源。我再多说一个扩展方向把对比度增强版本替换成基于暗通道先验去雾后的版本或者同时融合三份输入加上一个锐化版本就能覆盖更复杂的水下场景。权重图也可以做文章比如根据实际场景给三个权重因子配上不同的指数场景偏绿时提高饱和度权重的影响场景偏灰时提高对比度权重的影响。这些都是在现有代码骨架上做局部调整不需要推翻重来。另外如果后续要把算法集成到实时视频流建议先把金字塔部分改写成预分配内存的版本。Matlab里cell数组动态分配在循环里性能损耗明显我实测同样的流程改成预分配后处理一帧1080p图像的速度能提升大约三成。当然视频处理是另一个话题了算法逻辑本身不变。最后再分享一个在实际调试中很管用的小技巧永远保留中间结果的可视化窗口。我在主脚本里把每个阶段的输出都存了一份这样万一融合结果不对我能快速定位是颜色平衡环节的问题、对比度增强环节的问题还是金字塔融合的问题。有几次参数调乱全靠对比中间结果一步步倒推才找到原因。做图像处理可复现和可视化这两件事怎么强调都不过分。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询