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