/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef itkTransform_h
#define itkTransform_h
#include "itkTransformBase.h"
#include "itkVector.h"
#include "itkSymmetricSecondRankTensor.h"
#include "itkDiffusionTensor3D.h"
#include "itkVariableLengthVector.h"
#include "vnl/vnl_vector_fixed.h"
#include "itkMatrix.h"
namespace itk
{
/** \class Transform
* \brief Transform points and vectors from an input space to an output space.
*
* This abstract class defines the generic interface for a geometric
* transformation from one space to another. The class provides methods
* for mapping points, vectors and covariant vectors from the input space
* to the output space.
*
* Given that transformations are not necessarily invertible, this basic
* class does not provide the methods for back transformation. Back transform
* methods are implemented in derived classes where appropriate.
*
* \par Registration Framework Support
* Typically a Transform class has several methods for setting its
* parameters. For use in the registration framework, the parameters must
* also be represented by an array of doubles to allow communication
* with generic optimizers. The Array of transformation parameters is set using
* the SetParameters() method.
*
* Another requirement of the registration framework is the computation
* of the transform Jacobian. In general, an ImageToImageMetric requires
* the knowledge of the Jacobian in order to compute the metric derivatives.
* The Jacobian is a matrix whose element are the partial derivatives
* of the output point with respect to the array of parameters that defines
* the transform.
*
* Subclasses must provide implementations for:
* virtual OutputPointType TransformPoint(const InputPointType &) const
* virtual OutputVectorType TransformVector(const InputVectorType &) const
* virtual OutputVnlVectorType TransformVector(const InputVnlVectorType &) const
* virtual OutputCovariantVectorType TransformCovariantVector(const InputCovariantVectorType &) const
* virtual void SetParameters(const ParametersType &)
* virtual void SetFixedParameters(const FixedParametersType &)
* virtual void ComputeJacobianWithRespectToParameters(
* const InputPointType &,
* JacobianType &) const
* virtual void ComputeJacobianWithRespectToPosition(
* const InputPointType & x,
* JacobianType &jacobian ) const;
*
* Since TranformVector and TransformCovariantVector have multiple
* overloaded methods from the base class, subclasses must specify:
* using Superclass::TransformVector;
* using Superclass::TransformCovariantVector;
*
*
* \ingroup ITKTransform
*/
template
class ITK_TEMPLATE_EXPORT Transform : public TransformBaseTemplate
{
public:
/** Standard class typedefs. */
typedef Transform Self;
typedef TransformBaseTemplate Superclass;
typedef SmartPointer Pointer;
typedef SmartPointer ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro(Transform, TransformBaseTemplate);
/** Dimension of the domain space. */
itkStaticConstMacro(InputSpaceDimension, unsigned int, NInputDimensions);
itkStaticConstMacro(OutputSpaceDimension, unsigned int, NOutputDimensions);
/** define the Clone method */
itkCloneMacro(Self);
/** Get the size of the input space */
unsigned int GetInputSpaceDimension(void) const ITK_OVERRIDE
{
return NInputDimensions;
}
/** Get the size of the output space */
unsigned int GetOutputSpaceDimension(void) const ITK_OVERRIDE
{
return NOutputDimensions;
}
/** Type of the input parameters. */
typedef typename Superclass::FixedParametersType FixedParametersType;
typedef typename Superclass::FixedParametersValueType FixedParametersValueType;
typedef typename Superclass::ParametersType ParametersType;
typedef typename Superclass::ParametersValueType ParametersValueType;
typedef Array DerivativeType;
/** Type of the scalar representing coordinate and vector elements. */
typedef ParametersValueType ScalarType;
/** Type of the Jacobian matrix. */
typedef Array2D JacobianType;
/** Standard vector type for this class. */
typedef Vector InputVectorType;
typedef Vector OutputVectorType;
/** Standard variable length vector type for this class
* this provides an interface for the VectorImage class */
typedef VariableLengthVector InputVectorPixelType;
typedef VariableLengthVector OutputVectorPixelType;
/* Standard symmetric second rank tenosr type for this class */
typedef SymmetricSecondRankTensor
InputSymmetricSecondRankTensorType;
typedef SymmetricSecondRankTensor
OutputSymmetricSecondRankTensorType;
/* Standard tensor type for this class */
typedef DiffusionTensor3D InputDiffusionTensor3DType;
typedef DiffusionTensor3D OutputDiffusionTensor3DType;
/** Standard covariant vector type for this class */
typedef CovariantVector
InputCovariantVectorType;
typedef CovariantVector
OutputCovariantVectorType;
/** Standard vnl_vector type for this class. */
typedef vnl_vector_fixed InputVnlVectorType;
typedef vnl_vector_fixed OutputVnlVectorType;
/** Standard coordinate point type for this class */
typedef Point InputPointType;
typedef Point OutputPointType;
/** Base inverse transform type. This type should not be changed to the
* concrete inverse transform type or inheritance would be lost. */
typedef Transform InverseTransformBaseType;
typedef typename InverseTransformBaseType::Pointer InverseTransformBasePointer;
typedef Matrix MatrixType;
typedef Matrix
OutputDirectionMatrix;
typedef Matrix
InputDirectionMatrix;
typedef Matrix
DirectionChangeMatrix;
typedef typename Superclass::NumberOfParametersType NumberOfParametersType;
/** Method to transform a point.
* \warning This method must be thread-safe. See, e.g., its use
* in ResampleImageFilter.
*/
virtual OutputPointType TransformPoint(const InputPointType &) const = 0;
/** Method to transform a vector. */
virtual OutputVectorType TransformVector(const InputVectorType &) const
{
itkExceptionMacro( "TransformVector(const InputVectorType &)"
"is unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a vector at a given location.
* For global transforms, \c point is ignored and \c TransformVector( vector )
* is called. Local transforms (e.g. deformation
* field transform) must override and provide required behavior. */
virtual OutputVectorType TransformVector(
const InputVectorType & vector,
const InputPointType & point ) const;
/** Method to transform a vnl_vector. */
virtual OutputVnlVectorType TransformVector(const InputVnlVectorType &) const
{
itkExceptionMacro( "TransformVector( const InputVnlVectorType & ) is "
"unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a vnl_vector, at a point.
* For global transforms, \c point is ignored and \c TransformVector( vector )
* is called. Local transforms (e.g. deformation
* field transform) must override and provide required behavior. */
virtual OutputVnlVectorType TransformVector(
const InputVnlVectorType & vector,
const InputPointType & point ) const;
/** Method to transform a vector stored in a VectorImage. */
virtual OutputVectorPixelType TransformVector(
const InputVectorPixelType & itkNotUsed(vector) ) const
{
itkExceptionMacro( "TransformVector( const InputVectorPixelType & ) is "
"unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a vector stored in a VectorImage, at a point.
* For global transforms, \c point is ignored and \c TransformVector( vector )
* is called. Local transforms (e.g. deformation
* field transform) must override and provide required behavior. */
virtual OutputVectorPixelType TransformVector(
const InputVectorPixelType & vector,
const InputPointType & point ) const;
/** Method to transform a CovariantVector. */
virtual OutputCovariantVectorType TransformCovariantVector(const InputCovariantVectorType &) const
{
itkExceptionMacro( "TransformCovariantVector( const InputCovariantVectorType & ) is "
"unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a CovariantVector, using a point. Global transforms
* can ignore the \c point parameter. Local transforms (e.g. deformation
* field transform) must override and provide required behavior.
* By default, \c point is ignored and
* \c TransformCovariantVector(vector) is called */
virtual OutputCovariantVectorType TransformCovariantVector(
const InputCovariantVectorType & vector,
const InputPointType & point ) const;
/** Method to transform a CovariantVector stored in a VectorImage. */
virtual OutputVectorPixelType TransformCovariantVector(
const InputVectorPixelType & itkNotUsed(vector) ) const
{
itkExceptionMacro( "TransformCovariantVector(const InputVectorPixelType &)"
"is unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a CovariantVector, using a point. Global transforms
* can ignore the \c point parameter. Local transforms (e.g. deformation
* field transform) must override and provide required behavior.
* By default, \c point is ignored and \c TransformCovariantVector(vector) is
* called */
virtual OutputVectorPixelType TransformCovariantVector(
const InputVectorPixelType & vector,
const InputPointType & point ) const;
/** Method to transform a diffusion tensor */
virtual OutputDiffusionTensor3DType TransformDiffusionTensor3D(
const InputDiffusionTensor3DType & itkNotUsed(tensor) )
const
{
itkExceptionMacro(
"TransformDiffusionTensor3D( const InputDiffusionTensor3DType & ) is "
"unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a diffusion tensor at a point. Global transforms
* can ignore the \c point parameter. Local transforms (e.g. deformation
* field transform) must override and provide required behavior.
* By default, \c point is ignored and \c TransformDiffusionTensor(tensor) is
* called */
virtual OutputDiffusionTensor3DType TransformDiffusionTensor3D(
const InputDiffusionTensor3DType & tensor,
const InputPointType & point ) const;
/** Method to transform a diffusion tensor stored in a VectorImage */
virtual OutputVectorPixelType TransformDiffusionTensor3D(
const InputVectorPixelType & itkNotUsed(tensor) ) const
{
itkExceptionMacro(
"TransformDiffusionTensor( const InputVectorPixelType & ) is "
"unimplemented for " << this->GetNameOfClass() );
}
virtual OutputVectorPixelType TransformDiffusionTensor3D(
const InputVectorPixelType & tensor,
const InputPointType & point ) const;
/** Method to transform a diffusion tensor at a point. Global transforms
* can ignore the \c point parameter. Local transforms (e.g. deformation
* field transform) must override and provide required behavior.
* By default, \c point is ignored and \c TransformSymmetricSecondRankTensor(tensor) is
* called */
virtual OutputSymmetricSecondRankTensorType TransformSymmetricSecondRankTensor(
const InputSymmetricSecondRankTensorType & tensor,
const InputPointType & point ) const;
/** Method to transform a ssr tensor stored in a VectorImage */
virtual OutputSymmetricSecondRankTensorType TransformSymmetricSecondRankTensor(
const InputSymmetricSecondRankTensorType & itkNotUsed(tensor) ) const
{
itkExceptionMacro(
"TransformSymmetricSecondRankTensor( const InputSymmetricSecondRankTensorType & ) is "
"unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a ssr tensor stored in a VectorImage */
virtual OutputVectorPixelType TransformSymmetricSecondRankTensor(
const InputVectorPixelType & itkNotUsed(tensor) ) const
{
itkExceptionMacro(
"TransformSymmetricSecondRankTensor( const InputVectorPixelType & ) is "
"unimplemented for " << this->GetNameOfClass() );
}
/** Method to transform a diffusion tensor stored in a VectorImage, at
* a point. Global transforms
* can ignore the \c point parameter. Local transforms (e.g. deformation
* field transform) must override and provide required behavior.
* By default, \c point is ignored and \c TransformDiffusionTensor(tensor) is
* called */
virtual OutputVectorPixelType TransformSymmetricSecondRankTensor(
const InputVectorPixelType & tensor,
const InputPointType & point ) const;
/** Set the transformation parameters and update internal transformation.
* SetParameters gives the transform the option to set it's
* parameters by keeping a reference to the parameters, or by
* copying. To force the transform to copy it's parameters call
* SetParametersByValue.
* \sa SetParametersByValue
*/
virtual void SetParameters(const ParametersType &) ITK_OVERRIDE = 0;
/** Set the transformation parameters and update internal transformation.
* This method forces the transform to copy the parameters. The
* default implementation is to call SetParameters. This call must
* be overridden if the transform normally implements SetParameters
* by keeping a reference to the parameters.
* \sa SetParameters
*/
virtual void SetParametersByValue(const ParametersType & p) ITK_OVERRIDE
{
this->SetParameters(p);
}
/** This function allow copying a range of values into the Parameters
* The range of values must conform to std::copy(begin, end, m_Parameters)
* requirements.
*/
virtual void CopyInParameters(const ParametersValueType * const begin,
const ParametersValueType * const end) ITK_OVERRIDE;
/** This function allow copying a range of values into the FixedParameters
* The range of values must conform to std::copy(begin, end, m_FixedParameters)
* requirements.
*/
virtual void CopyInFixedParameters(const FixedParametersValueType * const begin,
const FixedParametersValueType * const end) ITK_OVERRIDE;
/** Get the Transformation Parameters. */
virtual const ParametersType & GetParameters(void) const ITK_OVERRIDE
{
return m_Parameters;
}
/** Set the fixed parameters and update internal transformation. */
virtual void SetFixedParameters(const FixedParametersType &) ITK_OVERRIDE = 0;
/** Get the Fixed Parameters. */
virtual const FixedParametersType & GetFixedParameters(void) const ITK_OVERRIDE
{
return m_FixedParameters;
}
/** Update the transform's parameters by the values in \c update.
* \param update must be of the same length as returned by
* GetNumberOfParameters(). Throw an exception otherwise.
* \param factor is a scalar multiplier for each value in \c update.
* SetParameters is called at the end of this method, to allow the transform
* to perform any required operations on the updated parameters - typically
* a conversion to member variables for use in TransformPoint. */
virtual void UpdateTransformParameters( const DerivativeType & update,
ParametersValueType factor = 1.0 );
/** Return the number of local parameters that completely defines the
* Transform at an individual voxel.
* For transforms with local support, this will enable downstream
* computation of the jacobian wrt only the local support region.
* For instance, in the case of a deformation field, this will be equal to
* the number of image dimensions. If it is an affine transform, this will
* be the same as the GetNumberOfParameters().
*/
virtual NumberOfParametersType GetNumberOfLocalParameters(void) const
{
return this->GetNumberOfParameters();
}
/** Return the number of parameters that completely define the Transfom */
virtual NumberOfParametersType GetNumberOfParameters(void) const ITK_OVERRIDE
{
return this->m_Parameters.Size();
}
/** Return the number of parameters that define the constant elements of a Transfom */
virtual NumberOfParametersType GetNumberOfFixedParameters() const
{
return this->m_FixedParameters.Size();
}
/** Returns a boolean indicating whether it is possible or not to compute the
* inverse of this current Transform. If it is possible, then the inverse of
* the transform is returned in the inverseTransform variable passed by the
* user. The inverse is recomputed if this current transform has been
* modified.
* This method is intended to be overriden as needed by derived classes.
*
*/
bool GetInverse( Self *itkNotUsed(inverseTransform) ) const
{
return false;
}
/** Return an inverse of this transform. If the inverse has not been
* implemented, return ITK_NULLPTR. The type of the inverse transform
* does not necessarily need to match the type of the forward
* transform. This allows one to return a numeric inverse transform
* instead.
*/
virtual InverseTransformBasePointer GetInverseTransform() const
{
return ITK_NULLPTR;
}
/** Generate a platform independent name */
virtual std::string GetTransformTypeAsString() const ITK_OVERRIDE;
typedef typename Superclass::TransformCategoryType TransformCategoryType;
/** Indicates the category transform.
* e.g. an affine transform, or a local one, e.g. a deformation field.
*/
virtual TransformCategoryType GetTransformCategory() const ITK_OVERRIDE
{
return Superclass::UnknownTransformCategory;
}
virtual bool IsLinear() const
{
return ( this->GetTransformCategory() == Superclass::Linear );
}
#ifdef ITKV3_COMPATIBILITY
/**
* This function is only here for ITKv3 backwards compatibility.
*
* This is not a thread-safe version for GetJacobian(), because the internal
* class member variable m_Jacobian could be changed for different values
* in different threads.
*
* All derived classes should move the computations of computing a jacobian
* from GetJacobian to ComputeJacobianWithRespectToParameters and then
* use this forwarding function for backwards compatibility.
*/
virtual const JacobianType & GetJacobian(const InputPointType & x) const
{
this->ComputeJacobianWithRespectToParameters(x, m_SharedLocalJacobian);
return m_SharedLocalJacobian;
}
#endif
/**
* Compute the Jacobian of the transformation
*
* This method computes the Jacobian matrix of the transformation
* at a given input point. The rank of the Jacobian will also indicate
* if the transform is invertible at this point.
*
* The Jacobian is be expressed as a matrix of partial derivatives of the
* output point components with respect to the parameters that defined
* the transform:
*
* \f[
*
J=\left[ \begin{array}{cccc}
\frac{\partial x_{1}}{\partial p_{1}} &
\frac{\partial x_{1}}{\partial p_{2}} &
\cdots & \frac{\partial x_{1}}{\partial p_{m}}\\
\frac{\partial x_{2}}{\partial p_{1}} &
\frac{\partial x_{2}}{\partial p_{2}} &
\cdots & \frac{\partial x_{2}}{\partial p_{m}}\\
\vdots & \vdots & \ddots & \vdots \\
\frac{\partial x_{n}}{\partial p_{1}} &
\frac{\partial x_{n}}{\partial p_{2}} &
\cdots & \frac{\partial x_{n}}{\partial p_{m}}
\end{array}\right]
*
* \f]
*
* This is also used for efficient computation of a point-local jacobian
* for dense transforms.
* \c jacobian is assumed to be thread-local variable, otherwise memory corruption
* will most likely occur during multi-threading.
* To avoid repeatitive memory allocation, pass in 'jacobian' with its size
* already set. */
virtual void ComputeJacobianWithRespectToParameters(const InputPointType & itkNotUsed(p), JacobianType & itkNotUsed(jacobian) ) const = 0;
virtual void ComputeJacobianWithRespectToParametersCachedTemporaries(const InputPointType & p, JacobianType & jacobian, JacobianType & itkNotUsed(jacobianWithRespectToPosition) ) const
{
//NOTE: default implementation is not optimized, and just falls back to original methods.
this->ComputeJacobianWithRespectToParameters(p, jacobian);
}
/** This provides the ability to get a local jacobian value
* in a dense/local transform, e.g. DisplacementFieldTransform. For such
* transforms it would be unclear what parameters would refer to.
* Generally, global transforms should return an indentity jacobian
* since there is no change with respect to position. */
virtual void ComputeJacobianWithRespectToPosition(const InputPointType & itkNotUsed(x), JacobianType & itkNotUsed(jacobian) ) const
{
itkExceptionMacro(
"ComputeJacobianWithRespectToPosition( InputPointType, JacobianType"
" is unimplemented for " << this->GetNameOfClass() );
}
/** This provides the ability to get a local jacobian value
* in a dense/local transform, e.g. DisplacementFieldTransform. For such
* transforms it would be unclear what parameters would refer to.
* Generally, global transforms should return an indentity jacobian
* since there is no change with respect to position. */
virtual void ComputeInverseJacobianWithRespectToPosition(const InputPointType & x, JacobianType & jacobian ) const;
protected:
/**
* Clone the current transform.
* This does a complete copy of the transform
* state to the new transform
*/
virtual typename LightObject::Pointer InternalClone() const ITK_OVERRIDE;
Transform();
Transform(NumberOfParametersType NumberOfParameters);
virtual ~Transform() ITK_OVERRIDE { }
mutable ParametersType m_Parameters;
mutable FixedParametersType m_FixedParameters;
OutputDiffusionTensor3DType PreservationOfPrincipalDirectionDiffusionTensor3DReorientation(
const InputDiffusionTensor3DType, const JacobianType ) const;
#ifdef ITKV3_COMPATIBILITY
// This is only needed to provide the old interface that returns a reference to the Jacobian.
// It is NOT thread-safe and should be avoided whenever possible.
mutable JacobianType m_SharedLocalJacobian;
#endif
mutable DirectionChangeMatrix m_DirectionChange;
private:
ITK_DISALLOW_COPY_AND_ASSIGN(Transform);
template
static std::string GetTransformTypeAsString(TType *)
{
std::string rval("other");
return rval;
}
static std::string GetTransformTypeAsString(float *)
{
std::string rval("float");
return rval;
}
static std::string GetTransformTypeAsString(double *)
{
std::string rval("double");
return rval;
}
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkTransform.hxx"
#endif
#endif