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);