212 lines
7.9 KiB
C#
212 lines
7.9 KiB
C#
// ============================================================================
|
|
// Copyright ツゥ 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
|
// 譁�サカ蜷? HierarchicalEnhancementProcessor.cs
|
|
// 謠剰ソー: 螻よャ。蠅槫シコ邂怜ュ撰シ悟渕莠主、壼ーコ蠎ヲ鬮俶民蛻�ァ」蟇ケ荳榊酔蟆コ蠎ヲ扈�鰍迢ャ遶句「槫シ?
|
|
// 蜉溯�:
|
|
// - 蟆�崟蜒丞�隗」荳コ螟壼アらサ�鰍螻?+ 蝓コ遑螻?
|
|
// - 蟇ケ豈丞アらサ�鰍迢ャ遶区而蛻カ蠅樒�?
|
|
// - 謾ッ謖∝渕遑螻ゆコョ蠎ヲ隹�紛蜥悟ッケ豈泌コヲ髯仙�?
|
|
// 邂玲ウ�: 螟壼ーコ蠎ヲ鬮俶民蟾ョ蛻��隗」荳朱㍾蟒コ
|
|
// 菴懆? 譚惹シ� 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 HierarchicalEnhancementProcessor : ImageProcessorBase
|
|
{
|
|
private static readonly ILogger _logger = Log.ForContext<HierarchicalEnhancementProcessor>();
|
|
|
|
public HierarchicalEnhancementProcessor()
|
|
{
|
|
Name = LocalizationHelper.GetString("HierarchicalEnhancementProcessor_Name");
|
|
Description = LocalizationHelper.GetString("HierarchicalEnhancementProcessor_Description");
|
|
}
|
|
|
|
protected override void InitializeParameters()
|
|
{
|
|
Parameters.Add("Levels", new ProcessorParameter(
|
|
"Levels",
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_Levels"),
|
|
typeof(int),
|
|
4,
|
|
2,
|
|
8,
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_Levels_Desc")));
|
|
|
|
Parameters.Add("FineGain", new ProcessorParameter(
|
|
"FineGain",
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_FineGain"),
|
|
typeof(double),
|
|
2.0,
|
|
0.0,
|
|
10.0,
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_FineGain_Desc")));
|
|
|
|
Parameters.Add("MediumGain", new ProcessorParameter(
|
|
"MediumGain",
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_MediumGain"),
|
|
typeof(double),
|
|
1.5,
|
|
0.0,
|
|
10.0,
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_MediumGain_Desc")));
|
|
|
|
Parameters.Add("CoarseGain", new ProcessorParameter(
|
|
"CoarseGain",
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_CoarseGain"),
|
|
typeof(double),
|
|
1.0,
|
|
0.0,
|
|
10.0,
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_CoarseGain_Desc")));
|
|
|
|
Parameters.Add("BaseGain", new ProcessorParameter(
|
|
"BaseGain",
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_BaseGain"),
|
|
typeof(double),
|
|
1.0,
|
|
0.0,
|
|
3.0,
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_BaseGain_Desc")));
|
|
|
|
Parameters.Add("ClipLimit", new ProcessorParameter(
|
|
"ClipLimit",
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_ClipLimit"),
|
|
typeof(double),
|
|
0.0,
|
|
0.0,
|
|
50.0,
|
|
LocalizationHelper.GetString("HierarchicalEnhancementProcessor_ClipLimit_Desc")));
|
|
|
|
_logger.Debug("InitializeParameters");
|
|
}
|
|
|
|
public override Image<Gray, byte> Process(Image<Gray, byte> inputImage)
|
|
{
|
|
int levels = GetParameter<int>("Levels");
|
|
double fineGain = GetParameter<double>("FineGain");
|
|
double mediumGain = GetParameter<double>("MediumGain");
|
|
double coarseGain = GetParameter<double>("CoarseGain");
|
|
double baseGain = GetParameter<double>("BaseGain");
|
|
double clipLimit = GetParameter<double>("ClipLimit");
|
|
|
|
_logger.Debug("Process: Levels={Levels}, Fine={Fine}, Medium={Medium}, Coarse={Coarse}, Base={Base}, Clip={Clip}",
|
|
levels, fineGain, mediumGain, coarseGain, baseGain, clipLimit);
|
|
|
|
int h = inputImage.Height;
|
|
int w = inputImage.Width;
|
|
|
|
// === 螟壼ーコ蠎ヲ鬮俶民蟾ョ蛻��隗」�亥�驛ィ蝨ィ蜴溷ァ句�霎ィ邇�ク頑桃菴懶シ梧裏髴驥大ュ怜。比ク贋ク矩㊦譬キ�� ===
|
|
// 逕ィ騾貞「� sigma 逧�ォ俶民讓。邉顔函謌仙ケウ貊大アょコ丞��哦0(蜴溷崟), G1, G2, ..., G_n(蝓コ遑螻?
|
|
// 扈�鰍螻?D_i = G_i - G_{i+1}
|
|
// 驥榊サコ�嗤utput = sum(D_i * gain_i) + G_n * baseGain
|
|
|
|
// 隶。邂玲ッ丞アら噪鬮俶�?sigma�域欠謨ー騾貞「橸シ?
|
|
var sigmas = new double[levels];
|
|
for (int i = 0; i < levels; i++)
|
|
sigmas[i] = Math.Pow(2, i + 1); // 2, 4, 8, 16, ...
|
|
|
|
// 逕滓�蟷ウ貊大アょコ丞���loat 謨ー扈�シ碁∩蜈?Emgu float Image 逧�琉鬚假シ�
|
|
var smoothLayers = new float[levels + 1][]; // [0]=蜴溷崟, [1..n]=鬮俶民讓。邉�
|
|
smoothLayers[0] = new float[h * w];
|
|
var srcData = inputImage.Data;
|
|
Parallel.For(0, h, y =>
|
|
{
|
|
int row = y * w;
|
|
for (int x = 0; x < w; x++)
|
|
smoothLayers[0][row + x] = srcData[y, x, 0];
|
|
});
|
|
|
|
for (int i = 0; i < levels; i++)
|
|
{
|
|
int ksize = ((int)(sigmas[i] * 3)) | 1; // 遑ョ菫晏・�焚
|
|
if (ksize < 3) ksize = 3;
|
|
|
|
using var src = new Image<Gray, byte>(w, h);
|
|
// 莉惹ク贋ク螻?float 霓?byte 蛛夐ォ俶民讓。邉?
|
|
var prevLayer = smoothLayers[i];
|
|
var sd = src.Data;
|
|
Parallel.For(0, h, y =>
|
|
{
|
|
int row = y * w;
|
|
for (int x = 0; x < w; x++)
|
|
sd[y, x, 0] = (byte)Math.Clamp((int)Math.Round(prevLayer[row + x]), 0, 255);
|
|
});
|
|
|
|
using var dst = new Image<Gray, byte>(w, h);
|
|
CvInvoke.GaussianBlur(src, dst, new System.Drawing.Size(ksize, ksize), sigmas[i]);
|
|
|
|
smoothLayers[i + 1] = new float[h * w];
|
|
var dd = dst.Data;
|
|
var nextLayer = smoothLayers[i + 1];
|
|
Parallel.For(0, h, y =>
|
|
{
|
|
int row = y * w;
|
|
for (int x = 0; x < w; x++)
|
|
nextLayer[row + x] = dd[y, x, 0];
|
|
});
|
|
}
|
|
|
|
// === 隶。邂怜「樒寢謠貞シ蟷カ逶エ謗・驥榊サコ ===
|
|
var gains = new double[levels];
|
|
for (int i = 0; i < levels; i++)
|
|
{
|
|
double t = levels <= 1 ? 0.0 : (double)i / (levels - 1);
|
|
if (t <= 0.5)
|
|
{
|
|
double t2 = t * 2.0;
|
|
gains[i] = fineGain * (1.0 - t2) + mediumGain * t2;
|
|
}
|
|
else
|
|
{
|
|
double t2 = (t - 0.5) * 2.0;
|
|
gains[i] = mediumGain * (1.0 - t2) + coarseGain * t2;
|
|
}
|
|
}
|
|
|
|
// 驥榊サコ�嗤utput = baseGain * G_n + sum(gain_i * (G_i - G_{i+1}))
|
|
float fBaseGain = (float)baseGain;
|
|
float fClip = (float)clipLimit;
|
|
var baseLayerData = smoothLayers[levels];
|
|
|
|
var result = new Image<Gray, byte>(w, h);
|
|
var resultData = result.Data;
|
|
|
|
// 鬚�スャ謐?gains 荳?float
|
|
var fGains = new float[levels];
|
|
for (int i = 0; i < levels; i++)
|
|
fGains[i] = (float)gains[i];
|
|
|
|
Parallel.For(0, h, y =>
|
|
{
|
|
int row = y * w;
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
int idx = row + x;
|
|
float val = baseLayerData[idx] * fBaseGain;
|
|
|
|
for (int i = 0; i < levels; i++)
|
|
{
|
|
float detail = smoothLayers[i][idx] - smoothLayers[i + 1][idx];
|
|
detail *= fGains[i];
|
|
if (fClip > 0)
|
|
detail = Math.Clamp(detail, -fClip, fClip);
|
|
val += detail;
|
|
}
|
|
|
|
resultData[y, x, 0] = (byte)Math.Clamp((int)Math.Round(val), 0, 255);
|
|
}
|
|
});
|
|
|
|
_logger.Debug("Process completed: {Levels} levels, output={W}x{H}", levels, w, h);
|
|
return result;
|
|
}
|
|
} |