1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #ifndef itkMultiTransform_h
19 #define itkMultiTransform_h
20 
21 #include "itkTransform.h"
22 
23 #include <deque>
24 
25 namespace itk
26 {
27 
28 /** \class MultiTransform
29  * \brief This abstract class contains a list of transforms and provides basic methods.
30  *
31  * This abstract base class is used by classes that operate on a list of
32  * sub-transforms. The sub-transforms can have a different dimensionality
33  * than the container transform.
34  *
35  * Transforms are stored in a container (queue), in the following order:
36  *    \f$ T_0, T_1, ... , T_N-1 \f$
37  *
38  * Transforms are added via a single method, AddTransform(). This adds the
39  * transforms to the back of the queue. A single method for adding transforms
40  * is meant to simplify the interface and prevent errors.
41  *
42  * Inverse
43  *  todo
44  *
45  * TODO
46  *
47  * Interface Issues/Comments
48  * x The PushFrontTransform and PushBackTransform methods are protected to
49  *   force the user to use the AddTransform method, forcing the order of
50  *   transforms. Are there use cases where the user would *need* to insert
51  *   transforms at the front of the queue? Or at arbitrary positions?
52  *
53  * GetParameters efficiency optimization
54  *  Can we optimize this to only query the sub-transforms when the params
55  *  in the sub transforms have changed since the previous call? Can't use
56  *  Modified time b/c that will get updated in sub-transforms with every
57  *  call to SetParameters. Is this worth worrying about? i.e. how much time
58  *  will it take in the overall registration process? Probably very little.
59  *
60  * \ingroup ITKTransform
61  */
62 template
63 <typename TParametersValueType=double, unsigned int NDimensions=3, unsigned int NSubDimensions=NDimensions>
64 class ITK_TEMPLATE_EXPORT MultiTransform :
65   public Transform<TParametersValueType, NDimensions, NSubDimensions>
66 {
67 public:
68   ITK_DISALLOW_COPY_AND_ASSIGN(MultiTransform);
69 
70   /** Standard class type aliases. */
71   using Self = MultiTransform;
72   using Superclass = Transform<TParametersValueType, NDimensions, NSubDimensions>;
73   using Pointer = SmartPointer<Self>;
74   using ConstPointer = SmartPointer<const Self>;
75 
76   /** Run-time type information (and related methods). */
77   itkTypeMacro( MultiTransform, Transform );
78 
79   /** Sub transform type **/
80   using TransformType = Transform<TParametersValueType, NSubDimensions, NSubDimensions >;
81   using TransformTypePointer = typename TransformType::Pointer;
82 
83   /* Types common to both container and sub transforms */
84 
85   /** Parameters type. */
86   using ParametersType = typename Superclass::ParametersType;
87   using ParametersValueType = typename Superclass::ParametersValueType;
88   using FixedParametersType = typename Superclass::FixedParametersType;
89   using FixedParametersValueType = typename Superclass::FixedParametersValueType;
90   using ScalarType = ParametersValueType;
91   /** Derivative type */
92   using DerivativeType = typename Superclass::DerivativeType;
93   /** Jacobian type. */
94   using JacobianType = typename Superclass::JacobianType;
95   using JacobianPositionType = typename Superclass::JacobianPositionType;
96   using InverseJacobianPositionType = typename Superclass::InverseJacobianPositionType;
97   /** Transform category type. */
98   using TransformCategoryType = typename Superclass::TransformCategoryType;
99 
100   /* Types relative to the container transform. */
101 
102   /** InverseTransform type. */
103   using InverseTransformBasePointer = typename Superclass::InverseTransformBasePointer;
104 
105   /** Standard coordinate point type for this class. */
106   using InputPointType = typename Superclass::InputPointType;
107   using OutputPointType = typename Superclass::OutputPointType;
108   /** Standard vector type for this class. */
109   using InputVectorType = typename Superclass::InputVectorType;
110   using OutputVectorType = typename Superclass::OutputVectorType;
111   /** Standard covariant vector type for this class */
112   using InputCovariantVectorType = typename Superclass::InputCovariantVectorType;
113   using OutputCovariantVectorType = typename Superclass::OutputCovariantVectorType;
114   /** Standard vnl_vector type for this class. */
115   using InputVnlVectorType = typename Superclass::InputVnlVectorType;
116   using OutputVnlVectorType = typename Superclass::OutputVnlVectorType;
117   /** Standard Vectorpixel type for this class */
118   using InputVectorPixelType = typename Superclass::InputVectorPixelType;
119   using OutputVectorPixelType = typename Superclass::OutputVectorPixelType;
120   /** Standard DiffusionTensor3D type alias for this class */
121   using InputDiffusionTensor3DType = typename Superclass::InputDiffusionTensor3DType;
122   using OutputDiffusionTensor3DType = typename Superclass::OutputDiffusionTensor3DType;
123   /** Standard SymmetricSecondRankTensor type alias for this class */
124   using InputSymmetricSecondRankTensorType = typename Superclass::InputSymmetricSecondRankTensorType;
125   using OutputSymmetricSecondRankTensorType = typename Superclass::OutputSymmetricSecondRankTensorType;
126 
127   /* Types relative to the sub transform type. */
128 
129   /** InverseTransform type. */
130   using SubTransformInverseTransformBasePointer = typename TransformType::InverseTransformBasePointer;
131 
132   /** Transform queue type */
133   using TransformQueueType = std::deque<TransformTypePointer>;
134 
135   /** The number of parameters defininig this transform. */
136   using NumberOfParametersType = typename Superclass::NumberOfParametersType;
137 
138   /** Dimension of the domain spaces. */
139   static constexpr unsigned int InputDimension = NDimensions;
140   static constexpr unsigned int OutputDimension = NDimensions;
141 
142   static constexpr unsigned int SubInputDimension = NSubDimensions;
143   static constexpr unsigned int SubOutputDimension = NSubDimensions;
144 
145   /** Functionality for sub transforms */
146 
147   /** Add transforms to the queue, as stack.
148    *  Most-recently added transform is always at back of queue, index N-1.
149    */
AddTransform(TransformType * t)150   virtual void AddTransform( TransformType *t  )
151   {
152     this->PushBackTransform( t );
153   }
154 
155   /** Same as AddTransform */
AppendTransform(TransformType * t)156   virtual void AppendTransform( TransformType *t  )
157   {
158     this->AddTransform( t );
159   }
160 
161   /** Add transform to the front of the stack */
PrependTransform(TransformType * t)162   virtual void PrependTransform( TransformType *t  )
163   {
164     this->PushFrontTransform( t );
165   }
166 
167   /** Remove transform from the back of the queue, index N-1 */
RemoveTransform()168   virtual void RemoveTransform()
169   {
170     this->PopBackTransform();
171   }
172 
173   /** Get transforms at the front and the back of the queue */
174   virtual const
GetFrontTransform()175   TransformType * GetFrontTransform() const
176   {
177     return this->m_TransformQueue.front().GetPointer();
178   }
179 
180   virtual const
GetBackTransform()181   TransformType * GetBackTransform() const
182   {
183     return this->m_TransformQueue.back().GetPointer();
184   }
185 
186   virtual const
GetNthTransform(SizeValueType n)187   TransformTypePointer GetNthTransform( SizeValueType n ) const
188   {
189     //NOTE: By returning a smart pointer type, the use of this function can
190     //      be a significant bottleneck in multithreaded applications.
191     return this->m_TransformQueue[n];
192   }
193 
194   /** Get the Nth transform.
195    * \warning No bounds checking is performed. */
196   virtual
GetNthTransformModifiablePointer(const SizeValueType n)197   TransformType * GetNthTransformModifiablePointer( const SizeValueType n ) const
198   {
199     return this->m_TransformQueue[n].GetPointer();
200   }
201 
202   virtual const
GetNthTransformConstPointer(const SizeValueType n)203   TransformType * GetNthTransformConstPointer( const SizeValueType n ) const
204   {
205     return this->m_TransformQueue[n].GetPointer();
206   }
207 
208   /** Access transform queue */
GetTransformQueue()209   virtual const TransformQueueType & GetTransformQueue() const
210   {
211     return this->m_TransformQueue;
212   }
213 
214   /** Misc. functionality */
IsTransformQueueEmpty()215   virtual bool IsTransformQueueEmpty() const
216   {
217     return this->m_TransformQueue.empty();
218   }
219 
220   /** Return the number of sub-transforms. */
GetNumberOfTransforms()221   virtual SizeValueType GetNumberOfTransforms() const
222   {
223     return static_cast<SizeValueType>(this->m_TransformQueue.size());
224   }
225 
226   /** Clear the transform queue. */
ClearTransformQueue()227   virtual void ClearTransformQueue()
228   {
229     this->m_TransformQueue.clear();
230     this->Modified();
231   }
232 
233   /** If all sub-transforms are linear, then the multi-transform is linear. */
234   bool IsLinear() const override;
235 
236   /** If all sub-transforms are of the same category, return that category.
237    * Otherwise return UnknownTransformCategory. */
238   TransformCategoryType GetTransformCategory() const override;
239 
240   /** Get/Set Parameter functions work on all sub-transforms.
241       The parameter data from each sub-transform is
242       concatenated into a single ParametersType object.
243       \note The sub-transforms are read in forward queue order,
244       so the returned array is ordered in the same way. That is,
245       first sub-transform to be added is returned first in the
246       parameter array.*/
247   const ParametersType & GetParameters() const override;
248 
249   /* SetParameters for all sub-transforms.
250    * See GetParameters() for parameter ordering. */
251   void  SetParameters(const ParametersType & p) override;
252 
253   /* GetFixedParameters for all sub-transforms.
254    * See GetParameters() for parameter ordering. */
255   const FixedParametersType & GetFixedParameters() const override;
256 
257   /* SetFixedParameters for all sub-transforms.
258    * See GetParameters() for parameter ordering. */
259   void SetFixedParameters(const FixedParametersType & fixedParameters) override;
260 
261   /* Get total number of parameters. Sum of all sub-transforms. */
262   NumberOfParametersType GetNumberOfParameters() const override;
263 
264   /* Get total number of local parameters, the sum of all sub-transforms. */
265   NumberOfParametersType GetNumberOfLocalParameters() const override;
266 
267   /* Get total number of fixed parameters, the sum of all sub-transforms. */
268   NumberOfParametersType GetNumberOfFixedParameters() const override;
269 
270   /** Update the transform's parameters by the values in \c update.
271    * See GetParameters() for parameter ordering. */
272   void UpdateTransformParameters( const DerivativeType & update, ScalarType  factor = 1.0 ) override;
273 
274   /** Returns a boolean indicating whether it is possible or not to compute the
275    * inverse of this current Transform. If it is possible, then the inverse of
276    * the transform is returned in the inverseTransform variable passed by the user.
277    * The inverse consists of the inverse of each sub-transform, in the same order
278    * as the forward transforms. */
279   bool GetInverse( Self *inverse ) const;
280 
281   /** Flatten the transform queue such that there are no nested composite transforms. */
282 //TODO - what do we need here?
283 //  virtual void FlattenTransformQueue();
284 
285 protected:
286   MultiTransform();
287   ~MultiTransform() override = default;
288   void PrintSelf( std::ostream& os, Indent indent ) const override;
289 
PushFrontTransform(TransformTypePointer t)290   virtual void PushFrontTransform( TransformTypePointer t  )
291   {
292     this->m_TransformQueue.push_front( t );
293     this->Modified();
294   }
295 
PushBackTransform(TransformTypePointer t)296   virtual void PushBackTransform( TransformTypePointer t  )
297   {
298     this->m_TransformQueue.push_back( t );
299     this->Modified();
300   }
301 
PopFrontTransform()302   virtual void PopFrontTransform()
303   {
304     this->m_TransformQueue.pop_front();
305     this->Modified();
306   }
307 
PopBackTransform()308   virtual void PopBackTransform()
309   {
310     this->m_TransformQueue.pop_back();
311     this->Modified();
312   }
313 
314   /** Transform container object. */
315   mutable TransformQueueType m_TransformQueue;
316 
317   /** Cache to save time returning the number of local parameters */
318   mutable NumberOfParametersType  m_NumberOfLocalParameters;
319   mutable ModifiedTimeType        m_LocalParametersUpdateTime;
320 };
321 
322 } // end namespace itk
323 
324 #ifndef ITK_MANUAL_INSTANTIATION
325 #include "itkMultiTransform.hxx"
326 #endif
327 
328 #endif // itkMultiTransform_h
329