Retinex 的核心假设:
I(x,y) = 光照L(x,y) × 反射率R(x,y)
增强的本质是:用高斯模糊估计低频光照L,然后在对数域把L"减掉",得到细节反射R,最后再做增益/偏置拉伸。
一、完整可运行代码
1.1 主文件:retinex_ssr_msr.m
matlab
%% SSR / MSR 图像增强(单尺度 & 多尺度 Retinex)
% 不使用任何搜索/下载工具箱函数
clear; clc; close all;
%% 1. 读图
img = imread('lena.png'); % ← 换成你的图
if isa(img,'uint8'), img = double(img)/255; end
if size(img,3)==1, img = repmat(img,[1 1 3]); end % 灰度伪造三通道显示用
%% 2. 参数
sigma_ssr = 80; % SSR 高斯尺度(大=光照更平滑,去雾感更强;小=保留更多局部)
sigmas_msr = [15, 80, 250]; % MSR 三个尺度(小/中/大)
weights_msr = [1 1 1]/3; % 权重和=1
gain = 1.0; % 最终对比度拉伸增益
bias = 0.0; % 亮度偏移(-0.2~0.3)
epsilon = 1e-6;
%% 3. SSR
img_ssr = single_scale_retinex(img, sigma_ssr, epsilon);
%% 4. MSR
img_msr = multi_scale_retinex(img, sigmas_msr, weights_msr, epsilon);
%% 5. 后处理:增益/offset + 色调再现(简单S形可选)
img_ssr = retinex_postprocess(img_ssr, gain, bias);
img_msr = retinex_postprocess(img_msr, gain, bias);
%% 6. 显示
figure('Color','w','Position',[80 80 1300 430]);
subplot(1,3,1); imshow(img); title('原图');
subplot(1,3,2); imshow(img_ssr); title(sprintf('SSR σ=%.0f',sigma_ssr));
subplot(1,3,3); imshow(img_msr); title('MSR [15,80,250]');
sgtitle('SSR / MSR Retinex 图像增强(纯手写)','FontSize',13,'FontWeight','bold');
%% 直方图对比(可选)
figure('Color','w','Position',[80 530 1300 260]);
subplot(1,3,1); imhist(uint8(img*255)); title('原图直方图'); xlim([0 255]); grid on
subplot(1,3,2); imhist(uint8(img_ssr*255)); title('SSR 直方图'); xlim([0 255]); grid on
subplot(1,3,3); imhist(uint8(img_msr*255)); title('MSR 直方图'); xlim([0 255]); grid on
1.2 SSR 核心函数:single_scale_retinex.m
matlab
function out = single_scale_retinex(img, sigma, eps_val)
% SSR: log(R) = log(I) - log(L), L = I * Gσ
% 输入 img H×W×3 double [0,1]
% 输出 out 同尺寸,double,可正可负(后续需后处理)
[H,W,C] = size(img);
out = zeros(H,W,C);
for c = 1:C
I = img(:,:,c);
I = max(I, eps_val);
% 高斯滤波估计光照L(手工二维高斯,无fspecial/fspecial调用)
L = gaussian_blur_2d(I, sigma);
L = max(L, eps_val);
% 对数域分离
out(:,:,c) = log(I) - log(L);
end
end
1.3 手工二维高斯模糊(最核心:你不让我用imgaussfilt/fspecial也完全成立)
matlab
function J = gaussian_blur_2d(I, sigma)
% 可分离二维高斯:先行再列(稳定+快)
% I: H×W double
[H,W] = size(I);
half = ceil(3*sigma);
x = (-half:half);
g1 = exp(-0.5*(x/sigma).^2);
g1 = g1 / sum(g1);
% 行方向
Jtmp = zeros(H,W);
for r = 1:H
Jtmp(r,:) = conv(Jtmp(r,:), g1, 'same');
end
% 列方向
J = zeros(H,W);
for c = 1:W
J(:,c) = conv(Jtmp(:,c), g1, 'same');
end
end
如果你担心边界振铃,把
conv(x,g1,'same')改成手动镜像填充(pad)再conv2(...,'valid')也行;但对Retinex来说'same'一般够用。
1.4 MSR 多尺度加权平均:multi_scale_retinex.m
matlab
function out = multi_scale_retinex(img, sigmas, weights, eps_val)
% MSR: 每个尺度做SSR,然后加权求和
% weights 和=1
[H,W,C] = size(img);
out = zeros(H,W,C);
N = length(sigmas);
for s = 1:N
tmp = single_scale_retinex(img, sigmas(s), eps_val);
out = out + weights(s) * tmp;
end
end
1.5 后处理(把Retinex的输出拉回0,1并给对比度)
matlab
function out = retinex_postprocess(R, gain, bias)
% R: Retinex输出(可能负,动态范围不一定好看)
% 策略:逐通道做 min-max stretch(最稳)
out = R;
[H,W,C] = size(R);
for c = 1:C
rc = R(:,:,c);
mn = min(rc(:)); mx = max(rc(:));
if mx-mn < 1e-8
out(:,:,c) = 0.5;
else
out(:,:,c) = (rc - mn)/(mx - mn);
end
end
% 可选:轻微Sigmoid防止过冲(去掉注释启用)
% out = 1./(1+exp(-gain*(out-0.5))) * 1.0;
% 最后线性增益/偏置
out = min(max(out*gain + bias, 0), 1);
end
二、运行效果你会看到什么
| 版本 | 视觉效果 | 适合场景 |
|---|---|---|
| SSR(σ大≈80~120) | 像"去不均匀光照/去雾" | 背光人脸、内窥镜、隧道监控 |
| SSR(σ小≈15~30) | 更像锐化+微对比增强 | 纹理增强 |
| MSR(15/80/250) | 同时保留细纹理 + 压制大尺度光照 | 最通用,但颜色会偏一点 |
颜色偏的原因:上面是对 RGB 三通道各自独立做 log(I)-log(L),L 的尺度是按亮度走的,但 R/G/B 的增益并不全等 ⇒ 色相漂移。
参考代码 SSR单尺度图像增强 MSR多尺度Retinex图像增强模型 www.youwenfan.com/contentcsv/81227.html
三、改进版:在 Intensity 通道 做 Retinex
把 RGB→HSV/HSI,只增强 V/I,再回去:
matlab
function out = retinex_on_intensity(img, sigma, eps_val, gain, bias)
% img: H×W×3 double [0,1]
% 用 HSV 的 V 通道(最安全)
[H,W,C] = size(img);
rgb = img;
% RGB→HSV 手工(不用rgb2hsv)
V = max(img,[],3); % intensity(最朴素;更好用 I=0.299R+0.587G+0.114B)
% 若你坚持用加权亮度:
% I = 0.299*img(:,:,1)+0.587*img(:,:,2)+0.114*img(:,:,3);
I = V; I = max(I,eps_val);
L = gaussian_blur_2d(I, sigma); L = max(L,eps_val);
R = log(I) - log(L); % 反射率对数
% 拉回[0,1]
mn=min(R(:)); mx=max(R(:));
Rmap = (R-mn)/(mx-mn+eps_val);
% 重新上色:把Rmap当成新的V缩放回原色
out = img;
scale = Rmap ./ (I + eps_val); % 关键:逐像素乘回原通道(保色调)
for c=1:3
out(:,:,c) = min(max(out(:,:,c) .* scale, 0),1);
end
out = min(max(out*gain+bias,0),1);
end