From e053f7c358fef7877ed612758cff4818229981d1 Mon Sep 17 00:00:00 2001 From: "Dr. Zygmunt L. Szpak" Date: Tue, 20 Mar 2018 23:06:05 +1030 Subject: [PATCH] Adds Hartley's data normalization method Includes methods for applying Hartley's data normalization on a set of N dimensional (N > 1) points. Also includes docstrings and basic tests. --- README.md | 2 + src/MultipleViewGeometry.jl | 19 +++ .../hartley_transformation.jl | 125 ++++++++++++++++++ src/math_aliases.jl | 2 + src/operators.jl | 37 ++++++ src/types.jl | 3 + test/data_normalization_tests.jl | 43 ++++++ test/operators_tests.jl | 8 ++ test/runtests.jl | 5 +- 9 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 src/data_normalization/hartley_transformation.jl create mode 100644 src/math_aliases.jl create mode 100644 src/operators.jl create mode 100644 src/types.jl create mode 100644 test/data_normalization_tests.jl create mode 100644 test/operators_tests.jl diff --git a/README.md b/README.md index f12a873..3dd4d9c 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # MultipleViewGeometry + +Under construction.. diff --git a/src/MultipleViewGeometry.jl b/src/MultipleViewGeometry.jl index f0082ba..33cb03d 100644 --- a/src/MultipleViewGeometry.jl +++ b/src/MultipleViewGeometry.jl @@ -1,5 +1,24 @@ +__precompile__() + module MultipleViewGeometry +using Compat + +include("types.jl") +include("math_aliases.jl") + +# Types exported from `types.jl` +export HomogeneousPoint + +# Functions exported from `operators.jl`. +export ๐‘› + +# Functions exported from `hartley_transformation.jl`. +export hartley_normalization, hartley_transformation + +include("operators.jl") +include("data_normalization/hartley_transformation.jl") + # package code goes here end # module diff --git a/src/data_normalization/hartley_transformation.jl b/src/data_normalization/hartley_transformation.jl new file mode 100644 index 0000000..b4d8de7 --- /dev/null +++ b/src/data_normalization/hartley_transformation.jl @@ -0,0 +1,125 @@ +""" + hartley_transformation(pts::AbstractArray{T}) where T<:HomogeneousPoint + +Returns a matrix which can be used to map a set of ``d``-dimensional Cartesian +points which are represented by ``\\text{length-}(d+1)`` homogeneous coordinates into a +data-dependent coordinate system. In the data-dependent coordinate system the +origin is the center of mass (centroid) of the points and the root-mean-square +distance of the points to the origin is equal to ``\\sqrt{d}``. + +# Details + +A point in ``\\mathbb{R}^d`` with +Cartesian coordinates ``\\left(m_1, m_2, \\ldots, m_d \\right)`` can also be +expressed in homogeneous coordinates with the vector ``\\mathbf{m} = +\\left[m_1, m_2, \\ldots, m_d , 1 \\right]^\\top``. + +Suppose one has a set ``\\left \\{ \\mathbf{m}_n \\right \\}_{n = 1}^{N} `` of +Cartesian points which are represented by homogeneous coordinates. +Let +```math +\\overline{\\mathbf{m}} = \\frac{1}{N} \\sum_{n = 1}^{N} \\mathbf{m}_n +\\quad \\text{and} \\quad +\\sigma = \\left( \\frac{1}{d \\times n} \\sum_{n = 1}^{N} \\left \\| \\mathbf{m}_n - +\\overline{\\mathbf{m}} \\right \\|^{2} \\right)^{1/2} +``` +represent the centroid of the points and the root-mean-square distance of the +points to the centroid, respectively. + +This function returns the matrix +```math +\\mathbf{T} = +\\begin{bmatrix} +\\sigma^{-1} & 0 & 0 & \\ldots & -\\sigma^{-1} \\overline{m}_1 \\\\ + 0 & \\sigma^{-1} & 0 & \\ldots & -\\sigma^{-1} \\overline{m}_2 \\\\ + 0 & 0 & \\ddots & 0 & \\vdots \\\\ + \\vdots & \\vdots & 0 & \\sigma^{-1} & -\\sigma^{-1} \\overline{m}_d \\\\ + 0 & 0 & 0 & 0 & 1 +\\end{bmatrix} +``` +such that a transformed point ``\\tilde{\\mathbf{m}}_n = \\mathbf{T} \\mathbf{m}_n`` +has a root-mean-square distance to the origin of a new coordinate system +equal to ``\\sqrt{d}``. + + +""" +function hartley_transformation(pts::AbstractArray{T}) where T<:HomogeneousPoint + if isempty(pts) + throw(ArgumentError("Array cannot be empty.")) + end + # Convert list of homogeneous coordinates into an npts x ndim matrix. + npts = length(pts); + ndim = length(pts[1].coords); + array = reinterpret(Float64,pts,(npts,ndim)) + ๐Œ = transpose(reshape(array,(ndim,npts))) + _hartley_transformation(view(๐Œ,:,1:ndim-1)) + +end + +function _hartley_transformation(๐Œ::AbstractArray{T}) where T<:Number + if isempty(๐Œ) + throw(ArgumentError("Array cannot be empty.")) + end + ๐œ = mean(๐Œ,1) + ndim = length(๐œ) + # Compute root mean square distance of each point to the centroid. + ฯƒ = โˆš((1/length(๐Œ)) * โˆ‘((๐Œ .- ๐œ).^2)) + ฯƒโปยน = 1./ฯƒ + ๐“ = [ฯƒโปยน*eye(ndim) -ฯƒโปยน*transpose(๐œ); + zeros(1,ndim) 1] + +end + +""" + hartley_normalization(pts::AbstractArray{T}) where T<:HomogeneousPoint + +Maps a set of ``d``-dimensional Cartesian points which are represented by +``\\text{length-}(d+1)`` homogeneous coordinates into a data-dependent coordinate system. +In the data-dependent coordinate system the origin is the center of mass +(centroid) of the points and the root-mean-square distance of the points to the +origin is equal to ``\\sqrt{d}``. + +# Details + +A point in ``\\mathbb{R}^d`` with +Cartesian coordinates ``\\left(m_1, m_2, \\ldots, m_d \\right)`` can also be +expressed in homogeneous coordinates with the vector ``\\mathbf{m} = +\\left[m_1, m_2, \\ldots, m_d , 1 \\right]^\\top``. + +Suppose one has a set ``\\left \\{ \\mathbf{m}_n \\right \\}_{n = 1}^{N} `` of +Cartesian points which are represented by homogeneous coordinates. +Let +```math +\\overline{\\mathbf{m}} = \\frac{1}{N} \\sum_{n = 1}^{N} \\mathbf{m}_n +\\quad \\text{and} \\quad +\\sigma = \\left( \\frac{1}{d \\times n} \\sum_{n = 1}^{N} \\left \\| \\mathbf{m}_n - +\\overline{\\mathbf{m}} \\right \\|^{2} \\right)^{1/2} +``` +represent the centroid of the points and the root-mean-square distance of the +points to the centroid, respectively. + +This function returns a new set of points ``\\left \\{ \\tilde{\\mathbf{m}}_n \\right \\}_{n = 1}^{N} `` +where ``\\tilde{\\mathbf{m}}_n = \\mathbf{T} \\mathbf{m}_n`` for each ``n``, and +```math +\\mathbf{T} = +\\begin{bmatrix} +\\sigma^{-1} & 0 & 0 & \\ldots & -\\sigma^{-1} \\overline{m}_1 \\\\ + 0 & \\sigma^{-1} & 0 & \\ldots & -\\sigma^{-1} \\overline{m}_2 \\\\ + 0 & 0 & \\ddots & 0 & \\vdots \\\\ + \\vdots & \\vdots & 0 & \\sigma^{-1} & -\\sigma^{-1} \\overline{m}_d \\\\ + 0 & 0 & 0 & 0 & 1 +\\end{bmatrix}. +``` +These new points have the property that their root-mean-square distance to +the origin of the coordinate system is equal to ``\\sqrt{d}``. + + +""" +function hartley_normalization(pts::AbstractArray{T}) where T<:HomogeneousPoint + ๐“ = hartley_transformation(pts) + map!(pts , pts) do p + ๐ฆ = collect(p.coords) + ๐ฆ = ๐‘›(๐“ * ๐ฆ) + HomogeneousPoint(tuple(๐ฆ...)) + end +end diff --git a/src/math_aliases.jl b/src/math_aliases.jl new file mode 100644 index 0000000..2afc6b2 --- /dev/null +++ b/src/math_aliases.jl @@ -0,0 +1,2 @@ +const โˆš = sqrt +const โˆ‘ = sum diff --git a/src/operators.jl b/src/operators.jl new file mode 100644 index 0000000..b111045 --- /dev/null +++ b/src/operators.jl @@ -0,0 +1,37 @@ +""" + ๐‘›(v::Vector{T}) where T<:Number + +Scales a length-``n`` vector ``v`` such that the last component +of the vector is one, provided that the last component is not zero. If the last +component is zero then the vector is left unchanged. + +# Details +Suppose the length-``n`` vector ``v`` represents the homogeneous coordinates of +a point in a projective space. The corresponding Cartesian coordinates usually +just the first ``n-1`` numbers of homogeneous coordinates divided by the last +component. So if the last component is one, then the first ``n-1`` homogeneous +coordinates can be interpreted as Cartesian. The exceptional case is when the +last component of the homogenenous coordinates is zero. These homogeneous +coordinates are associated with so-called *points at infinity* and have no +Cartesian counterparts. + +# Example +```julia +h = [4, 4 , 2] +c = ๐‘›(h) + +3-element Array{Float64,1}: + 2.0 + 2.0 + 1.0 +``` + + +""" +function ๐‘›(v::Vector{T}) where T<:Real + if v[end] != 0 && v[end] != 1 + v = v ./ v[end] + else + v + end +end diff --git a/src/types.jl b/src/types.jl new file mode 100644 index 0000000..2593420 --- /dev/null +++ b/src/types.jl @@ -0,0 +1,3 @@ +struct HomogeneousPoint{T <: AbstractFloat,N} + coords::NTuple{N, T} +end diff --git a/test/data_normalization_tests.jl b/test/data_normalization_tests.jl new file mode 100644 index 0000000..bfdddbc --- /dev/null +++ b/test/data_normalization_tests.jl @@ -0,0 +1,43 @@ +using MultipleViewGeometry, Base.Test + + +# Tests for a set of two-dimensional Cartesian points represented by homogeneous +# coordinates. +pts2D = map(HomogeneousPoint, + [(-10.0, -10.0, 1.0), + (-10.0, 10.0, 1.0), + ( 10.0, -10.0, 1.0), + ( 10.0, 10.0, 1.0)]) + +@test hartley_normalization(pts2D) == map(HomogeneousPoint, + [(-1.0,-1.0, 1.0), + (-1.0, 1.0, 1.0), + (1.0, -1.0, 1.0), + (1.0, 1.0, 1.0)]) + + @test hartley_transformation(pts2D) == eye(3) + + +# Tests for a set of three-dimensional Cartesian points represented by homogeneous +# coordinates. +pts3D = map(HomogeneousPoint, + [(-10.0, -10.0, -10.0, 1.0), + (-10.0, -10.0, 10.0, 1.0), + (-10.0, 10.0, -10.0, 1.0), + (-10.0, 10.0, 10.0, 1.0), + ( 10.0, -10.0, -10.0, 1.0), + ( 10.0, -10.0, 10.0, 1.0), + ( 10.0, 10.0, -10.0, 1.0), + ( 10.0, 10.0, 10.0, 1.0)]) + +@test hartley_normalization(pts3D) == map(HomogeneousPoint, + [(-1.0,-1.0, -1.0, 1.0), + (-1.0,-1.0, 1.0, 1.0), + (-1.0, 1.0, -1.0, 1.0), + (-1.0, 1.0, 1.0, 1.0), + (1.0, -1.0, -1.0, 1.0), + (1.0, -1.0, 1.0, 1.0), + (1.0, 1.0, -1.0, 1.0), + (1.0, 1.0, 1.0, 1.0)]) + +@test hartley_transformation(pts3D) == eye(4) diff --git a/test/operators_tests.jl b/test/operators_tests.jl new file mode 100644 index 0000000..f216f30 --- /dev/null +++ b/test/operators_tests.jl @@ -0,0 +1,8 @@ +using MultipleViewGeometry, Base.Test + +# Vectors are scaled so that the last component is unity. +v = [2,2,2] +@test ๐‘›(v) == [1.0, 1.0, 1.0] +# Vectors which represent points at infinity are unchanged. +v = [2,2,0] +@test ๐‘›(v) == [2,2,0] diff --git a/test/runtests.jl b/test/runtests.jl index 3571811..45f6b14 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,6 @@ using MultipleViewGeometry using Base.Test -# write your own tests here -@test 1 == 2 + +@testset "Operators Tests" begin include("operators_tests.jl") end +@testset "Data Normalization Tests" begin include("data_normalization_tests.jl") end