// ============================================================================ // 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; } }