过曝图像修复:基于Retinex反射分量分离的MATLAB实现
简介基于反射分量分离的单图像过曝区域修复Matlab实现面向图像处理、计算机视觉方向的本硕学生及科研人员解决过曝区域细节丢失、色彩失真等常见问题可用于算法对比与课程实验。压缩包共7个文件核心为1个m脚本另含3个bmp测试样张、2个png结果示意图及1个txt说明文件整体约217KB结构精简便于快速定位与复现。目前已有124人学习/浏览。资源提供完整仿真结果与运行方法说明测试图像可直接运行验证修复效果m代码注释清晰便于理解反射分量分离的实现流程说明文件对运行环境Matlab2014/2019a/2021a及操作要点做了提示可降低复现门槛。适合本科、硕士教研学习也适合作为智能优化、信号处理等方向Matlab仿真的入门参考更多内容可访问博主主页获取。1. 过曝修复从反射分量分离开始拍逆光建筑或者强光下的金属表面时高光区域在直方图最右侧直接截断像素值饱和在255附近纹理信息几乎全部丢失。常规处理是先压曝光再提阴影但暗部噪声和色彩偏移会一起冒出来过曝区依然是一块平白。基于反射分量分离的修复思路把图像拆成光照分量和反射分量的乘积过曝区域的问题被归结为光照项过大反射项里残留的边缘与纹理仍然可用。这套matlab工程包内含主脚本scp_spec_removal_web.m、过曝样本train.bmp以及comp_sp.bmp、comp_df.bmp两张仿真对照图可以把分量分离、过曝判定、亮度重建和结果对比这一整条链路完整跑通。适合正在做图像处理课设、毕业设计或仿真预研的本科与硕士阶段使用。2. 反射分量分离Retinex理论与matlab实现2.1 从乘法成像模型到对数域分解Retinex理论把观测图像I建模为光照分量L与反射分量R的逐像素乘积I(x, y) R(x, y) · L(x, y)其中L表示入射到场景的光照强度变化平缓R取决于物体表面材质携带边缘、纹理和颜色信息。对两边取对数后乘法变成加法log I log R log L后续的滤波和分离都在对数域完成避免大动态范围下直接做除法产生的数值溢出。这里最关键的假设是过曝区域并没有真正丢失所有信息只是L取值超出传感器动态范围R仍在可恢复范围内。这个假设在绝大多数高光截断场景下成立也是整个修复流程的理论前提。在matlab实现时我一般先把图像用im2double归一化到[0,1]再做log变换。如果直接在线性域用高斯滤波估计L并做除法强边缘两侧会出现明显的明暗光晕原因是线性域下边缘两侧的局部均值被两侧亮度同时拉偏。对数域把边缘两侧的差值从乘法量级变成加法量级滤波时的过冲会小很多这也是Retinex类方法在实现层面统一取log的原因。实际调试中如果发现修复结果边缘有白边优先怀疑是不是跳过了log这一步。2.2 光照分量估计的三种滤波方式光照分量估计是反射分量分离的核心滤波器的保边性能直接决定修复质量。常见做法有三种高斯滤波、加权高斯滤波和引导滤波。标准高斯滤波速度最快但在亮暗交界处会把两侧亮度平均产生明显的边缘光晕引导滤波需要额外提供引导图保边效果最好但多一层参数标定加权高斯滤波是在高斯核内按中心像素与邻域像素的亮度差重新赋权差异大的区域减少参与平均兼顾速度和边缘保持适合作为过曝修复主流程的默认选择。估计方式保边能力计算量适用场景标准高斯滤波弱边缘有光晕低快速原型验证加权高斯滤波中边缘较干净中过曝修复主流程引导滤波强边缘锐利较高精细反射分离选择加权高斯滤波的另一个原因是对matlab版本依赖低不需要额外工具箱2014到2021a版本都能稳定运行。如果换成引导滤波还要维护引导图和正则化参数epsilon对单图像过曝修复来说属于投入产出比不高的复杂度增加。2.3 反射分量分离的matlab函数与参数语义下面这份代码是工程中分离步骤的最小可运行版本function [R, L] reflectance_decompose(I, sigma, w) % 加权高斯滤波估计光照分量并分离出反射分量 % I : [0,1]范围的double图像灰度或彩色 % sigma: 高斯核标准差控制光照平滑程度 % w : 光照权重0.6~0.9之间的修复结果更自然 L0 imgaussfilt(I, sigma); % 初始光照估计 L w * L0 (1 - w) * mean(I(:)); % 加权融合防止暗区过暗 R I ./ max(L, eps); % 逐像素除得到反射分量 endimgaussfilt是matlab自带的快速高斯滤波比fspecial配合imfilter的传统写法在双精度大图上快20%左右。参数w相当于在平滑光照和全局平均亮度之间做插值w越接近1光照分量越贴合低频亮度对过曝区域的补偿强度越大w偏小时整体色调更保守适合画面中大面积高光的场景。sigma的合适起点是图像短边长度的1/30过曝区域面积大就适当加大到1/20。反射分量R在过曝像素处会明显大于1直接参与后续计算前必须做截断R_c min(R, 1.25); % 反射分量截断保留高光纹理 R_n mat2gray(R_c); % 线性归一化到[0,1]提示截断上限取1.25而不是1是为了让过曝区域残留的高光纹理保留在数据范围内后续掩码检测才能顺着这部分信息定位边界。彩色图像执行这套分离时要关注通道独立性。RGB三个通道分别计算R和L三个通道的sigma保持一致否则同一物体的不同颜色通道平滑程度不同分离后的反射分量会出现彩色边缘。如果只做灰度预览可以rgb2gray后单通道跑但正式出结果必须三通道分别处理这也是comp_df.bmp与单独压亮度的效果存在本质差异的原因。3. 过曝区域判定与亮度重建3.1 高光候选掩码的阈值与形态学处理过曝修复不是全图平均用力而是先框定哪些像素需要重建。工程里用灰度亮度阈值做初筛mask I_gray T。T取0.85时覆盖sRGB空间接近饱和的高光区域对应直方图右侧被截断的那一段。I_gray rgb2gray(I); mask I_gray 0.85; % 初始过曝候选 mask imdilate(mask, strel(disk, 3)); % 向外扩3像素过渡带 mask imclose(mask, strel(disk, 5)); % 闭合高光内部的细缝膨胀操作把掩码向外扩展3像素目的是覆盖过曝区边缘的半饱和渐变段避免修复后高光边界残留一条亮边。imclose处理的是高光内部因为表面划痕或灰尘产生的细小黑缝这些像素亮度其实也饱和了只是局部被遮挡闭合后掩码更连贯。形态学处理后建议增加连通域筛选cc bwconncomp(mask); num cellfun(numel, cc.PixelIdxList); mask false(size(mask)); for k 1:cc.NumObjects if num(k) 50 % 面积小于50像素的孤立高光点丢弃 mask(cc.PixelIdxList{k}) true; end end这一步把散落的噪点高光排除掉修复算法集中处理大面积过曝主区域。调试时如果发现修复结果里保留了一堆孤立亮斑就是这个面积阈值设得太小改成200或更大即可。3.2 光照分量替换与过曝区重建重建策略是在反射分量保持不变的前提下用全局平均光照替换过曝区的异常光照估计。这样做的理由有两个一是反射分量已经剥离了光照影响纹理信息还在二是过曝区的L估计值虚高直接乘回去会把R中残留的噪声放大到无法接受。Lr L; % 复制光照分量 Lr(mask) mean(L(~mask(:))); % 过曝区光照替换为正常区均值 O R .* Lr; % 逐像素重建 O min(O, 1); % 裁剪到动态范围对彩色图像应逐通道执行同一掩码下的替换否则重建后的三通道比例失衡高光区域会整体偏色。这里mean(L(~mask(:)))统计的是非过曝区域的光照均值可以理解成当前场景在没有过曝时的基准光照水平。mask作为逻辑索引在三个通道间保持一致保证色相不会因为各通道替换值不同而产生漂移。重建方式过曝区亮度纹理恢复计算开销光照替换本工程中贴近场景好低反射分量直接拉伸高仍偏亮中低泊松方程填充可调好高反射分量直接拉伸实现最简但会放大已有噪声泊松方程填充保真度最高需要迭代求解运行时间随掩码面积增长明显适合离线精细修复。本工程采用的光照替换方式在纹理恢复和计算开销之间取得平衡也是大多数过曝修复实现的首选方案。3.3 色偏校正与Gamma微调光照替换后过曝区域容易发灰原因在于R超过1的部分在截断时被压平三通道的均值不再对齐。工程里的处理方式是让修复区域的中值向非过曝区域对齐for c 1:3 ch O(:, :, c); if any(mask(:)) target median(O(~mask, c)); % 正常区中值 src median(ch(mask)); % 修复区中值 ch(mask) ch(mask) * (target / max(src, eps)); end O(:, :, c) ch; end中值比值修正比均值比值更抗异常值个别极端亮像素不会把修正系数整体拉偏。最后加一次Gamma映射把输出曲线整体向下微调半档视觉上更接近正常曝光O O .^ 0.92;调参时最容易观察到的失败模式是修复区域亮度断层掩码内部突然亮一截边界处却保留原过曝渐变。这通常是因为Lr替换范围没有包含过渡带或mask阈值设得过高。把膨胀半径从3加到5阈值从0.85降到0.80断层一般可以消除。另一个需要关注的点是Gamma调整不能叠加多次工程脚本每次运行都会重新读取train.bmp而不是在上一轮输出上二次处理。4. 工程仿真运行从train.bmp到comp_df.bmp4.1 主脚本scp_spec_removal_web.m的执行链路scp_spec_removal_web.m是整套仿真的唯一入口。它的执行链路包括加载图像、亮度预处理、反射分量分离、过曝掩码判定、区域重建、色彩校正和结果写出。脚本默认读取同目录下的train.bmp输出comp_df.bmp作为最终修复图同时把分离出的反射分量写成comp_sp.bmp方便对照检查分离是否准确。简化后的主函数结构如下function out scp_spec_removal_web(imgPath) % 主脚本单图像过曝修复全流程 if nargin 1 imgPath train.bmp; end I im2double(imread(imgPath)); sigma max(size(I)) / 30; % 尺度随图像自适应 [R, L] reflectance_decompose(I, sigma, 0.8); mask find_highlight_mask(I); % 过曝候选判定 % ... 光照替换与色彩校正 ... imwrite(R, comp_sp.bmp); % 反射分量结果 imwrite(out, comp_df.bmp); % 修复结果 end脚本对当前文件夹的读写是相对路径命令行运行时需要先用cd把matlab的Current Folder切到zip解压后的目录否则会报读取train.bmp失败。工程自带的两个bmp输出同样按相对路径写入建议每轮仿真前先删除旧结果避免把上一轮产物误当当前结果。4.2 文件清单与运行步骤zip解压后会看到下列核心文件文件类型作用scp_spec_removal_web.m主脚本全部修复流程入口train.bmp输入图像含大面积过曝的测试样本comp_sp.bmp输出图像反射分量分离结果comp_df.bmp输出图像最终过曝修复结果说明.txt文档运行环境与注意事项运行步骤只有三条把解压目录设为matlab当前工作目录在命令行窗口执行scp_spec_removal_web打开comp_df.bmp查看结果。完整执行时间在几十秒级具体取决于CPU和图像分辨率。cd(D:\work\overexposure_fix); scp_spec_removal_web;针对matlab版本差异2019a和2021a直接运行没有障碍。如果在matlab 2014上运行并提示imgaussfilt未定义把函数里的滤波调用替换成imfilter(I, fspecial(gaussian, hsize, sigma), replicate)即可hsize取2*ceil(3*sigma)1输出效果与新版基本一致。替换后要注意卷积边界的处理方式replicate会复制边缘像素避免边界出现黑框。4.3 仿真结果的定量验证修复效果不能只看视觉。工程里的comp_df.bmp可以用局部标准差衡量纹理恢复判定标准是过曝区局域方差接近非过曝区std_local stdfilt(rgb2gray(out)); mask imdilate(I_gray 0.85, strel(disk, 3)); recovered_texture mean(std_local(mask)); % 修复区局部标准差 baseline_texture mean(std_local(~mask)); % 正常区局部标准差如果recovered_texture明显低于baseline_texture说明修复区域被过度平滑需要把sigma调小并提高w如果两者接近说明边缘纹理恢复比较理想。另一个快速检查是看直方图右侧修复后的直方图应消除255处的长柱状堆积峰值右移到220到250之间同时暗部不过度前移。仿真中还有一个容易踩的坑train.bmp的像素类型是uint8后续所有阈值判断都依赖[0,1]浮点亮度。必须通过im2double转换而不是double()后者只改变数值类型不改变范围会让掩码全图误判。如果发现掩码全亮或全黑优先检查这一步。5. 让修复结果更自然的三个调参技巧5.1 sigma按过曝面积自适应主脚本里sigma按图像尺寸取max(size(I))/30但更稳的做法是根据mask面积调整。当mask占比超过20%说明过曝区域很大sigma要加大到短边长度的1/20让光照平滑范围足以覆盖整块高光区域mask占比小于5%时过曝区是孤立小斑块sigma反而要缩小到1/50避免小区域被周围正常亮度带偏。5.2 双阈值掩码代替单阈值单阈值0.85容易产生生硬边界双阈值可以同时保住高光核心和过渡带mask_hard I_gray 0.92; % 高光核心区 mask_all imdilate(mask_hard, strel(disk, 7));核心区用高阈值严格圈定过渡区由膨胀自动覆盖重建时对mask_all进行处理但gamma修正只作用于mask_hard这样过渡带里外侧过度平滑的现象会被明显抑制。相比单阈值加固定膨胀双阈值方案对图像内容变化的适应力更强。5.3 用HSV通道快速预检参数三通道完整跑一遍在大图上耗时明显快速标定参数的做法是转到HSV空间只重建V通道hsv rgb2hsv(O); hsv(:, :, 3) reconstructed_v; % 只替换亮度通道 O_preview hsv2rgb(hsv);V通道单独重建的速度接近完整流程的1/3适合批处理时快速确认sigma和w的取值方向。预检确认视觉质量后再用三通道完整流程输出正式结果。参数位置快速标定范围sigma反射分量分离短边/20 ~ 短边/50w光照融合权重0.6 ~ 0.9T过曝阈值0.78 ~ 0.92这套参数组合在遇到新的过曝图像时可以先按表中范围试跑一轮再根据comp_df.bmp与train.bmp的直方图差异决定向哪个方向收敛。本文还有配套的精品资源点击获取