1- # All matrices are DxN, where N is the number of positions and D is the dimensionality
1+ # The implementation that was originally developed here got upstreamed into CoordinateTransformations:
2+ # https://github.com/JuliaGeometry/CoordinateTransformations.jl/pull/97
3+ # This now extends the upstream implementation.
24
3- # Here, P is the probe (to be rotated) and Q is the refereence
4- # https://en.wikipedia.org/wiki/Kabsch_algorithm
5- # This has been generalized to support weighted points:
6- # https://igl.ethz.ch/projects/ARAP/svd_rot.pdf
5+ CoordinateTransformations. kabsch_centered (P:: PointSet , Q:: PointSet ) = kabsch_centered (P. coords, Q. coords, P. weights .* Q. weights);
6+ CoordinateTransformations. kabsch (P:: PointSet , Q:: PointSet ) = kabsch (P. coords => Q. coords, P. weights .* Q. weights);
77
8- # assuming P and Q are already centered at the origin
9- # returns the rotation for alignment
10- function kabsch_centered (P,Q,w)
11- @assert size (P) == size (Q)
12- W = diagm (w/ sum (w)) # here, the weights are assumed to sum to 1
13- H = P* W* Q'
14- D = Matrix {Float64} (I,size (H,1 ), size (H,2 ))
15- # U,Σ,V = svd(H)
16- U,Σ,V = GenericLinearAlgebra. svd (H)
17- D[end ] = sign (det (V* U' ))
18- return LinearMap (V * D * U' )
19- end
20-
21- function kabsch_centered (P,Q,matches:: AbstractVector{<:Tuple{Int,Int}} ,wp= ones (size (P,2 )),wq= ones (size (Q,2 )))
8+ function kabsch_centered_matches (P,Q,matches:: AbstractVector{<:Tuple{Int,Int}} ,wp= ones (size (P,2 )),wq= ones (size (Q,2 )))
229 matchedP, matchedQ = matched_points (P,Q,matches)
2310 w = [wp[i]* wq[j] for (i,j) in matches]
2411 return kabsch_centered (matchedP, matchedQ, w)
2512end
2613
27- kabsch_centered (P:: PointSet , Q:: PointSet ) = kabsch_centered (P. coords, Q. coords, P. weights .* Q. weights);
28- kabsch_centered (P:: PointSet , Q:: PointSet , matches:: AbstractVector{<:Tuple{Int,Int}} ) = kabsch_centered (P. coords, Q. coords, matches, P. weights, Q. weights);
14+ kabsch_centered_matches (P:: PointSet , Q:: PointSet , matches:: AbstractVector{<:Tuple{Int,Int}} ) = kabsch_centered_matches (P. coords, Q. coords, matches, P. weights, Q. weights);
2915
3016# transform DxN matrices
3117function (tform:: Translation )(A:: AbstractMatrix )
3218 return hcat ([tform (A[:,i]) for i= 1 : size (A,2 )]. .. )
3319end
3420
35- # P and Q are not necessarily centered
36- # returns the transformation for alignment
37- function kabsch (P, Q, w:: AbstractVector = ones (size (P,2 )))
38- @assert ! any (w.< 0 ) && sum (w)> 0
39- wn = w/ sum (w) # weights should sum to 1 for computing the centroid
40- centerP, centerQ = center_translation (P,wn), center_translation (Q,wn)
41- R = kabsch_centered (centerP (P), centerQ (Q), wn)
42- return inv (centerQ) ∘ R ∘ centerP
43- end
4421
45- function kabsch (P,Q,matches:: AbstractVector{<:Tuple{Int,Int}} ,wp= ones (size (P,2 )),wq= ones (size (Q,2 )))
22+ function kabsch_matches (P,Q,matches:: AbstractVector{<:Tuple{Int,Int}} ,wp= ones (size (P,2 )),wq= ones (size (Q,2 )))
4623 matchedP, matchedQ = matched_points (P,Q,matches)
4724 w = [wp[i]* wq[j] for (i,j) in matches]
48- return kabsch (matchedP, matchedQ, w)
25+ return kabsch (matchedP => matchedQ, w)
4926end
5027
51- kabsch (P:: PointSet , Q:: PointSet ) = kabsch (P. coords, Q. coords, P. weights .* Q. weights);
52- kabsch (P:: PointSet , Q:: PointSet , matches:: AbstractVector{<:Tuple{Int,Int}} ) = kabsch (P. coords, Q. coords, matches, P. weights, Q. weights);
28+ kabsch_matches (P:: PointSet , Q:: PointSet , matches:: AbstractVector{<:Tuple{Int,Int}} ) = kabsch_matches (P. coords, Q. coords, matches, P. weights, Q. weights);
5329
54- function kabsch (P:: AbstractMultiPointSet{N,T,K} , Q:: AbstractMultiPointSet{N,T,K} , matchesdict, wp = weights (P), wq = weights (Q)) where {N,T,K}
30+ function kabsch_matches (P:: AbstractMultiPointSet{N,T,K} , Q:: AbstractMultiPointSet{N,T,K} , matchesdict, wp = weights (P), wq = weights (Q)) where {N,T,K}
5531 matchedP, matchedQ = matched_points (P,Q,matchesdict)
5632 w = Vector {T} ()
5733
@@ -61,7 +37,7 @@ function kabsch(P::AbstractMultiPointSet{N,T,K}, Q::AbstractMultiPointSet{N,T,K}
6137 end
6238 end
6339
64- return kabsch (matchedP, matchedQ, w)
40+ return kabsch (matchedP => matchedQ, w)
6541end
6642
6743# align via translation only (no rotation)
@@ -72,11 +48,11 @@ function translation_align(P,Q,w)
7248 return Translation (sum (dists; dims= 2 ))
7349end
7450
75- function translation_align (P,Q,matches:: AbstractVector{<:Tuple{Int,Int}} ,wp= ones (size (P,2 )),wq= ones (size (Q,2 )))
51+ function translation_align_matches (P,Q,matches:: AbstractVector{<:Tuple{Int,Int}} ,wp= ones (size (P,2 )),wq= ones (size (Q,2 )))
7652 matchedP, matchedQ = matched_points (P,Q,matches)
7753 w = [wp[i]* wq[j] for (i,j) in matches]
7854 return translation_align (matchedP, matchedQ, w)
7955end
8056
8157translation_align (P:: PointSet , Q:: PointSet ) = translation_align (P. coords, Q. coords, P. weights .* Q. weights)
82- translation_align (P:: PointSet , Q:: PointSet , matches:: AbstractVector{<:Tuple{Int,Int}} ) = translation_align (P. coords, Q. coords, matches, P. weights .* Q. weights)
58+ translation_align_matches (P:: PointSet , Q:: PointSet , matches:: AbstractVector{<:Tuple{Int,Int}} ) = translation_align_matches (P. coords, Q. coords, matches, P. weights .* Q. weights)
0 commit comments