/*========================================================================= * * 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