规范类名及命名空间名称
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
// ============================================================================
|
||||
// Copyright 穢 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
||||
// ��辣�? ContrastProcessor.cs
|
||||
// �讛膩: 撖寞�摨西��渡�摮琜��其�憓𧼮撩�曉�撖寞�摨?
|
||||
// �蠘�:
|
||||
// - 蝥踵�批笆瘥𥪜漲�䔶漁摨西��?
|
||||
// - �芸𢆡撖寞�摨行�隡?
|
||||
// - CLAHE嚗�笆瘥𥪜漲�烾��芷����湔䲮�曉�銵∪�嚗?
|
||||
// - �舀�憭𡁶�撖寞�摨血�撘箸䲮瘜?
|
||||
// 蝞埈�: 蝥踵�批��U��凒�孵㦛��﹛�硔��LAHE
|
||||
// 雿𡏭�? �𦒘� wei.lw.li@hexagon.com
|
||||
// ============================================================================
|
||||
|
||||
using Emgu.CV;
|
||||
using Emgu.CV.Structure;
|
||||
using Serilog;
|
||||
using System.Drawing;
|
||||
using XP.ImageProcessing.Core;
|
||||
|
||||
namespace XP.ImageProcessing.Processors;
|
||||
|
||||
/// <summary>
|
||||
/// 撖寞�摨西��渡�摮?
|
||||
/// </summary>
|
||||
public class ContrastProcessor : ImageProcessorBase
|
||||
{
|
||||
private static readonly ILogger _logger = Log.ForContext<ContrastProcessor>();
|
||||
|
||||
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")));
|
||||
|
||||
Parameters.Add("Brightness", new ProcessorParameter(
|
||||
"Brightness",
|
||||
LocalizationHelper.GetString("ContrastProcessor_Brightness"),
|
||||
typeof(int),
|
||||
0,
|
||||
-100,
|
||||
100,
|
||||
LocalizationHelper.GetString("ContrastProcessor_Brightness_Desc")));
|
||||
|
||||
Parameters.Add("AutoContrast", new ProcessorParameter(
|
||||
"AutoContrast",
|
||||
LocalizationHelper.GetString("ContrastProcessor_AutoContrast"),
|
||||
typeof(bool),
|
||||
false,
|
||||
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")));
|
||||
|
||||
Parameters.Add("ClipLimit", new ProcessorParameter(
|
||||
"ClipLimit",
|
||||
LocalizationHelper.GetString("ContrastProcessor_ClipLimit"),
|
||||
typeof(double),
|
||||
2.0,
|
||||
1.0,
|
||||
10.0,
|
||||
LocalizationHelper.GetString("ContrastProcessor_ClipLimit_Desc")));
|
||||
_logger.Debug("InitializeParameters");
|
||||
}
|
||||
|
||||
public override Image<Gray, byte> Process(Image<Gray, byte> 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");
|
||||
|
||||
var result = inputImage.Clone();
|
||||
|
||||
if (useCLAHE)
|
||||
{
|
||||
result = ApplyCLAHE(inputImage, clipLimit);
|
||||
}
|
||||
else if (autoContrast)
|
||||
{
|
||||
result = AutoContrastStretch(inputImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = inputImage * contrast + brightness;
|
||||
}
|
||||
_logger.Debug("Process: Contrast = {contrast},Brightness = {brightness}," +
|
||||
"AutoContrast = {autoContrast},UseCLAHE = {useCLAHE}, ClipLimit = {clipLimit}", contrast, brightness, autoContrast, useCLAHE, clipLimit);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Image<Gray, byte> AutoContrastStretch(Image<Gray, byte> inputImage)
|
||||
{
|
||||
double minVal = 0, maxVal = 0;
|
||||
Point minLoc = new Point();
|
||||
Point maxLoc = new Point();
|
||||
CvInvoke.MinMaxLoc(inputImage, ref minVal, ref maxVal, ref minLoc, ref maxLoc);
|
||||
|
||||
if (minVal == 0 && maxVal == 255)
|
||||
{
|
||||
return inputImage.Clone();
|
||||
}
|
||||
|
||||
var floatImage = inputImage.Convert<Gray, float>();
|
||||
|
||||
if (maxVal > minVal)
|
||||
{
|
||||
floatImage = (floatImage - minVal) * (255.0 / (maxVal - minVal));
|
||||
}
|
||||
_logger.Debug("AutoContrastStretch");
|
||||
return floatImage.Convert<Gray, byte>();
|
||||
}
|
||||
|
||||
private Image<Gray, byte> ApplyCLAHE(Image<Gray, byte> inputImage, double clipLimit)
|
||||
{
|
||||
int tileSize = 8;
|
||||
int width = inputImage.Width;
|
||||
int height = inputImage.Height;
|
||||
|
||||
int tilesX = (width + tileSize - 1) / tileSize;
|
||||
int tilesY = (height + tileSize - 1) / tileSize;
|
||||
|
||||
var result = new Image<Gray, byte>(width, height);
|
||||
|
||||
for (int ty = 0; ty < tilesY; ty++)
|
||||
{
|
||||
for (int tx = 0; tx < tilesX; tx++)
|
||||
{
|
||||
int x = tx * tileSize;
|
||||
int y = ty * tileSize;
|
||||
int w = Math.Min(tileSize, width - x);
|
||||
int h = Math.Min(tileSize, height - y);
|
||||
|
||||
var roi = new System.Drawing.Rectangle(x, y, w, h);
|
||||
inputImage.ROI = roi;
|
||||
var tile = inputImage.Copy();
|
||||
inputImage.ROI = System.Drawing.Rectangle.Empty;
|
||||
|
||||
var equalizedTile = new Image<Gray, byte>(tile.Size);
|
||||
CvInvoke.EqualizeHist(tile, equalizedTile);
|
||||
|
||||
result.ROI = roi;
|
||||
equalizedTile.CopyTo(result);
|
||||
result.ROI = System.Drawing.Rectangle.Empty;
|
||||
|
||||
tile.Dispose();
|
||||
equalizedTile.Dispose();
|
||||
}
|
||||
}
|
||||
_logger.Debug("ApplyCLAHE");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user