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_hxx
19 #define itkMultiTransform_hxx
20 
21 #include "itkMultiTransform.h"
22 
23 namespace itk
24 {
25 
26 
27 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
MultiTransform()28 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>::MultiTransform() : Superclass( 0 )
29 {
30   this->m_NumberOfLocalParameters = NumericTraits< NumberOfParametersType >::ZeroValue();
31   this->m_LocalParametersUpdateTime = NumericTraits< ModifiedTimeType >::ZeroValue();
32   this->m_TransformQueue.clear();
33 }
34 
35 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
36 typename MultiTransform<TParametersValueType, NDimensions, NSubDimensions>::TransformCategoryType
37 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
GetTransformCategory() const38 ::GetTransformCategory() const
39 {
40   // If all sub-transforms are the same, return that type. Otherwise
41   // return Unknown.
42   TransformCategoryType result = Self::UnknownTransformCategory;
43 
44   for( SizeValueType tind = 0; tind < this->GetNumberOfTransforms(); tind++ )
45     {
46     const TransformCategoryType type = this->GetNthTransformConstPointer( tind )->GetTransformCategory();
47     if( tind == 0 )
48       {
49       result = type;
50       }
51     else
52       {
53       if( type != result )
54         {
55         result = Self::UnknownTransformCategory;
56         break;
57         }
58       }
59     }
60 
61   return result;
62 }
63 
64 
65 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
66 bool
67 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
IsLinear() const68 ::IsLinear() const
69 {
70   // If all sub-transforms are linear, return true.
71   for( SizeValueType tind = 0; tind < this->GetNumberOfTransforms(); tind++ )
72     {
73     if( ! this->GetNthTransformConstPointer( tind )->IsLinear() )
74       {
75       return false;
76       }
77     }
78   return true;
79 }
80 
81 
82 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
83 const typename MultiTransform<TParametersValueType, NDimensions, NSubDimensions>::ParametersType &
84 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
GetParameters() const85 ::GetParameters() const
86 {
87   /* Resize destructively. But if it's already this size, nothing is done so
88    * it's efficient. */
89   this->m_Parameters.SetSize( this->GetNumberOfParameters() );
90   NumberOfParametersType offset = NumericTraits< NumberOfParametersType >::ZeroValue();
91   TransformQueueType transforms = this->GetTransformQueue();
92   typename TransformQueueType::const_iterator it;
93   it = transforms.begin();
94 
95   do
96     {
97     const ParametersType & subParameters = (*it)->GetParameters();
98     /* use vnl_vector data_block() to get data ptr */
99     std::copy(subParameters.data_block(),
100               subParameters.data_block()+subParameters.Size(),
101               &(this->m_Parameters.data_block() )[offset]);
102     offset += subParameters.Size();
103     ++it;
104     }
105   while( it != transforms.end() );
106 
107   return this->m_Parameters;
108 }
109 
110 
111 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
112 void
113 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
SetParameters(const ParametersType & inputParameters)114 ::SetParameters(const ParametersType & inputParameters)
115 {
116   /* We do not copy inputParameters into m_Parameters,
117    * to avoid unnecessary copying. */
118 
119   /* We assume input params are concatenation of the parameters of the
120      sub-transforms, in order of the queue from begin() to end(). */
121 
122   /* Verify proper input size. */
123   if( inputParameters.Size() != this->GetNumberOfParameters() )
124     {
125     itkExceptionMacro(<< "Input parameter list size is not expected size. " << inputParameters.Size() << " instead of " << this->GetNumberOfParameters() << ".");
126     }
127 
128   TransformQueueType transforms = this->GetTransformQueue();
129   NumberOfParametersType offset = NumericTraits< NumberOfParametersType >::ZeroValue();
130   auto it = transforms.begin();
131 
132   do
133     {
134     /* If inputParams is same object as m_Parameters, we just pass
135      * each sub-transforms own m_Parameters in. This is needed to
136      * avoid unnecessary copying of parameters in the sub-transforms,
137      * while still allowing SetParameters to do any oprations on the
138      * parameters to update member variable states. A hack. */
139     if( &inputParameters == &this->m_Parameters )
140       {
141       (*it)->SetParameters( (*it)->GetParameters() );
142       }
143     else
144       {
145       const size_t parameterSize = (*it)->GetParameters().Size();
146       (*it)->CopyInParameters( &( inputParameters.data_block() )[offset],
147                               &( inputParameters.data_block() )[offset] + parameterSize );
148       offset += static_cast<NumberOfParametersType>(parameterSize);
149       }
150     ++it;
151     }
152   while( it != transforms.end() );
153 }
154 
155 
156 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
157 const typename MultiTransform<TParametersValueType, NDimensions, NSubDimensions>::FixedParametersType &
158 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
GetFixedParameters() const159 ::GetFixedParameters() const
160 {
161   /* Resize destructively. But if it's already this size, nothing is done so
162    * it's efficient. */
163   this->m_FixedParameters.SetSize( this->GetNumberOfFixedParameters() );
164 
165   NumberOfParametersType offset = NumericTraits< NumberOfParametersType >::ZeroValue();
166   typename TransformQueueType::const_iterator it;
167   TransformQueueType transforms = this->GetTransformQueue();
168   it = transforms.begin();
169 
170   do
171     {
172     const FixedParametersType & subFixedParameters = (*it)->GetFixedParameters();
173     /* use vnl_vector data_block() to get data ptr */
174     std::copy(subFixedParameters.data_block(),
175               subFixedParameters.data_block()+subFixedParameters.Size(),
176               &(this->m_FixedParameters.data_block() )[offset]);
177     offset += subFixedParameters.Size();
178     ++it;
179     }
180   while( it != transforms.end() );
181 
182   return this->m_FixedParameters;
183 }
184 
185 
186 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
187 void
188 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
SetFixedParameters(const FixedParametersType & inputParameters)189 ::SetFixedParameters(const FixedParametersType & inputParameters)
190 {
191   /* Verify proper input size. */
192   if( inputParameters.Size() != this->GetNumberOfFixedParameters() )
193     {
194     itkExceptionMacro(<< "Input parameter list size is not expected size. "
195                       << inputParameters.Size() << " instead of "
196                       << this->GetNumberOfFixedParameters() << ".");
197     }
198 
199   /* Assumes input params are concatenation of the parameters of the
200    * sub transforms. */
201   TransformQueueType transforms = this->GetTransformQueue();
202   NumberOfParametersType offset = NumericTraits< NumberOfParametersType >::ZeroValue();
203 
204   /* Why is this done? Seems unnecessary. */
205   this->m_FixedParameters = inputParameters;
206 
207   auto it = transforms.begin();
208   do
209     {
210     const size_t fixedParameterSize = (*it)->GetFixedParameters().Size();
211     (*it)->CopyInFixedParameters( &( this->m_FixedParameters.data_block() )[offset],
212               &( this->m_FixedParameters.data_block() )[offset] + fixedParameterSize );
213     offset += static_cast<NumberOfParametersType>(fixedParameterSize);
214     ++it;
215     }
216   while( it != transforms.end() );
217 }
218 
219 
220 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
221 typename MultiTransform<TParametersValueType, NDimensions, NSubDimensions>::NumberOfParametersType
222 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
GetNumberOfParameters() const223 ::GetNumberOfParameters() const
224 {
225   /* Returns to total number of params in all transforms currently
226    * set to be used for optimized.
227    * NOTE: Ideally we'd optimize this to store the result and
228    * only re-calc when the composite object has been modified.
229    * However, it seems that number of parameter might change for dense
230    * field transforms (deformation, bspline) during processing and
231    * we wouldn't know that in this class, so this is safest. */
232   NumberOfParametersType result = NumericTraits< NumberOfParametersType >::ZeroValue();
233 
234 
235   for( SizeValueType tind = 0; tind < this->GetNumberOfTransforms(); tind++ )
236     {
237     /* Use raw pointer for efficiency */
238     const TransformType * transform = this->GetNthTransformConstPointer( tind );
239     result += transform->GetNumberOfParameters();
240     }
241   return result;
242 }
243 
244 
245 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
246 typename MultiTransform<TParametersValueType, NDimensions, NSubDimensions>::NumberOfParametersType
247 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
GetNumberOfLocalParameters() const248 ::GetNumberOfLocalParameters() const
249 {
250   if ( this->GetMTime() == this->m_LocalParametersUpdateTime )
251    {
252    return this->m_NumberOfLocalParameters;
253    }
254 
255   this->m_LocalParametersUpdateTime = this->GetMTime();
256 
257   /* Note that unlike in GetNumberOfParameters(), we don't expect the
258    * number of local parameters to possibly change, so we can cache
259    * the value. */
260   NumberOfParametersType result = NumericTraits< NumberOfParametersType >::ZeroValue();
261 
262   for( SizeValueType tind = 0; tind < this->GetNumberOfTransforms(); tind++ )
263     {
264     const TransformType * transform = this->GetNthTransformConstPointer( tind );
265     result += transform->GetNumberOfLocalParameters();
266     }
267   this->m_NumberOfLocalParameters = result;
268   return result;
269 }
270 
271 
272 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
273 typename MultiTransform<TParametersValueType, NDimensions, NSubDimensions>::NumberOfParametersType
274 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
GetNumberOfFixedParameters() const275 ::GetNumberOfFixedParameters() const
276 {
277   NumberOfParametersType result = NumericTraits< NumberOfParametersType >::ZeroValue();
278 
279   for( SizeValueType tind = 0; tind < this->GetNumberOfTransforms(); tind++ )
280     {
281     const TransformType * transform = this->GetNthTransformConstPointer( tind );
282     result += transform->GetFixedParameters().Size();
283     }
284   return result;
285 }
286 
287 
288 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
289 void
290 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
UpdateTransformParameters(const DerivativeType & update,ScalarType factor)291 ::UpdateTransformParameters( const DerivativeType & update, ScalarType factor )
292 {
293   /* Update parameters within the sub-transforms. */
294   /* NOTE: We might want to thread this over each sub-transform, if we
295    * find we're working with longer lists of sub-transforms that do
296    * not implement any threading of their own for UpdateTransformParameters.
297    * Since the plan is for a UpdateTransformParameters functor that is
298    * user-assignable, we would need a method in the
299    * functor to return whether or not it does therading. If all sub-transforms
300    * return that they don't thread, we could do each sub-transform in its
301    * own thread from here. */
302   NumberOfParametersType numberOfParameters = this->GetNumberOfParameters();
303 
304   if( update.Size() != numberOfParameters )
305     {
306     itkExceptionMacro("Parameter update size, " << update.Size() << ", must "
307                       " be same as transform parameter size, " << numberOfParameters << std::endl);
308     }
309 
310   NumberOfParametersType offset = NumericTraits< NumberOfParametersType >::ZeroValue();
311 
312   for( SizeValueType tind = 0; tind < this->GetNumberOfTransforms(); tind++ )
313     {
314     // HACK:  The following line looks wrong.  We should not need to const_cast
315     TransformType * subtransform = this->GetNthTransformModifiablePointer( tind );
316     /* The input values are in a monolithic block, so we have to point
317      * to the subregion corresponding to the individual subtransform.
318      * This simply creates an Array object with data pointer, no
319      * memory is allocated or copied.
320      * NOTE: the use of const_cast is used to avoid a deep copy in the underlying vnl_vector
321      * by using LetArrayManageMemory=false, and being very careful here we can
322      * ensure that casting away consteness does not result in memory corruption. */
323     auto * nonConstDataRefForPerformance = const_cast< typename DerivativeType::ValueType * >(
324                                              &( (update.data_block() )[offset]) );
325     const DerivativeType subUpdate( nonConstDataRefForPerformance, subtransform->GetNumberOfParameters(), false );
326     /* This call will also call SetParameters, so don't need to call it
327      * expliclity here. */
328     subtransform->UpdateTransformParameters( subUpdate, factor );
329     offset += subtransform->GetNumberOfParameters();
330     }
331   this->Modified();
332 }
333 
334 
335 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
336 bool
337 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
GetInverse(Self * inverse) const338 ::GetInverse( Self *inverse ) const
339 {
340   typename TransformQueueType::const_iterator it;
341 
342   //NOTE: MultiTransform delegagtes to
343   //      individual transform for setting FixedParameters
344   //      inverse->SetFixedParameters( this->GetFixedParameters() );
345   inverse->ClearTransformQueue();
346   for( it = this->m_TransformQueue.begin(); it != this->m_TransformQueue.end(); ++it )
347     {
348     TransformTypePointer inverseTransform = dynamic_cast<TransformType *>( ( ( *it )->GetInverseTransform() ).GetPointer() );
349     if( !inverseTransform )
350       {
351       inverse->ClearTransformQueue();
352       return false;
353       }
354     else
355       {
356       /* Add to end of queue to preserve transform order */
357       inverse->PushBackTransform( inverseTransform );
358       }
359     }
360 
361   return true;
362 }
363 
364 
365 template<typename TParametersValueType, unsigned int NDimensions, unsigned int NSubDimensions>
366 void
367 MultiTransform<TParametersValueType, NDimensions, NSubDimensions>
PrintSelf(std::ostream & os,Indent indent) const368 ::PrintSelf( std::ostream& os, Indent indent ) const
369 {
370   Superclass::PrintSelf( os, indent );
371 
372   if( this->m_TransformQueue.empty() )
373     {
374     os << indent << "Transform queue is empty." << std::endl;
375     return;
376     }
377 
378   os << indent <<  "Transforms in queue, from begin to end:" << std::endl;
379   typename TransformQueueType::const_iterator cit;
380   for( cit = this->m_TransformQueue.begin(); cit != this->m_TransformQueue.end(); ++cit )
381     {
382     os << indent << ">>>>>>>>>" << std::endl;
383     (*cit)->Print( os, indent );
384     }
385 
386   os << indent <<  "End of MultiTransform." << std::endl << "<<<<<<<<<<" << std::endl;
387 }
388 
389 } // end namespace itk
390 
391 #endif
392