From bd7c8e422a6b61dcd62b6bf2ac0b090edf8eed91 Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Tue, 11 Aug 2026 16:29:46 +0800 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=8F=8C=E8=BE=B9?= =?UTF-8?q?=E6=BB=A4=E6=B3=A2=E5=AF=B916=E4=BD=8D=E5=9B=BE=E5=83=8F?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenCV BilateralFilter仅支持8U和32F格式,16位图像(CV_16U)直接传入会抛异常。 修复方式:16位图像先转换为32F执行滤波,再转回原始位深。 --- .../滤波处理/BilateralFilterProcessor.cs | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs index b89fbf98..69604787 100644 --- a/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs +++ b/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs @@ -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 From 561d1dd4777f9ca46f815480a6f4f798af130a1b Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Tue, 11 Aug 2026 16:34:59 +0800 Subject: [PATCH 2/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=AD=E5=80=BC?= =?UTF-8?q?=E6=BB=A4=E6=B3=A2=E5=92=8C=E5=8E=BB=E7=A6=BB=E7=BE=A4=E7=82=B9?= =?UTF-8?q?=E7=AE=97=E5=AD=90=E5=9C=A816=E4=BD=8D=E5=9B=BE=E5=83=8F+?= =?UTF-8?q?=E5=A4=A7=E6=A0=B8=E6=97=B6=E7=9A=84=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenCV MedianBlur在kernel>5时仅支持CV_8U,不支持CV_16U。 修复方式:16位图像且kernel>5时,先转float执行MedianBlur再转回。 涉及文件:MedianFilterProcessor、RemoveOutliersProcessor。 --- .../滤波处理/MedianFilterProcessor.cs | 14 ++++++++++++++ .../滤波处理/RemoveOutliersProcessor.cs | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs index 5b0e446f..9bbe4a4f 100644 --- a/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs +++ b/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs @@ -52,6 +52,20 @@ public class MedianFilterProcessor : ImageProcessorBase if (kernelSize % 2 == 0) kernelSize++; var result = inputImage.Clone(); + + // OpenCV MedianBlur: kernel > 5 only supports CV_8U. + // For 16-bit images with large kernels, convert to float first. + if (kernelSize > 5 && typeof(TDepth) == typeof(ushort)) + { + using var floatInput = inputImage.Convert(); + using var floatResult = floatInput.CopyBlank(); + CvInvoke.MedianBlur(floatInput, floatResult, kernelSize); + var converted = floatResult.Convert(); + _logger.Debug("Process (16-bit via 32F): KernelSize = {KernelSize}", kernelSize); + result.Dispose(); + return converted; + } + CvInvoke.MedianBlur(inputImage, result, kernelSize); _logger.Debug("Process: KernelSize = {KernelSize}", kernelSize); diff --git a/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs index 31fd01f6..d90a2adb 100644 --- a/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs +++ b/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs @@ -89,7 +89,21 @@ public class RemoveOutliersProcessor : ImageProcessorBase // 计算邻域中值图 var medianImage = inputImage.Clone(); - CvInvoke.MedianBlur(inputImage, medianImage, kernelSize); + + // OpenCV MedianBlur: kernel > 5 only supports CV_8U. + // For 16-bit images with large kernels, convert to float first. + if (kernelSize > 5 && typeof(TDepth) == typeof(ushort)) + { + using var floatInput = inputImage.Convert(); + using var floatMedian = floatInput.CopyBlank(); + CvInvoke.MedianBlur(floatInput, floatMedian, kernelSize); + using var converted = floatMedian.Convert(); + Array.Copy(converted.Data, medianImage.Data, converted.Data.Length); + } + else + { + CvInvoke.MedianBlur(inputImage, medianImage, kernelSize); + } // 逐像素比较并替换离群点 var result = inputImage.Clone(); From d4bd69fb1f66942bb582ca3ee261781be382dae5 Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Tue, 11 Aug 2026 16:44:46 +0800 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20=E4=B8=AD=E5=80=BC=E6=BB=A4=E6=B3=A2?= =?UTF-8?q?16=E4=BD=8D=E6=94=AF=E6=8C=81-=E8=BD=ACfloat+=E9=99=90=E5=88=B6?= =?UTF-8?q?kernel=E6=9C=80=E5=A4=A7=E5=80=BC=E4=B8=BA5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenCV MedianBlur不支持CV_16U,CV_32F仅支持kernel 3和5。 修复:16位图像转32F再执行MedianBlur;KernelSize最大值限制为5。 --- .../滤波处理/MedianFilterProcessor.cs | 20 ++++++++----------- .../滤波处理/RemoveOutliersProcessor.cs | 15 +++++++------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/MedianFilterProcessor.cs index 9bbe4a4f..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,24 +51,20 @@ public class MedianFilterProcessor : ImageProcessorBase int kernelSize = GetParameter("KernelSize"); if (kernelSize % 2 == 0) kernelSize++; - var result = inputImage.Clone(); - - // OpenCV MedianBlur: kernel > 5 only supports CV_8U. - // For 16-bit images with large kernels, convert to float first. - if (kernelSize > 5 && typeof(TDepth) == typeof(ushort)) + // 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); - var converted = floatResult.Convert(); _logger.Debug("Process (16-bit via 32F): KernelSize = {KernelSize}", kernelSize); - result.Dispose(); - return converted; + return floatResult.Convert(); } - CvInvoke.MedianBlur(inputImage, result, kernelSize); + 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 d90a2adb..d19405d5 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,20 +88,18 @@ public class RemoveOutliersProcessor : ImageProcessorBase int height = inputImage.Height; // 计算邻域中值图 - var medianImage = inputImage.Clone(); - - // OpenCV MedianBlur: kernel > 5 only supports CV_8U. - // For 16-bit images with large kernels, convert to float first. - if (kernelSize > 5 && typeof(TDepth) == typeof(ushort)) + // 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); - using var converted = floatMedian.Convert(); - Array.Copy(converted.Data, medianImage.Data, converted.Data.Length); + medianImage = floatMedian.Convert(); } else { + medianImage = inputImage.CopyBlank(); CvInvoke.MedianBlur(inputImage, medianImage, kernelSize); } @@ -146,4 +144,5 @@ public class RemoveOutliersProcessor : ImageProcessorBase return result; } + } From 3677bb85a4de021304280acb968c43d25ef9dc15 Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Wed, 12 Aug 2026 09:24:36 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E9=AB=98?= =?UTF-8?q?=E7=BA=A7=E5=8F=82=E6=95=B0=E6=8A=98=E5=8F=A0=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?(IsAdvanced)=E6=A1=86=E6=9E=B6=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProcessorParameter新增IsAdvanced属性 - ProcessorParameterVM导出IsAdvanced供XAML绑定 - PipelineNodeViewModel新增HasAdvancedParameters - PipelineEditorView增加'高级参数'Expander(默认折叠) - 修复Expander内FontWeight继承导致的字体样式不一致 --- .../Core/ProcessorParameter.cs | 3 + .../ImageProcessing/PipelineNodeViewModel.cs | 3 + .../ImageProcessing/ProcessorParameterVM.cs | 198 +++++++++--------- .../ImageProcessing/PipelineEditorView.xaml | 167 +++++++++++++++ 4 files changed, 273 insertions(+), 98 deletions(-) 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/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs b/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs index e12889b3..d733858f 100644 --- a/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs +++ b/XplorePlane/ViewModels/ImageProcessing/PipelineNodeViewModel.cs @@ -70,6 +70,9 @@ namespace XplorePlane.ViewModels.ImageProcessing /// 是否存在输出控制参数(用于 UI 控制 GroupBox 显隐) public bool HasOutputControls => 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 34c6c28c..3be8ff84 100644 --- a/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml +++ b/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml @@ -526,6 +526,9 @@ + + + @@ -672,6 +675,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Wed, 12 Aug 2026 09:25:08 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=E6=BB=A4=E6=B3=A2/=E8=BE=B9?= =?UTF-8?q?=E7=BC=98=E6=A3=80=E6=B5=8B=E7=AE=97=E5=AD=90=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E9=AB=98=E7=BA=A7=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 滤波类:GaussianBlur(Sigma)、Bilateral(SigmaSpace)、 BandPass(FilterType,Order)、ShockFilter(Dt) 边缘检测:Sobel(Direction,KernelSize,Scale)、 HorizontalEdge(Method,Sensitivity)、Kirsch(Scale) --- .../滤波处理/BandPassFilterProcessor.cs | 4 ++-- .../滤波处理/BilateralFilterProcessor.cs | 2 +- .../滤波处理/GaussianBlurProcessor.cs | 2 +- .../滤波处理/ShockFilterProcessor.cs | 2 +- .../边缘检测/HorizontalEdgeProcessor.cs | 4 ++-- .../边缘检测/KirschEdgeProcessor.cs | 2 +- .../边缘检测/SobelEdgeProcessor.cs | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs index 88d3b84b..65d68aaa 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"); } diff --git a/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/BilateralFilterProcessor.cs index 69604787..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"); } 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/滤波处理/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"); } From 7768c086ea78b09afeaa162b0530e82403d8cd7d Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Wed, 12 Aug 2026 09:25:34 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=E5=9B=BE=E5=83=8F=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA/=E5=8F=98=E6=8D=A2=E7=AE=97=E5=AD=90=E6=A0=87?= =?UTF-8?q?=E8=AE=B0=E9=AB=98=E7=BA=A7=E5=8F=82=E6=95=B0+=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=80=BC=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 高级参数标记: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默认开启 --- .../图像变换/RotateProcessor.cs | 6 +++--- .../图像增强/ContrastProcessor.cs | 10 +++++----- .../图像增强/EmbossProcessor.cs | 4 ++-- .../图像增强/GammaProcessor.cs | 2 +- .../图像增强/HDREnhancementProcessor.cs | 10 +++++----- .../图像增强/HierarchicalEnhancementProcessor.cs | 4 ++-- .../图像增强/HistogramEqualizationProcessor.cs | 6 +++--- .../图像增强/RetinexProcessor.cs | 10 +++++----- .../图像增强/SharpenProcessor.cs | 6 +++--- 9 files changed, 29 insertions(+), 29 deletions(-) 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/图像增强/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/图像增强/GammaProcessor.cs b/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs index 2f3b0de5..7345562a 100644 --- a/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs +++ b/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs @@ -53,7 +53,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"); } 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"); } From 3ffcb2452ad5ccbc9f50a982992cbc5cbf9c29d5 Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Wed, 12 Aug 2026 09:32:32 +0800 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4'=E5=85=B6?= =?UTF-8?q?=E4=BB=96'=E5=88=86=E7=B1=BB=EF=BC=8C=E7=AE=97=E5=AD=90?= =?UTF-8?q?=E6=8C=89=E5=8A=9F=E8=83=BD=E5=BD=92=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FilmEffectProcessor → 图像增强(窗宽窗位本质是增强/显示调整) - PseudoColorProcessor → 图像变换(伪色彩映射是可视化变换) - 删除空的'其他'文件夹 --- .../PseudoColorProcessor.cs | 308 ++++++------- .../{其他 => 图像增强}/FilmEffectProcessor.cs | 426 +++++++++--------- XplorePlane/Doc/NOVA/.kiro/settings/mcp.json | 8 - XplorePlane/Doc/NOVA/.vscode/settings.json | 3 - 4 files changed, 367 insertions(+), 378 deletions(-) rename XP.ImageProcessing.Processors/{其他 => 图像变换}/PseudoColorProcessor.cs (97%) rename XP.ImageProcessing.Processors/{其他 => 图像增强}/FilmEffectProcessor.cs (97%) delete mode 100644 XplorePlane/Doc/NOVA/.kiro/settings/mcp.json delete mode 100644 XplorePlane/Doc/NOVA/.vscode/settings.json 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/其他/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/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 From bbb3bf5326172b6b5199a25695c9d08639dcbe18 Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Mon, 17 Aug 2026 09:40:31 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=AE=97=E5=AD=90?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=AE=A1=E6=9F=A5=E5=8F=91=E7=8E=B0=E7=9A=84?= =?UTF-8?q?=E5=85=B3=E9=94=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 资源泄漏:HighPass/LowPass滤波器Image对象未dispose(改用using+Clone) 2. 线程安全:GammaProcessor LUT从实例字段改为方法局部变量 3. 资源泄漏:BandPassFilter补充floatImage和mask的Dispose 4. 边界情况:ThresholdProcessor Otsu16初始阈值改为中值(防全黑/全白) 5. 异常安全:RemoveOutliers的medianImage用try-finally保证释放 6. 空引用:SuperResolution InputMetadata空检查 --- .../图像变换/ThresholdProcessor.cs | 20 ++++--- .../图像增强/GammaProcessor.cs | 22 ++++---- .../图像增强/SuperResolutionProcessor.cs | 3 +- .../滤波处理/BandPassFilterProcessor.cs | 4 +- .../滤波处理/HighPassFilterProcessor.cs | 4 +- .../滤波处理/LowPassFilterProcessor.cs | 4 +- .../滤波处理/RemoveOutliersProcessor.cs | 55 ++++++++++--------- 7 files changed, 62 insertions(+), 50 deletions(-) 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/图像增强/GammaProcessor.cs b/XP.ImageProcessing.Processors/图像增强/GammaProcessor.cs index 7345562a..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() @@ -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/图像增强/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 65d68aaa..57e58326 100644 --- a/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs +++ b/XP.ImageProcessing.Processors/滤波处理/BandPassFilterProcessor.cs @@ -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/滤波处理/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/滤波处理/RemoveOutliersProcessor.cs b/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs index d19405d5..8b8438ba 100644 --- a/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs +++ b/XP.ImageProcessing.Processors/滤波处理/RemoveOutliersProcessor.cs @@ -106,38 +106,43 @@ public class RemoveOutliersProcessor : ImageProcessorBase // 逐像素比较并替换离群点 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);