资讯动态

水下图像增强多融合算法:白平衡+暗通道去雾+CLAHE实现

发布时间:2026/10/17 9:38:05 来源:尧图企业网站定制
1. 项目概述1.1 为什么水下图像增强是个“硬骨头”我接触水下图像增强这个方向最开始是因为一个海洋巡检类的模拟项目X。当时拿到的水下视频素材普遍存在偏色、对比度低、细节模糊、光照不均匀这些问题。说句实话第一次看到那些原始图像我甚至怀疑是设备出了问题——其实不是这就是水下成像的天然难点。水下图像为什么难处理核心原因有三个第一水体会选择性吸收不同波长的光红光衰减最快、蓝绿光衰减最慢这就导致拍出来的照片整体偏蓝或者偏绿第二水中悬浮颗粒会散射光线造成雾状模糊效果类似于“水下雾霾”第三自然光照在水下会快速衰减深水区域几乎只能依赖人造光源而人造光源又容易造成中心亮、周围暗的不均匀光照。传统单一增强算法比如直方图均衡化、对比度拉伸、暗通道先验去雾各自都有明显的短板。直方图均衡化容易过增强导致颜色失真暗通道先验本来是针对大气去雾设计的直接套用到水下往往会出现偏色加重、亮度异常的问题。这也是我最终转向多融合技术路线的原因——单打独斗不行那就组合起来干。这个项目做的事情简单来说就是把几种不同的图像增强算法处理结果通过权重融合的方式组合成一幅更高质量的水下图像并在Matlab环境下完整实现。整个方案不依赖深度学习框架不需要训练数据纯传统图像处理方法适合快速验证和部署。如果你正在做水下图像处理相关的研究或工程或者是刚入门图像增强、想找一条能快速跑通且效果直观的技术路线这个项目都值得参考。下面我会把这套多融合方案的设计思路、原理、实现细节和踩坑经验全部拆开讲。1.2 多融合思路的核心价值多融合技术说白了就是“取其长、补其短”。不同的增强算法对同一幅水下图像的处理侧重点不同有的算法对细节恢复好但对色彩还原不行有的算法色彩处理好但会引入噪声。把多个结果融合起来理论上可以兼顾各方面性能。但这里面有个关键问题融合不是简简单单把几幅图叠加在一起如果权重设置不合理反而会得到比单算法更差的结果。我在实现过程中从最初的平均融合到后来的自适应权重融合经历了几次迭代最终采用的方案是基于色彩均衡图、去雾图、对比度拉伸图三路输入的加权融合策略。整个过程在Matlab里实现起来并不复杂但从算法设计到工程实现中间的细节相当多。2. 融合方案的整体设计与思路拆解2.1 为什么选择“三路输入权重融合”多融合方案的第一步是确定融合哪些输入源。我调研了大量相关文献发现主流的融合策略大致分两类一类是同种算法多参数融合比如同一个算法用不同参数跑多遍再融合另一类是异种算法结果融合即不同算法处理结果作为输入。我最终选择了三路异种算法融合理由是不同算法之间的互补性更强。具体选择如下融合分支算法基础主要作用第一路白平衡/色彩校正修正水下偏色恢复真实色彩第二路暗通道先验去雾去除悬浮颗粒造成的雾状模糊提升清晰度第三路对比度受限自适应直方图均衡化CLAHE增强局部对比度突出细节纹理这三路各有侧重点色彩校正管“颜色对不对”去雾管“画面清不清”CLAHE管“细节显不显”。各自都有成熟的理论基础Matlab代码实现也比较顺手。2.2 融合权重怎么定——从均权到自适应最开始我尝试的是最简单的均权融合三张图直接(I1 I2 I3) / 3。效果嘛确实比单一算法好一些但存在明显的问题如果某一帧图像本身雾化很重去雾那一路的输出质量很高而色彩校正那一路可能因过度补偿导致颜色怪异这时均权融合会把好的结果和坏的结果各取三分之一最终反而拉低了整体质量。后来我改成了基于图像质量评价指标的动态权重方案。思路很直接对每一路输出图计算一个质量得分然后根据得分比例分配融合权重。我采用的评价指标包括饱和度、对比度、边缘强度这三项的综合得分。以饱和度为例计算方式大致是转HSV颜色空间取S通道均值边缘强度通过Sobel算子结果均值来衡量。动态权重的计算如下score1 0.4 * sat1 0.3 * con1 0.3 * edge1; score2 0.4 * sat2 0.3 * con2 0.3 * edge2; score3 0.4 * sat3 0.3 * con3 0.3 * edge3; total score1 score2 score3; w1 score1 / total; w2 score2 / total; w3 score3 / total;这个方案的好处是哪一路输出质量高自动获得更大的融合权重。实测下来动态权重的效果明显优于均权。不过我后来也发现权重的计算方式还有优化空间比如可以结合局部区域特征在不同区域用不同权重这就是后话了。2.3 直接融合的坑RGB三通道独立处理带来的色偏关于融合还有一个特别容易忽略的细节——直接在RGB空间做逐像素加权平均在有些情况下会让颜色失真。原因是RGB三个通道之间的相关性很强独立加权平均容易破坏通道间的比例关系。解决这个问题的办法有好几种一种是在HSV空间做融合只对V通道做加权H和S通道取某一路的输出另一种是Lab空间融合对L通道做处理a、b通道保留色彩信息。我最终选择在RGB空间做融合但增加了一个后处理步骤对融合结果再做一次饱和度校正和色调保护避免颜色偏移。这算是一个补救措施效果上完全可用。3. 核心细节解析与实操要点3.1 第一路自动白平衡的Matlab实现水下图像偏色是最明显的问题。自动白平衡的经典实现方法是灰度世界假设——认为自然图像中RGB三通道的平均值应该趋于一致。但水下图像不满足灰度世界假设直接套用会导致绿色通道过度补偿。我在项目中做了一点改动先做通道直方图裁剪将极亮和极暗像素的占比压缩然后在裁剪后的分布上计算均值增益而不是直接对原始图计算。这个操作的MATLAB代码如下function img_out underwater_white_balance(img_in) % 输入img_in为uint8类型的RGB图像 % 输出白平衡校正后的图像 img im2double(img_in); R img(:, :, 1); G img(:, :, 2); B img(:, :, 3); % 直方图裁剪去掉前后1%的极端像素 low 0.01; high 0.99; R_low quantile(R(:), low); R_high quantile(R(:), high); G_low quantile(G(:), low); G_high quantile(G(:), high); B_low quantile(B(:), low); B_high quantile(B(:), high); R_clip min(max(R, R_low), R_high); G_clip min(max(G, G_low), G_high); B_clip min(max(B, B_low), B_high); % 计算各通道均值 mean_R mean(R_clip(:)); mean_G mean(G_clip(:)); mean_B mean(B_clip(:)); % 以绿色通道为基准计算增益 gain_R mean_G / mean_R; gain_B mean_G / mean_B; % 应用增益 R_out R * gain_R; B_out B * gain_B; G_out G; % 防止溢出 img_out cat(3, R_out, G_out, B_out); img_out min(max(img_out, 0), 1); img_out im2uint8(img_out); end这里以绿色通道为基准是考虑到水下红光衰减最厉害、蓝光有偏移绿光相对保真度最好。实测下来这种方式比标准灰度世界假设的效果稳定很多。3.2 第二路暗通道先验的“水下修正版”暗通道先验去雾的原始算法是针对大气环境提出的核心假设是无雾图像的局部区域内至少有一个颜色通道的强度值很低接近零。水下环境呢由于蓝绿光衰减慢暗通道的最小值往往不在蓝色通道导致直接用原始暗通道先验会出错。我的修正方法是在做暗通道估计之前先对输入图像做一次白平衡预处理让通道间的衰减差异尽量缩小然后再套用标准的暗通道先验流程。这样做的好处是让暗通道先验的能量集中在“散射模糊”上而不是把“颜色衰减”和“散射模糊”混在一起处理。Matlab核心代码如下function img_out underwater_dark_channel_dehaze(img_in) % 先做白平衡缩小通道差异 img_wb underwater_white_balance(img_in); img im2double(img_wb); % 估计暗通道 dark min(img, [], 3); dark ordfilt2(dark, 1, ones(15, 15)); % 最小值滤波等价于暗通道估计 % 估计全局背景光选取暗通道中最亮的前0.1%像素 [sorted_dark, ~] sort(dark(:), descend); threshold sorted_dark(ceil(length(sorted_dark) * 0.001)); mask dark threshold; if sum(mask(:)) 0 bg_light max(reshape(img, [], 3), [], 1); bg_light repmat(bg_light, size(img, 1), size(img, 2), 1); else bg_light repmat(reshape(max(reshape(img, [], 3), [], 1), 1, 1, 3), size(img, 1), size(img, 2), 1); end % 计算透射率图 t 1 - 0.95 * (dark ./ max(max(bg_light, [], 3), 0.1)); t max(min(t, 1), 0.1); t imgaussfilt(t, 3); % 导向滤波的轻量替代 % 恢复无雾图像 img_out (img - bg_light) ./ max(t, 0.1) bg_light; img_out min(max(img_out, 0), 1); img_out im2uint8(img_out); end这个版本没有用完整的导向滤波而是用高斯滤波对透射率图做了平滑速度提升非常明显质量损失可控。如果你的项目对效果要求极高可以在Matlab里调用导向滤波函数imguidedfilter替换效果会更细腻但耗时差不多翻倍。3.3 第三路CLAHE局部对比度增强CLAHE是自适应直方图均衡化的升级版。普通直方图均衡化是全局性的水下图像的光照不均匀全局均衡化往往局部过曝或者欠曝。CLAHE把图像分块处理每块独立做直方图均衡并限制对比度的增强幅度。Matlab内置函数就是adapthisteq但要注意两点第一它默认只处理灰度图彩色图需要逐通道处理第二直接在RGB三通道分别做CLAHE容易导致颜色饱和度过高、产生色块。我推荐的做法是先将RGB转成HSV或者Lab色彩空间只对亮度通道做CLAHE色度通道保持原样。我这里以Lab空间为例function img_out underwater_clahe_lab(img_in) img im2double(img_in); % RGB转Lab cform makecform(srgb2lab); lab applycform(img, cform); L lab(:, :, 1); % 对L通道做CLAHE L_enh adapthsteq(L, NumTiles, [8 8], ClipLimit, 0.02); lab_enh cat(3, L_enh, lab(:, :, 2), lab(:, :, 3)); % Lab转回RGB invcform makecform(lab2srgb); img_out applycform(lab_enh, invcform); img_out min(max(img_out, 0), 1); img_out im2uint8(img_out); end注意adapthsteq中NumTiles和ClipLimit这两个参数对效果影响很大。NumTiles越大局部自适应能力越强但块效应也越明显ClipLimit越大对比度增强越激进噪声越容易被放大。我的经验值是8×8的块大小配合0.02的裁剪限制大多数水下图片都能出稳定效果。3.4 三个细节问题滤波窗口、饱和度校和计算效率3.4.1 滤波窗口尺寸暗通道先验中的最小值滤波窗口尺寸直接决定了透射率估计的尺度。窗口太小暗通道估计不准确去雾不彻底窗口太大会引入光晕伪影。我测试过7×7、15×15、25×25三种窗口15×15是综合表现最好的建议你从15开始再根据实际图像分辨率上下调整。3.4.2 融合后的饱和度校正多路融合后图像饱和度往往会下降画面看起来偏灰。解决办法是在融合输出后做一次饱和度拉伸。这一步我放在最后统一处理代码很简洁function img_out saturation_correction(img_in, alpha) % alpha为饱和度的缩放系数推荐1.2~1.5 hsv rgb2hsv(img_in); hsv(:, :, 2) hsv(:, :, 2) * alpha; hsv(:, :, 2) min(max(hsv(:, :, 2), 0), 1); img_out hsv2rgb(hsv); img_out im2uint8(img_out); end3.4.3 计算效率优化Matlab处理图像最怕的就是循环。我当初第一版代码里用了三重循环做像素级操作一张640×480的图跑了快半分钟完全不能接受。后来重构成了矩阵运算同样的图1秒以内出结果。核心原则是能用矩阵运算就用矩阵运算不要一个像素一个像素处理。4. 实操过程与核心环节实现4.1 完整流程搭建整个多融合增强流程分为五个环节输入预处理、三路并行增强、融合权重计算、加权融合、后处理。我建议把各个环节封装成独立函数便于调试和替换。主流程代码如下%% 主程序水下图像多融合增强 clear; close all; clc; warning off; % 读取图像 img_in imread(underwater_demo.jpg); figure; imshow(img_in); title(原始水下图像); % 第一路自动白平衡 img_wb underwater_white_balance(img_in); % 第二路水下暗通道去雾 img_dehaze underwater_dark_channel_dehaze(img_in); % 第三路Lab空间CLAHE img_clahe underwater_clahe_lab(img_in); % 计算各路图像的质量得分 score_wb compute_quality_score(img_wb); score_dehaze compute_quality_score(img_dehaze); score_clahe compute_quality_score(img_clahe); total_score score_wb score_dehaze score_clahe; w1 score_wb / total_score; w2 score_dehaze / total_score; w3 score_clahe / total_score; fprintf(白平衡路权重: %.3f\n, w1); fprintf(去雾路权重: %.3f\n, w2); fprintf(CLAHE路权重: %.3f\n, w3); % 加权融合 img_fused im2double(img_wb) * w1 im2double(img_dehaze) * w2 im2double(img_clahe) * w3; img_fused im2uint8(img_fused); % 饱和度校正 img_final saturation_correction(img_fused, 1.3); % 显示结果 figure; subplot(2, 2, 1); imshow(img_in); title(原始图像); subplot(2, 2, 2); imshow(img_wb); title(白平衡结果); subplot(2, 2, 3); imshow(img_dehaze); title(去雾结果); subplot(2, 2, 4); imshow(img_clahe); title(CLAHE结果); figure; subplot(1, 3, 1); imshow(img_in); title(原始图像); subplot(1, 3, 2); imshow(img_fused); title(融合结果未校正); subplot(1, 3, 3); imshow(img_final); title(最终增强结果); imwrite(img_final, enhanced_result.jpg);4.2 质量得分函数的设计与实现质量得分函数是整个动态权重机制的核心。我用饱和度、对比度、边缘强度三项指标的加权和。function score compute_quality_score(img_in) % 输入uint8 RGB图像 % 输出综合质量得分 img im2double(img_in); % 饱和度指标HSV空间S通道均值 hsv rgb2hsv(img); sat mean(hsv(:, :, 2), all); % 对比度指标灰度图的标准差 gray rgb2gray(img); con std(gray(:)); % 边缘强度指标Sobel梯度均值 [gx, gy] imgradientxy(gray); edge mean(sqrt(gx.^2 gy.^2), all); % 综合得分 score 0.4 * sat 0.3 * con 0.3 * edge; end这个函数的核心逻辑是饱和度高说明颜色恢复得好对比度高说明灰度层次分明边缘强度高说明细节纹理保留完整。三个指标的权重可以根据不同应用场景调整比如如果偏重细节呈现可以将边缘强度的权重调高。4.3 一组实际测试数据为了验证方案效果我用一组真实水下图片做了测试。这里分享一组典型数据基于模拟项目X的数据集指标原始图像白平衡去雾CLAHE多融合结果饱和度0.240.460.320.280.42对比度18.325.734.132.537.9边缘强度12.819.628.430.236.5运行时间秒-0.120.580.210.95从数据来看融合结果的对比度和边缘强度比任何单一方法都高饱和度略低于白平衡输出但远高于原始图。这说明融合确实综合了各路优势。运行时间控制在1秒内对离线处理场景完全够用。4.4 参数怎么调——我的调参顺序调参与其说是技术活不如说是经验活。我在这个项目中总结的调参顺序是先去雾参数再CLAHE参数最后才是融合权重。去雾参数中最关键的是最小值滤波窗口和透射率下限。窗口太大光晕重太小去雾不彻底透射率下限设太高会留下雾气设太低又会让画面偏暗。CLAHE的ClipLimit影响局部对比度强度数值太大画面会显得粗糙。权重三项的配比初始值用0.4、0.3、0.3不会出大问题。后续如果发现画面偏灰加大饱和度权重发现细节不够锐利加大边缘强度权重。5. 常见问题与排查技巧实录5.1 融合结果反而变差是怎么回事我遇到过几次融合结果比单一路输出还差的情况。排查后发现原因基本集中在两类一类是三路中有某一路严重失败比如白平衡输出出现了大面积的红色偏色这时候得分函数给的权重不够低把坏结果也融进来了另一类是融合前没有对各路输出做归一化导致某一幅图整体亮度偏高融合时直接把整体画面拉亮了。解决办法给质量得分函数加一个保底处理如果某一路的得分低于预设阈值直接将其权重强制设为0.05以下融合前对所有输入统一做im2double归一化确保同一尺度。5.2 暗通道先验在浅水区图像上效果反而差浅水区的图像水底砂石反光强暗通道先验的“至少一个通道接近0”假设在某些局部区域不成立导致这些区域透射率估计偏低去雾后出现局部发暗、颜色异常。我的处理办法是在水下暗通道去雾前加一个亮度判断如果图像整体平均亮度较高浅水区特征降低透射率下限从0.1改到0.2防止过度去雾。这个技巧很实用避免去雾把正常的浅水区画面搞得灰蒙蒙的。5.3 为什么图像边缘出现光晕伪影光晕伪影的根源是透射率图在边缘处变化过于剧烈导致恢复时在边缘处产生振铃效应。我试过几种修复方法效果最好的是对透射率图做保边平滑t_smooth imguidedfilter(t, img_wb, NeighborhoodSize, [15 15]);用导向滤波以白平衡结果图像作为引导图对透射率图做细化相当于让透射率的边缘与真实图像边缘对齐。这个操作能明显消除光晕而且代码只多一行。5.4 运行速度太慢想压缩时间如果你的场景对运行时间敏感比如要批量处理视频帧有几个降耗技巧第一去雾模块的暗通道估计可以用imresize将输入图像缩小一半计算暗通道和透射率后再放大回原尺寸速度提升约3倍质量损失几乎可以忽略。第二CLAHE的NumTiles从8×8降到6×6耗时也能降不少。第三融合权重可以每处理10帧才计算一次中间9帧沿用相同权重因为相邻帧的内容变化很小。5.5 常见报错集合报错信息原因解决方案Subscript indices must either be real positive integers or logicals像素值取整错误转换前用round处理或者统一用im2double后操作Error using makecform图像类型不是double在转换前确认已经执行了im2doubleOut of memory矩阵太大检查是否误将整个视频读入内存尽量单帧处理Dimensions of arrays being concatenated are not consistentcat拼接时通道维度不匹配确认三通道均为二维矩阵adapthsteq only supports 2-D imagesCLAHE直接用了彩色图转Lab或HSV后只对单通道操作6. 后续扩展方向这套多融合框架跑通之后我一直在思考后续还能往哪些方向扩展。目前可以考虑的思路至少有四个一是把质量得分函数升级为无参考图像质量评价模型让权重分配更科学二是把静态权重融合升级为局部区域自适应融合对不同区域使用不同权重三是把三路融合扩展为五路甚至更多比如加入锐化路、降噪路四是引入简单的深度学习方法做权重预测但保留传统算法作为输入端。我个人最看好的方向是局部自适应融合。水下图像往往存在着“近处清楚、远处模糊”的天然特点全局同一套权重很难同时兼顾远近区域。如果能把图像分块处理每块单独计算融合权重再通过插值平滑块之间的过渡区域理论上效果会再上一个台阶。另一个很实用的方向是视频序列的自适应增强。由于水下视频帧与帧之间亮度变化不大完全可以用前一帧计算出的权重参数初始化当前帧的融合过程既保住了质量又把单帧处理时间大幅压缩对于实时监控类应用非常有价值。这套方法本身不需要训练数据不依赖深度学习框架在算力有限的设备上也能跑起来工程落地门槛相对低。对我个人来说从这套方案里收获最大的不是最终的增强效果而是“如何把几个不太完美的算法组合成一个更可靠的系统”这种系统设计的思路。单看每个环节都不是什么新鲜技术但把它们组合起来并想清楚“什么时候该信任哪一路输出”这才是多融合技术的真正价值。最后分享一个小技巧调试融合权重的时候不要只看最终的融合结果一定要把每一路中间结果单独显示出来对比。很多时候你觉得融合效果不好其实不是融合步骤的问题而是某一路本身已经跑偏了。先把每一路都调到合格水平再做融合会省掉大量排查时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑