// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: RetinexProcessor.cs
// 描述: 基于Retinex的多尺度阴影校正算子(支持 8 位 / 16 位)
// 功能:
// - 单尺度Retinex (SSR)
// - 多尺度Retinex (MSR)
// - 带色彩恢复的多尺度Retinex (MSRCR)
// - 光照不均匀校正
// - 阴影去除
// 算法: Retinex理论 - 将图像分解为反射分量和光照分量
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
namespace XP.ImageProcessing.Processors;
///
/// Retinex多尺度阴影校正算子(支持 8 位和 16 位灰度图像)。
/// 全程在浮点对数域运算,输入按位深转 float、输出经 min-max 归一化回目标位深,无 8 位降位损失。
///
public class RetinexProcessor : ImageProcessorBase
where TDepth : struct, IComparable
{
private static readonly ILogger _logger = Log.ForContext>();
public RetinexProcessor()
{
Name = LocalizationHelper.GetString("RetinexProcessor_Name");
Description = LocalizationHelper.GetString("RetinexProcessor_Description");
}
protected override void InitializeParameters()
{
Parameters.Add("Method", new ProcessorParameter(
"Method",
LocalizationHelper.GetString("RetinexProcessor_Method"),
typeof(string),
"MSR",
null,
null,
LocalizationHelper.GetString("RetinexProcessor_Method_Desc"),
new string[] { "SSR", "MSR", "MSRCR" }) { IsAdvanced = true });
Parameters.Add("Sigma1", new ProcessorParameter(
"Sigma1",
LocalizationHelper.GetString("RetinexProcessor_Sigma1"),
typeof(double),
15.0,
1.0,
100.0,
LocalizationHelper.GetString("RetinexProcessor_Sigma1_Desc")) { IsAdvanced = true });
Parameters.Add("Sigma2", new ProcessorParameter(
"Sigma2",
LocalizationHelper.GetString("RetinexProcessor_Sigma2"),
typeof(double),
80.0,
1.0,
200.0,
LocalizationHelper.GetString("RetinexProcessor_Sigma2_Desc")) { IsAdvanced = true });
Parameters.Add("Sigma3", new ProcessorParameter(
"Sigma3",
LocalizationHelper.GetString("RetinexProcessor_Sigma3"),
typeof(double),
250.0,
1.0,
500.0,
LocalizationHelper.GetString("RetinexProcessor_Sigma3_Desc")) { IsAdvanced = true });
Parameters.Add("Gain", new ProcessorParameter(
"Gain",
LocalizationHelper.GetString("RetinexProcessor_Gain"),
typeof(double),
1.0,
0.1,
5.0,
LocalizationHelper.GetString("RetinexProcessor_Gain_Desc")));
Parameters.Add("Offset", new ProcessorParameter(
"Offset",
LocalizationHelper.GetString("RetinexProcessor_Offset"),
typeof(int),
0,
-100,
100,
LocalizationHelper.GetString("RetinexProcessor_Offset_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
public override Image Process(Image inputImage)
{
string method = GetParameter("Method");
double sigma1 = GetParameter("Sigma1");
double sigma2 = GetParameter("Sigma2");
double sigma3 = GetParameter("Sigma3");
double gain = GetParameter("Gain");
int offset = GetParameter("Offset");
// Offset 参数按 8 位语义定义(对数域偏移),16 位无需放大(对数域偏移与位深无关,
// 但为保持与 8 位一致的视觉效果,此处不缩放,仅作用于对数域)
Image result;
if (method == "SSR")
{
result = SingleScaleRetinex(inputImage, sigma2, gain, offset);
}
else if (method == "MSR")
{
result = MultiScaleRetinex(inputImage, new[] { sigma1, sigma2, sigma3 }, gain, offset);
}
else // MSRCR
{
result = MultiScaleRetinexCR(inputImage, new[] { sigma1, sigma2, sigma3 }, gain, offset);
}
_logger.Debug("Process: Method = {Method}, Sigma1 = {Sigma1}, Sigma2 = {Sigma2}, Sigma3 = {Sigma3}, Gain = {Gain}, Offset = {Offset}",
method, sigma1, sigma2, sigma3, gain, offset);
return result;
}
///
/// 单尺度Retinex (SSR)
/// R(x,y) = log(I(x,y)) - log(I(x,y) * G(x,y))
///
private Image SingleScaleRetinex(Image inputImage, double sigma, double gain, int offset)
{
int w = inputImage.Width, h = inputImage.Height;
// 转换为浮点图像并添加小常数避免log(0)
Image floatImage = inputImage.Convert();
floatImage = floatImage + 1.0f;
// 计算log(I)
var logImage = new Image(w, h);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
logImage.Data[y, x, 0] = (float)Math.Log(floatImage.Data[y, x, 0]);
// 高斯模糊得到光照分量
var blurred = new Image(w, h);
int kernelSize = (int)(sigma * 6) | 1;
if (kernelSize < 3) kernelSize = 3;
CvInvoke.GaussianBlur(floatImage, blurred, new System.Drawing.Size(kernelSize, kernelSize), sigma);
// 计算log(I * G)
var logBlurred = new Image(w, h);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
logBlurred.Data[y, x, 0] = (float)Math.Log(blurred.Data[y, x, 0]);
// R = log(I) - log(I*G)
var retinex = logImage - logBlurred;
// 应用增益和偏移
retinex = retinex * gain + offset;
var result = PixelDepthHelper.FromFloatImage(retinex);
floatImage.Dispose();
logImage.Dispose();
blurred.Dispose();
logBlurred.Dispose();
retinex.Dispose();
return result;
}
///
/// 多尺度Retinex (MSR)
/// MSR = Σ(w_i * SSR_i) / N
///
private Image MultiScaleRetinex(Image inputImage, double[] sigmas, double gain, int offset)
{
int w = inputImage.Width, h = inputImage.Height;
Image floatImage = inputImage.Convert();
floatImage = floatImage + 1.0f;
var logImage = new Image(w, h);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
logImage.Data[y, x, 0] = (float)Math.Log(floatImage.Data[y, x, 0]);
var msrResult = new Image(w, h);
msrResult.SetZero();
foreach (double sigma in sigmas)
{
var blurred = new Image(w, h);
int kernelSize = (int)(sigma * 6) | 1;
if (kernelSize < 3) kernelSize = 3;
CvInvoke.GaussianBlur(floatImage, blurred, new System.Drawing.Size(kernelSize, kernelSize), sigma);
var logBlurred = new Image(w, h);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
logBlurred.Data[y, x, 0] = (float)Math.Log(blurred.Data[y, x, 0]);
msrResult = msrResult + (logImage - logBlurred);
blurred.Dispose();
logBlurred.Dispose();
}
msrResult = msrResult / sigmas.Length;
msrResult = msrResult * gain + offset;
var result = PixelDepthHelper.FromFloatImage(msrResult);
floatImage.Dispose();
logImage.Dispose();
msrResult.Dispose();
return result;
}
///
/// 带色彩恢复的多尺度Retinex (MSRCR)
/// 对于灰度图像,使用简化版本
///
private Image MultiScaleRetinexCR(Image inputImage, double[] sigmas, double gain, int offset)
{
int w = inputImage.Width, h = inputImage.Height;
double maxVal = MaxPixelValue;
// 先执行MSR(得到目标位深结果),再转 float 做色彩恢复
var msrResult = MultiScaleRetinex(inputImage, sigmas, gain, offset);
var floatMsr = msrResult.Convert();
var floatInput = inputImage.Convert();
// 色彩恢复因子:基于原始灰度对数(归一化到位深中点)
double logMid = Math.Log(maxVal / 2.0 + 1.0);
var enhanced = new Image(w, h);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
float msr = floatMsr.Data[y, x, 0];
float original = floatInput.Data[y, x, 0];
float c = (float)(Math.Log(original + 1.0) / logMid);
enhanced.Data[y, x, 0] = msr * c;
}
var result = PixelDepthHelper.FromFloatImage(enhanced);
msrResult.Dispose();
floatMsr.Dispose();
floatInput.Dispose();
enhanced.Dispose();
return result;
}
}