diff --git a/XP.ImageProcessing.Processors/Core/ProcessorParameter.cs b/XP.ImageProcessing.Processors/Core/ProcessorParameter.cs
index 777b0f2e..b0c0fc4c 100644
--- a/XP.ImageProcessing.Processors/Core/ProcessorParameter.cs
+++ b/XP.ImageProcessing.Processors/Core/ProcessorParameter.cs
@@ -45,6 +45,9 @@ public class ProcessorParameter
/// 参数是否可见
public bool IsVisible { get; set; } = true;
+ /// 是否为高级参数(默认折叠隐藏,展开后可见)
+ public bool IsAdvanced { get; set; } = false;
+
public ProcessorParameter(string name, string displayName, Type valueType, object defaultValue,
object? minValue = null, object? maxValue = null, string description = "", string[]? options = null)
{
diff --git a/XP.ImageProcessing.Processors/其他/PseudoColorProcessor.cs b/XP.ImageProcessing.Processors/图像变换/PseudoColorProcessor.cs
similarity index 97%
rename from XP.ImageProcessing.Processors/其他/PseudoColorProcessor.cs
rename to XP.ImageProcessing.Processors/图像变换/PseudoColorProcessor.cs
index 10209c49..addd9b36 100644
--- a/XP.ImageProcessing.Processors/其他/PseudoColorProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像变换/PseudoColorProcessor.cs
@@ -1,155 +1,155 @@
-// ============================================================================
-// 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 XP.ImageProcessing.Core;
-using Serilog;
-
-namespace XP.ImageProcessing.Processors;
-
-///
-/// 伪色彩渲染算子(支持 8 位和 16 位灰度图像)。
-/// 灰度主图按原位深透传(无损失);彩色叠加图因 OpenCV ApplyColorMap 仅支持 CV_8U,
-/// 会将灰度归一化到 8 位后映射(彩色显示固有限制,不影响主数据链路精度)。
-///
-public class PseudoColorProcessor : ImageProcessorBase
- where TDepth : struct, IComparable
-{
- private static readonly ILogger _logger = Log.ForContext>();
-
- 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 Process(Image inputImage)
- {
- string colorMapName = GetParameter("ColorMap");
- int minValue = GetParameter("MinValue");
- int maxValue = GetParameter("MaxValue");
- bool invertMap = GetParameter("InvertMap");
-
- OutputData.Clear();
-
- // 彩色映射只能作用于 8 位:先将输入降到 8 位(彩色显示固有限制)
- using var input8 = PixelDepthHelper.ToByteImage(inputImage);
-
- // 灰度范围裁剪与归一化(MinValue/MaxValue 按 8 位语义 0-255)
- Image normalized;
- if (minValue > 0 || maxValue < 255)
- {
- // 将 [minValue, maxValue] 映射到 [0, 255]
- normalized = input8.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 = input8.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();
-
- // 将彩色图像存入 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();
- }
+// ============================================================================
+// 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 XP.ImageProcessing.Core;
+using Serilog;
+
+namespace XP.ImageProcessing.Processors;
+
+///
+/// 伪色彩渲染算子(支持 8 位和 16 位灰度图像)。
+/// 灰度主图按原位深透传(无损失);彩色叠加图因 OpenCV ApplyColorMap 仅支持 CV_8U,
+/// 会将灰度归一化到 8 位后映射(彩色显示固有限制,不影响主数据链路精度)。
+///
+public class PseudoColorProcessor : ImageProcessorBase
+ where TDepth : struct, IComparable
+{
+ private static readonly ILogger _logger = Log.ForContext>();
+
+ 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 Process(Image inputImage)
+ {
+ string colorMapName = GetParameter("ColorMap");
+ int minValue = GetParameter("MinValue");
+ int maxValue = GetParameter("MaxValue");
+ bool invertMap = GetParameter("InvertMap");
+
+ OutputData.Clear();
+
+ // 彩色映射只能作用于 8 位:先将输入降到 8 位(彩色显示固有限制)
+ using var input8 = PixelDepthHelper.ToByteImage(inputImage);
+
+ // 灰度范围裁剪与归一化(MinValue/MaxValue 按 8 位语义 0-255)
+ Image normalized;
+ if (minValue > 0 || maxValue < 255)
+ {
+ // 将 [minValue, maxValue] 映射到 [0, 255]
+ normalized = input8.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 = input8.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();
+
+ // 将彩色图像存入 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();
+ }
}
\ No newline at end of file
diff --git a/XP.ImageProcessing.Processors/图像变换/RotateProcessor.cs b/XP.ImageProcessing.Processors/图像变换/RotateProcessor.cs
index e1e2dad9..72902919 100644
--- a/XP.ImageProcessing.Processors/图像变换/RotateProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像变换/RotateProcessor.cs
@@ -52,7 +52,7 @@ public class RotateProcessor : ImageProcessorBase
false,
null,
null,
- LocalizationHelper.GetString("RotateProcessor_ExpandCanvas_Desc")));
+ LocalizationHelper.GetString("RotateProcessor_ExpandCanvas_Desc")) { IsAdvanced = true });
Parameters.Add("BackgroundValue", new ProcessorParameter(
"BackgroundValue",
@@ -61,7 +61,7 @@ public class RotateProcessor : ImageProcessorBase
0,
0,
255,
- LocalizationHelper.GetString("RotateProcessor_BackgroundValue_Desc")));
+ LocalizationHelper.GetString("RotateProcessor_BackgroundValue_Desc")) { IsAdvanced = true });
Parameters.Add("Interpolation", new ProcessorParameter(
"Interpolation",
@@ -71,7 +71,7 @@ public class RotateProcessor : ImageProcessorBase
null,
null,
LocalizationHelper.GetString("RotateProcessor_Interpolation_Desc"),
- new string[] { "Nearest", "Bilinear", "Bicubic" }));
+ new string[] { "Nearest", "Bilinear", "Bicubic" }) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/图像变换/ThresholdProcessor.cs b/XP.ImageProcessing.Processors/图像变换/ThresholdProcessor.cs
index b6db5de6..6dd25dbd 100644
--- a/XP.ImageProcessing.Processors/图像变换/ThresholdProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像变换/ThresholdProcessor.cs
@@ -35,20 +35,20 @@ public class ThresholdProcessor : ImageProcessorBase
protected override void InitializeParameters()
{
- // 参数范围必须跟随当前算子的位深。主流程使用 ushort,因此默认阈值也按
- // 16 位满量程计算,避免把 8 位的 64/192 直接套用到 0~65535。
- int quarterValue = MaxPixelValue / 4;
- int threeQuarterValue = MaxPixelValue * 3 / 4;
- Parameters.Add("MinThreshold", new ProcessorParameter(
+ // 参数范围必须跟随当前算子的位深。主流程使用 ushort,因此默认阈值也按
+ // 16 位满量程计算,避免把 8 位的 64/192 直接套用到 0~65535。
+ int quarterValue = MaxPixelValue / 4;
+ int threeQuarterValue = MaxPixelValue * 3 / 4;
+ Parameters.Add("MinThreshold", new ProcessorParameter(
"MinThreshold",
LocalizationHelper.GetString("ThresholdProcessor_MinThreshold"),
- typeof(int), quarterValue, 0, MaxPixelValue,
+ typeof(int), quarterValue, 0, MaxPixelValue,
LocalizationHelper.GetString("ThresholdProcessor_MinThreshold_Desc")));
Parameters.Add("MaxThreshold", new ProcessorParameter(
"MaxThreshold",
LocalizationHelper.GetString("ThresholdProcessor_MaxThreshold"),
- typeof(int), threeQuarterValue, 0, MaxPixelValue,
+ typeof(int), threeQuarterValue, 0, MaxPixelValue,
LocalizationHelper.GetString("ThresholdProcessor_MaxThreshold_Desc")));
Parameters.Add("UseOtsu", new ProcessorParameter(
@@ -128,6 +128,8 @@ public class ThresholdProcessor : ImageProcessorBase
histogram[data[y, x, 0]]++;
long totalPixels = (long)w * h;
+ if (totalPixels == 0) return maxVal / 2;
+
double totalSum = 0;
for (int i = 0; i < levels; i++)
totalSum += (double)i * histogram[i];
@@ -135,7 +137,7 @@ public class ThresholdProcessor : ImageProcessorBase
double bgSum = 0;
long bgPixels = 0;
double maxVariance = -1;
- int bestThreshold = 0;
+ int bestThreshold = maxVal / 2; // Default to midpoint if no valid threshold found
for (int t = 0; t < levels; t++)
{
@@ -159,4 +161,4 @@ public class ThresholdProcessor : ImageProcessorBase
return bestThreshold;
}
-}
+}
diff --git a/XP.ImageProcessing.Processors/图像增强/ContrastProcessor.cs b/XP.ImageProcessing.Processors/图像增强/ContrastProcessor.cs
index 4a57ff54..a8863503 100644
--- a/XP.ImageProcessing.Processors/图像增强/ContrastProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/ContrastProcessor.cs
@@ -42,7 +42,7 @@ public class ContrastProcessor : ImageProcessorBase
1.0,
0.1,
3.0,
- LocalizationHelper.GetString("ContrastProcessor_Contrast_Desc")));
+ LocalizationHelper.GetString("ContrastProcessor_Contrast_Desc")) { IsAdvanced = true });
Parameters.Add("Brightness", new ProcessorParameter(
"Brightness",
@@ -51,13 +51,13 @@ public class ContrastProcessor : ImageProcessorBase
0,
-100,
100,
- LocalizationHelper.GetString("ContrastProcessor_Brightness_Desc")));
+ LocalizationHelper.GetString("ContrastProcessor_Brightness_Desc")) { IsAdvanced = true });
Parameters.Add("AutoContrast", new ProcessorParameter(
"AutoContrast",
LocalizationHelper.GetString("ContrastProcessor_AutoContrast"),
typeof(bool),
- false,
+ true,
null,
null,
LocalizationHelper.GetString("ContrastProcessor_AutoContrast_Desc")));
@@ -69,7 +69,7 @@ public class ContrastProcessor : ImageProcessorBase
false,
null,
null,
- LocalizationHelper.GetString("ContrastProcessor_UseCLAHE_Desc")));
+ LocalizationHelper.GetString("ContrastProcessor_UseCLAHE_Desc")) { IsAdvanced = true });
Parameters.Add("ClipLimit", new ProcessorParameter(
"ClipLimit",
@@ -78,7 +78,7 @@ public class ContrastProcessor : ImageProcessorBase
2.0,
1.0,
10.0,
- LocalizationHelper.GetString("ContrastProcessor_ClipLimit_Desc")));
+ LocalizationHelper.GetString("ContrastProcessor_ClipLimit_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/图像增强/EmbossProcessor.cs b/XP.ImageProcessing.Processors/图像增强/EmbossProcessor.cs
index aa411345..0857568e 100644
--- a/XP.ImageProcessing.Processors/图像增强/EmbossProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/EmbossProcessor.cs
@@ -64,7 +64,7 @@ public class EmbossProcessor : ImageProcessorBase
0.5,
0.0,
1.0,
- LocalizationHelper.GetString("EmbossProcessor_BlendRatio_Desc")));
+ LocalizationHelper.GetString("EmbossProcessor_BlendRatio_Desc")) { IsAdvanced = true });
Parameters.Add("GrayOffset", new ProcessorParameter(
"GrayOffset",
@@ -73,7 +73,7 @@ public class EmbossProcessor : ImageProcessorBase
128,
0,
255,
- LocalizationHelper.GetString("EmbossProcessor_GrayOffset_Desc")));
+ LocalizationHelper.GetString("EmbossProcessor_GrayOffset_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/其他/FilmEffectProcessor.cs b/XP.ImageProcessing.Processors/图像增强/FilmEffectProcessor.cs
similarity index 97%
rename from XP.ImageProcessing.Processors/其他/FilmEffectProcessor.cs
rename to XP.ImageProcessing.Processors/图像增强/FilmEffectProcessor.cs
index 9b2f2180..34ff3e79 100644
--- a/XP.ImageProcessing.Processors/其他/FilmEffectProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/FilmEffectProcessor.cs
@@ -1,214 +1,214 @@
-// ============================================================================
-// 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.CvEnum;
-using Emgu.CV.Structure;
-using XP.ImageProcessing.Core;
-using Serilog;
-
-namespace XP.ImageProcessing.Processors;
-
-///
-/// 电子胶片效果算子(支持 8 位和 16 位灰度图像)
-/// 16 位模式下窗宽窗位参数范围扩展到 0-65535,LUT 升级为 ushort[65536]
-///
-public class FilmEffectProcessor : ImageProcessorBase
- where TDepth : struct, IComparable
-{
- private static readonly ILogger _logger = Log.ForContext>();
- private byte[] _lut8 = new byte[256];
- private ushort[] _lut16 = new ushort[65536];
-
- public FilmEffectProcessor()
- {
- Name = LocalizationHelper.GetString("FilmEffectProcessor_Name");
- Description = LocalizationHelper.GetString("FilmEffectProcessor_Description");
- }
-
- protected override void InitializeParameters()
- {
- // 窗宽窗位默认值适配:8 位用 0-255,16 位用 0-65535
- // 运行时通过 MaxPixelValue 动态决定,参数范围设为最大(65535)
- Parameters.Add("WindowCenter", new ProcessorParameter(
- "WindowCenter",
- LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter"),
- typeof(int), 32768, 0, 65535,
- LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter_Desc")));
-
- Parameters.Add("WindowWidth", new ProcessorParameter(
- "WindowWidth",
- LocalizationHelper.GetString("FilmEffectProcessor_WindowWidth"),
- typeof(int), 65535, 1, 65535,
- 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 Process(Image inputImage)
- {
- int windowCenter = GetParameter("WindowCenter");
- int windowWidth = GetParameter("WindowWidth");
- bool invert = GetParameter("Invert");
- string curve = GetParameter("Curve");
- double curveStrength = GetParameter("CurveStrength");
- double edgeEnhance = GetParameter("EdgeEnhance");
-
- bool is16 = typeof(TDepth) == typeof(ushort);
-
- if (is16)
- {
- BuildLUT16(windowCenter, windowWidth, invert, curve, curveStrength);
- var img16 = new Image(inputImage.Width, inputImage.Height);
- var ushortData = inputImage.Data as ushort[,,];
- int h = inputImage.Height, w = inputImage.Width;
- Parallel.For(0, h, y =>
- {
- for (int x = 0; x < w; x++)
- img16.Data[y, x, 0] = _lut16[ushortData![y, x, 0]];
- });
-
- if (edgeEnhance > 0.01)
- {
- using var blurred = new Image(w, h);
- CvInvoke.GaussianBlur(inputImage, blurred, new System.Drawing.Size(3, 3), 0);
- Parallel.For(0, h, y =>
- {
- for (int x = 0; x < w; x++)
- {
- int diff = ushortData![y, x, 0] - blurred.Data[y, x, 0];
- int enhanced = img16.Data[y, x, 0] + (int)(diff * edgeEnhance);
- img16.Data[y, x, 0] = (ushort)Math.Clamp(enhanced, 0, 65535);
- }
- });
- }
-
- _logger.Debug("Process(16bit): WC={WC}, WW={WW}", windowCenter, windowWidth);
- return (img16 as Image)!;
- }
- else
- {
- BuildLUT8(
- Math.Clamp(windowCenter, 0, 255),
- Math.Clamp(windowWidth, 1, 255),
- invert, curve, curveStrength);
-
- var byteInput = inputImage as Image;
- var resultImg = byteInput!.Clone();
- int h = inputImage.Height, w = inputImage.Width;
-
- for (int y = 0; y < h; y++)
- for (int x = 0; x < w; x++)
- resultImg.Data[y, x, 0] = _lut8[resultImg.Data[y, x, 0]];
-
- if (edgeEnhance > 0.01)
- {
- using var blurred = byteInput.SmoothGaussian(3);
- for (int y = 0; y < h; y++)
- for (int x = 0; x < w; x++)
- {
- float diff = byteInput.Data[y, x, 0] - blurred.Data[y, x, 0];
- int enhanced = resultImg.Data[y, x, 0] + (int)(diff * edgeEnhance);
- resultImg.Data[y, x, 0] = (byte)Math.Clamp(enhanced, 0, 255);
- }
- }
-
- _logger.Debug("Process(8bit): WC={WC}, WW={WW}", windowCenter, windowWidth);
- return (resultImg as Image)!;
- }
- }
-
- private void BuildLUT8(int wc, int ww, bool invert, string curve, double strength)
- {
- double halfW = ww / 2.0;
- double low = wc - halfW, high = wc + halfW;
- for (int i = 0; i < 256; i++)
- {
- double normalized = ww <= 1 ? (i >= wc ? 1.0 : 0.0)
- : Math.Clamp((i - low) / (high - low), 0.0, 1.0);
- double mapped = ApplyCurve(normalized, curve, strength);
- if (invert) mapped = 1.0 - mapped;
- _lut8[i] = (byte)Math.Clamp((int)(mapped * 255.0), 0, 255);
- }
- }
-
- private void BuildLUT16(int wc, int ww, bool invert, string curve, double strength)
- {
- double halfW = ww / 2.0;
- double low = wc - halfW, high = wc + halfW;
- for (int i = 0; i < 65536; i++)
- {
- double normalized = ww <= 1 ? (i >= wc ? 1.0 : 0.0)
- : Math.Clamp((i - low) / (high - low), 0.0, 1.0);
- double mapped = ApplyCurve(normalized, curve, strength);
- if (invert) mapped = 1.0 - mapped;
- _lut16[i] = (ushort)Math.Clamp((int)(mapped * 65535.0), 0, 65535);
- }
- }
-
- private static double ApplyCurve(double x, string curve, double strength)
- => curve switch
- {
- "Sigmoid" => ApplySigmoid(x, strength),
- "Logarithmic" => ApplyLogarithmic(x, strength),
- "Exponential" => ApplyExponential(x, strength),
- _ => x
- };
-
- /// S曲线(Sigmoid):增强中间调对比度
- private static double ApplySigmoid(double x, double strength)
- {
- double k = strength * 10.0;
- return 1.0 / (1.0 + Math.Exp(-k * (x - 0.5)));
- }
-
- /// 对数曲线:提亮暗部,压缩亮部
- private static double ApplyLogarithmic(double x, double strength)
- {
- double c = strength;
- return Math.Log(1.0 + c * x) / Math.Log(1.0 + c);
- }
-
- /// 指数曲线:压缩暗部,增强亮部
- private static double ApplyExponential(double x, double strength)
- {
- double c = strength;
- return (Math.Exp(c * x) - 1.0) / (Math.Exp(c) - 1.0);
- }
+// ============================================================================
+// 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.CvEnum;
+using Emgu.CV.Structure;
+using XP.ImageProcessing.Core;
+using Serilog;
+
+namespace XP.ImageProcessing.Processors;
+
+///
+/// 电子胶片效果算子(支持 8 位和 16 位灰度图像)
+/// 16 位模式下窗宽窗位参数范围扩展到 0-65535,LUT 升级为 ushort[65536]
+///
+public class FilmEffectProcessor : ImageProcessorBase
+ where TDepth : struct, IComparable
+{
+ private static readonly ILogger _logger = Log.ForContext>();
+ private byte[] _lut8 = new byte[256];
+ private ushort[] _lut16 = new ushort[65536];
+
+ public FilmEffectProcessor()
+ {
+ Name = LocalizationHelper.GetString("FilmEffectProcessor_Name");
+ Description = LocalizationHelper.GetString("FilmEffectProcessor_Description");
+ }
+
+ protected override void InitializeParameters()
+ {
+ // 窗宽窗位默认值适配:8 位用 0-255,16 位用 0-65535
+ // 运行时通过 MaxPixelValue 动态决定,参数范围设为最大(65535)
+ Parameters.Add("WindowCenter", new ProcessorParameter(
+ "WindowCenter",
+ LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter"),
+ typeof(int), 32768, 0, 65535,
+ LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter_Desc")));
+
+ Parameters.Add("WindowWidth", new ProcessorParameter(
+ "WindowWidth",
+ LocalizationHelper.GetString("FilmEffectProcessor_WindowWidth"),
+ typeof(int), 65535, 1, 65535,
+ 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 Process(Image inputImage)
+ {
+ int windowCenter = GetParameter("WindowCenter");
+ int windowWidth = GetParameter("WindowWidth");
+ bool invert = GetParameter("Invert");
+ string curve = GetParameter("Curve");
+ double curveStrength = GetParameter("CurveStrength");
+ double edgeEnhance = GetParameter("EdgeEnhance");
+
+ bool is16 = typeof(TDepth) == typeof(ushort);
+
+ if (is16)
+ {
+ BuildLUT16(windowCenter, windowWidth, invert, curve, curveStrength);
+ var img16 = new Image(inputImage.Width, inputImage.Height);
+ var ushortData = inputImage.Data as ushort[,,];
+ int h = inputImage.Height, w = inputImage.Width;
+ Parallel.For(0, h, y =>
+ {
+ for (int x = 0; x < w; x++)
+ img16.Data[y, x, 0] = _lut16[ushortData![y, x, 0]];
+ });
+
+ if (edgeEnhance > 0.01)
+ {
+ using var blurred = new Image(w, h);
+ CvInvoke.GaussianBlur(inputImage, blurred, new System.Drawing.Size(3, 3), 0);
+ Parallel.For(0, h, y =>
+ {
+ for (int x = 0; x < w; x++)
+ {
+ int diff = ushortData![y, x, 0] - blurred.Data[y, x, 0];
+ int enhanced = img16.Data[y, x, 0] + (int)(diff * edgeEnhance);
+ img16.Data[y, x, 0] = (ushort)Math.Clamp(enhanced, 0, 65535);
+ }
+ });
+ }
+
+ _logger.Debug("Process(16bit): WC={WC}, WW={WW}", windowCenter, windowWidth);
+ return (img16 as Image)!;
+ }
+ else
+ {
+ BuildLUT8(
+ Math.Clamp(windowCenter, 0, 255),
+ Math.Clamp(windowWidth, 1, 255),
+ invert, curve, curveStrength);
+
+ var byteInput = inputImage as Image;
+ var resultImg = byteInput!.Clone();
+ int h = inputImage.Height, w = inputImage.Width;
+
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ resultImg.Data[y, x, 0] = _lut8[resultImg.Data[y, x, 0]];
+
+ if (edgeEnhance > 0.01)
+ {
+ using var blurred = byteInput.SmoothGaussian(3);
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ {
+ float diff = byteInput.Data[y, x, 0] - blurred.Data[y, x, 0];
+ int enhanced = resultImg.Data[y, x, 0] + (int)(diff * edgeEnhance);
+ resultImg.Data[y, x, 0] = (byte)Math.Clamp(enhanced, 0, 255);
+ }
+ }
+
+ _logger.Debug("Process(8bit): WC={WC}, WW={WW}", windowCenter, windowWidth);
+ return (resultImg as Image)!;
+ }
+ }
+
+ private void BuildLUT8(int wc, int ww, bool invert, string curve, double strength)
+ {
+ double halfW = ww / 2.0;
+ double low = wc - halfW, high = wc + halfW;
+ for (int i = 0; i < 256; i++)
+ {
+ double normalized = ww <= 1 ? (i >= wc ? 1.0 : 0.0)
+ : Math.Clamp((i - low) / (high - low), 0.0, 1.0);
+ double mapped = ApplyCurve(normalized, curve, strength);
+ if (invert) mapped = 1.0 - mapped;
+ _lut8[i] = (byte)Math.Clamp((int)(mapped * 255.0), 0, 255);
+ }
+ }
+
+ private void BuildLUT16(int wc, int ww, bool invert, string curve, double strength)
+ {
+ double halfW = ww / 2.0;
+ double low = wc - halfW, high = wc + halfW;
+ for (int i = 0; i < 65536; i++)
+ {
+ double normalized = ww <= 1 ? (i >= wc ? 1.0 : 0.0)
+ : Math.Clamp((i - low) / (high - low), 0.0, 1.0);
+ double mapped = ApplyCurve(normalized, curve, strength);
+ if (invert) mapped = 1.0 - mapped;
+ _lut16[i] = (ushort)Math.Clamp((int)(mapped * 65535.0), 0, 65535);
+ }
+ }
+
+ private static double ApplyCurve(double x, string curve, double strength)
+ => curve switch
+ {
+ "Sigmoid" => ApplySigmoid(x, strength),
+ "Logarithmic" => ApplyLogarithmic(x, strength),
+ "Exponential" => ApplyExponential(x, strength),
+ _ => x
+ };
+
+ /// S曲线(Sigmoid):增强中间调对比度
+ private static double ApplySigmoid(double x, double strength)
+ {
+ double k = strength * 10.0;
+ return 1.0 / (1.0 + Math.Exp(-k * (x - 0.5)));
+ }
+
+ /// 对数曲线:提亮暗部,压缩亮部
+ private static double ApplyLogarithmic(double x, double strength)
+ {
+ double c = strength;
+ return Math.Log(1.0 + c * x) / Math.Log(1.0 + c);
+ }
+
+ /// 指数曲线:压缩暗部,增强亮部
+ private static double ApplyExponential(double x, double strength)
+ {
+ double c = strength;
+ return (Math.Exp(c * x) - 1.0) / (Math.Exp(c) - 1.0);
+ }
}
\ No newline at end of file
diff --git a/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs b/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs
index 2f3b0de5..e65d3d84 100644
--- a/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs
@@ -25,8 +25,6 @@ namespace XP.ImageProcessing.Processors;
public class GammaProcessor : ImageProcessorBase
where TDepth : struct, IComparable
{
- private byte[] _lookupTable8 = new byte[256];
- private ushort[] _lookupTable16 = new ushort[65536];
private static readonly ILogger _logger = Log.ForContext>();
public GammaProcessor()
@@ -53,7 +51,7 @@ public class GammaProcessor : ImageProcessorBase
1.0,
0.1,
3.0,
- LocalizationHelper.GetString("GammaProcessor_Gain_Desc")));
+ LocalizationHelper.GetString("GammaProcessor_Gain_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
@@ -64,49 +62,53 @@ public class GammaProcessor : ImageProcessorBase
if (typeof(TDepth) == typeof(ushort))
{
- BuildLUT16(gamma, gain);
+ var lut16 = BuildLUT16(gamma, gain);
var img16 = inputImage as Image;
var result = new Image(inputImage.Width, inputImage.Height);
Parallel.For(0, inputImage.Height, y =>
{
for (int x = 0; x < inputImage.Width; x++)
- result.Data[y, x, 0] = _lookupTable16[img16!.Data[y, x, 0]];
+ result.Data[y, x, 0] = lut16[img16!.Data[y, x, 0]];
});
_logger.Debug("Process(16bit): Gamma={G}, Gain={Gain}", gamma, gain);
return (result as Image)!;
}
else
{
- BuildLUT8(gamma, gain);
+ var lut8 = BuildLUT8(gamma, gain);
var result = (inputImage as Image)!.Clone();
int h = inputImage.Height, w = inputImage.Width;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
- result.Data[y, x, 0] = _lookupTable8[result.Data[y, x, 0]];
+ result.Data[y, x, 0] = lut8[result.Data[y, x, 0]];
_logger.Debug("Process(8bit): Gamma={G}, Gain={Gain}", gamma, gain);
return (result as Image)!;
}
}
- private void BuildLUT8(double gamma, double gain)
+ private static byte[] BuildLUT8(double gamma, double gain)
{
+ var lut = new byte[256];
double invGamma = 1.0 / gamma;
for (int i = 0; i < 256; i++)
{
double normalized = i / 255.0;
double corrected = Math.Pow(normalized, invGamma) * gain;
- _lookupTable8[i] = (byte)Math.Clamp((int)(corrected * 255.0), 0, 255);
+ lut[i] = (byte)Math.Clamp((int)(corrected * 255.0), 0, 255);
}
+ return lut;
}
- private void BuildLUT16(double gamma, double gain)
+ private static ushort[] BuildLUT16(double gamma, double gain)
{
+ var lut = new ushort[65536];
double invGamma = 1.0 / gamma;
for (int i = 0; i < 65536; i++)
{
double normalized = i / 65535.0;
double corrected = Math.Pow(normalized, invGamma) * gain;
- _lookupTable16[i] = (ushort)Math.Clamp((int)(corrected * 65535.0), 0, 65535);
+ lut[i] = (ushort)Math.Clamp((int)(corrected * 65535.0), 0, 65535);
}
+ return lut;
}
}
\ No newline at end of file
diff --git a/XP.ImageProcessing.Processors/图像增强/HDREnhancementProcessor.cs b/XP.ImageProcessing.Processors/图像增强/HDREnhancementProcessor.cs
index 6ab21778..bc970434 100644
--- a/XP.ImageProcessing.Processors/图像增强/HDREnhancementProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/HDREnhancementProcessor.cs
@@ -45,7 +45,7 @@ public class HDREnhancementProcessor : ImageProcessorBase
null,
null,
LocalizationHelper.GetString("HDREnhancementProcessor_Method_Desc"),
- new string[] { "LocalToneMap", "AdaptiveLog", "Drago", "BilateralToneMap" }));
+ new string[] { "LocalToneMap", "AdaptiveLog", "Drago", "BilateralToneMap" }) { IsAdvanced = true });
Parameters.Add("Gamma", new ProcessorParameter(
"Gamma",
@@ -63,7 +63,7 @@ public class HDREnhancementProcessor : ImageProcessorBase
1.0,
0.0,
3.0,
- LocalizationHelper.GetString("HDREnhancementProcessor_Saturation_Desc")));
+ LocalizationHelper.GetString("HDREnhancementProcessor_Saturation_Desc")) { IsAdvanced = true });
Parameters.Add("DetailBoost", new ProcessorParameter(
"DetailBoost",
@@ -81,7 +81,7 @@ public class HDREnhancementProcessor : ImageProcessorBase
20.0,
1.0,
100.0,
- LocalizationHelper.GetString("HDREnhancementProcessor_SigmaSpace_Desc")));
+ LocalizationHelper.GetString("HDREnhancementProcessor_SigmaSpace_Desc")) { IsAdvanced = true });
Parameters.Add("SigmaColor", new ProcessorParameter(
"SigmaColor",
@@ -90,7 +90,7 @@ public class HDREnhancementProcessor : ImageProcessorBase
30.0,
1.0,
100.0,
- LocalizationHelper.GetString("HDREnhancementProcessor_SigmaColor_Desc")));
+ LocalizationHelper.GetString("HDREnhancementProcessor_SigmaColor_Desc")) { IsAdvanced = true });
Parameters.Add("Bias", new ProcessorParameter(
"Bias",
@@ -99,7 +99,7 @@ public class HDREnhancementProcessor : ImageProcessorBase
0.85,
0.0,
1.0,
- LocalizationHelper.GetString("HDREnhancementProcessor_Bias_Desc")));
+ LocalizationHelper.GetString("HDREnhancementProcessor_Bias_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/图像增强/HierarchicalEnhancementProcessor.cs b/XP.ImageProcessing.Processors/图像增强/HierarchicalEnhancementProcessor.cs
index 4955657c..c7647a44 100644
--- a/XP.ImageProcessing.Processors/图像增强/HierarchicalEnhancementProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/HierarchicalEnhancementProcessor.cs
@@ -77,7 +77,7 @@ public class HierarchicalEnhancementProcessor : ImageProcessorBase : ImageProcessorBase : ImageProcessorBase
"Method",
LocalizationHelper.GetString("HistogramEqualizationProcessor_Method"),
typeof(string),
- "Global",
+ "CLAHE",
null,
null,
LocalizationHelper.GetString("HistogramEqualizationProcessor_Method_Desc"),
- new string[] { "Global", "CLAHE" }));
+ new string[] { "Global", "CLAHE" }) { IsAdvanced = true });
Parameters.Add("ClipLimit", new ProcessorParameter(
"ClipLimit",
@@ -63,7 +63,7 @@ public class HistogramEqualizationProcessor : ImageProcessorBase
8,
4,
32,
- LocalizationHelper.GetString("HistogramEqualizationProcessor_TileSize_Desc")));
+ LocalizationHelper.GetString("HistogramEqualizationProcessor_TileSize_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/图像增强/RetinexProcessor.cs b/XP.ImageProcessing.Processors/图像增强/RetinexProcessor.cs
index 445aafd4..6d2a775a 100644
--- a/XP.ImageProcessing.Processors/图像增强/RetinexProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/RetinexProcessor.cs
@@ -44,7 +44,7 @@ public class RetinexProcessor : ImageProcessorBase
null,
null,
LocalizationHelper.GetString("RetinexProcessor_Method_Desc"),
- new string[] { "SSR", "MSR", "MSRCR" }));
+ new string[] { "SSR", "MSR", "MSRCR" }) { IsAdvanced = true });
Parameters.Add("Sigma1", new ProcessorParameter(
"Sigma1",
@@ -53,7 +53,7 @@ public class RetinexProcessor : ImageProcessorBase
15.0,
1.0,
100.0,
- LocalizationHelper.GetString("RetinexProcessor_Sigma1_Desc")));
+ LocalizationHelper.GetString("RetinexProcessor_Sigma1_Desc")) { IsAdvanced = true });
Parameters.Add("Sigma2", new ProcessorParameter(
"Sigma2",
@@ -62,7 +62,7 @@ public class RetinexProcessor : ImageProcessorBase
80.0,
1.0,
200.0,
- LocalizationHelper.GetString("RetinexProcessor_Sigma2_Desc")));
+ LocalizationHelper.GetString("RetinexProcessor_Sigma2_Desc")) { IsAdvanced = true });
Parameters.Add("Sigma3", new ProcessorParameter(
"Sigma3",
@@ -71,7 +71,7 @@ public class RetinexProcessor : ImageProcessorBase
250.0,
1.0,
500.0,
- LocalizationHelper.GetString("RetinexProcessor_Sigma3_Desc")));
+ LocalizationHelper.GetString("RetinexProcessor_Sigma3_Desc")) { IsAdvanced = true });
Parameters.Add("Gain", new ProcessorParameter(
"Gain",
@@ -89,7 +89,7 @@ public class RetinexProcessor : ImageProcessorBase
0,
-100,
100,
- LocalizationHelper.GetString("RetinexProcessor_Offset_Desc")));
+ LocalizationHelper.GetString("RetinexProcessor_Offset_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/图像增强/SharpenProcessor.cs b/XP.ImageProcessing.Processors/图像增强/SharpenProcessor.cs
index 5f1703a7..8a4b49f3 100644
--- a/XP.ImageProcessing.Processors/图像增强/SharpenProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/SharpenProcessor.cs
@@ -39,11 +39,11 @@ public class SharpenProcessor : ImageProcessorBase
"Method",
LocalizationHelper.GetString("SharpenProcessor_Method"),
typeof(string),
- "Laplacian",
+ "UnsharpMask",
null,
null,
LocalizationHelper.GetString("SharpenProcessor_Method_Desc"),
- new string[] { "Laplacian", "UnsharpMask" }));
+ new string[] { "Laplacian", "UnsharpMask" }) { IsAdvanced = true });
Parameters.Add("Strength", new ProcessorParameter(
"Strength",
@@ -61,7 +61,7 @@ public class SharpenProcessor : ImageProcessorBase
3,
1,
15,
- LocalizationHelper.GetString("SharpenProcessor_KernelSize_Desc")));
+ LocalizationHelper.GetString("SharpenProcessor_KernelSize_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/图像增强/SuperResolutionProcessor.cs b/XP.ImageProcessing.Processors/图像增强/SuperResolutionProcessor.cs
index 6f2b7956..7d0753f5 100644
--- a/XP.ImageProcessing.Processors/图像增强/SuperResolutionProcessor.cs
+++ b/XP.ImageProcessing.Processors/图像增强/SuperResolutionProcessor.cs
@@ -145,7 +145,8 @@ public class SuperResolutionProcessor : ImageProcessorBase
int w = inputImage.Width;
// 获取模型输入信息
- string inputName = session.InputMetadata.Keys.First();
+ string inputName = session.InputMetadata.Keys.FirstOrDefault()
+ ?? throw new InvalidOperationException("ONNX model has no input metadata");
var inputMeta = session.InputMetadata[inputName];
int[] dims = inputMeta.Dimensions;
// dims 格式: [1, H, W, C] (NHWC),C 可能是 1 或 3
diff --git a/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs
index 88d3b84b..57e58326 100644
--- a/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs
@@ -62,7 +62,7 @@ public class BandPassFilterProcessor : ImageProcessorBase
null,
null,
LocalizationHelper.GetString("BandPassFilterProcessor_FilterType_Desc"),
- new string[] { "Ideal", "Butterworth", "Gaussian" }));
+ new string[] { "Ideal", "Butterworth", "Gaussian" }) { IsAdvanced = true });
Parameters.Add("Order", new ProcessorParameter(
"Order",
@@ -71,7 +71,7 @@ public class BandPassFilterProcessor : ImageProcessorBase
2,
1,
10,
- LocalizationHelper.GetString("BandPassFilterProcessor_Order_Desc")));
+ LocalizationHelper.GetString("BandPassFilterProcessor_Order_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
@@ -85,7 +85,7 @@ public class BandPassFilterProcessor : ImageProcessorBase
if (highCutoff <= lowCutoff) highCutoff = lowCutoff + 10;
var floatImage = inputImage.Convert();
- var imaginaryImage = new Image(floatImage.Size);
+ using var imaginaryImage = new Image(floatImage.Size);
imaginaryImage.SetZero();
using (var planes = new Emgu.CV.Util.VectorOfMat())
@@ -139,6 +139,8 @@ public class BandPassFilterProcessor : ImageProcessorBase
result = (result - minVal) * (255.0 / (maxVal - minVal));
}
+ floatImage.Dispose();
+ mask.Dispose();
complexMat.Dispose();
dftMat.Dispose();
filteredDft.Dispose();
diff --git a/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs
index b89fbf98..8e32e4ea 100644
--- a/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs
@@ -58,7 +58,7 @@ public class BilateralFilterProcessor : ImageProcessorBase
75.0,
1.0,
200.0,
- LocalizationHelper.GetString("BilateralFilterProcessor_SigmaSpace_Desc")));
+ LocalizationHelper.GetString("BilateralFilterProcessor_SigmaSpace_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
@@ -69,14 +69,32 @@ public class BilateralFilterProcessor : ImageProcessorBase
double sigmaColor = GetParameter("SigmaColor");
double sigmaSpace = GetParameter("SigmaSpace");
- // 16 位时 sigmaColor 按比例放大(原始参数按 8 位语义定义)
- double effectiveSigmaColor = sigmaColor;
- if (typeof(TDepth) == typeof(ushort))
- effectiveSigmaColor = sigmaColor * 256.0;
+ Image result;
+
+ if (typeof(TDepth) == typeof(ushort))
+ {
+ // OpenCV BilateralFilter only supports 8U and 32F.
+ // For 16-bit images: convert to 32F, apply filter, then convert back.
+ double effectiveSigmaColor = sigmaColor * 256.0;
+
+ using var floatInput = inputImage.Convert();
+ using var floatResult = floatInput.CopyBlank();
+ CvInvoke.BilateralFilter(floatInput, floatResult, diameter, effectiveSigmaColor, sigmaSpace);
+ result = floatResult.Convert();
+
+ _logger.Debug("Process (16-bit via 32F): Diameter={D}, SigmaColor={SC}, SigmaSpace={SS}",
+ diameter, effectiveSigmaColor, sigmaSpace);
+ }
+ else
+ {
+ // 8-bit (byte) path: directly supported by OpenCV
+ result = inputImage.CopyBlank();
+ CvInvoke.BilateralFilter(inputImage, result, diameter, sigmaColor, sigmaSpace);
+
+ _logger.Debug("Process (8-bit): Diameter={D}, SigmaColor={SC}, SigmaSpace={SS}",
+ diameter, sigmaColor, sigmaSpace);
+ }
- var result = inputImage.Clone();
- CvInvoke.BilateralFilter(inputImage, result, diameter, effectiveSigmaColor, sigmaSpace);
- _logger.Debug("Process: Diameter={D}, SigmaColor={SC}, SigmaSpace={SS}", diameter, effectiveSigmaColor, sigmaSpace);
return result;
}
}
\ No newline at end of file
diff --git a/XP.ImageProcessing.Processors/滤波处理/GaussianBlurProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/GaussianBlurProcessor.cs
index ab06e930..f68b88c4 100644
--- a/XP.ImageProcessing.Processors/滤波处理/GaussianBlurProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/GaussianBlurProcessor.cs
@@ -50,7 +50,7 @@ public class GaussianBlurProcessor : ImageProcessorBase
1.5,
0.1,
10.0,
- LocalizationHelper.GetString("GaussianBlurProcessor_Sigma_Desc")));
+ LocalizationHelper.GetString("GaussianBlurProcessor_Sigma_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/滤波处理/HighPassFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/HighPassFilterProcessor.cs
index 7ef3d845..b2022175 100644
--- a/XP.ImageProcessing.Processors/滤波处理/HighPassFilterProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/HighPassFilterProcessor.cs
@@ -126,7 +126,7 @@ public class HighPassFilterProcessor : ImageProcessorBase
///
private Mat CreateHighPassFilter(int rows, int cols, double d0)
{
- var filter = new Image(cols, rows);
+ using var filter = new Image(cols, rows);
int centerX = cols / 2;
int centerY = rows / 2;
@@ -141,6 +141,6 @@ public class HighPassFilterProcessor : ImageProcessorBase
}
}
- return filter.Mat;
+ return filter.Mat.Clone();
}
}
\ No newline at end of file
diff --git a/XP.ImageProcessing.Processors/滤波处理/LowPassFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/LowPassFilterProcessor.cs
index f2a3e5c2..bdee984f 100644
--- a/XP.ImageProcessing.Processors/滤波处理/LowPassFilterProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/LowPassFilterProcessor.cs
@@ -122,7 +122,7 @@ public class LowPassFilterProcessor : ImageProcessorBase
///
private Mat CreateLowPassFilter(int rows, int cols, double d0)
{
- var filter = new Image(cols, rows);
+ using var filter = new Image(cols, rows);
int centerX = cols / 2;
int centerY = rows / 2;
@@ -137,6 +137,6 @@ public class LowPassFilterProcessor : ImageProcessorBase
}
}
- return filter.Mat;
+ return filter.Mat.Clone();
}
}
\ No newline at end of file
diff --git a/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs
index 5b0e446f..3d188ab8 100644
--- a/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs
@@ -38,9 +38,9 @@ public class MedianFilterProcessor : ImageProcessorBase
"KernelSize",
LocalizationHelper.GetString("MedianFilterProcessor_KernelSize"),
typeof(int),
- 5,
+ 3,
1,
- 31,
+ 5,
LocalizationHelper.GetString("MedianFilterProcessor_KernelSize_Desc")));
_logger.Debug("InitializeParameters");
@@ -51,10 +51,20 @@ public class MedianFilterProcessor : ImageProcessorBase
int kernelSize = GetParameter("KernelSize");
if (kernelSize % 2 == 0) kernelSize++;
- var result = inputImage.Clone();
- CvInvoke.MedianBlur(inputImage, result, kernelSize);
+ // OpenCV MedianBlur: CV_16U not supported. Convert to 32F for 16-bit images.
+ if (typeof(TDepth) == typeof(ushort))
+ {
+ using var floatInput = inputImage.Convert();
+ using var floatResult = floatInput.CopyBlank();
+ CvInvoke.MedianBlur(floatInput, floatResult, kernelSize);
+ _logger.Debug("Process (16-bit via 32F): KernelSize = {KernelSize}", kernelSize);
+ return floatResult.Convert();
+ }
+
+ var output = inputImage.CopyBlank();
+ CvInvoke.MedianBlur(inputImage, output, kernelSize);
_logger.Debug("Process: KernelSize = {KernelSize}", kernelSize);
- return result;
+ return output;
}
}
\ No newline at end of file
diff --git a/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs
index 31fd01f6..8b8438ba 100644
--- a/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs
@@ -44,7 +44,7 @@ public class RemoveOutliersProcessor : ImageProcessorBase
typeof(int),
5,
1,
- 31,
+ 5,
LocalizationHelper.GetString("RemoveOutliersProcessor_KernelSize_Desc")));
Parameters.Add("Threshold", new ProcessorParameter(
@@ -88,48 +88,66 @@ public class RemoveOutliersProcessor : ImageProcessorBase
int height = inputImage.Height;
// 计算邻域中值图
- var medianImage = inputImage.Clone();
- CvInvoke.MedianBlur(inputImage, medianImage, kernelSize);
+ // OpenCV MedianBlur: CV_16U not supported. Convert to 32F for 16-bit images.
+ Image medianImage;
+ if (typeof(TDepth) == typeof(ushort))
+ {
+ using var floatInput = inputImage.Convert();
+ using var floatMedian = floatInput.CopyBlank();
+ CvInvoke.MedianBlur(floatInput, floatMedian, kernelSize);
+ medianImage = floatMedian.Convert();
+ }
+ else
+ {
+ medianImage = inputImage.CopyBlank();
+ CvInvoke.MedianBlur(inputImage, medianImage, kernelSize);
+ }
// 逐像素比较并替换离群点
var result = inputImage.Clone();
- for (int y = 0; y < height; y++)
+ try
{
- for (int x = 0; x < width; x++)
+ for (int y = 0; y < height; y++)
{
- double original = Convert.ToDouble(inputImage.Data[y, x, 0]);
- double median = Convert.ToDouble(medianImage.Data[y, x, 0]);
- double diff = original - median;
-
- bool isOutlier = false;
-
- switch (outlierType)
+ for (int x = 0; x < width; x++)
{
- case "Bright":
- isOutlier = diff > threshold; // 亮离群点:比邻域中值亮太多
- break;
- case "Dark":
- isOutlier = -diff > threshold; // 暗离群点:比邻域中值暗太多
- break;
- case "Both":
- default:
- isOutlier = System.Math.Abs(diff) > threshold;
- break;
- }
+ double original = Convert.ToDouble(inputImage.Data[y, x, 0]);
+ double median = Convert.ToDouble(medianImage.Data[y, x, 0]);
+ double diff = original - median;
- if (isOutlier)
- {
- result.Data[y, x, 0] = medianImage.Data[y, x, 0];
+ bool isOutlier = false;
+
+ switch (outlierType)
+ {
+ case "Bright":
+ isOutlier = diff > threshold; // 亮离群点:比邻域中值亮太多
+ break;
+ case "Dark":
+ isOutlier = -diff > threshold; // 暗离群点:比邻域中值暗太多
+ break;
+ case "Both":
+ default:
+ isOutlier = System.Math.Abs(diff) > threshold;
+ break;
+ }
+
+ if (isOutlier)
+ {
+ result.Data[y, x, 0] = medianImage.Data[y, x, 0];
+ }
}
}
}
-
- medianImage.Dispose();
+ finally
+ {
+ medianImage.Dispose();
+ }
_logger.Debug("Process: KernelSize={K}, Threshold={T}, Type={Type}",
kernelSize, threshold, outlierType);
return result;
}
+
}
diff --git a/XP.ImageProcessing.Processors/滤波处理/ShockFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/ShockFilterProcessor.cs
index ae26f088..d6ef79c2 100644
--- a/XP.ImageProcessing.Processors/滤波处理/ShockFilterProcessor.cs
+++ b/XP.ImageProcessing.Processors/滤波处理/ShockFilterProcessor.cs
@@ -59,7 +59,7 @@ public class ShockFilterProcessor : ImageProcessorBase
0.25,
0.1,
1.0,
- LocalizationHelper.GetString("ShockFilterProcessor_Dt_Desc")));
+ LocalizationHelper.GetString("ShockFilterProcessor_Dt_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/边缘检测/HorizontalEdgeProcessor.cs b/XP.ImageProcessing.Processors/边缘检测/HorizontalEdgeProcessor.cs
index cb481a2a..633feecc 100644
--- a/XP.ImageProcessing.Processors/边缘检测/HorizontalEdgeProcessor.cs
+++ b/XP.ImageProcessing.Processors/边缘检测/HorizontalEdgeProcessor.cs
@@ -43,7 +43,7 @@ public class HorizontalEdgeProcessor : ImageProcessorBase
null,
null,
LocalizationHelper.GetString("HorizontalEdgeProcessor_Method_Desc"),
- new string[] { "Sobel", "Prewitt", "Simple" }));
+ new string[] { "Sobel", "Prewitt", "Simple" }) { IsAdvanced = true });
Parameters.Add("Sensitivity", new ProcessorParameter(
"Sensitivity",
@@ -52,7 +52,7 @@ public class HorizontalEdgeProcessor : ImageProcessorBase
1.0,
0.1,
5.0,
- LocalizationHelper.GetString("HorizontalEdgeProcessor_Sensitivity_Desc")));
+ LocalizationHelper.GetString("HorizontalEdgeProcessor_Sensitivity_Desc")) { IsAdvanced = true });
Parameters.Add("Threshold", new ProcessorParameter(
"Threshold",
diff --git a/XP.ImageProcessing.Processors/边缘检测/KirschEdgeProcessor.cs b/XP.ImageProcessing.Processors/边缘检测/KirschEdgeProcessor.cs
index f399eb96..bccd5287 100644
--- a/XP.ImageProcessing.Processors/边缘检测/KirschEdgeProcessor.cs
+++ b/XP.ImageProcessing.Processors/边缘检测/KirschEdgeProcessor.cs
@@ -71,7 +71,7 @@ public class KirschEdgeProcessor : ImageProcessorBase
1.0,
0.1,
5.0,
- LocalizationHelper.GetString("KirschEdgeProcessor_Scale_Desc")));
+ LocalizationHelper.GetString("KirschEdgeProcessor_Scale_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XP.ImageProcessing.Processors/边缘检测/SobelEdgeProcessor.cs b/XP.ImageProcessing.Processors/边缘检测/SobelEdgeProcessor.cs
index a30a00a8..7e85b551 100644
--- a/XP.ImageProcessing.Processors/边缘检测/SobelEdgeProcessor.cs
+++ b/XP.ImageProcessing.Processors/边缘检测/SobelEdgeProcessor.cs
@@ -43,7 +43,7 @@ public class SobelEdgeProcessor : ImageProcessorBase
null,
null,
LocalizationHelper.GetString("SobelEdgeProcessor_Direction_Desc"),
- new string[] { "Both", "Horizontal", "Vertical" }));
+ new string[] { "Both", "Horizontal", "Vertical" }) { IsAdvanced = true });
Parameters.Add("KernelSize", new ProcessorParameter(
"KernelSize",
@@ -52,7 +52,7 @@ public class SobelEdgeProcessor : ImageProcessorBase
3,
1,
7,
- LocalizationHelper.GetString("SobelEdgeProcessor_KernelSize_Desc")));
+ LocalizationHelper.GetString("SobelEdgeProcessor_KernelSize_Desc")) { IsAdvanced = true });
Parameters.Add("Scale", new ProcessorParameter(
"Scale",
@@ -61,7 +61,7 @@ public class SobelEdgeProcessor : ImageProcessorBase
1.0,
0.1,
5.0,
- LocalizationHelper.GetString("SobelEdgeProcessor_Scale_Desc")));
+ LocalizationHelper.GetString("SobelEdgeProcessor_Scale_Desc")) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
diff --git a/XplorePlane/Doc/NOVA/.kiro/settings/mcp.json b/XplorePlane/Doc/NOVA/.kiro/settings/mcp.json
deleted file mode 100644
index 4dfcf89a..00000000
--- a/XplorePlane/Doc/NOVA/.kiro/settings/mcp.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "mcpServers": {
- "nova-hexagon-s-digital-design-system": {
- "type": "http",
- "url": "https://mcp.zeroheight.com/mcp/47b75aea1487aad02a5a1ad939378e390931e2a9"
- }
- }
-}
\ No newline at end of file
diff --git a/XplorePlane/Doc/NOVA/.vscode/settings.json b/XplorePlane/Doc/NOVA/.vscode/settings.json
deleted file mode 100644
index 6f3a2913..00000000
--- a/XplorePlane/Doc/NOVA/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "liveServer.settings.port": 5501
-}
\ No newline at end of file
diff --git a/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs b/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs
index 1e958bca..c1774e77 100644
--- a/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs
+++ b/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs
@@ -84,6 +84,9 @@ namespace XplorePlane.ViewModels.ImageProcessing
public bool HasOutputControls =>
OutputFieldOperatorKeys.Contains(OperatorKey) && Parameters.Any(p => p.IsOutputControl);
+ /// 是否存在高级参数(用于 UI 控制 Expander 显隐)
+ public bool HasAdvancedParameters => Parameters.Any(p => p.IsAdvanced && p.IsVisible && !p.IsOutputControl);
+
public bool IsExecutionEndNode
{
get => _isExecutionEndNode;
diff --git a/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs b/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs
index 3d1e1ae5..f34b8c14 100644
--- a/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs
+++ b/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs
@@ -4,9 +4,9 @@
using Prism.Mvvm;
using System;
using System.Globalization;
-using System.Linq;
-using XP.ImageProcessing.Core;
-using XP.ImageProcessing.Processors;
+using System.Linq;
+using XP.ImageProcessing.Core;
+using XP.ImageProcessing.Processors;
namespace XplorePlane.ViewModels.ImageProcessing
{
@@ -23,11 +23,12 @@ namespace XplorePlane.ViewModels.ImageProcessing
_value = parameter.Value;
MinValue = parameter.MinValue;
MaxValue = parameter.MaxValue;
- Options = parameter.Options;
- LocalizedOptions = parameter.Options?
- .Select(option => LocalizeOption(option))
- .ToArray();
+ Options = parameter.Options;
+ LocalizedOptions = parameter.Options?
+ .Select(option => LocalizeOption(option))
+ .ToArray();
IsVisible = parameter.IsVisible;
+ IsAdvanced = parameter.IsAdvanced;
ParameterType = parameter.ValueType?.Name?.ToLowerInvariant() switch
{
"int32" or "int" => "int",
@@ -42,11 +43,12 @@ namespace XplorePlane.ViewModels.ImageProcessing
public string DisplayName { get; }
public object MinValue { get; }
public object MaxValue { get; }
- public string[]? Options { get; }
- public string[]? LocalizedOptions { get; }
+ public string[]? Options { get; }
+ public string[]? LocalizedOptions { get; }
public bool IsVisible { get; }
+ public bool IsAdvanced { get; }
public string ParameterType { get; }
- public bool HasOptions => Options is { Length: > 0 };
+ public bool HasOptions => Options is { Length: > 0 };
public bool IsBool => ParameterType == "bool";
public bool IsNumeric => ParameterType is "int" or "double";
public bool HasRange => IsNumeric && MinValue != null && MaxValue != null;
@@ -139,17 +141,17 @@ namespace XplorePlane.ViewModels.ImageProcessing
}
}
- public string SelectedOption
- {
- get => HasOptions
- ? GetLocalizedOption(Convert.ToString(_value, CultureInfo.InvariantCulture) ?? string.Empty)
- : string.Empty;
- set
- {
- if (HasOptions)
- Value = GetRawOption(value);
- }
- }
+ public string SelectedOption
+ {
+ get => HasOptions
+ ? GetLocalizedOption(Convert.ToString(_value, CultureInfo.InvariantCulture) ?? string.Empty)
+ : string.Empty;
+ set
+ {
+ if (HasOptions)
+ Value = GetRawOption(value);
+ }
+ }
private void ValidateValue(object value)
{
@@ -283,84 +285,84 @@ namespace XplorePlane.ViewModels.ImageProcessing
return 3;
}
- private static string NormalizeNumericText(string value)
+ private static string NormalizeNumericText(string value)
{
return value.Trim().TrimEnd('、', ',', ',', '。', '.', ';', ';', ':', ':');
- }
-
- private static string LocalizeOption(string option)
- {
- if (string.IsNullOrWhiteSpace(option))
- return option;
-
- // 先允许资源覆盖通用名称;没有资源时使用内置通用翻译,保证历史算子
- // 即使尚未补齐专属资源,也不会把内部英文枚举直接暴露到中文界面。
- var localized = LocalizationHelper.GetString($"ProcessorOption_{option}");
- if (!string.Equals(localized, $"ProcessorOption_{option}", StringComparison.Ordinal))
- return localized;
-
- if (!CultureInfo.CurrentUICulture.Name.StartsWith("zh", StringComparison.OrdinalIgnoreCase))
- return SplitWords(option);
-
- return option switch
- {
- "Uniform" => "均匀分层",
- "Otsu" => "大津法",
- "Peaks" => "波峰分层",
- "EqualSpaced" => "等间距",
- "MidValue" => "层中值",
- "Spectrum" => "光谱",
- "Traffic" => "交通灯",
- "Heat" => "热力图",
- "Fixed" => "固定阈值",
- "TopHat" => "顶帽",
- "LocalContrast" => "局部对比度",
- "AdaptiveStatistics" => "自适应统计",
- "Bright" => "亮区域",
- "Dark" => "暗区域",
- "Both" => "两者",
- "Horizontal" => "水平",
- "Vertical" => "垂直",
- "None" => "无",
- "Polygon" => "多边形",
- "White" => "白色",
- "Black" => "黑色",
- "Nearest" => "最近邻",
- "Bilinear" => "双线性",
- "Bicubic" => "双三次",
- "Lanczos" => "Lanczos",
- "Global" => "全局",
- "CLAHE" => "CLAHE",
- "Linear" => "线性",
- "Logarithmic" => "对数",
- "Exponential" => "指数",
- _ => option
- };
- }
-
- private string GetLocalizedOption(string rawOption)
- {
- if (Options == null || LocalizedOptions == null)
- return rawOption;
-
- var index = Array.IndexOf(Options, rawOption);
- return index >= 0 && index < LocalizedOptions.Length ? LocalizedOptions[index] : rawOption;
- }
-
- private string GetRawOption(string displayOption)
- {
- if (Options == null || LocalizedOptions == null)
- return displayOption;
-
- var index = Array.IndexOf(LocalizedOptions, displayOption);
- return index >= 0 && index < Options.Length ? Options[index] : displayOption;
- }
-
- private static string SplitWords(string value)
- {
- var chars = value.Select((ch, index) => index > 0 && char.IsUpper(ch) ? $" {ch}" : ch.ToString());
- return string.Concat(chars);
- }
+ }
+
+ private static string LocalizeOption(string option)
+ {
+ if (string.IsNullOrWhiteSpace(option))
+ return option;
+
+ // 先允许资源覆盖通用名称;没有资源时使用内置通用翻译,保证历史算子
+ // 即使尚未补齐专属资源,也不会把内部英文枚举直接暴露到中文界面。
+ var localized = LocalizationHelper.GetString($"ProcessorOption_{option}");
+ if (!string.Equals(localized, $"ProcessorOption_{option}", StringComparison.Ordinal))
+ return localized;
+
+ if (!CultureInfo.CurrentUICulture.Name.StartsWith("zh", StringComparison.OrdinalIgnoreCase))
+ return SplitWords(option);
+
+ return option switch
+ {
+ "Uniform" => "均匀分层",
+ "Otsu" => "大津法",
+ "Peaks" => "波峰分层",
+ "EqualSpaced" => "等间距",
+ "MidValue" => "层中值",
+ "Spectrum" => "光谱",
+ "Traffic" => "交通灯",
+ "Heat" => "热力图",
+ "Fixed" => "固定阈值",
+ "TopHat" => "顶帽",
+ "LocalContrast" => "局部对比度",
+ "AdaptiveStatistics" => "自适应统计",
+ "Bright" => "亮区域",
+ "Dark" => "暗区域",
+ "Both" => "两者",
+ "Horizontal" => "水平",
+ "Vertical" => "垂直",
+ "None" => "无",
+ "Polygon" => "多边形",
+ "White" => "白色",
+ "Black" => "黑色",
+ "Nearest" => "最近邻",
+ "Bilinear" => "双线性",
+ "Bicubic" => "双三次",
+ "Lanczos" => "Lanczos",
+ "Global" => "全局",
+ "CLAHE" => "CLAHE",
+ "Linear" => "线性",
+ "Logarithmic" => "对数",
+ "Exponential" => "指数",
+ _ => option
+ };
+ }
+
+ private string GetLocalizedOption(string rawOption)
+ {
+ if (Options == null || LocalizedOptions == null)
+ return rawOption;
+
+ var index = Array.IndexOf(Options, rawOption);
+ return index >= 0 && index < LocalizedOptions.Length ? LocalizedOptions[index] : rawOption;
+ }
+
+ private string GetRawOption(string displayOption)
+ {
+ if (Options == null || LocalizedOptions == null)
+ return displayOption;
+
+ var index = Array.IndexOf(LocalizedOptions, displayOption);
+ return index >= 0 && index < Options.Length ? Options[index] : displayOption;
+ }
+
+ private static string SplitWords(string value)
+ {
+ var chars = value.Select((ch, index) => index > 0 && char.IsUpper(ch) ? $" {ch}" : ch.ToString());
+ return string.Concat(chars);
+ }
private static bool TryConvertToInt(object value, out int result)
{
@@ -453,4 +455,4 @@ namespace XplorePlane.ViewModels.ImageProcessing
}
}
}
-}
+}
diff --git a/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml b/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml
index bebfeb92..4cb51603 100644
--- a/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml
+++ b/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml
@@ -526,6 +526,9 @@
+
+
+
@@ -672,6 +675,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+