Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add affine transformation in the geometry transformation module #1571

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

## New features / critical changes

## Changes

## Bug fixes

- *General*
Expand Down Expand Up @@ -40,6 +38,8 @@
- New VoronoiMapComplete class to store the full Voronoi map (with
all co-cycling sites (Robin Lamy, David Coeurjolly, Isabelle
Sivignon [#1605](https://github.com/DGtal-team/DGtal/pull/1605))
- Add Affine Transformation in the geometry transformation module
(Phuc Ngo,[#1571](https://github.com/DGtal-team/DGtal/pull/1571))

- *DEC*
- New discrete differential operators on polygonal meshes have been
Expand Down
1 change: 1 addition & 0 deletions examples/images/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set(DGTAL_EXAMPLES_SRC
exampleRigidtransformation3d
exampleArrayImageAdapter
exampleConstImageFunctorHolder
exampleAffinetransformation2d
)

if( WITH_HDF5 )
Expand Down
109 changes: 109 additions & 0 deletions examples/images/exampleAffinetransformation2d.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
**/

/**
* @file images/exampleAffinetransformation2d.cpp
* @ingroup Examples
* @author Phuc Ngo (\c hoai-diem-phuc.ngo@loria.fr )
* Laboratoire Lorrain de Recherche en Informatique et ses Applications (LORIA), France
*
* @date 12/05/2021
*
* An example file named affinetransformation2d.
*
* This file is part of the DGtal library.
*/

/**
* Example of 2D affine transformation using forward and backward models.
@see @ref moduleGeometricTransform
\image html church_AffineBackward.png "Result for backward model"
* \example images/exampleAffinetransformation2d.cpp
**/

///////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <cmath>
#include "DGtal/images/ImageSelector.h"
#include "DGtal/images/ImageContainerBySTLVector.h"
#include "DGtal/images/ConstImageAdapter.h"
#include "ConfigExamples.h"
#include "DGtal/helpers/StdDefs.h"
#include "DGtal/base/Common.h"
#include "DGtal/io/readers/PGMReader.h"
#include "DGtal/io/writers/GenericWriter.h"
//! [include]
#include "DGtal/images/AffineTransformation2D.h"
//! [include]
///////////////////////////////////////////////////////////////////////////////

using namespace std;
using namespace DGtal;
using namespace functors;
using namespace Z2i;

int main( int , char** )
{
typedef ImageSelector<Domain, unsigned char >::Type Image;
//! [def]
typedef ForwardAffineTransformation2D < Space > ForwardTrans;
typedef BackwardAffineTransformation2D < Space > BackwardTrans;
typedef DomainGeometricTransformation2D < Domain, ForwardTrans > MyDomainTransformer;
typedef MyDomainTransformer::Bounds Bounds;
//! [def]
trace.beginBlock ( "Example AffineTransformation2d" );
//! [trans]
ForwardTrans forwardTrans(1.2, -1, 1.6, 2, RealPoint ( 5, 5 ));
BackwardTrans backwardTrans(1.2, -1, 1.6, 2, RealPoint ( 5, 5 ));
//! [trans]
//![init_domain_helper]
MyDomainTransformer domainTransformer ( forwardTrans );

Image image = PGMReader<Image>::importPGM ( "../church-small.pgm" );
//! [domain]
Bounds bounds = domainTransformer ( image.domain() );
Domain transformedDomain ( bounds.first, bounds.second );
//! [domain]

trace.beginBlock ( "Backward - Eulerian model" );
//! [backward]
//![init_domain_helper]
Image backwardTransformedImage ( transformedDomain );
for ( Domain::ConstIterator it = backwardTransformedImage.domain().begin(); it != backwardTransformedImage.domain().end(); ++it )
{
Point p = backwardTrans ( *it );
if(image.domain().isInside(p))
backwardTransformedImage.setValue ( *it, image(backwardTrans ( *it )) );
}
backwardTransformedImage >> "backward_transform.pgm";
//! [backward]
trace.endBlock();

trace.beginBlock( "Forward - Lagrangian model" );
Image forwardTransformedImage ( transformedDomain );
//! [forward]
for ( Domain::ConstIterator it = image.domain().begin(); it != image.domain().end(); ++it )
{
forwardTransformedImage.setValue ( forwardTrans ( *it ), image (*it)) ;
}
forwardTransformedImage >> "forward_transform.pgm";
//! [forward]
trace.endBlock();
trace.endBlock();
return 0;
}
// //
///////////////////////////////////////////////////////////////////////////////
211 changes: 211 additions & 0 deletions src/DGtal/images/AffineTransformation2D.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
**/

#pragma once

/**
* @file AffineTransformation2D.h
* @author Phuc Ngo (\c hoai-diem-phuc.ngo@loria.fr )
* Laboratoire Lorrain de Recherche en Informatique et ses Applications (LORIA), France
*
* @date 01/05/2021
*
* This file is part of the DGtal library.
*/

#if defined(AffineTransformation2D_RECURSES)
#error Recursive header files inclusion detected in AffineTransformation2D.h
#else // defined(AffineTransformation2D_RECURSES)
/** Prevents recursive inclusion of headers. */
#define AffineTransformation2D_RECURSES

#if !defined AffineTransformation2D_h
/** Prevents repeated inclusion of headers. */
#define AffineTransformation2D_h

//////////////////////////////////////////////////////////////////////////////
// Inclusions
#include <iostream>
#include <cmath>
#include <climits>
#include <utility>
#include "DGtal/base/Common.h"
#include "DGtal/kernel/BasicPointFunctors.h"
#include <DGtal/helpers/StdDefs.h>
#include <DGtal/kernel/domains/CDomain.h>
#include <DGtal/kernel/CSpace.h>
#include "DGtal/images/ImageSelector.h"
#include "DGtal/images/GeometricTransformation2D.h"
//////////////////////////////////////////////////////////////////////////////

namespace DGtal
{
namespace functors
{
/////////////////////////////////////////////////////////////////////////////
// Template class ForwardAffineTransformation2D
/**
* Description of template functor like class 'ForwardAffineTransformation2D' <p>
* \brief Aim: implements forward rigid transformation of point in the 2D integer space.
* Warring: This version uses closest neighbor interpolation.
*
* @tparam TSpace a 2 dimensional space.
* @tparam TInputValue type of the input point e.g., TSpace::RealPoint
* @tparam TOutputValue type of the output point e.g., TSpace::Point
* @tparam TFunctor a functor operating on the output e.g., a rounding function.
*
* @see exampleAffineTransformation3d.cpp
*/
template < typename TSpace, typename TInputValue = typename TSpace::RealPoint, typename TOutputValue = typename TSpace::Point,
typename TFunctor = VectorRounding < TInputValue, TOutputValue > >
class ForwardAffineTransformation2D : public GeometricTransformation2D <TSpace,TInputValue,TOutputValue,TFunctor>
{
///Checking concepts
BOOST_CONCEPT_ASSERT(( concepts::CSpace<TSpace> ));
BOOST_STATIC_ASSERT(( TSpace::dimension == 2 ));
BOOST_STATIC_ASSERT(( TOutputValue::dimension == 2 ));
BOOST_STATIC_ASSERT(( TInputValue::dimension == 2 ));

// ----------------------- Types ------------------------------
public:
typedef typename TSpace::RealPoint RealPoint;
typedef typename TSpace::RealVector RealVector;
typedef Eigen::Matrix<double, TSpace::dimension, TSpace::dimension> RealMatrix;

// ----------------------- Interface --------------------------------------
public:
/**
* Constructor.
* @param aOrigin the center of affine transform.
* @param aMatrix the affine matrix.
* @param aTranslate the 2D dimensional vector which represents translation.
*/
ForwardAffineTransformation2D ( const RealPoint & aOrigin, const RealMatrix & aMatrix, const RealVector & aTranslate )
{
this->origin = aOrigin;
BOOST_ASSERT((aMatrix(0,0)*aMatrix(1,1)!=aMatrix(1,0)*aMatrix(0,1)));
this->transform_matrix = aMatrix;
this->translation = aTranslate;
}

/**
* Constructor.
* @param a11, a12, a21, a22 the values of affine matrix.
* @param aTranslate the 2D dimensional vector which represents translation.
*/
ForwardAffineTransformation2D ( const double a11, const double a12, const double a21, const double a22, const RealVector & aTranslate )
{
BOOST_ASSERT((a11*a22!=a12*a21));
this->origin = RealPoint(0,0);
RealMatrix aMatrix;
aMatrix(0,0) = a11;
aMatrix(0,1) = a12;
aMatrix(1,0) = a21;
aMatrix(1,1) = a22;
this->transform_matrix = aMatrix;
this->translation = aTranslate;
}
}; //end of class ForwardAffineTransformation2D

/////////////////////////////////////////////////////////////////////////////
// Template class BackwardAffineTransformation2D
/**
* Description of template functor like class 'BackwardAffineTransformation2D' <p>
* \brief Aim: implements backward rigid transformation of point in the 2D integer space.
* Warring: This version uses closest neighbor interpolation.
*
* @tparam TSpace a 2 dimensional space.
* @tparam TInputValue type of the input point e.g., TSpace::RealPoint
* @tparam TOutputValue type of the output point e.g., TSpace::Point
* @tparam TFunctor a functor operating on the output e.g., a rounding function.
*
* @see exampleAffineTransformation3d.cpp
*/
template < typename TSpace, typename TInputValue = typename TSpace::RealPoint, typename TOutputValue = typename TSpace::Point,
typename TFunctor = VectorRounding < TInputValue, TOutputValue > >
class BackwardAffineTransformation2D : public GeometricTransformation2D <TSpace,TInputValue,TOutputValue,TFunctor>
{
///Checking concepts
BOOST_CONCEPT_ASSERT(( concepts::CSpace<TSpace> ));
BOOST_STATIC_ASSERT(( TSpace::dimension == 2 ));
BOOST_STATIC_ASSERT(( TOutputValue::dimension == 2 ));
//BOOST_STATIC_ASSERT(( TInputValue::dimension == 2 ));

// ----------------------- Types ------------------------------
public:
typedef typename TSpace::RealPoint RealPoint;
typedef typename TSpace::RealVector RealVector;
typedef Eigen::Matrix<double, TSpace::dimension, TSpace::dimension> RealMatrix;

// ----------------------- Interface --------------------------------------
public:
/**
* Constructor.
* @param aOrigin the center of affine transform.
* @param aMatrix the affine matrix.
* @param aTranslate the 2D dimensional vector which represents translation.
*/
BackwardAffineTransformation2D ( const RealPoint& aOrigin, const RealMatrix & aMatrix, const RealVector & aTranslate )
{
this->origin = aOrigin;
BOOST_ASSERT((aMatrix(0,0)*aMatrix(1,1)!=aMatrix(1,0)*aMatrix(0,1)));
this->transform_matrix = aMatrix;
this->translation = aTranslate;
}

/**
* Constructor.
* @param a11, a12, a21, a22 the values of affine matrix.
* @param aTranslate the 2D dimensional vector which represents translation.
*/
BackwardAffineTransformation2D ( const double a11, const double a12, const double a21, const double a22, const RealVector & aTranslate )
{
BOOST_ASSERT((a11*a22!=a12*a21));
this->origin = RealPoint(0,0);
RealMatrix aMatrix;
double det = a11*a22-a21*a12;
aMatrix(0,0) = a22/det;
aMatrix(0,1) = -a12/det;
aMatrix(1,0) = -a21/det;
aMatrix(1,1) = a11/det;
this->transform_matrix = aMatrix;
this->translation = aTranslate;
}

TOutputValue operator()( const TInputValue & aInput ) const override
{
RealPoint p;
double a = this->transform_matrix(0,0);//transform_matrix.at(0).at(0);
double b = this->transform_matrix(0,1);//transform_matrix.at(0).at(1);
double c = this->transform_matrix(1,0);//transform_matrix.at(1).at(0);
double d = this->transform_matrix(1,1);//transform_matrix.at(1).at(1);
p[0] = ( a * ( aInput[0] - this->origin[0] - this->translation[0] ) +
b * ( aInput[1] - this->origin[1] - this->translation[1] ) ) + this->origin[0];

p[1] = ( c * ( aInput[0] - this->origin[0] - this->translation[0] ) +
d * ( aInput[1] - this->origin[1] - this->translation[1] ) ) + this->origin[1];
return this->functor ( p );
}
};

}// namespace DGtal::functors
}// namespace DGtal

#endif // !defined AffineTransformation2D_h

#undef AffineTransformation2D_RECURSES
#endif // else defined(AffineTransformation2D_RECURSES)

Loading