规范类名及命名空间名称
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
// ============================================================================
|
||||
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
||||
// 文件� FilmEffectProcessor.cs
|
||||
// æ��è¿°: 电å�胶片效果算å�ï¼Œæ¨¡æ‹Ÿä¼ ç»ŸX射线胶片的显示效æž?
|
||||
// 功能:
|
||||
// - 窗宽窗�(Window/Level)调�
|
||||
// - 胶片å��转(æ£ç‰?负片ï¼?
|
||||
// - 多�胶片特性曲线(线性�S曲线�对数�指数)
|
||||
// - 边缘增强(模拟胶片�化效果)
|
||||
// - 使用查找表(LUTï¼‰åŠ é€Ÿå¤„ç�?
|
||||
// 算法: 窗宽窗ä½�æ˜ å°„ + 特性曲线å�˜æ�?
|
||||
// 作� �伟 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 FilmEffectProcessor : ImageProcessorBase
|
||||
{
|
||||
private static readonly ILogger _logger = Log.ForContext<FilmEffectProcessor>();
|
||||
private byte[] _lut = new byte[256];
|
||||
|
||||
public FilmEffectProcessor()
|
||||
{
|
||||
Name = LocalizationHelper.GetString("FilmEffectProcessor_Name");
|
||||
Description = LocalizationHelper.GetString("FilmEffectProcessor_Description");
|
||||
}
|
||||
|
||||
protected override void InitializeParameters()
|
||||
{
|
||||
Parameters.Add("WindowCenter", new ProcessorParameter(
|
||||
"WindowCenter",
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter"),
|
||||
typeof(int),
|
||||
128,
|
||||
0,
|
||||
255,
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter_Desc")));
|
||||
|
||||
Parameters.Add("WindowWidth", new ProcessorParameter(
|
||||
"WindowWidth",
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_WindowWidth"),
|
||||
typeof(int),
|
||||
255,
|
||||
1,
|
||||
255,
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_WindowWidth_Desc")));
|
||||
|
||||
Parameters.Add("Invert", new ProcessorParameter(
|
||||
"Invert",
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_Invert"),
|
||||
typeof(bool),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_Invert_Desc")));
|
||||
|
||||
Parameters.Add("Curve", new ProcessorParameter(
|
||||
"Curve",
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_Curve"),
|
||||
typeof(string),
|
||||
"Linear",
|
||||
null,
|
||||
null,
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_Curve_Desc"),
|
||||
new string[] { "Linear", "Sigmoid", "Logarithmic", "Exponential" }));
|
||||
|
||||
Parameters.Add("CurveStrength", new ProcessorParameter(
|
||||
"CurveStrength",
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_CurveStrength"),
|
||||
typeof(double),
|
||||
1.0,
|
||||
0.1,
|
||||
5.0,
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_CurveStrength_Desc")));
|
||||
|
||||
Parameters.Add("EdgeEnhance", new ProcessorParameter(
|
||||
"EdgeEnhance",
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_EdgeEnhance"),
|
||||
typeof(double),
|
||||
0.0,
|
||||
0.0,
|
||||
3.0,
|
||||
LocalizationHelper.GetString("FilmEffectProcessor_EdgeEnhance_Desc")));
|
||||
|
||||
_logger.Debug("InitializeParameters");
|
||||
}
|
||||
|
||||
public override Image<Gray, byte> Process(Image<Gray, byte> inputImage)
|
||||
{
|
||||
int windowCenter = GetParameter<int>("WindowCenter");
|
||||
int windowWidth = GetParameter<int>("WindowWidth");
|
||||
bool invert = GetParameter<bool>("Invert");
|
||||
string curve = GetParameter<string>("Curve");
|
||||
double curveStrength = GetParameter<double>("CurveStrength");
|
||||
double edgeEnhance = GetParameter<double>("EdgeEnhance");
|
||||
|
||||
// 构建查找�
|
||||
BuildLUT(windowCenter, windowWidth, invert, curve, curveStrength);
|
||||
|
||||
// 应用 LUT
|
||||
var result = inputImage.Clone();
|
||||
int width = result.Width;
|
||||
int height = result.Height;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
result.Data[y, x, 0] = _lut[result.Data[y, x, 0]];
|
||||
}
|
||||
}
|
||||
|
||||
// 边缘增强(模拟胶片�化)
|
||||
if (edgeEnhance > 0.01)
|
||||
{
|
||||
using var blurred = inputImage.SmoothGaussian(3);
|
||||
using var detail = new Image<Gray, float>(width, height);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float diff = inputImage.Data[y, x, 0] - blurred.Data[y, x, 0];
|
||||
float enhanced = result.Data[y, x, 0] + (float)(diff * edgeEnhance);
|
||||
result.Data[y, x, 0] = (byte)Math.Clamp((int)enhanced, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Debug("Process: WC={WC}, WW={WW}, Invert={Inv}, Curve={Curve}, Strength={Str}, Edge={Edge}",
|
||||
windowCenter, windowWidth, invert, curve, curveStrength, edgeEnhance);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void BuildLUT(int wc, int ww, bool invert, string curve, double strength)
|
||||
{
|
||||
double halfW = ww / 2.0;
|
||||
double low = wc - halfW;
|
||||
double high = wc + halfW;
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
// 窗宽窗ä½�æ˜ å°„åˆ?[0, 1]
|
||||
double normalized;
|
||||
if (ww <= 1)
|
||||
normalized = i >= wc ? 1.0 : 0.0;
|
||||
else
|
||||
normalized = Math.Clamp((i - low) / (high - low), 0.0, 1.0);
|
||||
|
||||
// 应用特性曲�
|
||||
double mapped = curve switch
|
||||
{
|
||||
"Sigmoid" => ApplySigmoid(normalized, strength),
|
||||
"Logarithmic" => ApplyLogarithmic(normalized, strength),
|
||||
"Exponential" => ApplyExponential(normalized, strength),
|
||||
_ => normalized // Linear
|
||||
};
|
||||
|
||||
// �转(负片效果)
|
||||
if (invert)
|
||||
mapped = 1.0 - mapped;
|
||||
|
||||
_lut[i] = (byte)Math.Clamp((int)(mapped * 255.0), 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>S曲线(Sigmoid):增强ä¸é—´è°ƒå¯¹æ¯”度</summary>
|
||||
private static double ApplySigmoid(double x, double strength)
|
||||
{
|
||||
double k = strength * 10.0;
|
||||
return 1.0 / (1.0 + Math.Exp(-k * (x - 0.5)));
|
||||
}
|
||||
|
||||
/// <summary>对数曲线:�亮暗部,压缩亮部</summary>
|
||||
private static double ApplyLogarithmic(double x, double strength)
|
||||
{
|
||||
double c = strength;
|
||||
return Math.Log(1.0 + c * x) / Math.Log(1.0 + c);
|
||||
}
|
||||
|
||||
/// <summary>指数曲线:压缩暗部,增强亮部</summary>
|
||||
private static double ApplyExponential(double x, double strength)
|
||||
{
|
||||
double c = strength;
|
||||
return (Math.Exp(c * x) - 1.0) / (Math.Exp(c) - 1.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// ============================================================================
|
||||
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
||||
// 文件� PseudoColorProcessor.cs
|
||||
// æ��è¿°: 伪色彩渲染算å�,将ç�°åº¦å›¾åƒ�æ˜ å°„ä¸ºå½©è‰²å›¾åƒ�
|
||||
// 功能:
|
||||
// - 支æŒ�多ç§� OpenCV å†…ç½®è‰²å½©æ˜ å°„è¡¨ï¼ˆJetã€�Hotã€�Coolã€�Rainbow ç‰ï¼‰
|
||||
// - �选�度范围�剪,�出感兴趣的�度区间
|
||||
// - å�¯é€‰å��è½¬è‰²å½©æ˜ å°„æ–¹å�?
|
||||
// 算法: 查找表(LUTï¼‰è‰²å½©æ˜ å°?
|
||||
// 作� �伟 wei.lw.li@hexagon.com
|
||||
// ============================================================================
|
||||
|
||||
using Emgu.CV;
|
||||
using Emgu.CV.CvEnum;
|
||||
using Emgu.CV.Structure;
|
||||
using Serilog;
|
||||
using XP.ImageProcessing.Core;
|
||||
|
||||
namespace XP.ImageProcessing.Processors;
|
||||
|
||||
/// <summary>
|
||||
/// 伪色彩渲染算å?
|
||||
/// </summary>
|
||||
public class PseudoColorProcessor : ImageProcessorBase
|
||||
{
|
||||
private static readonly ILogger _logger = Log.ForContext<PseudoColorProcessor>();
|
||||
|
||||
public PseudoColorProcessor()
|
||||
{
|
||||
Name = LocalizationHelper.GetString("PseudoColorProcessor_Name");
|
||||
Description = LocalizationHelper.GetString("PseudoColorProcessor_Description");
|
||||
}
|
||||
|
||||
protected override void InitializeParameters()
|
||||
{
|
||||
Parameters.Add("ColorMap", new ProcessorParameter(
|
||||
"ColorMap",
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_ColorMap"),
|
||||
typeof(string),
|
||||
"Jet",
|
||||
null,
|
||||
null,
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_ColorMap_Desc"),
|
||||
new string[] { "Jet", "Hot", "Cool", "Rainbow", "HSV", "Turbo", "Inferno", "Magma", "Plasma", "Bone", "Ocean", "Spring", "Summer", "Autumn", "Winter" }));
|
||||
|
||||
Parameters.Add("MinValue", new ProcessorParameter(
|
||||
"MinValue",
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_MinValue"),
|
||||
typeof(int),
|
||||
0,
|
||||
0,
|
||||
255,
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_MinValue_Desc")));
|
||||
|
||||
Parameters.Add("MaxValue", new ProcessorParameter(
|
||||
"MaxValue",
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_MaxValue"),
|
||||
typeof(int),
|
||||
255,
|
||||
0,
|
||||
255,
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_MaxValue_Desc")));
|
||||
|
||||
Parameters.Add("InvertMap", new ProcessorParameter(
|
||||
"InvertMap",
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_InvertMap"),
|
||||
typeof(bool),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
LocalizationHelper.GetString("PseudoColorProcessor_InvertMap_Desc")));
|
||||
|
||||
_logger.Debug("InitializeParameters");
|
||||
}
|
||||
|
||||
public override Image<Gray, byte> Process(Image<Gray, byte> inputImage)
|
||||
{
|
||||
string colorMapName = GetParameter<string>("ColorMap");
|
||||
int minValue = GetParameter<int>("MinValue");
|
||||
int maxValue = GetParameter<int>("MaxValue");
|
||||
bool invertMap = GetParameter<bool>("InvertMap");
|
||||
|
||||
OutputData.Clear();
|
||||
|
||||
// �度范围�剪与归一�
|
||||
Image<Gray, byte> normalized;
|
||||
if (minValue > 0 || maxValue < 255)
|
||||
{
|
||||
// å°?[minValue, maxValue] æ˜ å°„åˆ?[0, 255]
|
||||
normalized = inputImage.Clone();
|
||||
double scale = 255.0 / Math.Max(maxValue - minValue, 1);
|
||||
for (int y = 0; y < normalized.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < normalized.Width; x++)
|
||||
{
|
||||
int val = normalized.Data[y, x, 0];
|
||||
val = Math.Clamp(val, minValue, maxValue);
|
||||
normalized.Data[y, x, 0] = (byte)((val - minValue) * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
normalized = inputImage.Clone();
|
||||
}
|
||||
|
||||
// å��转ç�°åº¦ï¼ˆå��è½¬è‰²å½©æ˜ å°„æ–¹å�‘)
|
||||
if (invertMap)
|
||||
{
|
||||
CvInvoke.BitwiseNot(normalized, normalized);
|
||||
}
|
||||
|
||||
// åº”ç”¨è‰²å½©æ˜ å°„
|
||||
ColorMapType cmType = colorMapName switch
|
||||
{
|
||||
"Hot" => ColorMapType.Hot,
|
||||
"Cool" => ColorMapType.Cool,
|
||||
"Rainbow" => ColorMapType.Rainbow,
|
||||
"HSV" => ColorMapType.Hsv,
|
||||
"Turbo" => ColorMapType.Turbo,
|
||||
"Inferno" => ColorMapType.Inferno,
|
||||
"Magma" => ColorMapType.Magma,
|
||||
"Plasma" => ColorMapType.Plasma,
|
||||
"Bone" => ColorMapType.Bone,
|
||||
"Ocean" => ColorMapType.Ocean,
|
||||
"Spring" => ColorMapType.Spring,
|
||||
"Summer" => ColorMapType.Summer,
|
||||
"Autumn" => ColorMapType.Autumn,
|
||||
"Winter" => ColorMapType.Winter,
|
||||
_ => ColorMapType.Jet
|
||||
};
|
||||
|
||||
using var colorMat = new Mat();
|
||||
CvInvoke.ApplyColorMap(normalized.Mat, colorMat, cmType);
|
||||
|
||||
var colorImage = colorMat.ToImage<Bgr, byte>();
|
||||
|
||||
// 将彩色图åƒ�å˜å…?OutputData,供 UI 显示
|
||||
OutputData["PseudoColorImage"] = colorImage;
|
||||
|
||||
_logger.Debug("Process: ColorMap={ColorMap}, MinValue={Min}, MaxValue={Max}, InvertMap={Invert}",
|
||||
colorMapName, minValue, maxValue, invertMap);
|
||||
|
||||
normalized.Dispose();
|
||||
|
||||
// 返回原始ç�°åº¦å›¾åƒ�(彩色图åƒ�通过 OutputData ä¼ é€’ï¼‰
|
||||
return inputImage.Clone();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user