Skip to content

Commit dda5e86

Browse files
authored
Update to CoordinateTransformations.kabsch (#50)
`kabsch` has now been implemented in CoordinateTransformations. While new features are ordinarily not breaking changes, for this package it is due to name conflict. This PR resolves the name conflict, and bumps `[compat]`. Closes #49
1 parent 5d298e4 commit dda5e86

File tree

4 files changed

+26
-48
lines changed

4 files changed

+26
-48
lines changed

Project.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "GaussianMixtureAlignment"
22
uuid = "f2431ed1-b9c2-4fdb-af1b-a74d6c93b3b3"
33
authors = ["Tom McGrath <[email protected]> and contributors"]
4-
version = "0.2.2"
4+
version = "0.2.3"
55

66
[deps]
77
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
@@ -27,15 +27,15 @@ Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
2727
GaussianMixtureAlignmentMakieExt = "Makie"
2828

2929
[compat]
30-
Colors = "0.12"
31-
CoordinateTransformations = "0.6"
30+
Colors = "0.12, 0.13"
31+
CoordinateTransformations = "0.6.4"
3232
Distances = "0.10"
3333
ForwardDiff = "0.10"
3434
GenericLinearAlgebra = "0.3"
35-
GeometryBasics = "0.4"
35+
GeometryBasics = "0.4, 0.5"
3636
Hungarian = "0.7"
37-
Makie = "0.21"
38-
MakieCore = "0.6, 0.7, 0.8"
37+
Makie = "0.21, 0.22"
38+
MakieCore = "0.6, 0.7, 0.8, 0.9"
3939
MutableConvexHulls = "0.2"
4040
NearestNeighbors = "0.4.1"
4141
Optim = "1.7"

src/GaussianMixtureAlignment.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ using MakieCore
3636
using GeometryBasics
3737
using Colors
3838

39+
using CoordinateTransformations: kabsch_centered
40+
3941
export AbstractGaussian, AbstractGMM
4042
export IsotropicGaussian, IsotropicGMM, IsotropicMultiGMM
4143
export overlap, force!, gogma_align, rot_gogma_align, trl_gogma_align, tiv_gogma_align

src/goicp/icp.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function iterate_kabsch(P, Q, wp=ones(size(P,2)), wq=ones(size(Q,2)); iterations
1414
# TO DO: make sure that, for hungarian assignment, the proper weights are selected
1515

1616
prevscore = score
17-
tform = kabsch(P, Q, matches, wp, wq)
17+
tform = kabsch_matches(P, Q, matches, wp, wq)
1818
score = squared_deviation(tform(P),Q,matches)
1919
if prevscore < score
2020
matches = prevmatches
@@ -38,12 +38,12 @@ function icp(P::AbstractMatrix, Q::AbstractMatrix, wp=ones(size(P,2)), wq=ones(s
3838
end
3939
icp(P::AbstractSinglePointSet, Q::AbstractSinglePointSet; kwargs...) = icp(P.coords, Q.coords, P.weights, Q.weights; kwargs...)
4040

41-
iterative_hungarian(args...; kwargs...) = iterate_kabsch(args...; correspondence = hungarian_assignment, kwargs...)
41+
iterative_hungarian(args...; kwargs...) = iterate_kabsch(args...; correspondence = hungarian_assignment, kwargs...)
4242

4343
function local_matching_alignment(x::AbstractPointSet, y::AbstractPointSet, block::SearchRegion; matching_fun = iterative_hungarian, kwargs...)
4444
tformedx = block.R*x + block.T
4545
matches = matching_fun(tformedx, y; kwargs...)
46-
tform = kabsch(x, y, matches)
46+
tform = kabsch_matches(x, y, matches)
4747
score = squared_deviation(tform(x), y, matches)
4848
R = RotationVec(tform.linear)
4949
params = (R.sx, R.sy, R.sz, tform.translation...)
@@ -53,7 +53,7 @@ end
5353
function local_matching_alignment(x::AbstractPointSet, y::AbstractPointSet, block::RotationRegion; matching_fun = iterative_hungarian, kwargs...)
5454
tformedx = block.R*x + block.T
5555
matches = matching_fun(tformedx, y; kwargs...)
56-
tform = kabsch(x, y, matches)
56+
tform = kabsch_matches(x, y, matches)
5757
score = squared_deviation(tform(x), y, matches)
5858
R = RotationVec(tform.linear)
5959
params = (R.sx, R.sy, R.sz)

src/goicp/kabsch.jl

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,33 @@
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)
2512
end
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
3117
function (tform::Translation)(A::AbstractMatrix)
3218
return hcat([tform(A[:,i]) for i=1:size(A,2)]...)
3319
end
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)
4926
end
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)
6541
end
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))
7349
end
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)
7955
end
8056

8157
translation_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

Comments
 (0)