407 lines
12 KiB
C++
407 lines
12 KiB
C++
// ____ ______ __
|
||
// / __ \ / ____// /
|
||
// / /_/ // / / /
|
||
// / ____// /___ / /___ PixInsight Class Library
|
||
// /_/ \____//_____/ PCL 2.4.23
|
||
// ----------------------------------------------------------------------------
|
||
// pcl/Homography.h - Released 2022-03-12T18:59:29Z
|
||
// ----------------------------------------------------------------------------
|
||
// This file is part of the PixInsight Class Library (PCL).
|
||
// PCL is a multiplatform C++ framework for development of PixInsight modules.
|
||
//
|
||
// Copyright (c) 2003-2022 Pleiades Astrophoto S.L. All Rights Reserved.
|
||
//
|
||
// Redistribution and use in both source and binary forms, with or without
|
||
// modification, is permitted provided that the following conditions are met:
|
||
//
|
||
// 1. All redistributions of source code must retain the above copyright
|
||
// notice, this list of conditions and the following disclaimer.
|
||
//
|
||
// 2. All redistributions in binary form must reproduce the above copyright
|
||
// notice, this list of conditions and the following disclaimer in the
|
||
// documentation and/or other materials provided with the distribution.
|
||
//
|
||
// 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
|
||
// of their contributors, may be used to endorse or promote products derived
|
||
// from this software without specific prior written permission. For written
|
||
// permission, please contact info@pixinsight.com.
|
||
//
|
||
// 4. All products derived from this software, in any form whatsoever, must
|
||
// reproduce the following acknowledgment in the end-user documentation
|
||
// and/or other materials provided with the product:
|
||
//
|
||
// "This product is based on software from the PixInsight project, developed
|
||
// by Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
|
||
//
|
||
// Alternatively, if that is where third-party acknowledgments normally
|
||
// appear, this acknowledgment must be reproduced in the product itself.
|
||
//
|
||
// THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
|
||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
|
||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
// EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
|
||
// INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
|
||
// DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
// POSSIBILITY OF SUCH DAMAGE.
|
||
// ----------------------------------------------------------------------------
|
||
|
||
#ifndef __PCL_Homography_h
|
||
#define __PCL_Homography_h
|
||
|
||
/// \file pcl/Homography.h
|
||
|
||
#include <pcl/Defs.h>
|
||
#include <pcl/Diagnostics.h>
|
||
|
||
#include <pcl/Algebra.h>
|
||
#include <pcl/Array.h>
|
||
#include <pcl/Matrix.h>
|
||
#include <pcl/Point.h>
|
||
|
||
namespace pcl
|
||
{
|
||
|
||
// ----------------------------------------------------------------------------
|
||
|
||
/*!
|
||
* \class Homography
|
||
* \brief Homography geometric transformation.
|
||
*
|
||
* A two-dimensional projective transformation, or \e homography, is a
|
||
* line-preserving geometric transformation between two sets of points in the
|
||
* plane. More formally, if P represents the set of points in the plane, a
|
||
* homography is an invertible mapping H from P^2 to itself such that three
|
||
* points p1, p2, p3 are collinear if and only if H(p1), H(p2), H(p3) do.
|
||
*
|
||
* Homographies have important practical applications in the field of computer
|
||
* vision. On the PixInsight platform, this class is an essential component of
|
||
* image registration and astrometry processes.
|
||
*/
|
||
template <class P = DPoint>
|
||
class PCL_CLASS Homography
|
||
{
|
||
public:
|
||
|
||
/*!
|
||
* Represents a reference point in two dimensions.
|
||
*/
|
||
typedef P point;
|
||
|
||
/*!
|
||
* Represents a list of two-dimensional reference points involved in a
|
||
* homography transformation.
|
||
*/
|
||
typedef Array<point> point_list;
|
||
|
||
/*!
|
||
* Default constructor. Constructs a no-op transformation with a unit matrix
|
||
* transformation matrix.
|
||
*/
|
||
Homography()
|
||
: m_H( Matrix::UnitMatrix( 3 ) )
|
||
{
|
||
}
|
||
|
||
/*!
|
||
* Constructor from a given homography matrix.
|
||
*/
|
||
Homography( const Matrix& H )
|
||
: m_H( H )
|
||
{
|
||
}
|
||
|
||
/*!
|
||
* Constructor from two 2D point lists.
|
||
*
|
||
* Computes a homography transformation to generate a list \a P2 of
|
||
* transformed points from a list \a P1 of original points. In other words,
|
||
* the computed homography H works as follows:
|
||
*
|
||
* P2 = H( P1 )
|
||
*
|
||
* The transformation matrix is calculated by the Direct Linear
|
||
* Transformation (DLT) method. Both point lists must contain at least four
|
||
* points.
|
||
*
|
||
* If one of the specified point lists contains less than four points, or if
|
||
* no homography can be estimated from the specified point lists (which
|
||
* leads to a singular transformation matrix), this constructor throws an
|
||
* appropriate Error exception.
|
||
*
|
||
* \b References
|
||
*
|
||
* R. Hartley, <em>In defense of the eight-point algorithm.</em> IEEE
|
||
* Transactions on Pattern Analysis and Machine Intelligence, vol. 19, pp.
|
||
* 580–593, June 1997.
|
||
*/
|
||
Homography( const point_list& P1, const point_list& P2 )
|
||
: m_H( DLT( P1, P2 ) )
|
||
{
|
||
}
|
||
|
||
/*!
|
||
* Copy constructor.
|
||
*/
|
||
Homography( const Homography& ) = default;
|
||
|
||
/*!
|
||
* Copy assignment operator.
|
||
*/
|
||
Homography& operator =( const Homography& ) = default;
|
||
|
||
/*!
|
||
* Move constructor.
|
||
*/
|
||
Homography( Homography&& ) = default;
|
||
|
||
/*!
|
||
* Move assignment operator.
|
||
*/
|
||
Homography& operator =( Homography&& ) = default;
|
||
|
||
/*!
|
||
* Coordinate transformation operator. Applies the homography matrix to the
|
||
* specified \a x and \a y coordinates. Returns the transformed point as a
|
||
* two-dimensional point with real coordinates.
|
||
*/
|
||
template <typename T>
|
||
DPoint operator ()( T x, T y ) const
|
||
{
|
||
double w = m_H[2][0]*x + m_H[2][1]*y + m_H[2][2];
|
||
PCL_CHECK( 1 + w != 1 )
|
||
return DPoint( (m_H[0][0]*x + m_H[0][1]*y + m_H[0][2])/w,
|
||
(m_H[1][0]*x + m_H[1][1]*y + m_H[1][2])/w );
|
||
}
|
||
|
||
/*!
|
||
* Point transformation operator. Applies the homography matrix to the
|
||
* coordinates of the specified point \a p. Returns the transformed point as
|
||
* a two-dimensional point with real coordinates.
|
||
*/
|
||
template <typename T>
|
||
DPoint operator ()( const GenericPoint<T>& p ) const
|
||
{
|
||
return operator ()( p.x, p.y );
|
||
}
|
||
|
||
/*!
|
||
* Returns the inverse of this homography transformation.
|
||
*
|
||
* If this transformation has been computed from two point lists \a P1 and
|
||
* \a P2:
|
||
*
|
||
* P2 = H( P1 )
|
||
*
|
||
* then this function returns a transformation H1 such that:
|
||
*
|
||
* P1 = H1( P2 )
|
||
*/
|
||
Homography Inverse() const
|
||
{
|
||
return Homography( m_H.Inverse() );
|
||
}
|
||
|
||
/*!
|
||
* Returns the homography transformation matrix.
|
||
*/
|
||
operator const Matrix&() const
|
||
{
|
||
return m_H;
|
||
}
|
||
|
||
/*!
|
||
* Returns true iff this transformation has been initialized and is valid.
|
||
*/
|
||
bool IsValid() const
|
||
{
|
||
return !m_H.IsEmpty();
|
||
}
|
||
|
||
/*!
|
||
* Returns true iff this is an affine homography transformation.
|
||
*
|
||
* An affine homography is a special type of a general homography where the
|
||
* last row of the 3x3 transformation matrix is equal to (0, 0, 1). This
|
||
* function verifies that this property holds for the current transformation
|
||
* matrix (if it is valid) up to the machine epsilon for the \c double
|
||
* numeric type.
|
||
*/
|
||
bool IsAffine() const
|
||
{
|
||
return IsValid()
|
||
&& Abs( m_H[2][0] ) <= std::numeric_limits<double>::epsilon()
|
||
&& Abs( m_H[2][1] ) <= std::numeric_limits<double>::epsilon()
|
||
&& 1 - Abs( m_H[2][2] ) <= std::numeric_limits<double>::epsilon();
|
||
}
|
||
|
||
/*!
|
||
* Ensures that the transformation uniquely references its internal matrix
|
||
* data.
|
||
*/
|
||
void EnsureUnique()
|
||
{
|
||
m_H.EnsureUnique();
|
||
}
|
||
|
||
private:
|
||
|
||
Matrix m_H;
|
||
|
||
/*
|
||
* Normalization of reference points.
|
||
*/
|
||
struct NormalizedPoints
|
||
{
|
||
Array<DPoint> N; // the normalized points
|
||
Matrix T; // 3x3 normalization matrix
|
||
|
||
NormalizedPoints( const point_list& points )
|
||
{
|
||
/*
|
||
* Calculate the centroid of the input set of points.
|
||
*/
|
||
DPoint centroid( 0 );
|
||
for ( const auto& p : points )
|
||
centroid += p;
|
||
centroid /= points.Length();
|
||
|
||
/*
|
||
* Calculate mean centroid distance and move origin to the centroid.
|
||
*/
|
||
double d0 = 0;
|
||
for ( const auto& p : points )
|
||
{
|
||
double x = p.x - centroid.x;
|
||
double y = p.y - centroid.y;
|
||
N << DPoint( x, y );
|
||
d0 += Sqrt( x*x + y*y );
|
||
}
|
||
d0 /= points.Length();
|
||
|
||
/*
|
||
* Scale point coordinates.
|
||
*/
|
||
double scale = Const<double>::sqrt2()/d0;
|
||
for ( auto& p : N )
|
||
p *= scale;
|
||
|
||
/*
|
||
* Form the normalization matrix.
|
||
*/
|
||
T = Matrix( scale, 0.0, -scale*centroid.x,
|
||
0.0, scale, -scale*centroid.y,
|
||
0.0, 0.0, 1.0 );
|
||
}
|
||
};
|
||
|
||
/*
|
||
* Implementation of the Direct Linear Transformation (DLT) method to
|
||
* compute a normalized homography matrix.
|
||
*/
|
||
static Matrix DLT( const point_list& P1, const point_list& P2 )
|
||
{
|
||
int n = Min( P1.Length(), P2.Length() );
|
||
if ( n < 4 )
|
||
throw Error( "Homography::DLT(): Less than four points specified." );
|
||
|
||
/*
|
||
* Normalize all points.
|
||
*/
|
||
NormalizedPoints p1( P1 );
|
||
NormalizedPoints p2( P2 );
|
||
|
||
/*
|
||
* Setup cross product matrix A.
|
||
*/
|
||
Matrix A( 2*n, 9 );
|
||
for ( int i = 0; i < n; ++i )
|
||
{
|
||
double x1 = p1.N[i].x;
|
||
double y1 = p1.N[i].y;
|
||
double x2 = p2.N[i].x;
|
||
double y2 = p2.N[i].y;
|
||
double* A0 = A[2*i];
|
||
double* A1 = A[2*i + 1];
|
||
//double* A2 = A[3*i + 2]; <-- linearly dependent eqn.
|
||
|
||
A0[0] = A0[1] = A0[2] = 0;
|
||
A0[3] = -x1;
|
||
A0[4] = -y1;
|
||
A0[5] = -1;
|
||
A0[6] = y2*x1;
|
||
A0[7] = y2*y1;
|
||
A0[8] = y2;
|
||
|
||
A1[0] = x1;
|
||
A1[1] = y1;
|
||
A1[2] = 1;
|
||
A1[3] = A1[4] = A1[5] = 0;
|
||
A1[6] = -x2*x1;
|
||
A1[7] = -x2*y1;
|
||
A1[8] = -x2;
|
||
|
||
/* <-- linearly dependent eqn.
|
||
A2[0] = -y2*x1;
|
||
A2[1] = -y2*y1;
|
||
A2[2] = -y2;
|
||
A2[3] = x2*x1;
|
||
A2[4] = x2*y1;
|
||
A2[5] = x2;
|
||
A2[6] = A2[7] = A2[8] = 0;
|
||
*/
|
||
}
|
||
|
||
/*
|
||
* SVD of cross product matrix.
|
||
*/
|
||
InPlaceSVD svd( A );
|
||
|
||
/*
|
||
* For sanity, set to zero all insignificant singular values.
|
||
*/
|
||
for ( int i = 0; i < svd.W.Length(); ++i )
|
||
if ( Abs( svd.W[i] ) <= std::numeric_limits<double>::epsilon() )
|
||
svd.W[i] = 0;
|
||
|
||
/*
|
||
* Locate the smallest nonzero singular value.
|
||
*/
|
||
int i = svd.IndexOfSmallestSingularValue();
|
||
|
||
/*
|
||
* The components of the homography matrix are those of the smallest
|
||
* eigenvector, i.e. the column vector of V corresponding to the smallest
|
||
* singular value.
|
||
*/
|
||
Matrix H( svd.V[0][i], svd.V[1][i], svd.V[2][i],
|
||
svd.V[3][i], svd.V[4][i], svd.V[5][i],
|
||
svd.V[6][i], svd.V[7][i], svd.V[8][i] );
|
||
|
||
if ( Abs( H[2][2] ) <= std::numeric_limits<double>::epsilon() )
|
||
throw Error( "Homography::DLT(): Singular matrix." );
|
||
|
||
/*
|
||
* Denormalize matrix components.
|
||
*/
|
||
H = p2.T.Inverse() * H * p1.T;
|
||
|
||
/*
|
||
* Normalize matrix so that H[2][2] = 1.
|
||
*/
|
||
return H *= 1/H[2][2];
|
||
}
|
||
};
|
||
|
||
// ----------------------------------------------------------------------------
|
||
|
||
} // pcl
|
||
|
||
#endif // __PCL_Homography_h
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// EOF pcl/Homography.h - Released 2022-03-12T18:59:29Z
|