From bbb3bf5326172b6b5199a25695c9d08639dcbe18 Mon Sep 17 00:00:00 2001 From: "wei.lw.li" Date: Mon, 17 Aug 2026 09:40:31 +0800 Subject: [PATCH] =?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);