7768c086ea
高级参数标记:Contrast(Contrast,Brightness,UseCLAHE,ClipLimit)、 Gamma(Gain)、Sharpen(Method,KernelSize)、Emboss(BlendRatio,GrayOffset)、 HistogramEq(Method,TileSize)、HDR(Method,Saturation,SigmaSpace,SigmaColor,Bias)、 Hierarchical(BaseGain,ClipLimit)、Retinex(Method,Sigma1/2/3,Offset)、 Rotate(ExpandCanvas,BackgroundValue,Interpolation) 默认值优化(适配平面CT DR图像): - Sharpen: Laplacian→UnsharpMask(对噪声更温和) - HistogramEq: Global→CLAHE(局部对比度更优) - Contrast: AutoContrast默认开启
137 lines
5.3 KiB
C#
137 lines
5.3 KiB
C#
// ============================================================================
|
|
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
|
// 文件名: ContrastProcessor.cs
|
|
// 描述: 对比度调整算子,用于增强图像对比度
|
|
// 功能:
|
|
// - 线性对比度和亮度调整
|
|
// - 自动对比度拉伸
|
|
// - CLAHE(对比度受限自适应直方图均衡化)
|
|
// - 支持多种对比度增强方法
|
|
// 算法: 线性变换、直方图均衡化、CLAHE
|
|
// 作者: 李伟 wei.lw.li@hexagon.com
|
|
// ============================================================================
|
|
|
|
using Emgu.CV;
|
|
using Emgu.CV.Structure;
|
|
using XP.ImageProcessing.Core;
|
|
using Serilog;
|
|
using System.Drawing;
|
|
|
|
namespace XP.ImageProcessing.Processors;
|
|
|
|
/// <summary>
|
|
/// 对比度调整算子(支持 8 位和 16 位灰度图像)
|
|
/// </summary>
|
|
public class ContrastProcessor<TDepth> : ImageProcessorBase<TDepth>
|
|
where TDepth : struct, IComparable
|
|
{
|
|
private static readonly ILogger _logger = Log.ForContext<ContrastProcessor<TDepth>>();
|
|
|
|
public ContrastProcessor()
|
|
{
|
|
Name = LocalizationHelper.GetString("ContrastProcessor_Name");
|
|
Description = LocalizationHelper.GetString("ContrastProcessor_Description");
|
|
}
|
|
|
|
protected override void InitializeParameters()
|
|
{
|
|
Parameters.Add("Contrast", new ProcessorParameter(
|
|
"Contrast",
|
|
LocalizationHelper.GetString("ContrastProcessor_Contrast"),
|
|
typeof(double),
|
|
1.0,
|
|
0.1,
|
|
3.0,
|
|
LocalizationHelper.GetString("ContrastProcessor_Contrast_Desc")) { IsAdvanced = true });
|
|
|
|
Parameters.Add("Brightness", new ProcessorParameter(
|
|
"Brightness",
|
|
LocalizationHelper.GetString("ContrastProcessor_Brightness"),
|
|
typeof(int),
|
|
0,
|
|
-100,
|
|
100,
|
|
LocalizationHelper.GetString("ContrastProcessor_Brightness_Desc")) { IsAdvanced = true });
|
|
|
|
Parameters.Add("AutoContrast", new ProcessorParameter(
|
|
"AutoContrast",
|
|
LocalizationHelper.GetString("ContrastProcessor_AutoContrast"),
|
|
typeof(bool),
|
|
true,
|
|
null,
|
|
null,
|
|
LocalizationHelper.GetString("ContrastProcessor_AutoContrast_Desc")));
|
|
|
|
Parameters.Add("UseCLAHE", new ProcessorParameter(
|
|
"UseCLAHE",
|
|
LocalizationHelper.GetString("ContrastProcessor_UseCLAHE"),
|
|
typeof(bool),
|
|
false,
|
|
null,
|
|
null,
|
|
LocalizationHelper.GetString("ContrastProcessor_UseCLAHE_Desc")) { IsAdvanced = true });
|
|
|
|
Parameters.Add("ClipLimit", new ProcessorParameter(
|
|
"ClipLimit",
|
|
LocalizationHelper.GetString("ContrastProcessor_ClipLimit"),
|
|
typeof(double),
|
|
2.0,
|
|
1.0,
|
|
10.0,
|
|
LocalizationHelper.GetString("ContrastProcessor_ClipLimit_Desc")) { IsAdvanced = true });
|
|
_logger.Debug("InitializeParameters");
|
|
}
|
|
|
|
public override Image<Gray, TDepth> Process(Image<Gray, TDepth> inputImage)
|
|
{
|
|
double contrast = GetParameter<double>("Contrast");
|
|
int brightness = GetParameter<int>("Brightness");
|
|
bool autoContrast = GetParameter<bool>("AutoContrast");
|
|
bool useCLAHE = GetParameter<bool>("UseCLAHE");
|
|
double clipLimit = GetParameter<double>("ClipLimit");
|
|
|
|
// 16 位时 brightness 按比例放大
|
|
if (typeof(TDepth) == typeof(ushort)) brightness *= 256;
|
|
|
|
if (useCLAHE)
|
|
{
|
|
// OpenCV CLAHE 原生支持 CV_8UC1 和 CV_16UC1,直接按位深处理,无需降位
|
|
var result = new Image<Gray, TDepth>(inputImage.Size);
|
|
CvInvoke.CLAHE(inputImage, clipLimit, new Size(8, 8), result);
|
|
_logger.Debug("Process (CLAHE native {Depth}bit): ClipLimit={ClipLimit}",
|
|
typeof(TDepth) == typeof(ushort) ? 16 : 8, clipLimit);
|
|
return result;
|
|
}
|
|
else if (autoContrast)
|
|
{
|
|
return AutoContrastStretch(inputImage);
|
|
}
|
|
else
|
|
{
|
|
// 线性对比度:result = input * contrast + brightness
|
|
var floatImg = inputImage.Convert<Gray, float>();
|
|
var result = floatImg * contrast;
|
|
if (brightness != 0) result = result + brightness;
|
|
floatImg.Dispose();
|
|
var r = PixelDepthHelper.FromFloatImageClamped<TDepth>(result);
|
|
result.Dispose();
|
|
_logger.Debug("Process: Contrast={C}, Brightness={B}", contrast, brightness);
|
|
return r;
|
|
}
|
|
}
|
|
|
|
private Image<Gray, TDepth> AutoContrastStretch(Image<Gray, TDepth> inputImage)
|
|
{
|
|
var floatImage = inputImage.Convert<Gray, float>();
|
|
double minVal = 0, maxVal = 0;
|
|
System.Drawing.Point minLoc = new System.Drawing.Point(), maxLoc = new System.Drawing.Point();
|
|
CvInvoke.MinMaxLoc(floatImage, ref minVal, ref maxVal, ref minLoc, ref maxLoc);
|
|
|
|
if (maxVal > minVal)
|
|
floatImage = (floatImage - minVal) * ((double)MaxPixelValue / (maxVal - minVal));
|
|
|
|
_logger.Debug("AutoContrastStretch: min={Min}, max={Max}", minVal, maxVal);
|
|
return PixelDepthHelper.FromFloatImage<TDepth>(floatImage);
|
|
}
|
|
}
|