// ============================================================================ // Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved. // 文件名: RetinexProcessor.cs // 描述: 基于Retinex的多尺度阴影校正算子 // 功能: // - 单尺度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多尺度阴影校正算子 /// public class RetinexProcessor : ImageProcessorBase { 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" })); Parameters.Add("Sigma1", new ProcessorParameter( "Sigma1", LocalizationHelper.GetString("RetinexProcessor_Sigma1"), typeof(double), 15.0, 1.0, 100.0, LocalizationHelper.GetString("RetinexProcessor_Sigma1_Desc"))); Parameters.Add("Sigma2", new ProcessorParameter( "Sigma2", LocalizationHelper.GetString("RetinexProcessor_Sigma2"), typeof(double), 80.0, 1.0, 200.0, LocalizationHelper.GetString("RetinexProcessor_Sigma2_Desc"))); Parameters.Add("Sigma3", new ProcessorParameter( "Sigma3", LocalizationHelper.GetString("RetinexProcessor_Sigma3"), typeof(double), 250.0, 1.0, 500.0, LocalizationHelper.GetString("RetinexProcessor_Sigma3_Desc"))); 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"))); _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"); Image result; if (method == "SSR") { // 单尺度Retinex result = SingleScaleRetinex(inputImage, sigma2, gain, offset); } else if (method == "MSR") { // 多尺度Retinex result = MultiScaleRetinex(inputImage, new[] { sigma1, sigma2, sigma3 }, gain, offset); } else // MSRCR { // 带色彩恢复的多尺度Retinex 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) { // 转换为浮点图像并添加小常数避免log(0) Image floatImage = inputImage.Convert(); floatImage = floatImage + 1.0f; // 计算log(I) Image logImage = new Image(inputImage.Size); for (int y = 0; y < inputImage.Height; y++) { for (int x = 0; x < inputImage.Width; x++) { logImage.Data[y, x, 0] = (float)Math.Log(floatImage.Data[y, x, 0]); } } // 高斯模糊得到光照分量 Image blurred = new Image(inputImage.Size); 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) Image logBlurred = new Image(inputImage.Size); for (int y = 0; y < inputImage.Height; y++) { for (int x = 0; x < inputImage.Width; x++) { logBlurred.Data[y, x, 0] = (float)Math.Log(blurred.Data[y, x, 0]); } } // R = log(I) - log(I*G) Image retinex = logImage - logBlurred; // 应用增益和偏移 retinex = retinex * gain + offset; // 归一化到0-255 Image result = NormalizeToByteImage(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) { // 转换为浮点图像 Image floatImage = inputImage.Convert(); floatImage = floatImage + 1.0f; // 计算log(I) Image logImage = new Image(inputImage.Size); for (int y = 0; y < inputImage.Height; y++) { for (int x = 0; x < inputImage.Width; x++) { logImage.Data[y, x, 0] = (float)Math.Log(floatImage.Data[y, x, 0]); } } // 累加多个尺度的结果 Image msrResult = new Image(inputImage.Size); msrResult.SetZero(); foreach (double sigma in sigmas) { // 高斯模糊 Image blurred = new Image(inputImage.Size); 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) Image logBlurred = new Image(inputImage.Size); for (int y = 0; y < inputImage.Height; y++) { for (int x = 0; x < inputImage.Width; x++) { logBlurred.Data[y, x, 0] = (float)Math.Log(blurred.Data[y, x, 0]); } } // 累加 SSR msrResult = msrResult + (logImage - logBlurred); blurred.Dispose(); logBlurred.Dispose(); } // 平均 msrResult = msrResult / sigmas.Length; // 应用增益和偏移 msrResult = msrResult * gain + offset; // 归一化 Image result = NormalizeToByteImage(msrResult); floatImage.Dispose(); logImage.Dispose(); msrResult.Dispose(); return result; } /// /// 带色彩恢复的多尺度Retinex (MSRCR) /// 对于灰度图像,使用简化版本 /// private Image MultiScaleRetinexCR(Image inputImage, double[] sigmas, double gain, int offset) { // 先执行MSR Image msrResult = MultiScaleRetinex(inputImage, sigmas, gain, offset); // 对于灰度图像,色彩恢复简化为对比度增强 Image floatMsr = msrResult.Convert(); Image floatInput = inputImage.Convert(); // 简单的色彩恢复:增强局部对比度 Image enhanced = new Image(inputImage.Size); for (int y = 0; y < inputImage.Height; y++) { for (int x = 0; x < inputImage.Width; x++) { float msr = floatMsr.Data[y, x, 0]; float original = floatInput.Data[y, x, 0]; // 色彩恢复因子 float c = (float)Math.Log(original + 1.0) / (float)Math.Log(128.0); enhanced.Data[y, x, 0] = msr * c; } } Image result = NormalizeToByteImage(enhanced); msrResult.Dispose(); floatMsr.Dispose(); floatInput.Dispose(); enhanced.Dispose(); return result; } /// /// 归一化浮点图像到字节图像 /// private Image NormalizeToByteImage(Image floatImage) { // 找到最小值和最大值 double minVal = double.MaxValue; double maxVal = double.MinValue; for (int y = 0; y < floatImage.Height; y++) { for (int x = 0; x < floatImage.Width; x++) { float val = floatImage.Data[y, x, 0]; if (val < minVal) minVal = val; if (val > maxVal) maxVal = val; } } // 归一化到0-255 Image result = new Image(floatImage.Size); double range = maxVal - minVal; if (range > 0) { for (int y = 0; y < floatImage.Height; y++) { for (int x = 0; x < floatImage.Width; x++) { float val = floatImage.Data[y, x, 0]; int normalized = (int)((val - minVal) / range * 255.0); result.Data[y, x, 0] = (byte)Math.Max(0, Math.Min(255, normalized)); } } } return result; } }