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

142 lines
5.0 KiB
C#

// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件å? HistogramEqualizationProcessor.cs
// æè¿°: 直方图å‡è¡¡åŒ–ç®—å­ï¼Œç”¨äºŽå¢žå¼ºå›¾åƒå¯¹æ¯”度
// 功能:
// - 全局直方图å‡è¡¡åŒ–
// - 自适应直方图å‡è¡¡åŒ–(CLAHEï¼?
// - é™åˆ¶å¯¹æ¯”度增å¼?
// - 改善图åƒçš„æ•´ä½“对比度
// 算法: 直方图å‡è¡¡åŒ–ã€CLAHE
// 作è€? æŽä¼Ÿ wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
using Serilog;
using XP.ImageProcessing.Core;
namespace XP.ImageProcessing.Processors;
/// <summary>
/// 直方图å‡è¡¡åŒ–ç®—å­
/// </summary>
public class HistogramEqualizationProcessor : ImageProcessorBase
{
private static readonly ILogger _logger = Log.ForContext<HistogramEqualizationProcessor>();
public HistogramEqualizationProcessor()
{
Name = LocalizationHelper.GetString("HistogramEqualizationProcessor_Name");
Description = LocalizationHelper.GetString("HistogramEqualizationProcessor_Description");
}
protected override void InitializeParameters()
{
Parameters.Add("Method", new ProcessorParameter(
"Method",
LocalizationHelper.GetString("HistogramEqualizationProcessor_Method"),
typeof(string),
"Global",
null,
null,
LocalizationHelper.GetString("HistogramEqualizationProcessor_Method_Desc"),
new string[] { "Global", "CLAHE" }));
Parameters.Add("ClipLimit", new ProcessorParameter(
"ClipLimit",
LocalizationHelper.GetString("HistogramEqualizationProcessor_ClipLimit"),
typeof(double),
2.0,
1.0,
10.0,
LocalizationHelper.GetString("HistogramEqualizationProcessor_ClipLimit_Desc")));
Parameters.Add("TileSize", new ProcessorParameter(
"TileSize",
LocalizationHelper.GetString("HistogramEqualizationProcessor_TileSize"),
typeof(int),
8,
4,
32,
LocalizationHelper.GetString("HistogramEqualizationProcessor_TileSize_Desc")));
_logger.Debug("InitializeParameters");
}
public override Image<Gray, byte> Process(Image<Gray, byte> inputImage)
{
string method = GetParameter<string>("Method");
double clipLimit = GetParameter<double>("ClipLimit");
int tileSize = GetParameter<int>("TileSize");
Image<Gray, byte> result;
if (method == "CLAHE")
{
result = ApplyCLAHE(inputImage, clipLimit, tileSize);
}
else // Global
{
result = new Image<Gray, byte>(inputImage.Size);
CvInvoke.EqualizeHist(inputImage, result);
}
_logger.Debug("Process: Method = {Method}, ClipLimit = {ClipLimit}, TileSize = {TileSize}",
method, clipLimit, tileSize);
return result;
}
private Image<Gray, byte> ApplyCLAHE(Image<Gray, byte> inputImage, double clipLimit, int tileSize)
{
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);
// 对æ¯ä¸ªtile进行直方图å‡è¡¡åŒ–
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);
// 应用é™åˆ¶ï¼ˆç®€åŒ–版本)
var floatTile = tile.Convert<Gray, float>();
var floatEqualized = equalizedTile.Convert<Gray, float>();
var diff = floatEqualized - floatTile;
var limited = floatTile + diff * Math.Min(clipLimit / 10.0, 1.0);
var limitedByte = limited.Convert<Gray, byte>();
// å¤åˆ¶åˆ°ç»“果图åƒ?
result.ROI = roi;
limitedByte.CopyTo(result);
result.ROI = System.Drawing.Rectangle.Empty;
tile.Dispose();
equalizedTile.Dispose();
floatTile.Dispose();
floatEqualized.Dispose();
diff.Dispose();
limited.Dispose();
limitedByte.Dispose();
}
}
return result;
}
}