fix: 修复算子代码审查发现的关键问题

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空检查
This commit is contained in:
wei.lw.li
2026-08-17 09:40:31 +08:00
parent 3ffcb2452a
commit bbb3bf5326
7 changed files with 62 additions and 50 deletions
@@ -128,6 +128,8 @@ public class ThresholdProcessor<TDepth> : ImageProcessorBase<TDepth>
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<TDepth> : ImageProcessorBase<TDepth>
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++)
{
@@ -25,8 +25,6 @@ namespace XP.ImageProcessing.Processors;
public class GammaProcessor<TDepth> : ImageProcessorBase<TDepth>
where TDepth : struct, IComparable
{
private byte[] _lookupTable8 = new byte[256];
private ushort[] _lookupTable16 = new ushort[65536];
private static readonly ILogger _logger = Log.ForContext<GammaProcessor<TDepth>>();
public GammaProcessor()
@@ -64,49 +62,53 @@ public class GammaProcessor<TDepth> : ImageProcessorBase<TDepth>
if (typeof(TDepth) == typeof(ushort))
{
BuildLUT16(gamma, gain);
var lut16 = BuildLUT16(gamma, gain);
var img16 = inputImage as Image<Gray, ushort>;
var result = new Image<Gray, ushort>(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<Gray, TDepth>)!;
}
else
{
BuildLUT8(gamma, gain);
var lut8 = BuildLUT8(gamma, gain);
var result = (inputImage as Image<Gray, byte>)!.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<Gray, TDepth>)!;
}
}
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;
}
}
@@ -145,7 +145,8 @@ public class SuperResolutionProcessor : ImageProcessorBase<byte>
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
@@ -85,7 +85,7 @@ public class BandPassFilterProcessor<TDepth> : ImageProcessorBase<TDepth>
if (highCutoff <= lowCutoff) highCutoff = lowCutoff + 10;
var floatImage = inputImage.Convert<Gray, float>();
var imaginaryImage = new Image<Gray, float>(floatImage.Size);
using var imaginaryImage = new Image<Gray, float>(floatImage.Size);
imaginaryImage.SetZero();
using (var planes = new Emgu.CV.Util.VectorOfMat())
@@ -139,6 +139,8 @@ public class BandPassFilterProcessor<TDepth> : ImageProcessorBase<TDepth>
result = (result - minVal) * (255.0 / (maxVal - minVal));
}
floatImage.Dispose();
mask.Dispose();
complexMat.Dispose();
dftMat.Dispose();
filteredDft.Dispose();
@@ -126,7 +126,7 @@ public class HighPassFilterProcessor<TDepth> : ImageProcessorBase<TDepth>
/// </summary>
private Mat CreateHighPassFilter(int rows, int cols, double d0)
{
var filter = new Image<Gray, float>(cols, rows);
using var filter = new Image<Gray, float>(cols, rows);
int centerX = cols / 2;
int centerY = rows / 2;
@@ -141,6 +141,6 @@ public class HighPassFilterProcessor<TDepth> : ImageProcessorBase<TDepth>
}
}
return filter.Mat;
return filter.Mat.Clone();
}
}
@@ -122,7 +122,7 @@ public class LowPassFilterProcessor<TDepth> : ImageProcessorBase<TDepth>
/// </summary>
private Mat CreateLowPassFilter(int rows, int cols, double d0)
{
var filter = new Image<Gray, float>(cols, rows);
using var filter = new Image<Gray, float>(cols, rows);
int centerX = cols / 2;
int centerY = rows / 2;
@@ -137,6 +137,6 @@ public class LowPassFilterProcessor<TDepth> : ImageProcessorBase<TDepth>
}
}
return filter.Mat;
return filter.Mat.Clone();
}
}
@@ -106,6 +106,8 @@ public class RemoveOutliersProcessor<TDepth> : ImageProcessorBase<TDepth>
// 逐像素比较并替换离群点
var result = inputImage.Clone();
try
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
@@ -136,8 +138,11 @@ public class RemoveOutliersProcessor<TDepth> : ImageProcessorBase<TDepth>
}
}
}
}
finally
{
medianImage.Dispose();
}
_logger.Debug("Process: KernelSize={K}, Threshold={T}, Type={Type}",
kernelSize, threshold, outlierType);