Files
XplorePlane/XP.ImageProcessing.Processors/图像增强/HistogramEqualizationProcessor.cs
T
wei.lw.li 7768c086ea feat: 图像增强/变换算子标记高级参数+默认值优化
高级参数标记: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默认开启
2026-08-12 09:25:34 +08:00

183 lines
7.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: HistogramEqualizationProcessor.cs
// 描述: 直方图均衡化算子,用于增强图像对比度(原生 8 位 / 16 位实现)
// 功能:
// - 全局直方图均衡化(8 位走 OpenCV16 位走原生 65536-bin CDF 映射)
// - 自适应直方图均衡化(CLAHEOpenCV 原生支持 8U / 16U
// - 限制对比度增强
// - 改善图像的整体对比度
// 算法: 直方图均衡化、CLAHE
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
namespace XP.ImageProcessing.Processors;
/// <summary>
/// 直方图均衡化算子(支持 8 位和 16 位灰度图像)。
/// 16 位全局均衡化在完整位深(0-65535)上直接做累积分布映射,
/// 避免降位到 8 位后往返带来的梳状(离散间隔放大)问题。
/// </summary>
public class HistogramEqualizationProcessor<TDepth> : ImageProcessorBase<TDepth>
where TDepth : struct, IComparable
{
private static readonly ILogger _logger = Log.ForContext<HistogramEqualizationProcessor<TDepth>>();
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),
"CLAHE",
null,
null,
LocalizationHelper.GetString("HistogramEqualizationProcessor_Method_Desc"),
new string[] { "Global", "CLAHE" }) { IsAdvanced = true });
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")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
public override Image<Gray, TDepth> Process(Image<Gray, TDepth> inputImage)
{
string method = GetParameter<string>("Method");
double clipLimit = GetParameter<double>("ClipLimit");
int tileSize = GetParameter<int>("TileSize");
if (tileSize < 1) tileSize = 1;
Image<Gray, TDepth> result = method == "CLAHE"
? ApplyClahe(inputImage, clipLimit, tileSize)
: ApplyGlobal(inputImage);
_logger.Debug("Process: Depth = {Depth}, Method = {Method}, ClipLimit = {ClipLimit}, TileSize = {TileSize}",
typeof(TDepth) == typeof(ushort) ? 16 : 8, method, clipLimit, tileSize);
return result;
}
/// <summary>
/// 全局直方图均衡化。8 位调用 OpenCV EqualizeHist16 位使用原生 CDF 映射。
/// </summary>
private Image<Gray, TDepth> ApplyGlobal(Image<Gray, TDepth> inputImage)
{
if (typeof(TDepth) == typeof(byte))
{
var result8 = new Image<Gray, byte>(inputImage.Size);
CvInvoke.EqualizeHist((inputImage as Image<Gray, byte>)!, result8);
return (result8 as Image<Gray, TDepth>)!;
}
return (EqualizeHist16((inputImage as Image<Gray, ushort>)!) as Image<Gray, TDepth>)!;
}
/// <summary>
/// 16 位原生全局直方图均衡化。
/// 在完整 65536 灰度级上统计直方图并做累积分布函数(CDF)映射:
/// newVal = round( (cdf(v) - cdfMin) / (N - cdfMin) * 65535 )
/// 与 OpenCV 8 位 EqualizeHist 采用相同的归一化公式,仅位深不同。
/// </summary>
private Image<Gray, ushort> EqualizeHist16(Image<Gray, ushort> input)
{
const int levels = 65536;
int width = input.Width;
int height = input.Height;
var src = input.Data;
// 1. 统计直方图
var histogram = new long[levels];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
histogram[src[y, x, 0]]++;
// 2. 计算累积分布函数(CDF)并找到最小非零累积值
var cdf = new long[levels];
long cumulative = 0;
long cdfMin = 0;
bool cdfMinFound = false;
for (int i = 0; i < levels; i++)
{
cumulative += histogram[i];
cdf[i] = cumulative;
if (!cdfMinFound && cumulative > 0)
{
cdfMin = cumulative;
cdfMinFound = true;
}
}
long totalPixels = (long)width * height;
// 3. 构建灰度映射查找表(LUT)
var lut = new ushort[levels];
double denominator = totalPixels - cdfMin;
if (denominator <= 0)
{
// 全图单一灰度或退化情况:恒等映射,避免除零
for (int i = 0; i < levels; i++)
lut[i] = (ushort)i;
}
else
{
for (int i = 0; i < levels; i++)
{
double mapped = (cdf[i] - cdfMin) / denominator * (levels - 1);
if (mapped < 0) mapped = 0;
if (mapped > levels - 1) mapped = levels - 1;
lut[i] = (ushort)(mapped + 0.5);
}
}
// 4. 应用映射
var result = new Image<Gray, ushort>(width, height);
var dst = result.Data;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
dst[y, x, 0] = lut[src[y, x, 0]];
return result;
}
/// <summary>
/// CLAHE(对比度受限自适应直方图均衡化)。
/// OpenCV 的 CvInvoke.CLAHE 原生支持 CV_8UC1 与 CV_16UC1,直接按位深处理,无需降位。
/// </summary>
private Image<Gray, TDepth> ApplyClahe(Image<Gray, TDepth> inputImage, double clipLimit, int tileSize)
{
var result = new Image<Gray, TDepth>(inputImage.Size);
var gridSize = new System.Drawing.Size(tileSize, tileSize);
// clipLimit 参数范围 1-10,直接作为 OpenCV clipLimit 使用
CvInvoke.CLAHE(inputImage, clipLimit, gridSize, result);
return result;
}
}