You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1289 lines
52 KiB
1289 lines
52 KiB
//////////////////////////////////////////////////////////////////// |
|
// Util.inl |
|
// |
|
// Copyright 2007 cDc@seacave |
|
// Distributed under the Boost Software License, Version 1.0 |
|
// (See http://www.boost.org/LICENSE_1_0.txt) |
|
|
|
|
|
// D E F I N E S /////////////////////////////////////////////////// |
|
|
|
|
|
namespace SEACAVE { |
|
|
|
// I N L I N E ///////////////////////////////////////////////////// |
|
|
|
// normalize inhomogeneous 2D point by the given camera intrinsics K |
|
// K is assumed to be the [3,3] triangular matrix with: fx, fy, s, cx, cy and scale 1 |
|
template<typename TYPE1, typename TYPE2, typename TYPE3> |
|
inline void NormalizeProjection(const TYPE1* K, const TYPE2* x, TYPE3* n) { |
|
ASSERT(ISZERO(K[3]) && ISZERO(K[6]) && ISZERO(K[7]) && ISEQUAL(K[8], TYPE1(1))); |
|
n[0] = TYPE3(K[0]*x[0] + K[1]*x[1] + K[2]); |
|
n[1] = TYPE3( K[4]*x[1] + K[5]); |
|
} // NormalizeProjection |
|
// same as above, but using K inverse |
|
template<typename TYPE1, typename TYPE2, typename TYPE3> |
|
inline void NormalizeProjectionInv(const TYPE1* K, const TYPE2* x, TYPE3* n) { |
|
ASSERT(ISZERO(K[3]) && ISZERO(K[6]) && ISZERO(K[7]) && ISEQUAL(K[8], TYPE1(1))); |
|
n[0] = TYPE3((K[4]*x[0] - K[1]*x[1] + (K[5]*K[1]-K[2]*K[4])) / (K[0]*K[4])); |
|
n[1] = TYPE3((x[1] - K[5]) / K[4]); |
|
} // NormalizeProjectionInv |
|
/*----------------------------------------------------------------*/ |
|
|
|
// compute relative pose of the given two cameras |
|
template<typename TYPE> |
|
inline void ComputeRelativeRotation(const TMatrix<TYPE,3,3>& Ri, const TMatrix<TYPE,3,3>& Rj, TMatrix<TYPE,3,3>& Rij) { |
|
Rij = Rj * Ri.t(); |
|
} // ComputeRelativeRotation |
|
template<typename TYPE> |
|
inline void ComputeRelativePose(const TMatrix<TYPE,3,3>& Ri, const TPoint3<TYPE>& Ci, const TMatrix<TYPE,3,3>& Rj, const TPoint3<TYPE>& Cj, TMatrix<TYPE,3,3>& Rij, TPoint3<TYPE>& Cij) { |
|
Rij = Rj * Ri.t(); |
|
Cij = Ri * (Cj - Ci); |
|
} // ComputeRelativePose |
|
/*----------------------------------------------------------------*/ |
|
|
|
// Triangulate the position of a 3D point |
|
// given two corresponding normalized projections and the pose of the second camera relative to the first one; |
|
// returns the triangulated 3D point from the point of view of the first camera |
|
template<typename TYPE1, typename TYPE2> |
|
bool TriangulatePoint3D( |
|
const TMatrix<TYPE1,3,3>& R, const TPoint3<TYPE1>& C, |
|
const TPoint2<TYPE2>& pt1, const TPoint2<TYPE2>& pt2, |
|
TPoint3<TYPE1>& X |
|
) { |
|
// convert image points to 3-vectors (of unit length) |
|
// used to describe landmark observations/bearings in camera frames |
|
const TPoint3<TYPE1> f1(pt1.x,pt1.y,1); |
|
const TPoint3<TYPE1> f2(pt2.x,pt2.y,1); |
|
const TPoint3<TYPE1> f2_unrotated = R.t() * f2; |
|
const TPoint2<TYPE1> b(C.dot(f1), C.dot(f2_unrotated)); |
|
// optimized inversion of A |
|
const TYPE1 a = normSq(f1); |
|
const TYPE1 c = f1.dot(f2_unrotated); |
|
const TYPE1 d = -normSq(f2_unrotated); |
|
const TYPE1 det = a*d+c*c; |
|
if (ABS(det) < EPSILONTOLERANCE<TYPE1>()) |
|
return false; |
|
const TYPE1 invDet = TYPE1(1)/det; |
|
const TPoint2<TYPE1> lambda((d*b.x+c*b.y)*invDet, (a*b.y-c*b.x)*invDet); |
|
const TPoint3<TYPE1> xm = lambda.x * f1; |
|
const TPoint3<TYPE1> xn = C + lambda.y * f2_unrotated; |
|
X = (xm + xn)*TYPE1(0.5); |
|
return true; |
|
} // TriangulatePoint3D |
|
// same as above, but using the two camera poses; |
|
// returns the 3D point in world coordinates |
|
template<typename TYPE1, typename TYPE2> |
|
bool TriangulatePoint3D( |
|
const TMatrix<TYPE1,3,3>& K1, const TMatrix<TYPE1,3,3>& K2, |
|
const TMatrix<TYPE1,3,3>& R1, const TMatrix<TYPE1,3,3>& R2, |
|
const TPoint3<TYPE1>& C1, const TPoint3<TYPE1>& C2, |
|
const TPoint2<TYPE2>& x1, const TPoint2<TYPE2>& x2, |
|
TPoint3<TYPE1>& X |
|
) { |
|
TPoint2<TYPE1> pt1, pt2; |
|
NormalizeProjectionInv(K1.val, x1.ptr(), pt1.ptr()); |
|
NormalizeProjectionInv(K2.val, x2.ptr(), pt2.ptr()); |
|
TMatrix<TYPE1,3,3> R; TPoint3<TYPE1> C; |
|
ComputeRelativePose(R1, C1, R2, C2, R, C); |
|
if (!TriangulatePoint3D(R, C, pt1, pt2, X)) |
|
return false; |
|
X = R1.t() * X + C1; |
|
return true; |
|
} // TriangulatePoint3D |
|
/*----------------------------------------------------------------*/ |
|
|
|
// compute the homography matrix transforming points from image A to image B, |
|
// given the relative pose of B with respect to A, and the plane |
|
// (see R. Hartley, "Multiple View Geometry," 2004, pp. 234) |
|
template <typename TYPE> |
|
TMatrix<TYPE,3,3> HomographyMatrixComposition(const TMatrix<TYPE,3,3>& R, const TPoint3<TYPE>& C, const TMatrix<TYPE,3,1>& n, const TYPE& d) { |
|
const TMatrix<TYPE,3,1> t(R*(-C)); |
|
return TMatrix<TYPE,3,3>(R - t*(n.t()*INVERT(d))); |
|
} |
|
template <typename TYPE> |
|
TMatrix<TYPE,3,3> HomographyMatrixComposition(const TMatrix<TYPE,3,3>& R, const TPoint3<TYPE>& C, const TMatrix<TYPE,3,1>& n, const TMatrix<TYPE,3,1>& X) { |
|
return HomographyMatrixComposition(R, C, n, -n.dot(X)); |
|
} |
|
template <typename TYPE> |
|
TMatrix<TYPE,3,3> HomographyMatrixComposition(const TMatrix<TYPE,3,3>& Ri, const TPoint3<TYPE>& Ci, const TMatrix<TYPE,3,3>& Rj, const TPoint3<TYPE>& Cj, const TMatrix<TYPE,3,1>& n, const TYPE& d) { |
|
#if 0 |
|
Matrix3x3 R; Point3 C; |
|
ComputeRelativePose(Ri, Ci, Rj, Cj, R, C); |
|
return HomographyMatrixComposition(R, C, n, d); |
|
#else |
|
const TMatrix<TYPE,3,1> t((Ci - Cj)*INVERT(d)); |
|
return TMatrix<TYPE,3,3>(Rj * (Ri.t() - t*n.t())); |
|
#endif |
|
} |
|
template <typename TYPE> |
|
TMatrix<TYPE,3,3> HomographyMatrixComposition(const TMatrix<TYPE,3,3>& Ri, const TPoint3<TYPE>& Ci, const TMatrix<TYPE,3,3>& Rj, const TPoint3<TYPE>& Cj, const TMatrix<TYPE,3,1>& n, const TMatrix<TYPE,3,1>& X) { |
|
#if 0 |
|
Matrix3x3 R; Point3 C; |
|
ComputeRelativePose(Ri, Ci, Rj, Cj, R, C); |
|
return HomographyMatrixComposition(R, C, n, X); |
|
#else |
|
const TMatrix<TYPE,3,1> t((Ci - Cj)*INVERT(n.dot(X))); |
|
return TMatrix<TYPE,3,3>(Rj * (Ri.t() + t*n.t())); |
|
#endif |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
// transform essential matrix to fundamental matrix |
|
template<typename TYPE> |
|
inline TMatrix<TYPE,3,3> TransformE2F(const TMatrix<TYPE,3,3>& E, const TMatrix<TYPE,3,3>& K1, const TMatrix<TYPE,3,3>& K2) { |
|
return K2.inv().t() * E * K1.inv(); |
|
} // TransformE2F |
|
// transform fundamental matrix to essential matrix |
|
template<typename TYPE> |
|
inline TMatrix<TYPE,3,3> TransformF2E(const TMatrix<TYPE,3,3>& F, const TMatrix<TYPE,3,3>& K1, const TMatrix<TYPE,3,3>& K2) { |
|
return K2.t() * F * K1; |
|
} // TransformF2E |
|
/*----------------------------------------------------------------*/ |
|
|
|
// Creates a skew symmetric cross product matrix from the provided tuple. |
|
// compose the cross-product matrix from the given vector: axb=[a]xb |
|
template<typename TYPE> |
|
inline TMatrix<TYPE,3,3> CreateCrossProductMatrix3(const TMatrix<TYPE,3,1>& x) { |
|
TMatrix<TYPE,3,3> X; |
|
X(0, 0) = TYPE(0); X(1, 0) = -x(2); X(2, 0) = x(1); |
|
X(0, 1) = x(2); X(1, 1) = TYPE(0); X(2, 1) = -x(0); |
|
X(0, 2) = -x(1); X(1, 2) = x(0); X(2, 2) = TYPE(0); |
|
return X; |
|
} // CreateCrossProductMatrix3 |
|
// same as above, but transposed: axb=[b]xTa |
|
template<typename TYPE> |
|
inline TMatrix<TYPE,3,3> CreateCrossProductMatrix3T(const TMatrix<TYPE,3,1>& x) { |
|
TMatrix<TYPE,3,3> X; |
|
X(0, 0) = TYPE(0); X(1, 0) = x(2); X(2, 0) = -x(1); |
|
X(0, 1) = -x(2); X(1, 1) = TYPE(0); X(2, 1) = x(0); |
|
X(0, 2) = x(1); X(1, 2) = -x(0); X(2, 2) = TYPE(0); |
|
return X; |
|
} // CreateCrossProductMatrix3T |
|
/*----------------------------------------------------------------*/ |
|
|
|
// compose essential matrix from the given rotation and direction |
|
template<typename TYPE> |
|
inline TMatrix<TYPE,3,3> CreateE(const TMatrix<TYPE,3,3>& R, const TMatrix<TYPE,3,1>& C) { |
|
const TMatrix<TYPE,3,1> t = -(R * C); |
|
return CreateCrossProductMatrix3(t) * R; |
|
} // CreateE |
|
// compose fundamental matrix from the given rotation, direction and camera matrix |
|
template<typename TYPE> |
|
inline TMatrix<TYPE,3,3> CreateF(const TMatrix<TYPE,3,3>& R, const TMatrix<TYPE,3,1>& C, const TMatrix<TYPE,3,3>& K1, const TMatrix<TYPE,3,3>& K2) { |
|
const TMatrix<TYPE,3,3> E = CreateE(R, C); |
|
return TransformE2F(E, K1, K2); |
|
} // CreateF |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// computes an RQ decomposition of 3x3 matrices as in: |
|
// "Computing euler angles from a rotation matrix", Gregory G Slabaugh, 1999 |
|
template <typename Scalar> |
|
inline void RQDecomp3x3(Eigen::Matrix<Scalar,3,3> M, Eigen::Matrix<Scalar,3,3>& R, Eigen::Matrix<Scalar,3,3>& Q) { |
|
// find Givens rotation for x axis: |
|
// | 1 0 0 | |
|
// Qx = | 0 c s |, c = m33/sqrt(m32^2 + m33^2), s = m32/sqrt(m32^2 + m33^2) |
|
// | 0 -s c | |
|
Eigen::Matrix<Scalar,2,1> cs = Eigen::Matrix<Scalar,2,1>(M(2,2), M(2,1)).normalized(); |
|
Eigen::Matrix<Scalar,3,3> Qx{ {1, 0, 0}, {0, cs.x(), cs.y()}, {0, -cs.y(), cs.x()} }; |
|
R.noalias() = M * Qx; |
|
ASSERT(std::abs(R(2,1)) < FLT_EPSILON); |
|
R(2,1) = 0; |
|
// find Givens rotation for y axis: |
|
// | c 0 -s | |
|
// Qy = | 0 1 0 |, c = m33/sqrt(m31^2 + m33^2), s = -m31/sqrt(m31^2 + m33^2) |
|
// | s 0 c | |
|
cs = Eigen::Matrix<Scalar,2,1>(R(2,2), -R(2,0)).normalized(); |
|
Eigen::Matrix<Scalar,3,3> Qy{ {cs.x(), 0, -cs.y()}, {0, 1, 0}, {cs.y(), 0, cs.x()} }; |
|
M.noalias() = R * Qy; |
|
ASSERT(std::abs(M(2,0)) < FLT_EPSILON); |
|
M(2,0) = 0; |
|
// find Givens rotation for z axis: |
|
// | c s 0 | |
|
// Qz = |-s c 0 |, c = m22/sqrt(m21^2 + m22^2), s = m21/sqrt(m21^2 + m22^2) |
|
// | 0 0 1 | |
|
cs = Eigen::Matrix<Scalar,2,1>(M(1,1), M(1,0)).normalized(); |
|
Eigen::Matrix<Scalar,3,3> Qz{ {cs.x(), cs.y(), 0}, {-cs.y(), cs.x(), 0}, {0, 0, 1} }; |
|
R.noalias() = M * Qz; |
|
ASSERT(std::abs(R(1,0)) < FLT_EPSILON); |
|
R(1,0) = 0; |
|
// solve the decomposition ambiguity: |
|
// - diagonal entries of R, except the last one, shall be positive |
|
// - rotate R by 180 degree if necessary |
|
if (R(0,0) < 0) { |
|
if (R(1,1) < 0) { |
|
// rotate around z for 180 degree: |
|
// |-1, 0, 0| |
|
// | 0, -1, 0| |
|
// | 0, 0, 1| |
|
R(0,0) *= -1; |
|
R(0,1) *= -1; |
|
R(1,1) *= -1; |
|
Qz(0,0) *= -1; |
|
Qz(0,1) *= -1; |
|
Qz(1,0) *= -1; |
|
Qz(1,1) *= -1; |
|
} else { |
|
// rotate around y for 180 degree: |
|
// |-1, 0, 0| |
|
// | 0, 1, 0| |
|
// | 0, 0, -1| |
|
R(0,0) *= -1; |
|
R(0,2) *= -1; |
|
R(1,2) *= -1; |
|
R(2,2) *= -1; |
|
Qz.transposeInPlace(); |
|
Qy(0,0) *= -1; |
|
Qy(0,2) *= -1; |
|
Qy(2,0) *= -1; |
|
Qy(2,2) *= -1; |
|
} |
|
} else if (R(1,1) < 0) { |
|
// rotate around x for 180 degree: |
|
// | 1, 0, 0| |
|
// | 0, -1, 0| |
|
// | 0, 0, -1| |
|
R(0,1) *= -1; |
|
R(0,2) *= -1; |
|
R(1,1) *= -1; |
|
R(1,2) *= -1; |
|
R(2,2) *= -1; |
|
Qz.transposeInPlace(); |
|
Qy.transposeInPlace(); |
|
Qx(1,1) *= -1; |
|
Qx(1,2) *= -1; |
|
Qx(2,1) *= -1; |
|
Qx(2,2) *= -1; |
|
} |
|
// calculate orthogonal matrix |
|
Q.noalias() = Qz.transpose() * Qy.transpose() * Qx.transpose(); |
|
} |
|
template <typename TYPE> |
|
inline void RQDecomp3x3(const TMatrix<TYPE,3,3>& M, TMatrix<TYPE,3,3>& R, TMatrix<TYPE,3,3>& Q) { |
|
const Eigen::Matrix<TYPE,3,3> _M((typename TMatrix<TYPE,3,3>::CEMatMap)M); |
|
Eigen::Matrix<TYPE,3,3> _R, _Q; |
|
RQDecomp3x3<TYPE>(_M, _R, _Q); |
|
R = _R; Q = _Q; |
|
} // RQDecomp3x3 |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// compute the angle between the two rotations given |
|
// as in: "Disambiguating Visual Relations Using Loop Constraints", 2010 |
|
// returns cos(angle) (same as cos(ComputeAngleSO3(I)) |
|
template<typename TYPE> |
|
inline TYPE ComputeAngle(const TMatrix<TYPE,3,3>& I) { |
|
return CLAMP(((TYPE)cv::trace(I)-TYPE(1))*TYPE(0.5), TYPE(-1), TYPE(1)); |
|
} // ComputeAngle |
|
template<typename TYPE> |
|
FORCEINLINE TYPE ComputeAngle(const TMatrix<TYPE,3,3>& R1, const TMatrix<TYPE,3,3>& R2) { |
|
return ComputeAngle(TMatrix<TYPE,3,3>(R1*R2.t())); |
|
} // ComputeAngle |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// compute the Frobenius (or Euclidean) norm of the distance between the two matrices |
|
template<typename TYPE, int m, int n> |
|
inline TYPE FrobeniusNorm(const TMatrix<TYPE,m,n>& M) { |
|
return SQRT(((typename TMatrix<TYPE,m,n>::EMatMap)M).cwiseAbs2().sum()); |
|
} // FrobeniusNorm |
|
template<typename TYPE, int m, int n> |
|
FORCEINLINE TYPE FrobeniusNorm(const TMatrix<TYPE,m,n>& M1, const TMatrix<TYPE,m,n>& M2) { |
|
return FrobeniusNorm(TMatrix<TYPE,m,n>(M1-M2)); |
|
} // FrobeniusNorm |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// check if any three of the given 2D points are on the same line |
|
template<typename TYPE> |
|
bool CheckCollinearity(const TPoint2<TYPE>* ptr, int count, bool checkPartialSubsets=false) { |
|
for (int i = (checkPartialSubsets?count-1:2); i < count; ++i) { |
|
// check that the i-th selected point does not belong |
|
// to a line connecting some previously selected points |
|
for (int j = 1; j < i; ++j) { |
|
const TPoint2<TYPE> d1 = ptr[j] - ptr[i]; |
|
for (int k = 0; k < j; ++k) { |
|
const TPoint2<TYPE> d2 = ptr[k] - ptr[i]; |
|
if (ABS(d2.x*d1.y - d2.y*d1.x) <= FLT_EPSILON*(ABS(d1.x) + ABS(d1.y) + ABS(d2.x) + ABS(d2.y))) |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
// check if any three of the given 3D points are on the same line |
|
template<typename TYPE> |
|
bool CheckCollinearity(const TPoint3<TYPE>* ptr, int count, bool checkPartialSubsets=false) { |
|
for (int i = (checkPartialSubsets?count-1:2); i < count; ++i) { |
|
// check that the i-th selected point does not belong |
|
// to a line connecting some previously selected points |
|
for (int j = 1; j < i; ++j) { |
|
const TPoint3<TYPE> d1 = ptr[j] - ptr[i]; |
|
for (int k = 0; k < j; ++k) { |
|
const TPoint3<TYPE> d2 = ptr[k] - ptr[i]; |
|
if (normSq(d1.cross(d2)) < 1.e-16) |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// compute the corresponding ray for a given projection matrix P[3,4] and image point pt[2,1] |
|
// output ray[3,1] |
|
template<typename TYPE1, typename TYPE2> |
|
inline void RayPoint_3x4_2_3(const TYPE1* P, const TYPE2* pt, TYPE1* ray) { |
|
Eigen::Map< const Eigen::Matrix<TYPE1,3,4> > mP(P); |
|
const Eigen::Matrix<TYPE1,3,3> mM(mP.template topLeftCorner<3,3>()); |
|
TYPE1 M[9]; |
|
InvertMatrix3x3(mM.data(), M); |
|
ray[0] = M[0*3+0]*pt[0] + M[0*3+1]*pt[1] + M[0*3+2]; |
|
ray[1] = M[1*3+0]*pt[0] + M[1*3+1]*pt[1] + M[1*3+2]; |
|
ray[2] = M[2*3+0]*pt[0] + M[2*3+1]*pt[1] + M[2*3+2]; |
|
} // RayPoint_3x4_2_3 |
|
/*----------------------------------------------------------------*/ |
|
|
|
// project column vertex - used only for visualization purposes |
|
// (optimized ProjectVertex for P[3,4] and X[3,1], output pt[3,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline void ProjectVertex_3x4_3_3(const TYPE1* P, const TYPE1* X, TYPE2* pt) { |
|
pt[0] = (TYPE2)(P[0*4+0]*X[0] + P[0*4+1]*X[1] + P[0*4+2]*X[2] + P[0*4+3]); |
|
pt[1] = (TYPE2)(P[1*4+0]*X[0] + P[1*4+1]*X[1] + P[1*4+2]*X[2] + P[1*4+3]); |
|
pt[2] = (TYPE2)(P[2*4+0]*X[0] + P[2*4+1]*X[1] + P[2*4+2]*X[2] + P[2*4+3]); |
|
} // ProjectVertex_3x4_3_3 |
|
// (optimized ProjectVertex for P[3,4] and X[4,1], output pt[3,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline void ProjectVertex_3x4_4_3(const TYPE1* P, const TYPE1* X, TYPE2* pt) { |
|
pt[0] = (TYPE2)(P[0*4+0]*X[0] + P[0*4+1]*X[1] + P[0*4+2]*X[2] + P[0*4+3]*X[3]); |
|
pt[1] = (TYPE2)(P[1*4+0]*X[0] + P[1*4+1]*X[1] + P[1*4+2]*X[2] + P[1*4+3]*X[3]); |
|
pt[2] = (TYPE2)(P[2*4+0]*X[0] + P[2*4+1]*X[1] + P[2*4+2]*X[2] + P[2*4+3]*X[3]); |
|
} // ProjectVertex_3x4_4_3 |
|
// (optimized ProjectVertex for R[3,3], C[3,1] and X[3,1], output pt[2,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline void ProjectVertex_3x3_3_3_2(const TYPE1* R, const TYPE1* C, const TYPE1* X, TYPE2* pt) { |
|
const TYPE1 T[3] = {X[0]-C[0], X[1]-C[1], X[2]-C[2]}; |
|
const TYPE1 invW(INVERT(R[2*3+0]*T[0] + R[2*3+1]*T[1] + R[2*3+2]*T[2])); |
|
pt[0] = (TYPE2)((R[0*3+0]*T[0] + R[0*3+1]*T[1] + R[0*3+2]*T[2]) * invW); |
|
pt[1] = (TYPE2)((R[1*3+0]*T[0] + R[1*3+1]*T[1] + R[1*3+2]*T[2]) * invW); |
|
} // ProjectVertex_3x3_3_3_2 |
|
// (optimized ProjectVertex for R[3,3], C[3,1] and X[3,1], output pt[3,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline void ProjectVertex_3x3_3_3_3(const TYPE1* R, const TYPE1* C, const TYPE1* X, TYPE2* pt) { |
|
const TYPE1 T[3] = {X[0]-C[0], X[1]-C[1], X[2]-C[2]}; |
|
pt[0] = (TYPE2)(R[0*3+0]*T[0] + R[0*3+1]*T[1] + R[0*3+2]*T[2]); |
|
pt[1] = (TYPE2)(R[1*3+0]*T[0] + R[1*3+1]*T[1] + R[1*3+2]*T[2]); |
|
pt[2] = (TYPE2)(R[2*3+0]*T[0] + R[2*3+1]*T[1] + R[2*3+2]*T[2]); |
|
} // ProjectVertex_3x3_3_3_3 |
|
// (optimized ProjectVertex for H[3,3] and X[2,1], output pt[3,1]) |
|
template<typename TYPE1, typename TYPE2, typename TYPE3> |
|
inline void ProjectVertex_3x3_2_3(const TYPE1* H, const TYPE2* X, TYPE3* pt) { |
|
pt[0] = (TYPE3)(H[0*3+0]*X[0] + H[0*3+1]*X[1] + H[0*3+2]); |
|
pt[1] = (TYPE3)(H[1*3+0]*X[0] + H[1*3+1]*X[1] + H[1*3+2]); |
|
pt[2] = (TYPE3)(H[2*3+0]*X[0] + H[2*3+1]*X[1] + H[2*3+2]); |
|
} // ProjectVertex_3x3_2_3 |
|
// (optimized ProjectVertex for H[3,3] and X[2,1], output pt[2,1]) |
|
template<typename TYPE1, typename TYPE2, typename TYPE3> |
|
inline void ProjectVertex_3x3_2_2(const TYPE1* H, const TYPE2* X, TYPE3* pt) { |
|
const TYPE1 invZ(INVERT(H[2*3+0]*X[0] + H[2*3+1]*X[1] + H[2*3+2])); |
|
pt[0] = (TYPE3)((H[0*3+0]*X[0] + H[0*3+1]*X[1] + H[0*3+2])*invZ); |
|
pt[1] = (TYPE3)((H[1*3+0]*X[0] + H[1*3+1]*X[1] + H[1*3+2])*invZ); |
|
} // ProjectVertex_3x3_2_2 |
|
/*----------------------------------------------------------------*/ |
|
|
|
// project column vertex - used only for visualization purposes |
|
// (optimized ProjectVertex for P[3,4] and X[3,1], output pt[2,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline void ProjectVertex_3x4_3_2(const TYPE1* P, const TYPE1* X, TYPE2* pt) { |
|
const TYPE1 invW(INVERT(P[2*4+0]*X[0] + P[2*4+1]*X[1] + P[2*4+2]*X[2] + P[2*4+3])); |
|
pt[0] = (TYPE2)((P[0*4+0]*X[0] + P[0*4+1]*X[1] + P[0*4+2]*X[2] + P[0*4+3]) * invW); |
|
pt[1] = (TYPE2)((P[1*4+0]*X[0] + P[1*4+1]*X[1] + P[1*4+2]*X[2] + P[1*4+3]) * invW); |
|
} // ProjectVertex_3x4_3_2 |
|
// (optimized ProjectVertex for P[3,4] and X[4,1], output pt[2,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline void ProjectVertex_3x4_4_2(const TYPE1* P, const TYPE1* X, TYPE2* pt) { |
|
const TYPE1 invW(INVERT(P[2*4+0]*X[0] + P[2*4+1]*X[1] + P[2*4+2]*X[2] + P[2*4+3]*X[3])); |
|
pt[0] = (TYPE2)((P[0*4+0]*X[0] + P[0*4+1]*X[1] + P[0*4+2]*X[2] + P[0*4+3]*X[3]) * invW); |
|
pt[1] = (TYPE2)((P[1*4+0]*X[0] + P[1*4+1]*X[1] + P[1*4+2]*X[2] + P[1*4+3]*X[3]) * invW); |
|
} // ProjectVertex_3x4_4_2 |
|
/*----------------------------------------------------------------*/ |
|
|
|
// project column vertex using the transpose of the given projective matrix |
|
// (optimized ProjectVertex for P[3,4].t() and X[3,1], output pt[4,1]) |
|
template<typename TYPE1, typename TYPE2, typename TYPE3> |
|
inline void ProjectVertex_3x4t_3_4(const TYPE1* P, const TYPE2* X, TYPE3* pt) { |
|
pt[0] = (TYPE3)(P[0*4+0]*X[0] + P[1*4+0]*X[1] + P[2*4+0]*X[2]); |
|
pt[1] = (TYPE3)(P[0*4+1]*X[0] + P[1*4+1]*X[1] + P[2*4+1]*X[2]); |
|
pt[2] = (TYPE3)(P[0*4+2]*X[0] + P[1*4+2]*X[1] + P[2*4+2]*X[2]); |
|
pt[3] = (TYPE3)(P[0*4+3]*X[0] + P[1*4+3]*X[1] + P[2*4+3]*X[2]); |
|
} // ProjectVertex_3x4t_3_4 |
|
// (optimized ProjectVertex for P[3,4].t() and X[3,1], output pt[3,1] - the first 3 coordinates) |
|
template<typename TYPE1, typename TYPE2, typename TYPE3> |
|
inline void ProjectVertex_3x4t_3_3(const TYPE1* P, const TYPE2* X, TYPE3* pt) { |
|
pt[0] = (TYPE3)(P[0*4+0]*X[0] + P[1*4+0]*X[1] + P[2*4+0]*X[2]); |
|
pt[1] = (TYPE3)(P[0*4+1]*X[0] + P[1*4+1]*X[1] + P[2*4+1]*X[2]); |
|
pt[2] = (TYPE3)(P[0*4+2]*X[0] + P[1*4+2]*X[1] + P[2*4+2]*X[2]); |
|
} // ProjectVertex_3x4t_3_3 |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// given the camera matrix, compute the error between the given 2D point and the reprojected 3D point; |
|
// note that here we're using "dirty" projection method which is suitable only for visualization; on the upside, it shouldn't matter because |
|
// if a point is projected as infinite, there's something wrong with it anyway; |
|
// (optimized ComputeReprojectionError for P[3,4] and X[3,1], output pt[2,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline TYPE2 ComputeReprojectionError_3x4_3_2(const TYPE1* P, const TYPE1* X, const TYPE2* pt) { |
|
TYPE2 reprojection[2]; |
|
ProjectVertex_3x4_3_2(P, X, reprojection); |
|
return SQUARE(reprojection[0]-pt[0])+SQUARE(reprojection[1]-pt[1]); |
|
} // ComputeReprojectionError_3x4_3_2 |
|
// (optimized ComputeReprojectionError for P[3,4] and X[4,1], output pt[2,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline TYPE2 ComputeReprojectionError_3x4_4_2(const TYPE1* P, const TYPE1* X, const TYPE2* pt) { |
|
TYPE2 reprojection[2]; |
|
ProjectVertex_3x4_4_2(P, X, reprojection); |
|
return SQUARE(reprojection[0]-pt[0])+SQUARE(reprojection[1]-pt[1]); |
|
} // ComputeReprojectionError_3x4_4_2 |
|
// (optimized ComputeReprojectionError for R[3,3], C[3,1] and X[3,1], output pt[2,1]) |
|
template<typename TYPE1, typename TYPE2> |
|
inline TYPE2 ComputeReprojectionError_3x3_3_3_2(const TYPE1* R, const TYPE1* C, const TYPE1* X, const TYPE2* pt) { |
|
TYPE2 reprojection[2]; |
|
ProjectVertex_3x3_3_3_2(R, C, X, reprojection); |
|
return SQUARE(reprojection[0]-pt[0])+SQUARE(reprojection[1]-pt[1]); |
|
} // ComputeReprojectionError_3x3_3_3_2 |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// computes the depth of the given point |
|
// from the point of view of the given camera pose |
|
template<typename TYPE> |
|
inline TYPE PointDepth(const TYPE* R, const TYPE* C, const TYPE* X) { |
|
return R[2*3+0]*X[0] + R[2*3+1]*X[1] + R[2*3+2]*X[2] + C[2]; |
|
} // PointDepth |
|
template<typename TYPE> |
|
inline TYPE PointDepth(const TYPE* P, const TYPE* X) { |
|
return P[2*4+0]*X[0] + P[2*4+1]*X[1] + P[2*4+2]*X[2] + P[2*4+3]; |
|
} // PointDepth |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// reproject the given 3D point with the given view; |
|
// return squared reprojection error |
|
// or FLT_MAX if the point is behind the camera |
|
// reproj return the homogeneous reprojection |
|
template<typename TYPE1, typename TYPE2> |
|
inline TYPE2 ProjectPoint(const TYPE1* P, const TYPE1* X, const TYPE2* proj, TYPE2* reproj) { |
|
// reproject the 3D point on this view |
|
ProjectVertex_3x4_3_3(P, X, reproj); |
|
// filter out points behind |
|
if (reproj[2] <= TYPE2(0)) |
|
return FLT_MAX; |
|
// return the reprojection error |
|
return SQUARE(reproj[0]/reproj[2]-proj[0])+SQUARE(reproj[1]/reproj[2]-proj[1]); |
|
} // ProjectPoint |
|
template<typename TYPE1, typename TYPE2> |
|
inline TYPE2 ProjectPoint(const TYPE1* R, const TYPE1* C, const TYPE1* X, const TYPE2* proj, TYPE2* reproj) { |
|
// reproject the 3D point on this view |
|
ProjectVertex_3x3_3_3_3(R, C, X, reproj); |
|
// filter out points behind |
|
if (reproj[2] <= TYPE2(0)) |
|
return FLT_MAX; |
|
// return the reprojection error |
|
return SQUARE(reproj[0]/reproj[2]-proj[0])+SQUARE(reproj[1]/reproj[2]-proj[1]); |
|
} // ProjectPoint |
|
/*----------------------------------------------------------------*/ |
|
|
|
// reproject the given 3D point with the given view; |
|
// return true if the point is not behind the camera |
|
// and the reprojection error is small enough; |
|
// returns false otherwise |
|
// reproj returns the homogeneous reprojection (first 3) and errSq (last 1) |
|
template<typename TYPE1, typename TYPE2> |
|
inline bool IsPointInlier(const TYPE1* P, const TYPE1* X, const TYPE2* proj, TYPE2* reproj, TYPE2 thresholdSq) { |
|
// reproject the 3D point on this view |
|
ProjectVertex_3x4_3_3(P, X, reproj); |
|
// filter out points behind the camera or |
|
// having the reprojection error bigger than the given threshold |
|
return (reproj[2] > TYPE2(0) && |
|
(reproj[3]=(SQUARE(reproj[0]/reproj[2]-proj[0])+SQUARE(reproj[1]/reproj[2]-proj[1]))) <= thresholdSq); |
|
} // IsPointInlier |
|
template<typename TYPE1, typename TYPE2> |
|
inline bool IsPointInlier(const TYPE1* R, const TYPE1* C, const TYPE1* X, const TYPE2* proj, TYPE2* reproj, TYPE2 thresholdSq) { |
|
// reproject the 3D point on this view |
|
ProjectVertex_3x3_3_3_3(R, C, X, reproj); |
|
// filter out points behind the camera or |
|
// having the reprojection error bigger than the given threshold |
|
return (reproj[2] > TYPE2(0) && |
|
(reproj[3]=(SQUARE(reproj[0]/reproj[2]-proj[0])+SQUARE(reproj[1]/reproj[2]-proj[1]))) <= thresholdSq); |
|
} // IsPointInlier |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// given a rotation matrix, return the angle in radians corresponding to the axis/angle decomposition |
|
template<typename TYPE> |
|
inline TYPE AngleFromRotationMatrix(const TYPE* R) { |
|
const TYPE a = (R[0*3+0]+R[1*3+1]+R[2*3+2]-TYPE(1))/TYPE(2); |
|
if (a < TYPE(-1)) |
|
return acos(TYPE(-1)); |
|
if (a > TYPE(1)) |
|
return acos(TYPE(1)); |
|
return acos(a); |
|
} |
|
template<typename TYPE> |
|
FORCEINLINE TYPE AngleFromRotationMatrix(const TMatrix<TYPE,3,3>& R) { |
|
return AngleFromRotationMatrix(R.val); |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given two 3D vectors, |
|
// compute the angle between them |
|
// returns cos(angle) |
|
template<typename TYPE1, typename TYPE2 = TYPE1> |
|
inline TYPE2 ComputeAngle(const TYPE1* V1, const TYPE1* V2) { |
|
return CLAMP(TYPE2((V1[0]*V2[0]+V1[1]*V2[1]+V1[2]*V2[2])/SQRT((V1[0]*V1[0]+V1[1]*V1[1]+V1[2]*V1[2])*(V2[0]*V2[0]+V2[1]*V2[1]+V2[2]*V2[2]))), TYPE2(-1), TYPE2(1)); |
|
} // ComputeAngle |
|
// same as above, but with the vectors normalized |
|
template<typename TYPE1, typename TYPE2 = TYPE1> |
|
inline TYPE2 ComputeAngleN(const TYPE1* V1, const TYPE1* V2) { |
|
return CLAMP(TYPE2(V1[0]*V2[0]+V1[1]*V2[1]+V1[2]*V2[2]), TYPE2(-1), TYPE2(1)); |
|
} // ComputeAngleN |
|
// given three 3D points, |
|
// compute the angle between the vectors formed by the first point with the other two |
|
// returns cos(angle) |
|
template<typename TYPE1, typename TYPE2 = TYPE1> |
|
inline TYPE2 ComputeAngle(const TYPE1* B, const TYPE1* X1, const TYPE1* X2) { |
|
// create the two vectors |
|
const TYPE1 V1[] = {X1[0]-B[0], X1[1]-B[1], X1[2]-B[2]}; |
|
const TYPE1 V2[] = {X2[0]-B[0], X2[1]-B[1], X2[2]-B[2]}; |
|
return ComputeAngle<TYPE1,TYPE2>(V1, V2); |
|
} // ComputeAngle |
|
// given four 3D points, |
|
// compute the angle between the two vectors formed by the first and second pair of points |
|
// returns cos(angle) |
|
template<typename TYPE1, typename TYPE2 = TYPE1> |
|
inline TYPE2 ComputeAngle(const TYPE1* X1, const TYPE1* C1, const TYPE1* X2, const TYPE1* C2) { |
|
// subtract out the camera center |
|
const TYPE1 V1[] = {X1[0]-C1[0], X1[1]-C1[1], X1[2]-C1[2]}; |
|
const TYPE1 V2[] = {X2[0]-C2[0], X2[1]-C2[1], X2[2]-C2[2]}; |
|
return ComputeAngle<TYPE1,TYPE2>(V1, V2); |
|
} // ComputeAngle |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given a triangle defined by three 3D points, |
|
// compute its normal (plane's normal oriented according to the given points order) |
|
template<typename TYPE> |
|
inline TPoint3<TYPE> ComputeTriangleNormal(const TPoint3<TYPE>& v0, const TPoint3<TYPE>& v1, const TPoint3<TYPE>& v2) { |
|
return (v1-v0).cross(v2-v0); |
|
} // ComputeTriangleNormal |
|
/*----------------------------------------------------------------*/ |
|
|
|
// compute the area of a triangle using Heron's formula |
|
template <typename TYPE> |
|
TYPE ComputeTriangleAreaSqLen(TYPE lena, TYPE lenb, TYPE lenc) { |
|
const TYPE s((lena+lenb+lenc)/TYPE(2)); |
|
return s*(s-lena)*(s-lenb)*(s-lenc); |
|
} |
|
template <typename TYPE> |
|
TYPE ComputeTriangleAreaLen(TYPE lena, TYPE lenb, TYPE lenc) { |
|
return (TYPE)sqrt(ComputeTriangleAreaSqLen(lena, lenb, lenc)); |
|
} |
|
template <typename TYPE> |
|
TYPE ComputeTriangleAreaSqLenSq(TYPE lenaSq, TYPE lenbSq, TYPE lencSq) { |
|
return SQUARE(lenaSq+lenbSq+lencSq)/TYPE(2)-(lenaSq*lenaSq+lenbSq*lenbSq+lencSq*lencSq); |
|
} |
|
template <typename TYPE> |
|
TYPE ComputeTriangleAreaLenSq(TYPE lenaSq, TYPE lenbSq, TYPE lencSq) { |
|
return (TYPE)sqrt(ComputeTriangleAreaSqLenSq(lenaSq, lenbSq, lencSq)); |
|
} |
|
// compute area for a triangle defined by three 2D points |
|
template <typename TYPE> |
|
TYPE EdgeFunction(const TPoint2<TYPE>& x0, const TPoint2<TYPE>& x1, const TPoint2<TYPE>& x2) { |
|
return TYPE((x2-x0).cross(x1-x0)); |
|
} |
|
template <typename TYPE> |
|
TYPE ComputeTriangleArea(const TPoint2<TYPE>& x0, const TPoint2<TYPE>& x1, const TPoint2<TYPE>& x2) { |
|
return (TYPE)abs(EdgeFunction(x0, x1, x2)/TYPE(2)); |
|
} |
|
// compute area for a triangle defined by three 3D points |
|
template <typename TYPE> |
|
TYPE ComputeTriangleAreaSq(const TPoint3<TYPE>& x0, const TPoint3<TYPE>& x1, const TPoint3<TYPE>& x2) { |
|
return (TYPE)normSq((x1-x0).cross(x2-x0))/TYPE(4); |
|
} |
|
template <typename TYPE> |
|
TYPE ComputeTriangleArea(const TPoint3<TYPE>& x0, const TPoint3<TYPE>& x1, const TPoint3<TYPE>& x2) { |
|
return (TYPE)sqrt(ComputeTriangleAreaSq(x0, x1, x2)); |
|
} // ComputeTriangleArea |
|
/*----------------------------------------------------------------*/ |
|
|
|
// compute signed volume of the tetrahedron defined by the given triangle connected to origin |
|
template <typename TYPE> |
|
TYPE ComputeTriangleVolume(const TPoint3<TYPE>& x0, const TPoint3<TYPE>& x1, const TPoint3<TYPE>& x2) { |
|
return (TYPE)(x0.dot(x1.cross(x2)) / TYPE(6)); |
|
} // ComputeTriangleVolume |
|
/*----------------------------------------------------------------*/ |
|
|
|
// compute a shape quality measure of the triangle composed by vertices (v0,v1,v2) |
|
// returns 2*AreaTri/(MaxEdge^2) in range [0, 0.866] |
|
// (ex: equilateral sqrt(3)/2, half-square 1/2, up to a line that has zero quality) |
|
template<typename TYPE> |
|
inline TYPE ComputeTriangleQuality(const TPoint3<TYPE>& v0, const TPoint3<TYPE>& v1, const TPoint3<TYPE>& v2) { |
|
const TPoint3<TYPE> d10(v1-v0); |
|
const TPoint3<TYPE> d20(v2-v0); |
|
const TPoint3<TYPE> d12(v1-v2); |
|
const TYPE a((TYPE)norm(d10.cross(d20))); |
|
if (a == 0) return 0; // area zero triangles have surely zero quality |
|
const TYPE nd10(normSq(d10)); |
|
if (nd10 == 0) return 0; // area zero triangles have surely zero quality |
|
const TYPE b(MAXF3(nd10, normSq(d20), normSq(d12))); |
|
return a/b; |
|
} // ComputeTriangleQuality |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given a triangle defined by 3 vertex positions and a point, |
|
// compute the barycentric coordinates corresponding to that point |
|
// (only the first two values: alpha and beta) |
|
template <typename TYPE> |
|
inline TPoint2<TYPE> BarycentricCoordinatesUV(const TPoint3<TYPE>& A, const TPoint3<TYPE>& B, const TPoint3<TYPE>& C, const TPoint3<TYPE>& P) { |
|
#if 0 |
|
// the triangle normal |
|
const TPoint3<TYPE> normalVec((B-A).cross(C-A)); |
|
//const TYPE normalLen(norm(normalVec)); |
|
// the area of the triangles |
|
const TYPE invAreaABC(INVERT(normSq(normalVec)/*/(2*normalLen)*/)); |
|
const TPoint3<TYPE> CP(C-P); |
|
const TYPE areaPBC(normalVec.dot((B-P).cross(CP))/*/(2*normalLen)*/); |
|
const TYPE areaPCA(normalVec.dot(CP.cross(A-P))/*/(2*normalLen)*/); |
|
// the barycentric coordinates |
|
return TPoint2<TYPE>( |
|
areaPBC * invAreaABC, // alpha |
|
areaPCA * invAreaABC // beta |
|
); |
|
#else |
|
// using the Cramer's rule for solving a linear system |
|
const TPoint3<TYPE> v0(A-C), v1(B-C), v2(P-C); |
|
const TYPE d00(normSq(v0)); |
|
const TYPE d01(v0.dot(v1)); |
|
const TYPE d11(normSq(v1)); |
|
const TYPE d20(v2.dot(v0)); |
|
const TYPE d21(v2.dot(v1)); |
|
const TYPE invDenom(INVERT(d00 * d11 - d01 * d01)); |
|
return TPoint2<TYPE>( |
|
(d11 * d20 - d01 * d21) * invDenom, // alpha |
|
(d00 * d21 - d01 * d20) * invDenom // beta |
|
); |
|
#endif |
|
} |
|
// same as above, but returns all three barycentric coordinates |
|
template <typename TYPE> |
|
inline TPoint3<TYPE> BarycentricCoordinates(const TPoint3<TYPE>& A, const TPoint3<TYPE>& B, const TPoint3<TYPE>& C, const TPoint3<TYPE>& P) { |
|
TPoint2<TYPE> b(BarycentricCoordinatesUV(A, B, C, P)); |
|
return TPoint3<TYPE>( |
|
b.x, // alpha |
|
b.y, // beta |
|
TYPE(1)-b.x-b.y // gamma |
|
); |
|
} |
|
// same as above, but for 2D triangle case |
|
template <typename TYPE> |
|
inline TPoint2<TYPE> BarycentricCoordinatesUV(const TPoint2<TYPE>& A, const TPoint2<TYPE>& B, const TPoint2<TYPE>& C, const TPoint2<TYPE>& P) { |
|
const TPoint2<TYPE> D(P - C); |
|
const TYPE d00(A.x - C.x); |
|
const TYPE d01(B.x - C.x); |
|
const TYPE d10(A.y - C.y); |
|
const TYPE d11(B.y - C.y); |
|
const TYPE invDet(INVERT(d00 * d11 - d10 * d01)); |
|
return TPoint2<TYPE>( |
|
(d11 * D.x - d01 * D.y) * invDet, // alpha |
|
(d00 * D.y - d10 * D.x) * invDet // beta |
|
); |
|
} |
|
// same as above, but returns all three barycentric coordinates |
|
template <typename TYPE> |
|
inline TPoint3<TYPE> BarycentricCoordinates(const TPoint2<TYPE>& A, const TPoint2<TYPE>& B, const TPoint2<TYPE>& C, const TPoint2<TYPE>& P) { |
|
TPoint2<TYPE> b(BarycentricCoordinatesUV(A, B, C, P)); |
|
return TPoint3<TYPE>( |
|
b.x, // alpha |
|
b.y, // beta |
|
TYPE(1)-b.x-b.y // gamma |
|
); |
|
} |
|
// correct the barycentric coordinates in case of numerical errors |
|
// (the corresponding point is assumed to be inside the triangle) |
|
template <typename TYPE> |
|
inline TPoint3<TYPE> CorrectBarycentricCoordinates(TPoint2<TYPE> b) { |
|
if (b.x < TYPE(0)) // alpha |
|
b.x = TYPE(0); |
|
else if (b.x > TYPE(1)) |
|
b.x = TYPE(1); |
|
if (b.y < TYPE(0)) // beta |
|
b.y = TYPE(0); |
|
else if (b.y > TYPE(1)) |
|
b.y = TYPE(1); |
|
TYPE z(TYPE(1) - b.x - b.y); // gamma |
|
if (z < 0) { |
|
// equally distribute the error |
|
const TYPE half(-z/TYPE(2)); |
|
if (half > b.x) { |
|
b.x = TYPE(0); |
|
b.y = TYPE(1); |
|
} else |
|
if (half > b.y) { |
|
b.x = TYPE(1); |
|
b.y = TYPE(0); |
|
} else { |
|
b.x -= half; |
|
b.y -= half; |
|
} |
|
z = TYPE(0); |
|
} |
|
// check that the given point is inside the triangle |
|
ASSERT((b.x >= 0) && (b.y >= 0) && (b.x+b.y <= 1) && ISEQUAL(b.x+b.y+z, TYPE(1))); |
|
return TPoint3<TYPE>(b.x, b.y, z); |
|
} |
|
template <typename TYPE> |
|
inline TPoint3<TYPE> PerspectiveCorrectBarycentricCoordinates(const TPoint3<TYPE>& b, TYPE z0, TYPE z1, TYPE z2) { |
|
const TPoint3<TYPE> pb(b.x * z1 * z2, b.y * z0 * z2, b.z * z0 * z1); |
|
return pb / (pb.x + pb.y + pb.z); |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
// Encodes/decodes a normalized 3D vector in two parameters for the direction |
|
template<typename T, typename TR> |
|
inline void Normal2Dir(const TPoint3<T>& d, TPoint2<TR>& p) { |
|
ASSERT(ISEQUAL(norm(d), T(1))); |
|
p.x = TR(atan2(d.y, d.x)); |
|
p.y = TR(acos(d.z)); |
|
} |
|
template<typename T, typename TR> |
|
inline void Dir2Normal(const TPoint2<T>& p, TPoint3<TR>& d) { |
|
const T siny(sin(p.y)); |
|
d.x = TR(cos(p.x)*siny); |
|
d.y = TR(sin(p.x)*siny); |
|
d.z = TR(cos(p.y)); |
|
ASSERT(ISEQUAL(norm(d), TR(1))); |
|
} |
|
// Encodes/decodes a 3D vector in two parameters for the direction and one parameter for the scale |
|
template<typename T, typename TR> |
|
inline void Vector2DirScale(const T vect[3], TR dir[2], TR* scale) |
|
{ |
|
const T scl(sqrt(SQUARE(vect[0])+SQUARE(vect[1])+SQUARE(vect[2]))); |
|
ASSERT(!ISZERO(scl)); |
|
scale[0] = TR(scl); |
|
dir[0] = TR(atan2(vect[1], vect[0])); |
|
dir[1] = TR(acos(vect[2] / scl)); |
|
} |
|
template<typename T, typename TR> |
|
inline void DirScale2Vector(const T dir[2], const T* scale, TR vect[3]) |
|
{ |
|
ASSERT(!ISZERO(*scale)); |
|
const T siny(*scale*sin(dir[1])); |
|
vect[0] = TR(cos(dir[0])*siny); |
|
vect[1] = TR(sin(dir[0])*siny); |
|
vect[2] = TR(*scale*cos(dir[1])); |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
template<typename T> |
|
inline T MaxDepthDifference(T d, T threshold) { |
|
#if 0 |
|
return (d*threshold*T(2))/(threshold+T(2)); |
|
#else |
|
return d*threshold; |
|
#endif |
|
} |
|
template<typename T> |
|
inline T DepthSimilarity(T d0, T d1) { |
|
ASSERT(d0 > 0); |
|
#if 0 |
|
return ABS(d0-d1)*T(2)/(d0+d1); |
|
#else |
|
return ABS(d0-d1)/d0; |
|
#endif |
|
} |
|
template<typename T> |
|
inline bool IsDepthSimilar(T d0, T d1, T threshold=T(0.01)) { |
|
return DepthSimilarity(d0, d1) < threshold; |
|
} |
|
template<typename T> |
|
inline bool IsNormalSimilar(const TPoint3<T>& n0, const TPoint3<T>& n1, T threshold=T(0.996194698)/*COS(FD2R(5.f))*/) { |
|
return ComputeAngle<T,T>(n0.ptr(), n1.ptr()) > threshold; |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// searches min/max value |
|
template<typename TYPE> |
|
inline TYPE FindMinElement(const TYPE* values, size_t n) { |
|
TYPE m = std::numeric_limits<TYPE>::max(); |
|
while (n) if (values[--n] < m) m = values[n]; |
|
return m; |
|
} |
|
template<typename TYPE> |
|
inline TYPE FindMaxElement(const TYPE* values, size_t n) { |
|
TYPE m = -std::numeric_limits<TYPE>::max(); |
|
while (n) if (values[--n] > m) m = values[n]; |
|
return m; |
|
} |
|
// like above, but considers absolute value only |
|
template<typename TYPE> |
|
inline TYPE FindAbsMinElement(const TYPE* values, size_t n) { |
|
TYPE m = std::numeric_limits<TYPE>::max(); |
|
while (n) if (ABS(values[--n]) < m) m = ABS(values[n]); |
|
return m; |
|
} |
|
template<typename TYPE> |
|
inline TYPE FindAbsMaxElement(const TYPE* values, size_t n) { |
|
TYPE m = -std::numeric_limits<TYPE>::max(); |
|
while (n) if (ABS(values[--n]) > m) m = ABS(values[n]); |
|
return m; |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// given an array of values and their bound, approximate the area covered, in percentage |
|
template<typename TYPE, int n, int s, bool bCentered> |
|
inline TYPE ComputeCoveredArea(const TYPE* values, size_t size, const TYPE* bound, int stride=n) { |
|
ASSERT(size > 0); |
|
typedef Eigen::Matrix<TYPE,1,n,Eigen::RowMajor> Vector; |
|
typedef Eigen::Map<const Vector,Eigen::Unaligned> MapVector; |
|
typedef Eigen::Matrix<TYPE,Eigen::Dynamic,n,Eigen::RowMajor> Matrix; |
|
typedef Eigen::Map<const Matrix,Eigen::Unaligned,Eigen::OuterStride<> > MapMatrix; |
|
typedef Eigen::Matrix<unsigned,s,s,Eigen::RowMajor> MatrixSurface; |
|
const MapMatrix points(values, size, n, Eigen::OuterStride<>(stride)); |
|
const Vector norm = MapVector(bound); |
|
const Vector offset(Vector::Constant(bCentered ? TYPE(0.5) : TYPE(0))); |
|
MatrixSurface surface; |
|
surface.setZero(); |
|
for (size_t i=0; i<size; ++i) { |
|
const Vector point((points.row(i).cwiseQuotient(norm)+offset)*TYPE(s)); |
|
ASSERT((point(0)>=0 && point(0)<s) && (point(1)>=0 && point(1)<s)); |
|
surface(FLOOR2INT(point(0)), FLOOR2INT(point(1))) = 1; |
|
} |
|
return TYPE(surface.sum())/(s*s); |
|
} // ComputeCoveredArea |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// given an array of values, compute the variance-covariance matrix |
|
template<typename TYPE, int n> |
|
inline void ComputeCovarianceMatrix(const TYPE* values, size_t size, TYPE* cov, int stride=n) { |
|
ASSERT(size > 0); |
|
typedef Eigen::Matrix<TYPE,1,n,Eigen::RowMajor> Vector; |
|
typedef Eigen::Matrix<TYPE,Eigen::Dynamic,n,Eigen::RowMajor> Matrix; |
|
typedef Eigen::Map<const Matrix,Eigen::Unaligned,Eigen::OuterStride<> > MapMatrix; |
|
typedef Eigen::Matrix<TYPE,n,n,Eigen::RowMajor> MatrixOut; |
|
typedef Eigen::Map<MatrixOut> MapMatrixOut; |
|
const MapMatrix points(values, size, n, Eigen::OuterStride<>(stride)); |
|
const Vector mean(points.colwise().sum() * (TYPE(1)/size)); |
|
const Matrix transPoints(points.rowwise() - mean); |
|
MapMatrixOut mapCov(cov); |
|
mapCov = transPoints.transpose() * transPoints * (TYPE(1)/(size-1)); |
|
} // ComputeCovarianceMatrix |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given an array of values, compute the mean |
|
template<typename TYPE, typename TYPEW> |
|
inline void ComputeMean(const TYPE* values, size_t size, TYPEW& mean) { |
|
ASSERT(size > 0); |
|
TYPEW sum(0); |
|
for (size_t i=0; i<size; ++i) |
|
sum += TYPEW(values[i]); |
|
mean = sum / size; |
|
} // ComputeMean |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given an array of values, compute the geometric mean |
|
template<typename TYPE, typename TYPEW> |
|
inline void ComputeGeometricMean(const TYPE* values, size_t size, TYPEW& gmean) { |
|
ASSERT(size > 0); |
|
TYPEW prod(1); |
|
for (size_t i=0; i<size; ++i) |
|
prod *= TYPEW(values[i]); |
|
gmean = POW(prod, 1.f/size); |
|
} // ComputeGeometricMean |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given an array of values, compute the mean and standard deviance |
|
template<typename TYPE, typename TYPEW> |
|
inline void ComputeMeanStdOffline(const TYPE* values, size_t size, TYPEW& mean, TYPEW& stddev) { |
|
ASSERT(size > 0); |
|
TYPEW sum(0); |
|
FOREACHRAWPTR(pVal, values, size) |
|
sum += TYPEW(*pVal); |
|
mean = sum / (float)size; |
|
TYPEW sumSq(0); |
|
FOREACHRAWPTR(pVal, values, size) |
|
sumSq += SQUARE(TYPEW(*pVal)-mean); |
|
const TYPEW variance(sumSq / (size - 1)); |
|
stddev = SQRT(variance); |
|
} // ComputeMeanStdOffline |
|
// same as above, but uses one pass only (online) |
|
template<typename TYPE, typename TYPEW> |
|
inline void ComputeMeanStdOnline(const TYPE* values, size_t size, TYPEW& mean, TYPEW& stddev) { |
|
ASSERT(size > 0); |
|
TYPEW sum(0), sumSq(0); |
|
FOREACHRAWPTR(pVal, values, size) { |
|
const TYPEW val(*pVal); |
|
sum += val; |
|
sumSq += SQUARE(val); |
|
} |
|
const TYPEW invSize(TYPEW(1)/(float)size); |
|
mean = sum * invSize; |
|
const TYPEW variance((sumSq - SQUARE(sum) * invSize) / (float)(size - 1)); |
|
stddev = SQRT(variance); |
|
} // ComputeMeanStdOnlineFast |
|
#define ComputeMeanStd ComputeMeanStdOnline |
|
// same as above, but an interactive version |
|
template<typename TYPE, typename TYPEW=TYPE, typename ARGTYPE=const TYPE&, typename TYPER=REAL> |
|
struct MeanStd { |
|
typedef TYPE Type; |
|
typedef TYPEW TypeW; |
|
typedef TYPER TypeR; |
|
typedef ARGTYPE ArgType; |
|
TYPEW sum, sumSq; |
|
size_t size; |
|
MeanStd() : sum(0), sumSq(0), size(0) {} |
|
MeanStd(const Type* values, size_t _size) : MeanStd() { Compute(values, _size); } |
|
void Update(ArgType v) { |
|
const TYPEW val(static_cast<TYPEW>(v)); |
|
sum += val; |
|
sumSq += SQUARE(val); |
|
++size; |
|
} |
|
void Compute(const Type* values, size_t _size) { |
|
for (size_t i=0; i<_size; ++i) |
|
Update(values[i]); |
|
} |
|
TYPEW GetSum() const { return sum; } |
|
TYPEW GetMean() const { return static_cast<TYPEW>(sum / static_cast<TypeR>(size)); } |
|
TYPEW GetRMS() const { return static_cast<TYPEW>(SQRT(sumSq / static_cast<TypeR>(size))); } |
|
TYPEW GetVarianceN() const { return static_cast<TYPEW>(sumSq - SQUARE(sum) / static_cast<TypeR>(size)); } |
|
TYPEW GetVariance() const { return static_cast<TYPEW>(GetVarianceN() / static_cast<TypeR>(size)); } |
|
TYPEW GetStdDev() const { return SQRT(GetVariance()); } |
|
void Clear() { sum = sumSq = TYPEW(0); size = 0; } |
|
}; |
|
// same as above, but records also min/max values |
|
template<typename TYPE, typename TYPEW=TYPE, typename ARGTYPE=const TYPE&, typename TYPER=REAL> |
|
struct MeanStdMinMax : MeanStd<TYPE,TYPEW,ARGTYPE,TYPER> { |
|
typedef MeanStd<TYPE,TYPEW,ARGTYPE,TYPER> Base; |
|
typedef TYPE Type; |
|
typedef TYPEW TypeW; |
|
typedef TYPER TypeR; |
|
typedef ARGTYPE ArgType; |
|
Type minVal, maxVal; |
|
MeanStdMinMax() : minVal(std::numeric_limits<Type>::max()), maxVal(std::numeric_limits<Type>::lowest()) {} |
|
MeanStdMinMax(const Type* values, size_t _size) : MeanStdMinMax() { Compute(values, _size); } |
|
void Update(ArgType v) { |
|
if (minVal > v) |
|
minVal = v; |
|
if (maxVal < v) |
|
maxVal = v; |
|
Base::Update(v); |
|
} |
|
void Compute(const Type* values, size_t _size) { |
|
for (size_t i=0; i<_size; ++i) |
|
Update(values[i]); |
|
} |
|
}; |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given an array of values, compute the X84 threshold as in: |
|
// Hampel FR, Rousseeuw PJ, Ronchetti EM, Stahel WA |
|
// "Robust Statistics: the Approach Based on Influence Functions" |
|
// Wiley Series in Probability and Mathematical Statistics, John Wiley & Sons, 1986 |
|
// returns the pair(median,trust_region) |
|
// upper-bound threshold = median+trust_region |
|
// lower-bound threshold = median-trust_region |
|
template<typename TYPE, typename TYPEW> |
|
inline std::pair<TYPEW,TYPEW> ComputeX84Threshold(const TYPE* const values, size_t size, TYPEW mul=TYPEW(5.2), const uint8_t* mask=NULL) { |
|
ASSERT(size > 0); |
|
// median = MEDIAN(values); |
|
cList<TYPE, const TYPE&, 0> data; |
|
if (mask) { |
|
// use only masked data |
|
data.Reserve(size); |
|
for (size_t i=0; i<size; ++i) |
|
if (mask[i]) |
|
data.Insert(values[i]); |
|
} else { |
|
// use all data |
|
data.CopyOf(values, size); |
|
} |
|
TYPE* const mid = data.Begin() + size / 2; |
|
std::nth_element(data.Begin(), mid, data.End()); |
|
const TYPEW median(*mid); |
|
// threshold = 5.2 * MEDIAN(ABS(values-median)); |
|
using TYPEI = typename MakeSigned<TYPE>::type; |
|
for (TYPE& val: data) |
|
val = TYPE(ABS(TYPEI(val)-TYPEI(median))); |
|
std::nth_element(data.Begin(), mid, data.End()); |
|
return std::make_pair(median, mul*TYPEW(*mid)); |
|
} // ComputeX84Threshold |
|
/*----------------------------------------------------------------*/ |
|
|
|
// given an array of values, compute the upper/lower threshold using quartiles |
|
template<typename TYPE, typename TYPEW> |
|
inline std::tuple<TYPEW,TYPEW,TYPEW,TYPEW,TYPEW> ComputeQuartileThreshold(const TYPE* const values, size_t size, TYPEW mul=TYPEW(1.5), const uint8_t* mask=NULL) { |
|
ASSERT(size > 0); |
|
cList<TYPE, const TYPE&, 0> data(size); |
|
if (mask) { |
|
// use only masked data |
|
data.Reserve(size); |
|
for (size_t i=0; i<size; ++i) |
|
if (mask[i]) |
|
data.Insert(values[i]); |
|
} else { |
|
// use all data |
|
data.CopyOf(values, size); |
|
} |
|
TYPE* const pQB = data.Begin(); |
|
TYPE* const pQE = data.End(); |
|
TYPE* const pQ1 = pQB + size / 4; |
|
TYPE* const pQ2 = pQB + size / 2; |
|
TYPE* const pQ3 = pQB + size * 3 / 4; |
|
std::nth_element( pQB, pQ2, pQE); |
|
std::nth_element( pQB, pQ1, pQ2); |
|
std::nth_element(pQ2+1, pQ3, pQE); |
|
const TYPEW Q1(*pQ1), Q2(*pQ2), Q3(*pQ3); |
|
const TYPEW IQR(Q3 - Q1); |
|
return std::make_tuple(Q1-mul*IQR, Q3+mul*IQR, Q1, Q2, Q3); |
|
} // ComputeQuartileThreshold |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
template <typename TYPE> |
|
inline REAL ComputeSNR(const TDMatrix<TYPE>& x0, const TDMatrix<TYPE>& x) { |
|
ASSERT(x0.area() == x.area()); |
|
const REAL err(norm(TDMatrix<TYPE>(x0 - x))); |
|
const REAL x0Norm(norm(x0)); |
|
REAL ret(std::numeric_limits<REAL>::infinity()); |
|
if (ISZERO(x0Norm) && err > 0) ret = 0; |
|
else if (err > 0) ret = 20.0 * std::log10(x0Norm / err); |
|
return ret; |
|
} // ComputeSNR |
|
template<typename TYPE, int N> |
|
inline REAL ComputeSNR(const TMatrix<TYPE,N,1>& x0, const TMatrix<TYPE,N,1>& x) { |
|
return ComputeSNR(TDMatrix<TYPE>(N,1,(TYPE*)x0.val), TDMatrix<TYPE>(N,1,(TYPE*)x.val)); |
|
} // ComputeSNR |
|
template<typename TYPE> |
|
inline REAL ComputePSNR(const TDMatrix<TYPE>& x0, const TDMatrix<TYPE>& x) { |
|
ASSERT(x0.area() == x.area()); |
|
const size_t N(x0.area()); |
|
const REAL err(normSq(TDMatrix<TYPE>(x0 - x)) / N); |
|
TYPE max1(0), max2(0); |
|
for (unsigned i=0; i<N; ++i) { |
|
max1 = MAXF(max1, x0[i]); |
|
max2 = MAXF(max2, x[i]); |
|
} |
|
const TYPE maxBoth(MAXF(max1, max2)); |
|
REAL ret(std::numeric_limits<REAL>::infinity()); |
|
if (ISZERO(maxBoth) && err > 0) ret = 0; |
|
else if (err > 0) ret = 10.0 * std::log10(static_cast<REAL>(maxBoth*maxBoth) / err); |
|
return ret; |
|
} // ComputePSNR |
|
template<typename TYPE, int N> |
|
inline REAL ComputePSNR(const TMatrix<TYPE,N,1>& x0, const TMatrix<TYPE,N,1>& x) { |
|
return ComputePSNR(TDMatrix<TYPE>(N,1,(TYPE*)x0.val), TDMatrix<TYPE>(N,1,(TYPE*)x.val)); |
|
} // ComputePSNR |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// Builds histograms using "Kernel Density Estimation" and Gaussian kernels; |
|
// see: L. Wassermann: "All of Statistics" for example. |
|
// Samples should be composed of two point: value and weight |
|
template <typename TYPE> |
|
inline TYPE BandwidthGaussianKDE(const TMatrix<TYPE,2,1>* samples, size_t numSamples, TYPE sigma=0, TYPE* pSigma=NULL) { |
|
if (sigma == 0) { |
|
TYPE avg(0); |
|
FOREACHRAWPTR(pSample, samples, numSamples) { |
|
const TYPE& sample((*pSample)(0)); |
|
avg += sample; |
|
sigma += SQUARE(sample); |
|
} |
|
avg /= numSamples; |
|
sigma = SQRT(sigma/numSamples - avg*avg); // Standard Deviation |
|
if (pSigma) *pSigma = sigma; |
|
} |
|
// This is the optimal bandwidth if the point distribution is Gaussian. |
|
// (see "Applied Smoothing Techniques for Data Analysis" by Adrian W, Bowman & Adelchi Azzalini (1997)) |
|
return POW(TYPE(4)/(TYPE(3)*numSamples), TYPE(1)/TYPE(5))*sigma; |
|
} |
|
// estimate density at the given value |
|
template <typename TYPE> |
|
inline TYPE KernelDensityEstimation(const TMatrix<TYPE,2,1>* samples, size_t numSamples, TYPE x, TYPE bandwidth) { |
|
TYPE y(0); |
|
const TYPE invBandwidth(TYPE(1)/bandwidth); |
|
FOREACHRAWPTR(pSample, samples, numSamples) { |
|
const TMatrix<TYPE,2,1>& sample(*pSample); |
|
const TYPE val((x - sample(0))*invBandwidth); |
|
y += sample(1) * EXP(-TYPE(0.5)*val*val)*invBandwidth; |
|
} |
|
return (y / numSamples) * INV_SQRT_2PI; |
|
} |
|
// estimate density at the given range of values; |
|
// INSERTER should have defined the operator(TYPE,TYPE) that receives a pair of value and density |
|
template <typename TYPE, typename INSERTER> |
|
void KernelDensityEstimation(const TMatrix<TYPE,2,1>* samples, size_t numSamples, TYPE bandwidth, TYPE xMin, TYPE xMax, size_t steps, INSERTER& inserter) { |
|
const TYPE step((xMax-xMin)/steps); |
|
for (size_t i=0; i<steps; ++i) { |
|
const TYPE x(xMin+step*i); |
|
const TYPE y(KernelDensityEstimation(samples, numSamples, x, bandwidth)); |
|
inserter(x, y); |
|
} |
|
} |
|
// same as above, but tries to detect local minimum/maximum regions and sample them denser; |
|
// INSERTER should have defined also the function SamplePeak(bool) |
|
// that receives true/false if the region contains a maximum/minimum point respectively |
|
// and returns true/false if the region should or not be sampled more often; |
|
// at the end of the peak sampling EndPeak() is called |
|
template <typename TYPE, typename INSERTER> |
|
void KernelDensityEstimation(const TMatrix<TYPE,2,1>* samples, size_t numSamples, TYPE bandwidth, TYPE xMin, TYPE xMax, TYPE stepMax, INSERTER& inserter) { |
|
size_t smallSteps(0); |
|
TYPE step(stepMax); |
|
TYPE x(xMin), last_x; |
|
TYPE last_y(KernelDensityEstimation(samples, numSamples, x, bandwidth)); |
|
TYPE last_delta(FLT_EPSILON); |
|
inserter(x, last_y); |
|
x += step; |
|
do { |
|
const TYPE y(KernelDensityEstimation(samples, numSamples, x, bandwidth)); |
|
inserter(x, y); |
|
if (smallSteps) { |
|
if (--smallSteps == 0) { |
|
// continue from the last coarse step |
|
step = stepMax; |
|
inserter.EndPeak(); |
|
x = last_x; |
|
} |
|
} else { |
|
const TYPE delta(y-last_y); |
|
if (ABS(SIGN(last_delta)+SIGN(delta)) < 2 && inserter.SamplePeak(last_delta>0)) { |
|
// last step contains a local peak (minimum/maximum), |
|
// so go back and sample more often; |
|
// assuming the curve is locally symmetric and the step small enough, |
|
// the peak is inside the first or second step behind |
|
last_x = x; |
|
x -= (ABS(delta) < ABS(last_delta) ? step : step*TYPE(2)); |
|
smallSteps = 5; |
|
step /= TYPE(smallSteps+1); |
|
} |
|
last_y = y; |
|
last_delta = delta; |
|
} |
|
} while ((x+=step) < xMax); |
|
} |
|
#if TD_VERBOSE == TD_VERBOSE_DEBUG |
|
// example from http://en.wikipedia.org/wiki/Kernel_density_estimation |
|
inline void ExampleKDE() { |
|
typedef REAL Real; |
|
const Real values[6] ={-2.1, -1.3, -0.4, 1.9, 5.1, 6.2}; |
|
TMatrix<Real,2,1> samples[6]; |
|
for (int i=0; i<6; ++i) { |
|
samples[i](0) = values[i]; |
|
samples[i](1) = 1; |
|
} |
|
struct SamplesInserter { |
|
File f; |
|
SamplesInserter(const String& fileName) |
|
: f(fileName, File::WRITE, File::CREATE | File::TRUNCATE) {} |
|
inline void operator() (Real x, Real y) { |
|
f.print("%g\t%g\n", x, y); |
|
} |
|
inline bool SamplePeak(bool bMaxRegion) const { |
|
return bMaxRegion; |
|
} |
|
inline void EndPeak() { |
|
} |
|
}; |
|
const size_t steps(30); |
|
const Real bandwidth = BandwidthGaussianKDE(samples, 6)/2; |
|
const Real xMin(-15), xMax(25), stepMax((xMax-xMin)/steps); |
|
SamplesInserter inserter("kde.txt"); |
|
KernelDensityEstimation(samples, 6, bandwidth, xMin, xMax, stepMax, inserter); |
|
} |
|
#endif |
|
/*----------------------------------------------------------------*/ |
|
|
|
|
|
// normalize image points (inhomogeneous 2D) so that their centroid and typical magnitude of the vector is (1,1) |
|
template <typename TYPE, typename HTYPE> |
|
void NormalizePoints(const CLISTDEF0(TPoint2<TYPE>)& pointsIn, CLISTDEF0(TPoint2<TYPE>)& pointsOut, TMatrix<HTYPE,3,3>* pH=NULL) { |
|
// find centroid |
|
TPoint2<HTYPE> ptAvg(0,0); |
|
FOREACHPTR(pPt, pointsIn) |
|
ptAvg.x += TPoint2<HTYPE>(*pPt); |
|
ptAvg *= HTYPE(1)/HTYPE(pointsIn.GetSize()); |
|
// move centroid to origin |
|
if (pointsOut.GetSize() != pointsIn.GetSize()) |
|
pointsOut.Resize(pointsIn.GetSize()); |
|
HTYPE var(0); |
|
const TPoint2<TYPE> ptAvgF(ptAvg); |
|
FOREACH(i, pointsIn) { |
|
const TPoint2<TYPE>& ptIn = pointsIn[i]; |
|
TPoint2<TYPE>& ptOut = pointsOut[i]; |
|
ptOut = ptIn - ptAvgF; |
|
var += norm(ptOut); |
|
} |
|
// calculate variance |
|
var /= HTYPE(pointsIn.GetSize()); |
|
// scale points |
|
HTYPE scale(1); |
|
if (!ISZERO(var)) |
|
scale = HTYPE(SQRT_2) / var; |
|
FOREACHPTR(pPt, pointsOut) |
|
*pPt *= scale; |
|
// initialize normalizing homography matrix from Scale and Translation (H = S * T); |
|
if (pH != NULL) { |
|
TMatrix<HTYPE,3,3>& H = *pH; |
|
H = TMatrix<HTYPE,3,3>::IDENTITY; |
|
H(0,2) = -scale * ptAvg.x; |
|
H(1,2) = -scale * ptAvg.y; |
|
H(0,0) = scale; |
|
H(1,1) = scale; |
|
} |
|
} |
|
// normalize scene points (inhomogeneous 3D) so that their centroid and typical magnitude of the vector is (1,1,1) |
|
// This normalization algorithm is suitable only for compact distribution of points (see Hartley04 p180) |
|
template <typename TYPE, typename HTYPE> |
|
void NormalizePoints(const CLISTDEF0(TPoint3<TYPE>)& pointsIn, CLISTDEF0(TPoint3<TYPE>)& pointsOut, TMatrix<HTYPE,4,4>* pH=NULL) { |
|
// find centroid |
|
TPoint3<HTYPE> ptAvg(0, 0, 0); |
|
FOREACHPTR(pPt, pointsIn) |
|
ptAvg += TPoint3<HTYPE>(*pPt); |
|
ptAvg *= HTYPE(1)/HTYPE(pointsIn.GetSize()); |
|
// move centroid to origin |
|
if (pointsOut.GetSize() != pointsIn.GetSize()) |
|
pointsOut.Resize(pointsIn.GetSize()); |
|
HTYPE var(0); |
|
FOREACH(i, pointsIn) { |
|
const TPoint3<TYPE>& ptIn = pointsIn[i]; |
|
TPoint3<TYPE>& ptOut = pointsOut[i]; |
|
ptOut = ptIn - ptAvg; |
|
var += norm(ptOut); |
|
} |
|
// calculate variance |
|
var /= HTYPE(pointsIn.GetSize()); |
|
// scale points |
|
HTYPE scale(1); |
|
if (!ISZERO(var)) |
|
scale = HTYPE(SQRT_3) / var; |
|
FOREACHPTR(pPt, pointsOut) |
|
*pPt *= scale; |
|
// initialize normalizing homography matrix |
|
if (pH != NULL) { |
|
TMatrix<HTYPE,4,4>& H = *pH; |
|
H = TMatrix<HTYPE,4,4>::IDENTITY; |
|
H(0,3) = -scale * ptAvg.x; |
|
H(1,3) = -scale * ptAvg.y; |
|
H(2,3) = -scale * ptAvg.z; |
|
H(0,0) = scale; |
|
H(1,1) = scale; |
|
H(2,2) = scale; |
|
} |
|
} |
|
/*----------------------------------------------------------------*/ |
|
|
|
} // namespace SEACAVE
|
|
|