From 232acc6ee2af399a77ec9493ff690864a8133011 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 29 Apr 2023 07:01:22 -0500 Subject: [PATCH] Construct affine transforms from point-pairs Given a set of point pairs `from => to`, compute the affine transformation that best reproduces the observed mapping. Closes #30 --- src/affine.jl | 19 +++++++++++++++++++ test/affine.jl | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/affine.jl b/src/affine.jl index 150feb2..164349e 100644 --- a/src/affine.jl +++ b/src/affine.jl @@ -209,3 +209,22 @@ recenter(trans::AbstractMatrix, origin::Union{AbstractVector, Tuple}) = recenter transform_deriv(trans::AffineMap, x) = trans.linear # TODO transform_deriv_params + +""" + AffineMap([from1=>to1, from2=>to2, ...]) → trans + +Create an Affine transformation that approximately maps the `from` points to the `to` points. +At least `n+1` non-degenerate points are required to map an `n`-dimensional space. +If there are more points than this, the transformation will be over-determined and a least-squares +solution will be computed. +""" +function AffineMap(mappedpairs::AbstractVector{<:Pair}) + froms = reduce(hcat, [vcat(pr.first, 1) for pr in mappedpairs]) + tos = reduce(hcat, [pr.second for pr in mappedpairs]) + AffineMap_(froms, tos) +end + +function AffineMap_(froms, tos) + M = tos * pinv(froms) + AffineMap(M[:, 1:end-1], M[:, end]) +end diff --git a/test/affine.jl b/test/affine.jl index cc5d541..0879bd8 100644 --- a/test/affine.jl +++ b/test/affine.jl @@ -127,4 +127,14 @@ end @test expected.origin ≈ result.origin @test expected.direction ≈ result.direction end + + @testset "construction from points" begin + M = [1.0 2.0; 3.0 4.0] + v = [-1.0, 1.0] + A = AffineMap(M,v) + froms = [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]] + mappedpoints = [from => A(from) for from in froms] + A2 = AffineMap(mappedpoints) + @test A2 ≈ A + end end