Files
XplorePlane/XP.ImageProcessing.Processors/图像增强/RetinexProcessor.cs
T
2026-04-13 14:36:18 +08:00

320 lines
11 KiB
C#

// ============================================================================
// 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 Serilog;
using XP.ImageProcessing.Core;
namespace XP.ImageProcessing.Processors;
/// <summary>
/// Retinex多尺度阴影校正算�
/// </summary>
public class RetinexProcessor : ImageProcessorBase
{
private static readonly ILogger _logger = Log.ForContext<RetinexProcessor>();
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<Gray, byte> Process(Image<Gray, byte> inputImage)
{
string method = GetParameter<string>("Method");
double sigma1 = GetParameter<double>("Sigma1");
double sigma2 = GetParameter<double>("Sigma2");
double sigma3 = GetParameter<double>("Sigma3");
double gain = GetParameter<double>("Gain");
int offset = GetParameter<int>("Offset");
Image<Gray, byte> 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;
}
/// <summary>
/// å•尺度Retinex (SSR)
/// R(x,y) = log(I(x,y)) - log(I(x,y) * G(x,y))
/// </summary>
private Image<Gray, byte> SingleScaleRetinex(Image<Gray, byte> inputImage, double sigma, double gain, int offset)
{
// 转æ¢ä¸ºæµ®ç‚¹å›¾åƒå¹¶æ·»åŠ å°å¸¸æ•°é¿å…log(0)
Image<Gray, float> floatImage = inputImage.Convert<Gray, float>();
floatImage = floatImage + 1.0f;
// 计算log(I)
Image<Gray, float> logImage = new Image<Gray, float>(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<Gray, float> blurred = new Image<Gray, float>(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<Gray, float> logBlurred = new Image<Gray, float>(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<Gray, float> retinex = logImage - logBlurred;
// 应用增益和åç§?
retinex = retinex * gain + offset;
// 归一化到0-255
Image<Gray, byte> result = NormalizeToByteImage(retinex);
floatImage.Dispose();
logImage.Dispose();
blurred.Dispose();
logBlurred.Dispose();
retinex.Dispose();
return result;
}
/// <summary>
/// 多尺度Retinex (MSR)
/// MSR = Σ(w_i * SSR_i) / N
/// </summary>
private Image<Gray, byte> MultiScaleRetinex(Image<Gray, byte> inputImage, double[] sigmas, double gain, int offset)
{
// 转æ¢ä¸ºæµ®ç‚¹å›¾åƒ?
Image<Gray, float> floatImage = inputImage.Convert<Gray, float>();
floatImage = floatImage + 1.0f;
// 计算log(I)
Image<Gray, float> logImage = new Image<Gray, float>(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<Gray, float> msrResult = new Image<Gray, float>(inputImage.Size);
msrResult.SetZero();
foreach (double sigma in sigmas)
{
// 高斯模糊
Image<Gray, float> blurred = new Image<Gray, float>(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<Gray, float> logBlurred = new Image<Gray, float>(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<Gray, byte> result = NormalizeToByteImage(msrResult);
floatImage.Dispose();
logImage.Dispose();
msrResult.Dispose();
return result;
}
/// <summary>
/// 带色彩æ¢å¤çš„多尺度Retinex (MSRCR)
/// 对于ç°åº¦å›¾åƒï¼Œä½¿ç”¨ç®€åŒ–版æœ?
/// </summary>
private Image<Gray, byte> MultiScaleRetinexCR(Image<Gray, byte> inputImage, double[] sigmas, double gain, int offset)
{
// 先执行MSR
Image<Gray, byte> msrResult = MultiScaleRetinex(inputImage, sigmas, gain, offset);
// 对于ç°åº¦å›¾åƒï¼Œè‰²å½©æ¢å¤ç®€åŒ–为对比度增å¼?
Image<Gray, float> floatMsr = msrResult.Convert<Gray, float>();
Image<Gray, float> floatInput = inputImage.Convert<Gray, float>();
// 简å•的色彩æ¢å¤ï¼šå¢žå¼ºå±€éƒ¨å¯¹æ¯”度
Image<Gray, float> enhanced = new Image<Gray, float>(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<Gray, byte> result = NormalizeToByteImage(enhanced);
msrResult.Dispose();
floatMsr.Dispose();
floatInput.Dispose();
enhanced.Dispose();
return result;
}
/// <summary>
/// 归一化浮点图åƒåˆ°å­—节图åƒ
/// </summary>
private Image<Gray, byte> NormalizeToByteImage(Image<Gray, float> 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<Gray, byte> result = new Image<Gray, byte>(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;
}
}