From 61290908f98c47222123862dc386848514d8f389 Mon Sep 17 00:00:00 2001 From: "Dr. Zygmunt L. Szpak" Date: Wed, 31 Oct 2018 16:48:09 +1030 Subject: [PATCH] Improves performance through consistent use of StaticArrays --- REQUIRE | 3 +- src/MultipleViewGeometry.jl | 6 +- src/constraints/constraints.jl | 8 +-- src/construct/construct_essentialmatrix.jl | 2 +- src/construct/construct_fundamentalmatrix.jl | 4 +- src/cost_function/cost_functions.jl | 54 +++++++-------- .../hartley_transformation.jl | 15 ++-- src/estimate/estimate_twoview.jl | 5 +- src/moments/moments_fundamentalmatrix.jl | 4 +- src/operators/operators.jl | 6 +- src/projection/project.jl | 2 +- src/triangulation/triangulate.jl | 41 ++++++----- src/types/ModuleTypes.jl | 2 +- src/types/types.jl | 19 +++--- test/construct_projectionmatrix_tests.jl | 2 +- test/cost_functions_tests.jl | 9 ++- test/data_normalization_tests.jl | 68 +++++++++---------- test/estimate_twoview_tests.jl | 12 ++-- test/satisfy_epipolar_constraints_tests.jl | 31 ++++----- test/transform_tests.jl | 56 +++++++-------- test/triangulate_tests.jl | 11 ++- 21 files changed, 183 insertions(+), 177 deletions(-) diff --git a/REQUIRE b/REQUIRE index bf90cf6..fa0c957 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,7 +1,6 @@ julia 0.7 -StaticArrays +StaticArrays 0.8.3 Juno -Plots LsqFit LinearAlgebra BenchmarkTools diff --git a/src/MultipleViewGeometry.jl b/src/MultipleViewGeometry.jl index f87d6bf..ede65d5 100644 --- a/src/MultipleViewGeometry.jl +++ b/src/MultipleViewGeometry.jl @@ -16,7 +16,7 @@ export CostFunction, ApproximateMaximumLikelihood, AML export HomogeneousCoordinates export CoordinateSystemTransformation, CanonicalToHartley, HartleyToCanonical export CovarianceMatrices -export Point2DH, Point3DH +export Point2D, Point2DH, Point3D, Point3DH export HessianApproximation, CanonicalApproximation, CovarianceEstimationScheme export NoiseModel, GaussianNoise @@ -81,7 +81,7 @@ include("moments/ModuleMoments.jl") include("cost_function/ModuleCostFunction.jl") include("estimate/ModuleEstimation.jl") include("construct/ModuleConstruct.jl") -include("draw/ModuleDraw.jl") +#include("draw/ModuleDraw.jl") include("constraints/ModuleConstraints.jl") include("triangulation/ModuleTriangulation.jl") include("noise/ModuleNoise.jl") @@ -98,7 +98,7 @@ using .ModuleEstimation using .ModuleMoments using .ModuleCostFunction using .ModuleConstruct -using .ModuleDraw +#using .ModuleDraw using .ModuleConstraints using .ModuleTriangulation using .ModuleNoise diff --git a/src/constraints/constraints.jl b/src/constraints/constraints.jl index f169dd0..9ed9063 100644 --- a/src/constraints/constraints.jl +++ b/src/constraints/constraints.jl @@ -14,8 +14,8 @@ function satisfy(entity::FundamentalMatrix, constraint::EpipolarConstraint, ๐… I = 10 for n = 1:N - ๐ฆ = โ„ณ[n] - ๐ฆสน = โ„ณสน[n] + ๐ฆ = hom(โ„ณ[n]) + ๐ฆสน = hom(โ„ณสน[n]) ๐ฆโ‚• = init_correction_view_1(๐…, ๐ฆ, ๐ฆสน, ๐โ‚‚) ๐ฆโ‚•สน= init_correction_view_2(๐…, ๐ฆ, ๐ฆสน, ๐โ‚‚) for i = 1:I @@ -24,8 +24,8 @@ function satisfy(entity::FundamentalMatrix, constraint::EpipolarConstraint, ๐… ๐ฆโ‚• = update_correction_view_1(๐…, ๐ฆ, ๐ฆโ‚•, ๐ฆโ‚œ, ๐ฆสน, ๐ฆโ‚•สน, ๐ฆโ‚œสน, ๐โ‚‚) ๐ฆโ‚•สน = update_correction_view_2(๐…, ๐ฆ, ๐ฆโ‚•, ๐ฆโ‚œ, ๐ฆสน, ๐ฆโ‚•สน, ๐ฆโ‚œสน, ๐โ‚‚) end - ๐’ช[n] = ๐ฆโ‚• - ๐’ชสน[n] = ๐ฆโ‚•สน + ๐’ช[n] = homโปยน(๐ฆโ‚•) + ๐’ชสน[n] = homโปยน(๐ฆโ‚•สน) end ๐’ช ,๐’ชสน end diff --git a/src/construct/construct_essentialmatrix.jl b/src/construct/construct_essentialmatrix.jl index b37cb1a..bc36810 100644 --- a/src/construct/construct_essentialmatrix.jl +++ b/src/construct/construct_essentialmatrix.jl @@ -7,5 +7,5 @@ function construct( e::EssentialMatrix, ๐…::AbstractArray, ๐Šโ‚::AbstractA end # Equation 9.12 Chapter 9 from Hartley & Zisserman ๐„ = ๐Šโ‚‚'*๐…*๐Šโ‚ - MMatrix{3,3,Float64,3*3}(๐„) + SMatrix{3,3,Float64,3*3}(๐„) end diff --git a/src/construct/construct_fundamentalmatrix.jl b/src/construct/construct_fundamentalmatrix.jl index fb8b312..020efb8 100644 --- a/src/construct/construct_fundamentalmatrix.jl +++ b/src/construct/construct_fundamentalmatrix.jl @@ -14,7 +14,7 @@ function construct( e::FundamentalMatrix, throw(ArgumentError("Expect length-3 translation vectors.")) end ๐… = vec2antisym(๐Šโ‚‚*๐‘โ‚‚*(๐ญโ‚ .- ๐ญโ‚‚))*๐Šโ‚‚*๐‘โ‚‚/๐‘โ‚/๐Šโ‚ - MMatrix{3,3,Float64,3*3}(๐…) + SMatrix{3,3,Float64,3*3}(๐…) end function construct( e::FundamentalMatrix, ๐โ‚::AbstractArray, ๐โ‚‚::AbstractArray) @@ -24,5 +24,5 @@ function construct( e::FundamentalMatrix, ๐โ‚::AbstractArray, ๐โ‚‚::Abstr ๐œโ‚ = SVector{4,Float64}(nullspace(Array(๐โ‚))) ๐žโ‚‚ = ๐โ‚‚*๐œโ‚ ๐… = vec2antisym(๐žโ‚‚)*๐โ‚‚*pinv(Array(๐โ‚)) - MMatrix{3,3,Float64,3*3}(๐…) + SMatrix{3,3,Float64,3*3}(๐…) end diff --git a/src/cost_function/cost_functions.jl b/src/cost_function/cost_functions.jl index e675945..ad2d9f2 100644 --- a/src/cost_function/cost_functions.jl +++ b/src/cost_function/cost_functions.jl @@ -12,8 +12,8 @@ function cost(c::CostFunction, entity::FundamentalMatrix, ๐›‰::AbstractArray, @inbounds for n = 1:N ๐šฒโ‚™[1:2,1:2] .= ฮ›โ‚[n][index,index] ๐šฒโ‚™[3:4,3:4] .= ฮ›โ‚‚[n][index,index] - ๐ฆ = โ„ณ[n] - ๐ฆสน= โ„ณสน[n] + ๐ฆ = hom(โ„ณ[n]) + ๐ฆสน= hom(โ„ณสน[n]) ๐”โ‚™ = (๐ฆ โŠ— ๐ฆสน) โˆ‚โ‚“๐ฎโ‚™ = [(๐žโ‚ โŠ— ๐ฆสน) (๐žโ‚‚ โŠ— ๐ฆสน) (๐ฆ โŠ— ๐žโ‚) (๐ฆ โŠ— ๐žโ‚‚)] ๐โ‚™ = โˆ‚โ‚“๐ฎโ‚™ * ๐šฒโ‚™ * โˆ‚โ‚“๐ฎโ‚™' @@ -229,9 +229,9 @@ end function _X(c::CostFunction, entity::ProjectiveEntity, ๐›‰::AbstractArray,๐’ž::Tuple{AbstractArray, Vararg{AbstractArray}}, ๐’Ÿ::Tuple{AbstractArray, Vararg{AbstractArray}}) l = length(๐›‰) - ๐ˆโ‚— = Matrix{Float64}(I,l,l) - ๐ = fill(0.0,(l,l)) - ๐Œ = fill(0.0,(l,l)) + ๐ˆโ‚— = SMatrix{l,l}(1.0I) + ๐ = @SMatrix zeros(l,l) + ๐Œ = @SMatrix zeros(l,l) N = length(๐’Ÿ[1]) โ„ณ, โ„ณสน = ๐’Ÿ ฮ›โ‚, ฮ›โ‚‚ = ๐’ž @@ -242,8 +242,8 @@ function _X(c::CostFunction, entity::ProjectiveEntity, ๐›‰::AbstractArray,๐’ž: index = SVector(1,2) ๐šฒโ‚™[1:2,1:2] .= ฮ›โ‚[n][index,index] ๐šฒโ‚™[3:4,3:4] .= ฮ›โ‚‚[n][index,index] - ๐ฆ = โ„ณ[n] - ๐ฆสน= โ„ณสน[n] + ๐ฆ = hom(โ„ณ[n]) + ๐ฆสน= hom(โ„ณสน[n]) ๐”โ‚™ = (๐ฆ โŠ— ๐ฆสน) โˆ‚โ‚“๐ฎโ‚™ = [(๐žโ‚ โŠ— ๐ฆสน) (๐žโ‚‚ โŠ— ๐ฆสน) (๐ฆ โŠ— ๐žโ‚) (๐ฆ โŠ— ๐žโ‚‚)] ๐โ‚™ = โˆ‚โ‚“๐ฎโ‚™ * ๐šฒโ‚™ * โˆ‚โ‚“๐ฎโ‚™' @@ -285,50 +285,50 @@ end function T(c::CostFunction, entity::ProjectiveEntity, ๐›‰::AbstractArray, ๐’ž::Tuple{AbstractArray, Vararg{AbstractArray}}, ๐’Ÿ::Tuple{AbstractArray, Vararg{AbstractArray}}) l = length(๐›‰) - ๐ˆโ‚— = Matrix{Float64}(I, l, l) - ๐ˆโ‚˜ = Iโ‚˜(entity) - ๐ = fill(0.0,(l,l)) - ๐Œ = fill(0.0,(l,l)) - ๐“ = fill(0.0,(l,l)) + ๐ˆโ‚— = SMatrix{l,l}(1.0I) + ๐ˆโ‚˜ = Iโ‚˜(entity) + ๐ = @SMatrix zeros(l,l) + ๐Œ = @SMatrix zeros(l,l) + ๐“ = @SMatrix zeros(l,l) N = length(๐’Ÿ[1]) โ„ณ, โ„ณสน = ๐’Ÿ ฮ›โ‚, ฮ›โ‚‚ = ๐’ž ๐šฒโ‚™ = @MMatrix zeros(4,4) ๐žโ‚ = @SMatrix [1.0; 0.0; 0.0] ๐žโ‚‚ = @SMatrix [0.0; 1.0; 0.0] - for n = 1:N + for n = 1: N index = SVector(1,2) ๐šฒโ‚™[1:2,1:2] .= ฮ›โ‚[n][index,index] ๐šฒโ‚™[3:4,3:4] .= ฮ›โ‚‚[n][index,index] - ๐ฆ = โ„ณ[n] - ๐ฆสน= โ„ณสน[n] + ๐ฆ = hom(โ„ณ[n]) + ๐ฆสน= hom(โ„ณสน[n]) ๐”โ‚™ = (๐ฆ โŠ— ๐ฆสน) โˆ‚โ‚“๐ฎโ‚™ = [(๐žโ‚ โŠ— ๐ฆสน) (๐žโ‚‚ โŠ— ๐ฆสน) (๐ฆ โŠ— ๐žโ‚) (๐ฆ โŠ— ๐žโ‚‚)] - ๐โ‚™ = โˆ‚โ‚“๐ฎโ‚™ * ๐šฒโ‚™ * โˆ‚โ‚“๐ฎโ‚™' + ๐โ‚™ = โˆ‚โ‚“๐ฎโ‚™ * ๐šฒโ‚™ * โˆ‚โ‚“๐ฎโ‚™' ๐šบโ‚™ = ๐›‰' * ๐โ‚™ * ๐›‰ ๐šบโ‚™โปยน = inv(๐šบโ‚™) - ๐“โ‚ = fill(0.0,(l,l)) - ๐“โ‚‚ = fill(0.0,(l,l)) - ๐“โ‚ƒ = fill(0.0,(l,l)) - ๐“โ‚„ = fill(0.0,(l,l)) - ๐“โ‚… = fill(0.0,(l,l)) + ๐“โ‚ = @SMatrix zeros(Float64,l,l) + ๐“โ‚‚ = @SMatrix zeros(Float64,l,l) + ๐“โ‚ƒ = @SMatrix zeros(Float64,l,l) + ๐“โ‚„ = @SMatrix zeros(Float64,l,l) + ๐“โ‚… = @SMatrix zeros(Float64,l,l) + # The additional parentheses around some of the terms are needed as + # a workaround to a bug where Base.afoldl allocates memory unnecessarily. + # https://github.com/JuliaArrays/StaticArrays.jl/issues/537 for k = 1:l - ๐žโ‚– = fill(0.0,(l,1)) - ๐žโ‚–[k] = 1 + ๐žโ‚– = ๐ˆโ‚—[:,k] โˆ‚๐žโ‚–๐šบโ‚™ = (๐ˆโ‚˜ โŠ— ๐žโ‚–') * ๐โ‚™ * (๐ˆโ‚˜ โŠ— ๐›‰) + (๐ˆโ‚˜ โŠ— ๐›‰') * ๐โ‚™ * (๐ˆโ‚˜ โŠ— ๐žโ‚–) - ๐“โ‚ = ๐“โ‚ + ๐”โ‚™ * ๐šบโ‚™โปยน * (โˆ‚๐žโ‚–๐šบโ‚™) * ๐šบโ‚™โปยน * ๐”โ‚™' * ๐›‰ * ๐žโ‚–' + ๐“โ‚ = ๐“โ‚ + (((๐”โ‚™ * ๐šบโ‚™โปยน) * (โˆ‚๐žโ‚–๐šบโ‚™)) * ๐šบโ‚™โปยน) * ๐”โ‚™' * ๐›‰ * ๐žโ‚–' ๐“โ‚‚ = ๐“โ‚‚ + (๐žโ‚–' * ๐”โ‚™ * ๐šบโ‚™โปยน โŠ— ๐ˆโ‚—) * ๐โ‚™ * (๐šบโ‚™โปยน * ๐”โ‚™' * ๐›‰ โŠ— ๐ˆโ‚—) * ๐›‰ * ๐žโ‚–' ๐“โ‚„ = ๐“โ‚„ + (๐›‰' * ๐”โ‚™ * ๐šบโ‚™โปยน * (โˆ‚๐žโ‚–๐šบโ‚™) * ๐šบโ‚™โปยน โŠ— ๐ˆโ‚—) * ๐โ‚™ * (๐šบโ‚™โปยน * ๐”โ‚™' * ๐›‰ โŠ— ๐ˆโ‚—) * ๐›‰ * ๐žโ‚–' ๐“โ‚… = ๐“โ‚… + (๐›‰' * ๐”โ‚™ * ๐šบโ‚™โปยน โŠ— ๐ˆโ‚—) * ๐โ‚™ * (๐šบโ‚™โปยน * (โˆ‚๐žโ‚–๐šบโ‚™) * ๐šบโ‚™โปยน * ๐”โ‚™' * ๐›‰ โŠ— ๐ˆโ‚—) * ๐›‰ * ๐žโ‚–' end ๐“โ‚ƒ = (๐›‰' * ๐”โ‚™ * ๐šบโ‚™โปยน โŠ— ๐ˆโ‚—) * ๐โ‚™ * (๐ˆโ‚˜ โŠ— ๐›‰) * ๐šบโ‚™โปยน * ๐”โ‚™' ๐“ = ๐“ + ๐“โ‚ + ๐“โ‚‚ + ๐“โ‚ƒ - ๐“โ‚„ - ๐“โ‚… - end ๐“ end - @inline function Iโ‚˜(entity::FundamentalMatrix) - Matrix{Float64}(I, 1, 1) + SMatrix{1,1}(1.0I) end diff --git a/src/data_normalization/hartley_transformation.jl b/src/data_normalization/hartley_transformation.jl index 6b202b9..aaf80cd 100644 --- a/src/data_normalization/hartley_transformation.jl +++ b/src/data_normalization/hartley_transformation.jl @@ -48,26 +48,25 @@ function hartley_transformation(โ„ณ::Vector{T})::SMatrix where T <:AbstractArray throw(ArgumentError("Array cannot be empty.")) end npts = length(โ„ณ) - ndim = length(โ„ณ[1])-1 + ndim = length(โ„ณ[1]) ๐œ = centroid(โ„ณ) ฯƒ = root_mean_square(โ„ณ, ๐œ) - ฯƒโปยน = 1 ./ ฯƒ - ๐“ = SMatrix{ndim+1,ndim+1,Float64, (ndim+1)^2}([ฯƒโปยน*Matrix{Float64}(I,ndim,ndim) -ฯƒโปยน*๐œ[1:end-1] ; zeros(1,ndim) 1.0]) + ฯƒโปยน = 1 / ฯƒ + ๐“ = SMatrix{ndim+1,ndim+1,Float64, (ndim+1)^2}([ฯƒโปยน*Matrix{Float64}(I,ndim,ndim) -ฯƒโปยน*๐œ ; zeros(1,ndim) 1.0]) end function centroid(positions::Vector{T}) where T <: AbstractArray x = zeros(T) for pos โˆˆ positions - x .= (+).(x, pos) + x = x + pos end - x .= (/).(x,length(positions)) - return x + return x / length(positions) end function root_mean_square(โ„ณ::Vector{T}, ๐œ::T ) where T <: AbstractArray total = 0.0 npts = length(โ„ณ) - ndim = length(โ„ณ[1])-1 + ndim = length(โ„ณ[1]) for ๐ฆ โˆˆ โ„ณ total = total + โˆ‘((๐ฆ-๐œ).^2) end @@ -126,7 +125,7 @@ end function hartley_normalization!(โ„ณ::Vector{<:AbstractArray}) ๐“ = hartley_transformation(โ„ณ) map!(โ„ณ , โ„ณ) do ๐ฆ - ๐“ * ๐ฆ + homโปยน(๐“ * hom(๐ฆ)) end โ„ณ, ๐“ end diff --git a/src/estimate/estimate_twoview.jl b/src/estimate/estimate_twoview.jl index 2af1a3b..7c286d9 100644 --- a/src/estimate/estimate_twoview.jl +++ b/src/estimate/estimate_twoview.jl @@ -10,7 +10,7 @@ function estimate(entity::FundamentalMatrix, method::DirectLinearTransform, ๐’Ÿ ฮป, f = smallest_eigenpair(Symmetric(๐€)) ๐… = reshape(f,(3,3)) ๐… = enforce_ranktwo!(Array(๐…)) - ๐… = ๐… / norm(๐…) + ๐… = SMatrix{3,3,Float64,9}(๐… / norm(๐…)) # Transform estimate back to the original (unnormalised) coordinate system. ๐“สน'*๐…*๐“ end @@ -58,6 +58,7 @@ function estimate(entity::FundamentalMatrix, method::FundamentalNumericalScheme, end ๐… = reshape(๐›‰,(3,3)) ๐… = enforce_ranktwo!(Array(๐…)) + ๐… = SMatrix{3,3,Float64,9}(๐…) # Transform estimate back to the original (unnormalised) coordinate system. ๐… = ๐“สน'*๐…*๐“ end @@ -68,7 +69,7 @@ function estimate(entity::FundamentalMatrix, method::BundleAdjustment, ๐’Ÿ::Tu if (N != length(โ„ณสน)) throw(ArgumentError("There should be an equal number of points for each view.")) end - ๐… = reshape(method.๐›‰โ‚€,(3,3)) + ๐… = SMatrix{3,3,Float64,9}(reshape(method.๐›‰โ‚€,(3,3))) ๐’ณ = triangulate(DirectLinearTransform(),๐…,(โ„ณ,โ„ณสน)) ๐โ‚, ๐โ‚‚ = construct(ProjectionMatrix(),๐…) diff --git a/src/moments/moments_fundamentalmatrix.jl b/src/moments/moments_fundamentalmatrix.jl index af0b961..919df10 100644 --- a/src/moments/moments_fundamentalmatrix.jl +++ b/src/moments/moments_fundamentalmatrix.jl @@ -7,8 +7,8 @@ function moments(entity::FundamentalMatrix, ๐’Ÿ::Tuple{AbstractArray, Vararg{Ab end ๐€ = @SMatrix zeros(9,9) for n = 1:N - ๐ฆ = ๐‘›(โ„ณ[n]) - ๐ฆสน = ๐‘›(โ„ณสน[n]) + ๐ฆ = hom(โ„ณ[n]) + ๐ฆสน = hom(โ„ณสน[n]) ๐€ = ๐€ + (๐ฆ*๐ฆ') โŠ— (๐ฆสน*๐ฆสน') end ๐€/N diff --git a/src/operators/operators.jl b/src/operators/operators.jl index c983bb6..4e41016 100644 --- a/src/operators/operators.jl +++ b/src/operators/operators.jl @@ -45,7 +45,11 @@ function ๐‘›(v::SVector) end function homโปยน(v::SVector) - pop(v / v[end]) + if isapprox(v[end], 0.0; atol = 1e-14) + pop(v) + else + pop(v / v[end]) + end end function hom(v::SVector) diff --git a/src/projection/project.jl b/src/projection/project.jl index 871fd53..8b3f5bf 100644 --- a/src/projection/project.jl +++ b/src/projection/project.jl @@ -5,6 +5,6 @@ function project(e::Pinhole, ๐::AbstractArray, ๐’ณ::Vector{<:AbstractArray}) throw(ArgumentError("Expect 3 x 4 projection matrix.")) end โ„ณ = map(๐’ณ) do ๐— - ๐ฆ = ๐‘›(Point2DH(๐ * ๐—)) + ๐ฆ = homโปยน(๐ * hom(๐—)) end end diff --git a/src/triangulation/triangulate.jl b/src/triangulation/triangulate.jl index d869ce9..8e4da0d 100644 --- a/src/triangulation/triangulate.jl +++ b/src/triangulation/triangulate.jl @@ -2,14 +2,19 @@ function triangulate(method::DirectLinearTransform, ๐…::AbstractArray, ๐’Ÿ::T โ„ณ, โ„ณสน = ๐’Ÿ ๐โ‚, ๐โ‚‚ = construct(ProjectionMatrix(),๐…) N = length(โ„ณ) - ๐’ด = Array{Point3DH}(undef,N) + ๐’ด = Array{Point3D}(undef,N) for n = 1:N ๐ฆ = โ„ณ[n] ๐ฆสน = โ„ณสน[n] - ๐€ = [ (๐ฆ[1] * ๐โ‚[3,:] - ๐โ‚[1,:])' ; - (๐ฆ[2] * ๐โ‚[3,:] - ๐โ‚[2,:])' ; - (๐ฆสน[1] * ๐โ‚‚[3,:] - ๐โ‚‚[1,:])' ; - (๐ฆสน[2] * ๐โ‚‚[3,:] - ๐โ‚‚[2,:])' ] + eq1 = ๐ฆ[1] * ๐โ‚[3,:] - ๐โ‚[1,:] + eq2 = ๐ฆ[2] * ๐โ‚[3,:] - ๐โ‚[2,:] + eq3 = ๐ฆสน[1] * ๐โ‚‚[3,:] - ๐โ‚‚[1,:] + eq4 = ๐ฆสน[2] * ๐โ‚‚[3,:] - ๐โ‚‚[2,:] + ๐€ = SMatrix{4,4}(transpose(hcat(eq1,eq2,eq3,eq4))) + # ๐€ = [ (๐ฆ[1] * ๐โ‚[3,:] - ๐โ‚[1,:])' ; + # (๐ฆ[2] * ๐โ‚[3,:] - ๐โ‚[2,:])' ; + # (๐ฆสน[1] * ๐โ‚‚[3,:] - ๐โ‚‚[1,:])' ; + # (๐ฆสน[2] * ๐โ‚‚[3,:] - ๐โ‚‚[2,:])' ] # ๐€โ‚ = vec2antisym(๐ฆ)*๐โ‚ # ๐€โ‚‚ = vec2antisym(๐ฆสน)*๐โ‚‚ # @show typeof(๐€โ‚) @@ -20,9 +25,8 @@ function triangulate(method::DirectLinearTransform, ๐…::AbstractArray, ๐’Ÿ::T # ฮป, f = smallest_eigenpair(Symmetric(Array(๐€'*๐€))) # @show ฮป # ๐’ด[n] = Point3DH(๐‘›(f)) - - U,S,V = svd(Array(๐€)) - ๐’ด[n] = Point3DH(๐‘›(V[:,4])) + U,S,V = svd(๐€) + ๐’ด[n] = homโปยน(V[:,4]) end ๐’ด end @@ -30,14 +34,20 @@ end function triangulate(method::DirectLinearTransform, ๐โ‚::AbstractArray, ๐โ‚‚::AbstractArray, ๐’Ÿ::Tuple{AbstractArray, Vararg{AbstractArray}}) โ„ณ, โ„ณสน = ๐’Ÿ N = length(โ„ณ) - ๐’ด = Array{Point3DH}(undef,N) + ๐’ด = Array{Point3D}(undef,N) for n = 1:N ๐ฆ = โ„ณ[n] ๐ฆสน = โ„ณสน[n] - ๐€ = [ (๐ฆ[1] * ๐โ‚[3,:] - ๐โ‚[1,:])' ; - (๐ฆ[2] * ๐โ‚[3,:] - ๐โ‚[2,:])' ; - (๐ฆสน[1] * ๐โ‚‚[3,:] - ๐โ‚‚[1,:])' ; - (๐ฆสน[2] * ๐โ‚‚[3,:] - ๐โ‚‚[2,:])' ] + eq1 = ๐ฆ[1] * ๐โ‚[3,:] - ๐โ‚[1,:] + eq2 = ๐ฆ[2] * ๐โ‚[3,:] - ๐โ‚[2,:] + eq3 = ๐ฆสน[1] * ๐โ‚‚[3,:] - ๐โ‚‚[1,:] + eq4 = ๐ฆสน[2] * ๐โ‚‚[3,:] - ๐โ‚‚[2,:] + ๐€ = SMatrix{4,4}(transpose(hcat(eq1,eq2,eq3,eq4))) + # ๐€ = [ (๐ฆ[1] * ๐โ‚[3,:] - ๐โ‚[1,:])' ; + # (๐ฆ[2] * ๐โ‚[3,:] - ๐โ‚[2,:])' ; + # (๐ฆสน[1] * ๐โ‚‚[3,:] - ๐โ‚‚[1,:])' ; + # (๐ฆสน[2] * ๐โ‚‚[3,:] - ๐โ‚‚[2,:])' ] + # ๐€โ‚ = vec2antisym(๐ฆ)*๐โ‚ # ๐€โ‚‚ = vec2antisym(๐ฆสน)*๐โ‚‚ # @show typeof(๐€โ‚) @@ -48,9 +58,8 @@ function triangulate(method::DirectLinearTransform, ๐โ‚::AbstractArray, ๐ # ฮป, f = smallest_eigenpair(Symmetric(Array(๐€'*๐€))) # @show ฮป # ๐’ด[n] = Point3DH(๐‘›(f)) - - U,S,V = svd(Array(๐€)) - ๐’ด[n] = Point3DH(๐‘›(V[:,4])) + U,S,V = svd(๐€) + ๐’ด[n] = homโปยน(V[:,4]) end ๐’ด end diff --git a/src/types/ModuleTypes.jl b/src/types/ModuleTypes.jl index 6bcc530..6200253 100644 --- a/src/types/ModuleTypes.jl +++ b/src/types/ModuleTypes.jl @@ -9,7 +9,7 @@ export BundleAdjustment export CostFunction, ApproximateMaximumLikelihood, AML export CoordinateSystemTransformation, CanonicalToHartley, HartleyToCanonical export CovarianceMatrices -export Point2DH, Point3DH +export Point2D, Point2DH, Point3D, Point3DH export HessianApproximation, CanonicalApproximation, CovarianceEstimationScheme export NoiseModel, GaussianNoise include("types.jl") diff --git a/src/types/types.jl b/src/types/types.jl index 2ad2e56..668762d 100644 --- a/src/types/types.jl +++ b/src/types/types.jl @@ -14,8 +14,11 @@ #const Point2DH = MMatrix{3,1,Float64,3} #const Point3DH = MMatrix{4,1,Float64,4} -const Point2DH = MVector{3,Float64} -const Point3DH = MVector{4,Float64} +const Point2DH = SVector{3,Float64} +const Point3DH = SVector{4,Float64} + +const Point2D = SVector{2,Float64} +const Point3D = SVector{3,Float64} # mutable struct Point2DH <: MMatrix{3,1,Float64,3} # x::Float64 @@ -71,15 +74,15 @@ end mutable struct DirectLinearTransform <: EstimationAlgorithm end -mutable struct BundleAdjustment <: EstimationAlgorithm - ๐›‰โ‚€::Matrix{Float64} - max_iter::Int8 +mutable struct BundleAdjustment{A<:AbstractVector} <: EstimationAlgorithm + ๐›‰โ‚€::A + max_iter::Int64 toleranceฮธ::Float64 end -mutable struct FundamentalNumericalScheme <: EstimationAlgorithm - ๐›‰โ‚€::Matrix{Float64} - max_iter::Int8 +mutable struct FundamentalNumericalScheme{A<:AbstractVector} <: EstimationAlgorithm + ๐›‰โ‚€::A + max_iter::Int64 toleranceฮธ::Float64 end diff --git a/test/construct_projectionmatrix_tests.jl b/test/construct_projectionmatrix_tests.jl index 8dc4537..d5739fd 100644 --- a/test/construct_projectionmatrix_tests.jl +++ b/test/construct_projectionmatrix_tests.jl @@ -8,7 +8,7 @@ using MultipleViewGeometry.ModuleTypes @test construct(ProjectionMatrix(),๐Š,๐‘,๐ญ) == [Matrix{Float64}(I, 3, 3) -ones(3)] # 1. Construct a Fundamental matrix from Camera matrices. -# 2. Construct projection matrices from the Fundamental matrix. +# 2. Construct Projection matrices from the Fundamental matrix. # 3. Construct a Fundamental matrix from the projection matrices. # 4. The Fundamental matrices in step 2 and 3 should be equivalent up to sign # and scale. diff --git a/test/cost_functions_tests.jl b/test/cost_functions_tests.jl index f1e0403..4fd1f46 100644 --- a/test/cost_functions_tests.jl +++ b/test/cost_functions_tests.jl @@ -8,8 +8,7 @@ using StaticArrays # Test cost function on Fundamental matrix estimation. -๐’ณ = [Point3DH(x,y,z,1.0) - for x=-1:0.5:10 for y=-1:0.5:10 for z=2:-0.1:1] +๐’ณ = [Point3D(x,y,z) for x=-1:0.5:10 for y=-1:0.5:10 for z=2:-0.1:1] # Intrinsic and extrinsic parameters of camera one. ๐Šโ‚ = SMatrix{3,3}(Matrix{Float64}(I, 3, 3)) @@ -34,7 +33,7 @@ using StaticArrays # Ensure the estimated and true matrix have the same scale and sign. ๐… = ๐… / norm(๐…) ๐… = ๐… / sign(๐…[1,3]) -๐Ÿ = reshape(๐…,9,1) +๐Ÿ = vec(๐…) ฮ›โ‚ = [SMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(โ„ณ)] ฮ›โ‚‚ = [SMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(โ„ณ)] @@ -43,14 +42,14 @@ Jโ‚โ‚˜โ‚— = cost(AML(),FundamentalMatrix(), SVector{9}(๐Ÿ), (ฮ›โ‚,ฮ›โ‚‚), ( @test isapprox(Jโ‚โ‚˜โ‚—, 0.0; atol = 1e-14) # Verify that the vectorised fundamental matrix is in the null space of X -๐— = X(AML(),FundamentalMatrix(), reshape(๐…,9,1), (ฮ›โ‚,ฮ›โ‚‚), (โ„ณ, โ„ณสน)) +๐— = X(AML(),FundamentalMatrix(), vec(๐…), (ฮ›โ‚,ฮ›โ‚‚), (โ„ณ, โ„ณสน)) # The true parameters should lie in the null space of the matrix X. @test all(isapprox.(๐— * ๐Ÿ, 0.0; atol = 1e-10)) # Verify that the the vectorised fundamental matrix is in the null space of H. # H represents the Hessian matrix of the AML cost function. -๐‡ = H(AML(),FundamentalMatrix(), reshape(๐…,9,1), (ฮ›โ‚,ฮ›โ‚‚), (โ„ณ, โ„ณสน)) +๐‡ = H(AML(),FundamentalMatrix(), vec(๐…), (ฮ›โ‚,ฮ›โ‚‚), (โ„ณ, โ„ณสน)) # The true parameters should lie in the null space of the matrix H. @test all(isapprox.(๐‡ * ๐Ÿ, 0.0; atol = 1e-10)) diff --git a/test/data_normalization_tests.jl b/test/data_normalization_tests.jl index 7c16025..916c049 100644 --- a/test/data_normalization_tests.jl +++ b/test/data_normalization_tests.jl @@ -3,53 +3,53 @@ using StaticArrays, LinearAlgebra # Tests for a set of two-dimensional Cartesian points represented by homogeneous # coordinates. -โ„ณ = map(Point2DH, - [(-10.0, -10.0, 1.0), - (-10.0, 10.0, 1.0), - ( 10.0, -10.0, 1.0), - ( 10.0, 10.0, 1.0)]) +โ„ณ = map(Point2D, [(-10.0, -10.0), + (-10.0, 10.0), + ( 10.0, -10.0), + ( 10.0, 10.0)]) โ„ณสน, ๐“ = hartley_normalization(โ„ณ) -@test โ„ณสน == map(Point2DH, - [(-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 โ„ณสน == map(Point2D, [(-1.0,-1.0), + (-1.0, 1.0), + (1.0, -1.0), + (1.0, 1.0,)]) + @test ๐“ == [0.1 0.0 -0.0; 0.0 0.1 -0.0; 0.0 0.0 1.0] @test hartley_transformation(โ„ณ) == [0.1 0.0 -0.0; - 0.0 0.1 -0.0; - 0.0 0.0 1.0] + 0.0 0.1 -0.0; + 0.0 0.0 1.0] # Tests for a set of three-dimensional Cartesian points represented by homogeneous # coordinates. -โ„ณ = map(Point3DH, - [(-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)]) +โ„ณ = map(Point3D, + [(-10.0, -10.0, -10.0), + (-10.0, -10.0, 10.0), + (-10.0, 10.0, -10.0), + (-10.0, 10.0, 10.0), + ( 10.0, -10.0, -10.0), + ( 10.0, -10.0, 10.0), + ( 10.0, 10.0, -10.0), + ( 10.0, 10.0, 10.0)]) โ„ณสน, ๐“ = hartley_normalization(โ„ณ) -@test โ„ณสน == map(Point3DH, - [(-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 โ„ณสน == map(Point3D, [(-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 ๐“ == [0.1 0.0 0.0 -0.0; 0.0 0.1 0.0 -0.0; 0.0 0.0 0.1 -0.0; 0.0 0.0 0.0 1.0] -@test hartley_transformation(โ„ณ) == [0.1 0.0 0.0 -0.0; - 0.0 0.1 0.0 -0.0; - 0.0 0.0 0.1 -0.0; - 0.0 0.0 0.0 1.0] +@test hartley_transformation(โ„ณ) == [0.1 0.0 0.0 -0.0; + 0.0 0.1 0.0 -0.0; + 0.0 0.0 0.1 -0.0; + 0.0 0.0 0.0 1.0] diff --git a/test/estimate_twoview_tests.jl b/test/estimate_twoview_tests.jl index 9e383c4..0132d1c 100644 --- a/test/estimate_twoview_tests.jl +++ b/test/estimate_twoview_tests.jl @@ -4,7 +4,7 @@ using StaticArrays, Calculus # Tests for fundamental matrix estimation -๐’ณ = [Point3DH(x,y,z,1.0) +๐’ณ = [Point3D(x,y,z) for x=-100:5:100 for y=-100:5:100 for z=1:-50:-100] ๐’ณ = ๐’ณ[1:50:end] # Intrinsic and extrinsic parameters of camera one. @@ -42,19 +42,19 @@ npts = length(โ„ณ) residual = zeros(Float64,npts,1) for correspondence in zip(1:length(โ„ณ),โ„ณ, โ„ณสน) i, m , mสน = correspondence - ๐ฆ = ๐‘›(m) - ๐ฆสน = ๐‘›(mสน) + ๐ฆ = hom(m) + ๐ฆสน = hom(mสน) residual[i] = (๐ฆสน'*๐…*๐ฆ) end @test isapprox(sum(residual), 0.0; atol = 1e-7) # Test the Fundamental Numerical Scheme on the Fundamental matrix problem. -ฮ›โ‚ = [SMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(โ„ณ)]sum(residual) +ฮ›โ‚ = [SMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(โ„ณ)] ฮ›โ‚‚ = [SMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(โ„ณ)] ๐…โ‚€ = estimate(FundamentalMatrix(),DirectLinearTransform(), (โ„ณ, โ„ณสน)) ๐… = estimate(FundamentalMatrix(), - FundamentalNumericalScheme(reshape(๐…โ‚€,9,1), 5, 1e-10), + FundamentalNumericalScheme(vec(๐…โ‚€), 5, 1e-10), (ฮ›โ‚,ฮ›โ‚‚), (โ„ณ, โ„ณสน)) ๐…โ‚œ = construct(FundamentalMatrix(),๐Šโ‚,๐‘โ‚,๐ญโ‚,๐Šโ‚‚,๐‘โ‚‚,๐ญโ‚‚) @@ -83,7 +83,7 @@ end # Test the Bundle Adjustment estimator on the Fundamental matrix problem. ๐…, lsqFit = estimate(FundamentalMatrix(), - BundleAdjustment(reshape(๐…โ‚€,9,1), 5, 1e-10), + BundleAdjustment(vec(๐…โ‚€), 5, 1e-10), (โ„ณ, โ„ณสน)) ๐… = ๐… / norm(๐…) ๐… = ๐… / sign(๐…[1,2]) diff --git a/test/satisfy_epipolar_constraints_tests.jl b/test/satisfy_epipolar_constraints_tests.jl index 5addae3..56c50fc 100644 --- a/test/satisfy_epipolar_constraints_tests.jl +++ b/test/satisfy_epipolar_constraints_tests.jl @@ -11,9 +11,7 @@ Random.seed!(1234) # Test for cost functions. # Test cost function on Fundamental matrix estimation. - -๐’ณ = [Point3DH(x,y,z,1.0) - for x=-1:0.5:10 for y=-1:0.5:10 for z=2:-0.1:1] +๐’ณ = [Point3D(x,y,z) for x=-1:0.5:10 for y=-1:0.5:10 for z=2:-0.1:1] # Intrinsic and extrinsic parameters of camera one. ๐Šโ‚ = SMatrix{3,3}(1.0I) @@ -37,21 +35,21 @@ Random.seed!(1234) # Verify that the algorithm returns the correct answer when the # constraint is already satisfied. -๐’ช ,๐’ชสน = satisfy(FundamentalMatrix(), EpipolarConstraint(), ๐…, (โ„ณ, โ„ณสน)) +๐’ช,๐’ชสน = satisfy(FundamentalMatrix(), EpipolarConstraint(), ๐…, (โ„ณ, โ„ณสน)) # Verify that the original corresponding points satisfy the epipolar constraint. N = length(โ„ณ) for n = 1:N - ๐ฆ = โ„ณ[n] - ๐ฆสน = โ„ณสน[n] + ๐ฆ = hom(โ„ณ[n]) + ๐ฆสน = hom(โ„ณสน[n]) @test isapprox(๐ฆ'*๐…*๐ฆสน, 0.0; atol = 1e-14) end # Verify that the 'corrected' points satisfy the epipolar constraint. N = length(โ„ณ) for n = 1:N - ๐ฆ = ๐’ช[n] - ๐ฆสน = ๐’ชสน[n] + ๐ฆ = hom(๐’ช[n]) + ๐ฆสน = hom(๐’ชสน[n]) @test isapprox(๐ฆ'*๐…*๐ฆสน, 0.0; atol = 1e-14) end @@ -60,10 +58,10 @@ end N = length(โ„ณ) ฯƒ = 1e-7 for n = 1:N - โ„ณ[n] = โ„ณ[n] + SVector{3}(ฯƒ * vcat(rand(2,1),0)) - โ„ณสน[n] = โ„ณสน[n] + SVector{3}(ฯƒ * vcat(rand(2,1),0)) - ๐ฆ = โ„ณ[n] - ๐ฆสน = โ„ณสน[n] + โ„ณ[n] = โ„ณ[n] + SVector{2}(ฯƒ * rand(2,1)) + โ„ณสน[n] = โ„ณสน[n] + SVector{2}(ฯƒ * rand(2,1)) + ๐ฆ = hom(โ„ณ[n]) + ๐ฆสน = hom(โ„ณสน[n]) @test abs(๐ฆ'*๐…*๐ฆสน) > 1e-12 end @@ -75,12 +73,7 @@ end # Verify that the 'corrected' points satisfy the epipolar constraint. N = length(โ„ณ) for n = 1:N - ๐ฆ = ๐’ช[n] - ๐ฆสน = ๐’ชสน[n] + ๐ฆ = hom(๐’ช[n]) + ๐ฆสน = hom(๐’ชสน[n]) @test isapprox(๐ฆ'*๐…*๐ฆสน, 0.0; atol = 1e-14) end - -# ฯƒ = 1e-7 -# SVector{3}(ฯƒ * vcat(rand(2,1),0)) -# -# โ„ณ[1] - ๐’ช[1] diff --git a/test/transform_tests.jl b/test/transform_tests.jl index e169f6e..23e7fc9 100644 --- a/test/transform_tests.jl +++ b/test/transform_tests.jl @@ -3,18 +3,18 @@ using StaticArrays, LinearAlgebra # Tests for a set of two-dimensional Cartesian points represented by homogeneous # coordinates. -โ„ณ = map(Point2DH, - [(-10.0, -10.0, 1.0), - (-10.0, 10.0, 1.0), - ( 10.0, -10.0, 1.0), - ( 10.0, 10.0, 1.0)]) +โ„ณ = map(Point2D, + [(-10.0, -10.0), + (-10.0, 10.0), + ( 10.0, -10.0), + ( 10.0, 10.0)]) โ„ณสน, ๐“ = transform(HomogeneousCoordinates(),CanonicalToHartley(),โ„ณ) -@test โ„ณสน == map(Point2DH, - [(-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 โ„ณสน == map(Point2D, + [(-1.0,-1.0), + (-1.0, 1.0), + (1.0, -1.0), + (1.0, 1.0)]) ฮ› = [MMatrix{3,3}(Matrix(Diagonal([1.0,1.0,0.0]))) for i = 1:length(โ„ณ)] ฮ›สน = transform(CovarianceMatrices(), CanonicalToHartley(), ฮ› , ๐“) @@ -28,26 +28,26 @@ end # Tests for a set of three-dimensional Cartesian points represented by homogeneous # coordinates. -โ„ณ = map(Point3DH, - [(-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)]) +โ„ณ = map(Point3D, + [(-10.0, -10.0, -10.0), + (-10.0, -10.0, 10.0), + (-10.0, 10.0, -10.0), + (-10.0, 10.0, 10.0), + ( 10.0, -10.0, -10.0), + ( 10.0, -10.0, 10.0), + ( 10.0, 10.0, -10.0), + ( 10.0, 10.0, 10.0)]) โ„ณสน, ๐“ = transform(HomogeneousCoordinates(),CanonicalToHartley(),โ„ณ) -@test โ„ณสน == map(Point3DH, - [(-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 โ„ณสน == map(Point3D, + [(-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 ๐“ == [0.1 0.0 0.0 -0.0; 0.0 0.1 0.0 -0.0; 0.0 0.0 0.1 -0.0; diff --git a/test/triangulate_tests.jl b/test/triangulate_tests.jl index 04760b8..75b9be1 100644 --- a/test/triangulate_tests.jl +++ b/test/triangulate_tests.jl @@ -9,8 +9,7 @@ using StaticArrays # Fix random seed. Random.seed!(1234) -๐’ณ = [Point3DH(x,y,z,1.0) - for x=-1:0.5:10 for y=-1:0.5:10 for z=2:-0.1:1] +๐’ณ = [Point3D(x,y,z) for x=-1:0.5:10 for y=-1:0.5:10 for z=2:-0.1:1] # Intrinsic and extrinsic parameters of camera one. ๐Šโ‚ = SMatrix{3,3}(1.0I) @@ -28,7 +27,7 @@ Random.seed!(1234) # Set of corresponding points. โ„ณ = project(Pinhole(),๐โ‚,๐’ณ) -โ„ณสน = project(Pinhole(),๐โ‚‚,๐’ณ) +โ„ณสน= project(Pinhole(),๐โ‚‚,๐’ณ) ๐’ด = triangulate(DirectLinearTransform(),๐โ‚,๐โ‚‚,(โ„ณ,โ„ณสน)) @@ -36,7 +35,7 @@ Random.seed!(1234) # (โ„ณ,โ„ณสน) should yield the same 3D points as the original ๐’ณ. N = length(๐’ด) for n = 1:N - @test isapprox(sum(abs.(๐’ณ[n]-๐’ด[n])/4), 0.0; atol = 1e-12) + @test isapprox(sum(abs.(๐’ณ[n]-๐’ด[n])/3), 0.0; atol = 1e-12) end @@ -56,7 +55,7 @@ end ๐’ชสน= project(Pinhole(),๐โ‚‚,๐’ด) N = length(๐’ช) for n = 1:N - ๐ฆ = ๐’ช[n] - ๐ฆสน = ๐’ชสน[n] + ๐ฆ = hom(๐’ช[n]) + ๐ฆสน = hom(๐’ชสน[n]) @test isapprox(๐ฆ'*๐…*๐ฆสน, 0.0; atol = 1e-14) end