
简介面向图像增强研究的MATLAB对比程序整合了SSR、MSR、MSRCR及MSRCP四种经典Retinex算法并配有可视化GUI界面适合高校学生、算法工程师以及图像处理爱好者进行算法效果对比、教学演示或项目选型。整个包共15个文件包括2个.m源码文件、1个.fig界面文件、1个mp4操作演示视频以及11张jpg/png测试图片压缩包大小4.95MB结构紧凑可直接运行。用户只需在MATLAB 2021a及以上版本运行入口脚本通过按钮选择TestImages中的任意图片即可自动输出增强结果免去手动调参的繁琐。多张不同场景的测试图像覆盖室内、室外、风景、人物等类型便于观察算法在不同光照条件下的表现操作演示视频也显著降低了入门门槛。目前已有1331人学习下载适合希望快速上手Retinex系列算法对比和二次开发的MATLAB使用者。1. 四个Retinex变体一套对比代码差异到底在哪在matlab里做图像增强算法对比最容易被问倒的不是怎么跑通SSR而是SSR、MSR、MSRCR、MSRCP四个Retinex变体到底该比什么。它们共用同一个理论起点把图像看成光照与反射的乘积在对数域减掉低频光照改动只发生在尺度数量、色彩恢复方式上输出差异却极其明显。对IT从业者来说这套算法适合课程设计大作业、论文对比实验也适合在给客户做算法选型预研的时候快速判断哪条路线值得深入。尤其是MSRCR和MSRCP名字相近实现路径完全不同很多人跑完发现颜色不对问题大多出在参数域而不是算法本身。下面这套实现顺序能让你用同一个输入图像、同一组sigma把四个算法串起来顺带搞清楚每个参数到底在控制什么。2. SSR和MSR的实现从Retinex公式到matlab里的对数域差分2.1 Retinex核心公式与matlab里“取对数”的原因Retinex把图像建模成入射光与物体反射的乘积取对数后乘性关系变成加性关系于是“分离反射率”就等价于对log图减掉一个估计的光照。光照在空间上变化缓慢用一个高斯低通滤波器平滑后的图像可以当作光照估计。这个基础公式是log(R) log(I) - log(L)其中I是原始图像L是对I做高斯滤波得到的低频估计R就是增强后的反射分量。matlab里实现这一步只需要imfilter加log但有几个细节决定了输出质量。2.1.1 高斯核半径与光照估计精度的关系高斯滤波器的大小直接影响L的平滑程度。常见做法是把核窗口设为round(sigma * 6) 1因为高斯分布的主要能量集中在3倍标准差内窗口太小会截断高斯形状窗口太大会让边界处的滤波结果被无效区域污染。边界处理必须用replicate而不是默认的same。replicate扩展边缘像素而不是补零能避免增强后的图像四周出现一圈明显的黑边。这个细节在单张图上不容易察觉一旦拿去做批量对比四周边框会直接影响后续信息熵和标准差的统计结果。2.2 用matlab复现SSR的最小可运行函数function out retinex_ssr(img, sigma) img im2double(img); % 统一转double保证log运算不溢出 h fspecial(gaussian, round(sigma * 6) 1, sigma); low imfilter(img, h, replicate); log_img log(max(img, 1e-6)); log_low log(max(low, 1e-6)); diff log_img - log_low; out diff - min(diff(:)); out out / max(out(:)); % 拉伸到[0,1]用于显示 end这里对img取max(…, 1e-6)是为了防止纯黑像素取对数产生无穷小这是图像增强代码最常见的崩溃点。log之后的结果可能是负值直接imshow会显示一团黑所以需要先减最小值再除以最大值。sigma参数控制增强尺度。单尺度SSR一般取sigma 80能兼顾局部细节和整体动态范围如果sigma太小比如10以下高频细节被保留但亮部会出现明显的光晕伪影暗部噪声也被一起放大。如果只是快速验证效果SSR适合拿来做基线不适合作为最终输出。2.3 把SSR升级成多尺度MSR三组sigma均衡亮暗MSR的思路是对同一张图用多个sigma分别做SSR再按权重叠加。这样小尺度保留细节中尺度兼顾局部对比大尺度压缩动态范围比单尺度更稳定。经典参数是三尺度等权尺度sigma作用小尺度15保持边缘和纹理细节中尺度80控制局部对比度大尺度250压缩亮度范围避免高光溢出实现时可以封装一个返回对数域原始值的核心函数方便后面MSRCR和MSRCP复用function diff retinex_msr_log(img, sigmas, weights) img im2double(img); diff zeros(size(img)); for k 1:numel(sigmas) h fspecial(gaussian, round(sigmas(k) * 6) 1, sigmas(k)); low imfilter(img, h, replicate); log_img log(max(img, 1e-6)); log_low log(max(low, 1e-6)); diff diff weights(k) * (log_img - log_low); end end这个函数对彩色图像是逐通道处理的保留了对数域的原始差值不做归一化。显示前再拉一次function out retinex_msr(img, sigmas, weights) diff retinex_msr_log(img, sigmas, weights); out diff - min(diff(:)); out out / max(out(:)); end权重数组理论求和应为1默认取[1 1 1]/3。如果想让暗部更亮可以加大尺度150或250的权重比如[0.2 0.3 0.5]如果想让细节更锐利加大尺度15的权重。MSR的最大问题是输出偏灰因为每个通道独立拉伸后通道间的比例关系被破坏了这就是下一章要解决的色彩偏色问题。3. MSRCR的色彩恢复C_i怎么算、怎么调不偏色3.1 为什么MSR输出总是灰蒙蒙的MSR对RGB三个通道分别做对数差分最后又各自独立归一化。这一步相当于把每个通道的均值都拉到一个水平线上通道之间的原始比例被消除结果的饱和度显著下降整体看起来像蒙了一层灰。要恢复颜色需要在MSR的输出上乘一个色彩恢复因子把通道间的差异重新拉出来。MSRCR的名字里多出来的C指的就是这个色彩恢复过程。它不是一个可选的后期滤镜而是直接乘在log域差值上位置很关键。3.2 用matlab实现MSRCR的色彩恢复因子色彩恢复因子的常见形式是对每个像素计算c beta * log(alpha * img255 1) - log(sumc 1);再把这个因子乘到MSR结果上做整体增益和偏移function out retinex_msrcr(img, sigmas, weights, alpha, beta, G, b) img im2double(img); msr retinex_msr_log(img, sigmas, weights); img255 img * 255; % 颜色恢复公式默认输入域是[0,255] sumc sum(img255, 3); c beta * log(alpha * img255 1) - log(sumc 1); out G * c .* msr b; out min(max(out, 0), 255); % 截断域外值而不是再归一化 out out / 255; end调用参数取经典组合r_msrcr retinex_msrcr(img, [15 80 250], [1 1 1]/3, 125, 46, 192, -30);其中G 192是增益b -30是偏移。alpha 125控制色彩恢复的敏感度beta 46控制恢复强度。这组参数来自论文里的经验值对大多数自然图像效果都稳定。3.2.1 参数域不一致是最常见的偏色原因MSR本身不受输入域影响因为log差会把常数缩放抵消掉。但MSRCR的c因子不一样它包含log(alpha * img255 1)和log(sumc 1)这两个真数对图像的数值范围极其敏感。如果先对img做了im2double变成[0,1]区间却仍然用alpha 125相当于把缩放因子缩小了255倍色彩恢复就会失效输出偏灰甚至偏绿。反过来如果在[0,255]域把alpha设成0.5色彩恢复强度会被放大到几乎改变色调的程度。所以写对比程序时一定要统一约定要么全程用[0,255]域要么用img * 255回到原域再算c。3.3 MSRCR最容易碰到的三个异常现象现象原因处理方式输出偏灰色彩恢复无效alpha/beta与输入域不匹配检查是否im2double后没有乘255高光区域过曝细节丢失G过大或b负数过多把G降到96到128之间b改为-10到0暗部出现彩色斑点色彩恢复因子对噪声敏感先做3x3中值滤波再算c第一个异常最常见排查时先打印img255的最大值和c的均值。正常情况下c应该在一个窄范围内波动比如0.8到1.2之间如果出现-5或10这种数量级一定是参数域写错了。第二个异常需要根据直方图观察如果增强后的图像80%像素都聚集在250以上就减少G或者b向正方向调整。4. MSRCP的色度保持思路和两种matlab实现4.1 共享比例MSRCP与MSRCR的本质区别MSRCR用色彩恢复因子校正每个通道MSRCP走的是另一条路先求亮度图在亮度图上做MSR得到新亮度后计算出每个像素的亮度变化比例再把这个比例同时乘到RGB三个通道上。这样做的好处是通道间的比例始终不变色调保持得很自然。实现时核心就一条线function out retinex_msrcp(img, sigmas, weights) img im2double(img); gray rgb2gray(img); % 加权亮度也可用三通道均值 diff retinex_msr_log(gray, sigmas, weights); m diff - min(diff(:)); m m / max(m(:)); % 新亮度归一化到[0,1] ratio m ./ max(gray, 1e-6); % 逐像素亮度变化比例 ratio min(ratio, 3); % 限制比例防止暗部噪声爆炸 out img .* ratio; % RGB三通道共享同一个比例 out min(max(out, 0), 1); endratio大于1的地方变亮小于1的地方变暗。因为三通道乘的是同一个值所以只有亮度改变色调不变。ratio限制在3以内是经验值如果场景暗部本身有细节放大到4或5也不会差太多但一旦超过5暗部噪声会被明显染色。4.2 在HSV空间实现的另一个版本有些场景下用rgb2gray作为亮度图会让红色和蓝色通道的比例偏移因为加权灰度对绿色更友好。另一个常见做法是转到HSV空间只替换V通道hsv rgb2hsv(img); v hsv(:, :, 3); diff retinex_msr_log(v, sigmas, weights); v2 diff - min(diff(:)); v2 v2 / max(v2(:)); hsv(:, :, 3) v2; out hsv2rgb(hsv);这种写法直接把原V通道替换成MSR结果而不是按比例乘回去所以高饱和区域的色彩表现更稳定但暗部噪声也会被一起放大。4.2.1 RGB乘比例和HSV替换V的差异实现方式色调保持方式高光表现暗部噪声RGB共享ratio三通道等比例缩放容易溢出轻微放大HSV替换V通道只改亮度色相饱和度不变过渡更平缓噪声放大更明显亮度通道取法公式适用场景加权灰度0.299R 0.587G 0.114B通用场景最稳定三通道均值(R G B) / 3颜色偏离严重的图像最大值法max(R, G, B)类似光晕效果但过曝敏感实际对比时建议把两种实现都跑一遍观察同一张图里天空边缘色块的过渡区域。MSRCP对雾霾图和夜间低照度图的效果最明显因为它不强行改变颜色比例处理后的图看起来更“像一张正常照片”而不是“一张调色夸张的滤镜图”。4.3 MSRCP的适用场景和溢出问题MSRCP适合三类图雾图、低照度图、航拍图。这三类图共同特点是亮度动态范围大但色彩本身的偏差不大。如果用MSRCR处理雾图天空区域容易出现色斑用MSRCP处理色调基本稳定。溢出问题主要发生在ratio超过3的区域。低照度图的暗部像素接近0分母gray很小ratio计算出来可能上百。这时如果直接把ratio乘上去原来暗部的噪声会变成明显的彩色噪点。解决办法不是去掉ratio上限而是先对原始图像做一次很小的中值滤波再做ratio计算。5. 用matlab搭建四算法对比脚本并计算客观指标5.1 统一入口一次调用四个函数把前面几个函数放到同一个目录下主脚本只要统一读取图像和参数就能得到四组结果clear; close all; img imread(test.png); sigmas [15 80 250]; weights [1 1 1] / 3; r_ssr retinex_ssr(img, 80); r_msr retinex_msr(img, sigmas, weights); r_msrcr retinex_msrcr(img, sigmas, weights, 125, 46, 192, -30); r_msrcp retinex_msrcp(img, sigmas, weights); figure; subplot(1, 5, 1); imshow(img); title(原图); subplot(1, 5, 2); imshow(r_ssr); title(SSR); subplot(1, 5, 3); imshow(r_msr); title(MSR); subplot(1, 5, 4); imshow(r_msrcr); title(MSRCR); subplot(1, 5, 5); imshow(r_msrcp); title(MSRCP);5.1.1 确保输入输出格式一致四个函数的输入都必须是double型在函数内部统一im2double输出统一在[0,1]区间这样imshow和指标计算才不会出现一个需要乘255、一个不需要的混乱情况。对比程序最怕格式不统一结果图看起来一样指标却差了一个量级。5.2 三个无参考指标信息熵、标准差、平均梯度因为没有标准参考图图像增强的效果只能用无参考指标判断。最常用的三个function e img_entropy(gray) p imhist(gray) / numel(gray); p(p 0) []; e -sum(p .* log2(p)); end gray rgb2gray(r_msr); entropy_val img_entropy(gray); std_val std2(gray); [gx, gy] gradient(double(gray)); grad_val mean(gx(:).^2 gy(:).^2);信息熵反映灰度分布的丰富程度熵越高细节越多标准差反映整体对比度标准差太低说明图像发灰平均梯度反映边缘强度梯度大说明边缘清晰。这三个指标在对比表里通常趋势一致但也有例外下面会提到。5.3 四种算法的参数、效果与适用方向对比表算法通道处理方式色彩恢复主要问题适用场景SSR单尺度逐通道log差无细节好但动态范围压缩不足暗部细节提取、快速实验MSR多尺度逐通道加权无偏灰、颜色不饱和亮度增强、动态范围压缩MSRCRMSR后乘色彩恢复因子有颜色饱和参数域敏感、高光易溢出室内暗图、工业相机图像MSRCP亮度图做MSR后共享比例色度保持暗部噪声放大雾图、低照度图、航拍图这个表也是对比程序里最终输出的核心内容建议把每一行对应的指标数字填进去比单独贴四张图更有说服力。5.4 指标高不代表好噪声的分数也会上涨信息熵和标准差有一个共同弱点对噪声特别友好。一张带大量椒盐噪声的图灰度层级变多信息熵反而升高对比度被噪声拉大标准差也变大。所以只看数值会误判噪声图是“好结果”。补充一个边缘保持判断用Sobel边缘占比来衡量e edge(gray, sobel); edge_ratio sum(e(:)) / numel(e);如果两张图的信息熵接近edge_ratio更高的那张通常细节保留更好。如果某张图edge_ratio异常高超过0.3就要怀疑是噪声而不是真实边缘。真实图像的边缘占比一般在0.02到0.15之间。6. 批量自动调参sigmas网格搜索与结果记录6.1 等比例插值网格不要等间隔调试MSR和MSRCP时最耗时间的是确定sigma组合。很多人习惯用等间隔网格比如从10到250每隔30取一个值这样在log尺度上小尺度区域挤了太多点大尺度区域反而稀疏。常见做法是采用几何平均插入中间尺度。给定两个端点sigma中间值取两者的几何平均这样三尺度在log域上均匀分布sPool [10 20 40 80 160 250]; score zeros(numel(sPool), numel(sPool)); for a 1:numel(sPool) for b a1:numel(sPool) sig [sPool(a), round(sqrt(sPool(a) * sPool(b))), sPool(b)]; r retinex_msr(img, sig, [1 1 1] / 3); g rgb2gray(r); score(a, b) img_entropy(g) std2(g); end end imagesc(score);这里用sqrt(sPool(a) * sPool(b))生成中间尺度让三个尺度在log域上等距。score用熵加标准差是在没有参考图情况下最简单的自动筛选方式。6.2 批量跑图时保存每组参数与结果单张图调参意义有限批量验证时要把每组参数和对应指标写进文件方便后续做参数敏感性分析writematrix(score, msr_search_score.csv); writematrix(sPool, msr_search_sigmas.csv);跑批处理时建议把原图文件名也一起记录下来因为不同内容对sigma的偏好差异很大只记参数不记图像分析时会对不上号。6.3 让结果可复现记录sigmas组合和随机种子网格搜索完成之后把选出的最优参数回填到对比脚本里替换掉写死的[15 80 250]。同时建议把matlab版本、图像工具箱版本一并记录在结果文件头部因为不同版本的高斯滤波实现会引入微小数值差异虽不影响视觉判断却会影响论文里的指标小数点。记录方式可以直接用writetable写一个meta表列名分别是sigma1、sigma2、sigma3、entropy、std、edge_ratio后面再写对比章节时直接引用这张表即可。本文还有配套的精品资源点击获取