Skip to content

Commit da7a031

Browse files
committed
Adaptive duplicate detection for quadrilaterals
Duplicate detection now uses a threshold based on 2% of the image's short side, instead of a fixed pixel value. Duplicates are only considered if all corresponding corners are within this threshold, and the smallest quadrilateral by area is retained. Updated `FilterDuplicates` and `AreDuplicates` methods to support this logic.
1 parent 51cc353 commit da7a031

File tree

1 file changed

+30
-23
lines changed

1 file changed

+30
-23
lines changed

MagickCrop/Helpers/QuadrilateralDetector.cs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public static class QuadrilateralDetector
2020
private const double SizeWeight = 0.6;
2121
private const double RectangularityWeight = 0.4;
2222

23-
// Duplicate detection threshold - quadrilaterals with corners closer than this are considered duplicates
24-
private const double DuplicateDistanceThreshold = 5.0; // pixels
23+
// Duplicate detection: fraction of the image's short side used as the proximity diameter
24+
private const double DuplicateDiameterFraction = 0.02;
2525

2626
/// <summary>
2727
/// Represents a detected quadrilateral with its corner points
@@ -185,8 +185,8 @@ public static DetectionResult DetectQuadrilateralsWithDimensions(string imagePat
185185
}
186186
}
187187

188-
// Remove duplicates before sorting
189-
result.Quadrilaterals = FilterDuplicates(result.Quadrilaterals);
188+
// Remove duplicates before sorting (threshold is 2% of the short side)
189+
result.Quadrilaterals = FilterDuplicates(result.Quadrilaterals, result.ImageWidth, result.ImageHeight);
190190

191191
// Sort by confidence (highest first) and take top results
192192
result.Quadrilaterals = [.. result.Quadrilaterals.OrderByDescending(q => q.Confidence).Take(maxResults)];
@@ -302,25 +302,36 @@ private static double CalculateAngle(System.Windows.Point p1, System.Windows.Poi
302302
}
303303

304304
/// <summary>
305-
/// Filter out duplicate quadrilaterals that have very similar corner positions
305+
/// Filter out duplicate quadrilaterals that have very similar corner positions.
306+
/// Two quads are duplicates when every corresponding corner is within a circle
307+
/// whose diameter is 2% of the image's short side. Among duplicates the smallest
308+
/// quadrilateral (by area) is kept.
306309
/// </summary>
307-
private static List<DetectedQuadrilateral> FilterDuplicates(List<DetectedQuadrilateral> quadrilaterals)
310+
private static List<DetectedQuadrilateral> FilterDuplicates(
311+
List<DetectedQuadrilateral> quadrilaterals, double imageWidth, double imageHeight)
308312
{
313+
double shortSide = Math.Min(imageWidth, imageHeight);
314+
double threshold = DuplicateDiameterFraction * shortSide;
315+
309316
List<DetectedQuadrilateral> filtered = [];
310317

311318
foreach (DetectedQuadrilateral quad in quadrilaterals)
312319
{
313-
bool isDuplicate = false;
314-
foreach (DetectedQuadrilateral existing in filtered)
320+
bool merged = false;
321+
for (int i = 0; i < filtered.Count; i++)
315322
{
316-
if (AreDuplicates(quad, existing))
323+
if (AreDuplicates(quad, filtered[i], threshold))
317324
{
318-
isDuplicate = true;
325+
// Keep the smaller quadrilateral
326+
if (quad.Area < filtered[i].Area)
327+
filtered[i] = quad;
328+
329+
merged = true;
319330
break;
320331
}
321332
}
322333

323-
if (!isDuplicate)
334+
if (!merged)
324335
{
325336
filtered.Add(quad);
326337
}
@@ -330,20 +341,16 @@ private static List<DetectedQuadrilateral> FilterDuplicates(List<DetectedQuadril
330341
}
331342

332343
/// <summary>
333-
/// Check if two quadrilaterals are duplicates based on corner proximity
344+
/// Check if two quadrilaterals are duplicates: every corresponding corner
345+
/// must be within the given distance threshold.
334346
/// </summary>
335-
private static bool AreDuplicates(DetectedQuadrilateral quad1, DetectedQuadrilateral quad2)
347+
private static bool AreDuplicates(
348+
DetectedQuadrilateral quad1, DetectedQuadrilateral quad2, double threshold)
336349
{
337-
// Calculate average distance between corresponding corners
338-
double totalDistance =
339-
Distance(quad1.TopLeft, quad2.TopLeft) +
340-
Distance(quad1.TopRight, quad2.TopRight) +
341-
Distance(quad1.BottomRight, quad2.BottomRight) +
342-
Distance(quad1.BottomLeft, quad2.BottomLeft);
343-
344-
double averageDistance = totalDistance / 4.0;
345-
346-
return averageDistance < DuplicateDistanceThreshold;
350+
return Distance(quad1.TopLeft, quad2.TopLeft) < threshold
351+
&& Distance(quad1.TopRight, quad2.TopRight) < threshold
352+
&& Distance(quad1.BottomRight, quad2.BottomRight) < threshold
353+
&& Distance(quad1.BottomLeft, quad2.BottomLeft) < threshold;
347354
}
348355

349356
/// <summary>

0 commit comments

Comments
 (0)