feat: 改进BGA焊点和气泡检测算法,提升复杂背景下的检测精度
- 焊点检测:增加开运算去噪、闭运算改为1次减少粘连、提高默认圆度阈值0.6 - 新增焊球最小中心距过滤和面积一致性过滤,去除粘连和背景误检 - 气泡检测:新增自适应阈值,基于焊球内部灰度分布自动计算阈值 - BGA检测面板增加新参数控件:自适应阈值开关、阈值百分位、中心距、面积一致性
This commit is contained in:
@@ -82,10 +82,30 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
Parameters.Add("BgaCircularity", new ProcessorParameter(
|
Parameters.Add("BgaCircularity", new ProcessorParameter(
|
||||||
"BgaCircularity",
|
"BgaCircularity",
|
||||||
LocalizationHelper.GetString("BgaVoidRateProcessor_BgaCircularity"),
|
LocalizationHelper.GetString("BgaVoidRateProcessor_BgaCircularity"),
|
||||||
typeof(double), 0.5, 0.0, 1.0,
|
typeof(double), 0.6, 0.0, 1.0,
|
||||||
LocalizationHelper.GetString("BgaVoidRateProcessor_BgaCircularity_Desc")));
|
LocalizationHelper.GetString("BgaVoidRateProcessor_BgaCircularity_Desc")));
|
||||||
|
|
||||||
|
Parameters.Add("BgaMinCenterDistance", new ProcessorParameter(
|
||||||
|
"BgaMinCenterDistance",
|
||||||
|
"焊球最小中心距", typeof(int), 0, 0, 5000,
|
||||||
|
"焊球中心之间的最小距离(像素),0=不启用。用于去除粘连和背景误检"));
|
||||||
|
|
||||||
|
Parameters.Add("BgaAreaConsistency", new ProcessorParameter(
|
||||||
|
"BgaAreaConsistency",
|
||||||
|
"焊球面积一致性", typeof(double), 0.5, 0.0, 1.0,
|
||||||
|
"焊球面积一致性系数(0-1),去除与均值偏差过大的离群焊球。0=不启用"));
|
||||||
|
|
||||||
// ── 第二步:气泡检测参数(阈值 0-65535)──
|
// ── 第二步:气泡检测参数(阈值 0-65535)──
|
||||||
|
Parameters.Add("UseAdaptiveThreshold", new ProcessorParameter(
|
||||||
|
"UseAdaptiveThreshold",
|
||||||
|
"自适应阈值", typeof(bool), false, null, null,
|
||||||
|
"启用自适应阈值后,每个焊球的气泡阈值根据其内部灰度分布自动计算"));
|
||||||
|
|
||||||
|
Parameters.Add("VoidThresholdPercentile", new ProcessorParameter(
|
||||||
|
"VoidThresholdPercentile",
|
||||||
|
"气泡阈值百分位", typeof(double), 80.0, 0.0, 100.0,
|
||||||
|
"自适应阈值百分位(0-100),例如80表示取焊球内部灰度第80百分位作为气泡阈值"));
|
||||||
|
|
||||||
Parameters.Add("MinThreshold", new ProcessorParameter(
|
Parameters.Add("MinThreshold", new ProcessorParameter(
|
||||||
"MinThreshold",
|
"MinThreshold",
|
||||||
LocalizationHelper.GetString("BgaVoidRateProcessor_MinThreshold"),
|
LocalizationHelper.GetString("BgaVoidRateProcessor_MinThreshold"),
|
||||||
@@ -131,6 +151,10 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
double bgaCircularity = GetParameter<double>("BgaCircularity");
|
double bgaCircularity = GetParameter<double>("BgaCircularity");
|
||||||
int bgaThreshLow = GetParameter<int>("BgaThresholdLow");
|
int bgaThreshLow = GetParameter<int>("BgaThresholdLow");
|
||||||
int bgaThreshHigh = GetParameter<int>("BgaThresholdHigh");
|
int bgaThreshHigh = GetParameter<int>("BgaThresholdHigh");
|
||||||
|
int bgaMinCenterDist = GetParameter<int>("BgaMinCenterDistance");
|
||||||
|
double bgaAreaConsistency = GetParameter<double>("BgaAreaConsistency");
|
||||||
|
bool useAdaptiveThreshold = GetParameter<bool>("UseAdaptiveThreshold");
|
||||||
|
double voidThresholdPercentile = GetParameter<double>("VoidThresholdPercentile");
|
||||||
int minThresh = GetParameter<int>("MinThreshold");
|
int minThresh = GetParameter<int>("MinThreshold");
|
||||||
int maxThresh = GetParameter<int>("MaxThreshold");
|
int maxThresh = GetParameter<int>("MaxThreshold");
|
||||||
int minVoidArea = GetParameter<int>("MinVoidArea");
|
int minVoidArea = GetParameter<int>("MinVoidArea");
|
||||||
@@ -166,7 +190,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
// 第一步:BGA焊球定位(在 16 位图上做双阈值分割)
|
// 第一步:BGA焊球定位(在 16 位图上做双阈值分割)
|
||||||
// ================================================================
|
// ================================================================
|
||||||
var bgaResults = DetectBgaBalls(inputImage, bgaBlurSize, bgaMinArea, bgaMaxArea,
|
var bgaResults = DetectBgaBalls(inputImage, bgaBlurSize, bgaMinArea, bgaMaxArea,
|
||||||
bgaCircularity, bgaThreshLow, bgaThreshHigh, roiMask);
|
bgaCircularity, bgaThreshLow, bgaThreshHigh, bgaMinCenterDist, bgaAreaConsistency, roiMask);
|
||||||
|
|
||||||
_logger.Information("第一步完成: 检测到 {Count} 个BGA焊球", bgaResults.Count);
|
_logger.Information("第一步完成: 检测到 {Count} 个BGA焊球", bgaResults.Count);
|
||||||
|
|
||||||
@@ -196,7 +220,8 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
|
|
||||||
foreach (var bga in bgaResults)
|
foreach (var bga in bgaResults)
|
||||||
{
|
{
|
||||||
DetectVoidsInBga(inputImage, bga, minThresh, maxThresh, minVoidArea);
|
DetectVoidsInBga(inputImage, bga, minThresh, maxThresh, minVoidArea,
|
||||||
|
useAdaptiveThreshold, voidThresholdPercentile);
|
||||||
totalBgaArea += bga.BgaArea;
|
totalBgaArea += bga.BgaArea;
|
||||||
totalVoidArea += bga.VoidPixels;
|
totalVoidArea += bga.VoidPixels;
|
||||||
totalVoidCount += bga.Voids.Count;
|
totalVoidCount += bga.Voids.Count;
|
||||||
@@ -229,12 +254,14 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 第一步:BGA焊球定位(16 位双阈值分割 → 8 位二值图 → FindContours)
|
/// 第一步:BGA焊球定位(16 位双阈值分割 → 8 位二值图 → 形态学 → FindContours → 过滤)
|
||||||
|
/// 改进:增加开运算去噪、闭运算1次防粘连、距离过滤、面积一致性过滤
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<BgaBallInfo> DetectBgaBalls(
|
private List<BgaBallInfo> DetectBgaBalls(
|
||||||
Image<Gray, ushort> input, int blurSize,
|
Image<Gray, ushort> input, int blurSize,
|
||||||
int minArea, int maxArea, double minCircularity,
|
int minArea, int maxArea, double minCircularity,
|
||||||
int threshLow, int threshHigh, Image<Gray, byte>? roiMask)
|
int threshLow, int threshHigh, int minCenterDist, double areaConsistency,
|
||||||
|
Image<Gray, byte>? roiMask)
|
||||||
{
|
{
|
||||||
var results = new List<BgaBallInfo>();
|
var results = new List<BgaBallInfo>();
|
||||||
int w = input.Width, h = input.Height;
|
int w = input.Width, h = input.Height;
|
||||||
@@ -258,9 +285,13 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
if (roiMask != null)
|
if (roiMask != null)
|
||||||
CvInvoke.BitwiseAnd(binary, roiMask, binary);
|
CvInvoke.BitwiseAnd(binary, roiMask, binary);
|
||||||
|
|
||||||
// 形态学闭运算
|
|
||||||
using var kernel = CvInvoke.GetStructuringElement(ElementShape.Ellipse, new Size(5, 5), new Point(-1, -1));
|
using var kernel = CvInvoke.GetStructuringElement(ElementShape.Ellipse, new Size(5, 5), new Point(-1, -1));
|
||||||
CvInvoke.MorphologyEx(binary, binary, MorphOp.Close, kernel, new Point(-1, -1), 2, BorderType.Default, new MCvScalar(0));
|
|
||||||
|
// 开运算去除小噪点(先腐蚀后膨胀)
|
||||||
|
CvInvoke.MorphologyEx(binary, binary, MorphOp.Open, kernel, new Point(-1, -1), 1, BorderType.Default, new MCvScalar(0));
|
||||||
|
|
||||||
|
// 闭运算填补焊球内部小孔(迭代1次,减少粘连)
|
||||||
|
CvInvoke.MorphologyEx(binary, binary, MorphOp.Close, kernel, new Point(-1, -1), 1, BorderType.Default, new MCvScalar(0));
|
||||||
|
|
||||||
// 轮廓检测
|
// 轮廓检测
|
||||||
using var contours = new VectorOfVectorOfPoint();
|
using var contours = new VectorOfVectorOfPoint();
|
||||||
@@ -297,6 +328,39 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 距离过滤:去除中心距过近的粘连轮廓 ──
|
||||||
|
if (minCenterDist > 0 && results.Count > 1)
|
||||||
|
{
|
||||||
|
var filtered = new List<BgaBallInfo>();
|
||||||
|
foreach (var bga in results)
|
||||||
|
{
|
||||||
|
bool tooClose = false;
|
||||||
|
foreach (var other in filtered)
|
||||||
|
{
|
||||||
|
double dx = bga.CenterX - other.CenterX;
|
||||||
|
double dy = bga.CenterY - other.CenterY;
|
||||||
|
double dist = Math.Sqrt(dx * dx + dy * dy);
|
||||||
|
if (dist < minCenterDist)
|
||||||
|
{
|
||||||
|
tooClose = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tooClose)
|
||||||
|
filtered.Add(bga);
|
||||||
|
}
|
||||||
|
results = filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 面积一致性过滤:去除与均值偏差过大的离群焊球 ──
|
||||||
|
if (areaConsistency > 0 && results.Count > 2)
|
||||||
|
{
|
||||||
|
double avgArea = results.Average(r => r.BgaArea);
|
||||||
|
double minAllowed = avgArea * (1.0 - areaConsistency);
|
||||||
|
double maxAllowed = avgArea * (1.0 + areaConsistency);
|
||||||
|
results = results.Where(r => r.BgaArea >= minAllowed && r.BgaArea <= maxAllowed).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
results.Sort((a, b) => b.BgaArea.CompareTo(a.BgaArea));
|
results.Sort((a, b) => b.BgaArea.CompareTo(a.BgaArea));
|
||||||
for (int i = 0; i < results.Count; i++) results[i].Index = i + 1;
|
for (int i = 0; i < results.Count; i++) results[i].Index = i + 1;
|
||||||
|
|
||||||
@@ -308,8 +372,9 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 第二步:在焊球区域内检测气泡(16 位双阈值分割 → 8 位二值图 → FindContours)
|
/// 第二步:在焊球区域内检测气泡(16 位双阈值分割 → 8 位二值图 → FindContours)
|
||||||
|
/// 改进:支持自适应阈值,基于焊球内部灰度分布自动计算气泡阈值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DetectVoidsInBga(Image<Gray, ushort> input, BgaBallInfo bga, int minThresh, int maxThresh, int minVoidArea)
|
private void DetectVoidsInBga(Image<Gray, ushort> input, BgaBallInfo bga, int minThresh, int maxThresh, int minVoidArea, bool useAdaptiveThreshold, double thresholdPercentile)
|
||||||
{
|
{
|
||||||
int w = input.Width, h = input.Height;
|
int w = input.Width, h = input.Height;
|
||||||
|
|
||||||
@@ -324,19 +389,45 @@ public class BgaVoidRateProcessor : ImageProcessorBase<ushort>
|
|||||||
int bgaPixels = CvInvoke.CountNonZero(mask);
|
int bgaPixels = CvInvoke.CountNonZero(mask);
|
||||||
bga.BgaArea = bgaPixels;
|
bga.BgaArea = bgaPixels;
|
||||||
|
|
||||||
|
// 确定气泡阈值
|
||||||
|
int effectiveMinThresh = minThresh;
|
||||||
|
int effectiveMaxThresh = maxThresh;
|
||||||
|
|
||||||
|
if (useAdaptiveThreshold && bgaPixels > 0)
|
||||||
|
{
|
||||||
|
// 收集焊球内部所有像素灰度值
|
||||||
|
var grayValues = new List<ushort>();
|
||||||
|
var srcData = input.Data;
|
||||||
|
var maskData = mask.Data;
|
||||||
|
|
||||||
|
for (int y = 0; y < h; y++)
|
||||||
|
for (int x = 0; x < w; x++)
|
||||||
|
if (maskData[y, x, 0] > 0)
|
||||||
|
grayValues.Add(srcData[y, x, 0]);
|
||||||
|
|
||||||
|
if (grayValues.Count > 0)
|
||||||
|
{
|
||||||
|
grayValues.Sort();
|
||||||
|
int percentileIndex = (int)(grayValues.Count * thresholdPercentile / 100.0);
|
||||||
|
percentileIndex = Math.Min(percentileIndex, grayValues.Count - 1);
|
||||||
|
effectiveMinThresh = grayValues[percentileIndex];
|
||||||
|
effectiveMaxThresh = 65535;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 在 16 位图上做双阈值分割,输出 8 位二值图
|
// 在 16 位图上做双阈值分割,输出 8 位二值图
|
||||||
var voidImg = new Image<Gray, byte>(w, h);
|
var voidImg = new Image<Gray, byte>(w, h);
|
||||||
var srcData = input.Data;
|
var srcData2 = input.Data;
|
||||||
var dstData = voidImg.Data;
|
var dstData = voidImg.Data;
|
||||||
var maskData = mask.Data;
|
var maskData2 = mask.Data;
|
||||||
|
|
||||||
for (int y = 0; y < h; y++)
|
for (int y = 0; y < h; y++)
|
||||||
for (int x = 0; x < w; x++)
|
for (int x = 0; x < w; x++)
|
||||||
{
|
{
|
||||||
if (maskData[y, x, 0] > 0)
|
if (maskData2[y, x, 0] > 0)
|
||||||
{
|
{
|
||||||
ushort val = srcData[y, x, 0];
|
ushort val = srcData2[y, x, 0];
|
||||||
dstData[y, x, 0] = (val >= minThresh && val <= maxThresh) ? (byte)255 : (byte)0;
|
dstData[y, x, 0] = (val >= effectiveMinThresh && val <= effectiveMaxThresh) ? (byte)255 : (byte)0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,9 +79,15 @@ namespace XplorePlane.ViewModels.ImageProcessing
|
|||||||
private int _blurSize = 5;
|
private int _blurSize = 5;
|
||||||
public int BlurSize { get => _blurSize; set => SetProperty(ref _blurSize, value); }
|
public int BlurSize { get => _blurSize; set => SetProperty(ref _blurSize, value); }
|
||||||
|
|
||||||
private double _circularity = 0.5;
|
private double _circularity = 0.6;
|
||||||
public double Circularity { get => _circularity; set => SetProperty(ref _circularity, value); }
|
public double Circularity { get => _circularity; set => SetProperty(ref _circularity, value); }
|
||||||
|
|
||||||
|
private int _bgaMinCenterDistance = 0;
|
||||||
|
public int BgaMinCenterDistance { get => _bgaMinCenterDistance; set => SetProperty(ref _bgaMinCenterDistance, value); }
|
||||||
|
|
||||||
|
private double _bgaAreaConsistency = 0.5;
|
||||||
|
public double BgaAreaConsistency { get => _bgaAreaConsistency; set => SetProperty(ref _bgaAreaConsistency, value); }
|
||||||
|
|
||||||
// BGA定位阈值参数
|
// BGA定位阈值参数
|
||||||
private int _bgaThresholdLow = 0;
|
private int _bgaThresholdLow = 0;
|
||||||
public int BgaThresholdLow { get => _bgaThresholdLow; set => SetProperty(ref _bgaThresholdLow, value); }
|
public int BgaThresholdLow { get => _bgaThresholdLow; set => SetProperty(ref _bgaThresholdLow, value); }
|
||||||
@@ -96,6 +102,12 @@ namespace XplorePlane.ViewModels.ImageProcessing
|
|||||||
private int _maxThreshold = 65535;
|
private int _maxThreshold = 65535;
|
||||||
public int MaxThreshold { get => _maxThreshold; set => SetProperty(ref _maxThreshold, value); }
|
public int MaxThreshold { get => _maxThreshold; set => SetProperty(ref _maxThreshold, value); }
|
||||||
|
|
||||||
|
private bool _useAdaptiveThreshold = false;
|
||||||
|
public bool UseAdaptiveThreshold { get => _useAdaptiveThreshold; set => SetProperty(ref _useAdaptiveThreshold, value); }
|
||||||
|
|
||||||
|
private double _voidThresholdPercentile = 80.0;
|
||||||
|
public double VoidThresholdPercentile { get => _voidThresholdPercentile; set => SetProperty(ref _voidThresholdPercentile, value); }
|
||||||
|
|
||||||
private int _minVoidArea = 10;
|
private int _minVoidArea = 10;
|
||||||
public int MinVoidArea { get => _minVoidArea; set => SetProperty(ref _minVoidArea, value); }
|
public int MinVoidArea { get => _minVoidArea; set => SetProperty(ref _minVoidArea, value); }
|
||||||
|
|
||||||
@@ -315,6 +327,10 @@ namespace XplorePlane.ViewModels.ImageProcessing
|
|||||||
processor.SetParameter("BgaCircularity", Circularity);
|
processor.SetParameter("BgaCircularity", Circularity);
|
||||||
processor.SetParameter("BgaThresholdLow", BgaThresholdLow);
|
processor.SetParameter("BgaThresholdLow", BgaThresholdLow);
|
||||||
processor.SetParameter("BgaThresholdHigh", BgaThresholdHigh);
|
processor.SetParameter("BgaThresholdHigh", BgaThresholdHigh);
|
||||||
|
processor.SetParameter("BgaMinCenterDistance", BgaMinCenterDistance);
|
||||||
|
processor.SetParameter("BgaAreaConsistency", BgaAreaConsistency);
|
||||||
|
processor.SetParameter("UseAdaptiveThreshold", UseAdaptiveThreshold);
|
||||||
|
processor.SetParameter("VoidThresholdPercentile", VoidThresholdPercentile);
|
||||||
processor.SetParameter("MinThreshold", MinThreshold);
|
processor.SetParameter("MinThreshold", MinThreshold);
|
||||||
processor.SetParameter("MaxThreshold", MaxThreshold);
|
processor.SetParameter("MaxThreshold", MaxThreshold);
|
||||||
processor.SetParameter("MinVoidArea", MinVoidArea);
|
processor.SetParameter("MinVoidArea", MinVoidArea);
|
||||||
@@ -471,6 +487,10 @@ namespace XplorePlane.ViewModels.ImageProcessing
|
|||||||
parameters["BgaCircularity"] = Circularity;
|
parameters["BgaCircularity"] = Circularity;
|
||||||
parameters["BgaThresholdLow"] = BgaThresholdLow;
|
parameters["BgaThresholdLow"] = BgaThresholdLow;
|
||||||
parameters["BgaThresholdHigh"] = BgaThresholdHigh;
|
parameters["BgaThresholdHigh"] = BgaThresholdHigh;
|
||||||
|
parameters["BgaMinCenterDistance"] = BgaMinCenterDistance;
|
||||||
|
parameters["BgaAreaConsistency"] = BgaAreaConsistency;
|
||||||
|
parameters["UseAdaptiveThreshold"] = UseAdaptiveThreshold;
|
||||||
|
parameters["VoidThresholdPercentile"] = VoidThresholdPercentile;
|
||||||
parameters["MinThreshold"] = MinThreshold;
|
parameters["MinThreshold"] = MinThreshold;
|
||||||
parameters["MaxThreshold"] = MaxThreshold;
|
parameters["MaxThreshold"] = MaxThreshold;
|
||||||
parameters["MinVoidArea"] = MinVoidArea;
|
parameters["MinVoidArea"] = MinVoidArea;
|
||||||
|
|||||||
@@ -114,10 +114,20 @@
|
|||||||
<Slider Minimum="1" Maximum="31" Value="{Binding BlurSize}" VerticalAlignment="Center" />
|
<Slider Minimum="1" Maximum="31" Value="{Binding BlurSize}" VerticalAlignment="Center" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<TextBlock Text="圆度阈值" Style="{StaticResource ParamLabel}" />
|
<TextBlock Text="圆度阈值" Style="{StaticResource ParamLabel}" />
|
||||||
<DockPanel>
|
<DockPanel Margin="0,0,0,6">
|
||||||
<TextBox DockPanel.Dock="Right" Width="55" Text="{Binding Circularity, StringFormat=F2, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Margin="6,0,0,0" />
|
<TextBox DockPanel.Dock="Right" Width="55" Text="{Binding Circularity, StringFormat=F2, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Margin="6,0,0,0" />
|
||||||
<Slider Minimum="0" Maximum="1" Value="{Binding Circularity}" SmallChange="0.01" LargeChange="0.1" VerticalAlignment="Center" />
|
<Slider Minimum="0" Maximum="1" Value="{Binding Circularity}" SmallChange="0.01" LargeChange="0.1" VerticalAlignment="Center" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
<TextBlock Text="焊球最小中心距" Style="{StaticResource ParamLabel}" />
|
||||||
|
<DockPanel Margin="0,0,0,6">
|
||||||
|
<TextBox DockPanel.Dock="Right" Width="55" Text="{Binding BgaMinCenterDistance, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Margin="6,0,0,0" />
|
||||||
|
<Slider Minimum="0" Maximum="500" Value="{Binding BgaMinCenterDistance}" VerticalAlignment="Center" />
|
||||||
|
</DockPanel>
|
||||||
|
<TextBlock Text="焊球面积一致性" Style="{StaticResource ParamLabel}" />
|
||||||
|
<DockPanel>
|
||||||
|
<TextBox DockPanel.Dock="Right" Width="55" Text="{Binding BgaAreaConsistency, StringFormat=F2, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Margin="6,0,0,0" />
|
||||||
|
<Slider Minimum="0" Maximum="1" Value="{Binding BgaAreaConsistency}" SmallChange="0.01" LargeChange="0.1" VerticalAlignment="Center" />
|
||||||
|
</DockPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@@ -125,6 +135,12 @@
|
|||||||
<Border Style="{StaticResource CardStyle}">
|
<Border Style="{StaticResource CardStyle}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="气泡检测参数" FontWeight="SemiBold" FontSize="12" Margin="0,0,0,8" />
|
<TextBlock Text="气泡检测参数" FontWeight="SemiBold" FontSize="12" Margin="0,0,0,8" />
|
||||||
|
<CheckBox IsChecked="{Binding UseAdaptiveThreshold}" Content="使用自适应阈值" FontSize="11" Margin="0,0,0,6" Foreground="#555" />
|
||||||
|
<TextBlock Text="气泡阈值百分位" Style="{StaticResource ParamLabel}" />
|
||||||
|
<DockPanel Margin="0,0,0,6">
|
||||||
|
<TextBox DockPanel.Dock="Right" Width="55" Text="{Binding VoidThresholdPercentile, StringFormat=F1, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Margin="6,0,0,0" />
|
||||||
|
<Slider Minimum="0" Maximum="100" Value="{Binding VoidThresholdPercentile}" VerticalAlignment="Center" />
|
||||||
|
</DockPanel>
|
||||||
<TextBlock Text="最小灰度阈值" Style="{StaticResource ParamLabel}" />
|
<TextBlock Text="最小灰度阈值" Style="{StaticResource ParamLabel}" />
|
||||||
<DockPanel Margin="0,0,0,6">
|
<DockPanel Margin="0,0,0,6">
|
||||||
<TextBox DockPanel.Dock="Right" Width="55" Text="{Binding MinThreshold, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Margin="6,0,0,0" />
|
<TextBox DockPanel.Dock="Right" Width="55" Text="{Binding MinThreshold, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" Margin="6,0,0,0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user