@@ -69,11 +69,7 @@ Imports std = System.Math
6969''' A static utility class for aligning two 2D polygons using the RANSAC algorithm.
7070''' This version is optimized for correctness and includes a proper least-squares refinement step.
7171''' </summary>
72- Public NotInheritable Class RANSACPointAlignment
73-
74- Private Sub New ()
75- ' Prevent instantiation of this static class
76- End Sub
72+ Public Module RANSACPointAlignment
7773
7874 ''' <summary>
7975 ''' Aligns a source polygon to a target polygon using RANSAC.
@@ -83,10 +79,10 @@ Public NotInheritable Class RANSACPointAlignment
8379 ''' <param name="iterations">The number of RANSAC iterations.</param>
8480 ''' <param name="distanceThreshold">The distance threshold to consider a point an inlier.</param>
8581 ''' <returns>The best-fit Transform object.</returns>
86- Public Shared Function AlignPolygons(sourcePoly As Polygon2D,
87- targetPoly As Polygon2D,
88- Optional iterations As Integer = 1000 ,
89- Optional distanceThreshold As Double = 0.1 ) As AffineTransform
82+ Public Function AlignPolygons(sourcePoly As Polygon2D,
83+ targetPoly As Polygon2D,
84+ Optional iterations As Integer = 1000 ,
85+ Optional distanceThreshold As Double = 0.1 ) As AffineTransform
9086
9187 ' Pre-check: need at least 3 points
9288 If sourcePoly.length < 2 OrElse targetPoly.length < 2 Then
@@ -156,7 +152,7 @@ Public NotInheritable Class RANSACPointAlignment
156152 ''' <summary>
157153 ''' Generates a list of candidate matches by finding the nearest neighbor in descriptor space.
158154 ''' </summary>
159- Private Shared Function GenerateCandidateMatches( ByRef sourceDesc As PointWithDescriptor(), ByRef targetDesc As PointWithDescriptor()) As List( Of (source As PointF, target As PointF))
155+ Private Function GenerateCandidateMatches( ByRef sourceDesc As PointWithDescriptor(), ByRef targetDesc As PointWithDescriptor()) As List( Of (source As PointF, target As PointF))
160156 Dim matches As New List( Of (source As PointF, target As PointF))()
161157
162158 Call $"Generates a list of candidate matches by finding the nearest neighbor in descriptor space." .debug
@@ -195,8 +191,8 @@ Public NotInheritable Class RANSACPointAlignment
195191 ''' <summary>
196192 ''' Computes an affine transform from exactly three point pairs.
197193 ''' </summary>
198- Private Shared Function ComputeAffineFrom3Pairs(p1 As PointF, p2 As PointF, p3 As PointF,
199- q1 As PointF, q2 As PointF, q3 As PointF) As AffineTransform
194+ Private Function ComputeAffineFrom3Pairs(p1 As PointF, p2 As PointF, p3 As PointF,
195+ q1 As PointF, q2 As PointF, q3 As PointF) As AffineTransform
200196 ' Solve the linear system for 6 parameters (a,b,c,d,e,f)
201197 ' |x1 y1 1 0 0 0| |a| |x'1|
202198 ' |x2 y2 1 0 0 0| |b| |x'2|
@@ -205,25 +201,31 @@ Public NotInheritable Class RANSACPointAlignment
205201 ' |0 0 0 x2 y2 1| |e| |y'2|
206202 ' |0 0 0 x3 y3 1| |f| |y'3|
207203 ' We can solve this by splitting it into two 3x3 systems.
208- Dim sourceX = {p1.X, p2.X, p3.X}
209- Dim sourceY = {p1.Y, p2.Y, p3.Y}
210- Dim targetX = {q1.X, q2.X, q3.X}
211- Dim targetY = {q1.Y, q2.Y, q3.Y}
204+ ' Dim sourceX = {p1.X, p2.X, p3.X}
205+ ' Dim sourceY = {p1.Y, p2.Y, p3.Y}
206+ ' Dim targetX = {q1.X, q2.X, q3.X}
207+ ' Dim targetY = {q1.Y, q2.Y, q3.Y}
212208
213209 ' Using the same least squares solver for the exact case
214- Dim pairs As New List( Of (source As PointF, target As PointF)) From {
215- (p1, q1), (p2, q2), (p3, q3)
216- }
210+ Dim pairs As (source As PointF, target As PointF)() = {(p1, q1), (p2, q2), (p3, q3)}
217211 Dim a, b, c, d, e, f As Double
218- SolveLeastSquaresAffine(pairs, a, b, c, d, e, f)
219212
220- Return New AffineTransform With {.a = a, .b = b, .c = c, .d = d, .e = e, .f = f}
213+ Call SolveLeastSquaresAffine(pairs, a, b, c, d, e, f)
214+
215+ Return New AffineTransform With {
216+ .a = a,
217+ .b = b,
218+ .c = c,
219+ .d = d,
220+ .e = e,
221+ .f = f
222+ }
221223 End Function
222224
223225 ''' <summary>
224226 ''' Refines the transformation using all inliers with a least-squares fit for an affine transform.
225227 ''' </summary>
226- Private Shared Function RefineTransformWithLeastSquares(candidateMatches As List( Of (source As PointF, target As PointF)), initialTransform As AffineTransform, threshold As Double ) As AffineTransform
228+ Private Function RefineTransformWithLeastSquares(candidateMatches As List( Of (source As PointF, target As PointF)), initialTransform As AffineTransform, threshold As Double ) As AffineTransform
227229 Dim inlierPairs As New List( Of (source As PointF, target As PointF))
228230 Dim thresholdSq = threshold * threshold
229231 Dim errors As New List( Of Double )
@@ -248,23 +250,32 @@ Public NotInheritable Class RANSACPointAlignment
248250 End If
249251
250252 Dim a, b, c, d, e, f As Double
251- SolveLeastSquaresAffine(inlierPairs, a, b, c, d, e, f)
252- Return New AffineTransform With {.a = a, .b = b, .c = c, .d = d, .e = e, .f = f}
253+
254+ Call SolveLeastSquaresAffine(inlierPairs.ToArray, a, b, c, d, e, f)
255+
256+ Return New AffineTransform With {
257+ .a = a,
258+ .b = b,
259+ .c = c,
260+ .d = d,
261+ .e = e,
262+ .f = f
263+ }
253264 End Function
254265
255266 ''' <summary>
256267 ''' Solves the least-squares problem for affine transformation parameters.
257268 ''' This function is unchanged from your original, as it was mathematically correct.
258269 ''' </summary>
259- Private Shared Sub SolveLeastSquaresAffine(points As List( Of ( source As PointF, target As PointF)), ByRef a As Double , ByRef b As Double , ByRef c As Double , ByRef d As Double , ByRef e As Double , ByRef f As Double )
260- Dim n = points.Count
270+ Private Sub SolveLeastSquaresAffine( ByRef points As ( source As PointF, target As PointF)( ), ByRef a As Double , ByRef b As Double , ByRef c As Double , ByRef d As Double , ByRef e As Double , ByRef f As Double )
271+ Dim n As Integer = points.Length
261272 Dim sumX, sumY, sumX2, sumY2, sumXY, sumXp, sumYp, sumXpX, sumXpY, sumYpX, sumYpY As Double
262273
263274 For Each pair In points
264- Dim x = pair.source.X
265- Dim y = pair.source.Y
266- Dim xp = pair.target.X
267- Dim yp = pair.target.Y
275+ Dim x As Double = pair.source.X
276+ Dim y As Double = pair.source.Y
277+ Dim xp As Double = pair.target.X
278+ Dim yp As Double = pair.target.Y
268279
269280 sumX += x : sumY += y
270281 sumX2 += x * x : sumY2 += y * y : sumXY += x * y
@@ -283,15 +294,17 @@ Public NotInheritable Class RANSACPointAlignment
283294
284295 ' Solve two 3x3 systems (one for x', one for y')
285296 Dim det = ata_00 * (ata_11 * ata_22 - ata_12 * ata_21) -
286- ata_01 * (ata_10 * ata_22 - ata_12 * ata_20) +
287- ata_02 * (ata_10 * ata_21 - ata_11 * ata_20)
297+ ata_01 * (ata_10 * ata_22 - ata_12 * ata_20) +
298+ ata_02 * (ata_10 * ata_21 - ata_11 * ata_20)
288299
289300 If std.Abs(det) < 0.000000001 Then
290301 ' Matrix is singular, cannot solve, return identity
291302 a = 1 : b = 0 : c = 0
292303 d = 0 : e = 1 : f = 0
304+
293305 Return
294306 End If
307+
295308 Dim invDet = 1.0 / det
296309
297310 Dim inv_00 = (ata_11 * ata_22 - ata_12 * ata_21) * invDet
@@ -313,4 +326,4 @@ Public NotInheritable Class RANSACPointAlignment
313326 e = inv_10 * atb_3 + inv_11 * atb_4 + inv_12 * atb_5
314327 f = inv_20 * atb_3 + inv_21 * atb_4 + inv_22 * atb_5
315328 End Sub
316- End Class
329+ End Module
0 commit comments