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 /*=========================================================================
19 *
20 * Portions of this file are subject to the VTK Toolkit Version 3 copyright.
21 *
22 * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
23 *
24 * For complete copyright, license and disclaimer of warranty information
25 * please refer to the NOTICE file at the top of the ITK source tree.
26 *
27 *=========================================================================*/
28 #ifndef itkImageBase_hxx
29 #define itkImageBase_hxx
30
31 #include "itkImageBase.h"
32
33 #include <mutex>
34 #include "itkProcessObject.h"
35 #include "itkSpatialOrientation.h"
36 #include <cstring>
37 #include "itkMath.h"
38
39 namespace itk
40 {
41
42 template< unsigned int VImageDimension >
43 ImageBase< VImageDimension >
ImageBase()44 ::ImageBase()
45 :
46 m_OffsetTable{}
47 {
48 m_Spacing.Fill(1.0);
49 m_Origin.Fill(0.0);
50 m_Direction.SetIdentity();
51 m_InverseDirection.SetIdentity();
52 m_IndexToPhysicalPoint.SetIdentity();
53 m_PhysicalPointToIndex.SetIdentity();
54 }
55
56
57 template< unsigned int VImageDimension >
58 void
59 ImageBase< VImageDimension >
Allocate(bool)60 ::Allocate(bool)
61 {
62 }
63
64
65 template< unsigned int VImageDimension >
66 void
67 ImageBase< VImageDimension >
Initialize()68 ::Initialize()
69 {
70 //
71 // We don't modify ourselves because the "ReleaseData" methods depend upon
72 // no modification when initialized. Otherwise BUG: 8490 will
73 // reoccur.
74 //
75 // DO NOT CALL ANY METHODS WHICH MODIFY OURSELVES
76
77 // Call the superclass which should initialize the BufferedRegion ivar.
78 Superclass::Initialize();
79
80 // Clear the offset table
81 memset(m_OffsetTable, 0, sizeof(m_OffsetTable));
82
83 // Clear the BufferedRegion ivar
84 this->InitializeBufferedRegion();
85 }
86
87 template< unsigned int VImageDimension >
88 void
89 ImageBase< VImageDimension >
SetSpacing(const SpacingType & spacing)90 ::SetSpacing(const SpacingType & spacing)
91 {
92 for ( unsigned int i = 0; i < VImageDimension; i++ )
93 {
94 if ( this->m_Spacing[i] < 0.0 )
95 {
96 #if ! defined ( ITK_LEGACY_REMOVE )
97 itkWarningMacro("Negative spacing is not supported and may result in undefined behavior. Spacing is " << this->m_Spacing);
98 break;
99 #else
100 itkExceptionMacro("Negative spacing is not allowed: Spacing is " << this->m_Spacing);
101 #endif
102 }
103 }
104
105 itkDebugMacro("setting Spacing to " << spacing);
106 if ( this->m_Spacing != spacing )
107 {
108 this->m_Spacing = spacing;
109 this->ComputeIndexToPhysicalPointMatrices();
110 this->Modified();
111 }
112 }
113
114
115 template< unsigned int VImageDimension >
116 void
117 ImageBase< VImageDimension >
SetSpacing(const double spacing[VImageDimension])118 ::SetSpacing(const double spacing[VImageDimension])
119 {
120 this->InternalSetSpacing(spacing);
121 }
122
123
124 template< unsigned int VImageDimension >
125 void
126 ImageBase< VImageDimension >
SetSpacing(const float spacing[VImageDimension])127 ::SetSpacing(const float spacing[VImageDimension])
128 {
129 this->InternalSetSpacing(spacing);
130 }
131
132
133 template< unsigned int VImageDimension >
134 void
135 ImageBase< VImageDimension >
SetOrigin(const double origin[VImageDimension])136 ::SetOrigin(const double origin[VImageDimension])
137 {
138 PointType p(origin);
139
140 this->SetOrigin(p);
141 }
142
143
144 template< unsigned int VImageDimension >
145 void
146 ImageBase< VImageDimension >
SetOrigin(const float origin[VImageDimension])147 ::SetOrigin(const float origin[VImageDimension])
148 {
149 Point< float, VImageDimension > of(origin);
150 PointType p;
151 p.CastFrom(of);
152 this->SetOrigin(p);
153 }
154
155
156 template< unsigned int VImageDimension >
157 void
158 ImageBase< VImageDimension >
SetDirection(const DirectionType & direction)159 ::SetDirection(const DirectionType & direction)
160 {
161 bool modified = false;
162
163 for ( unsigned int r = 0; r < VImageDimension; r++ )
164 {
165 for ( unsigned int c = 0; c < VImageDimension; c++ )
166 {
167 if ( Math::NotExactlyEquals(m_Direction[r][c], direction[r][c]) )
168 {
169 m_Direction[r][c] = direction[r][c];
170 modified = true;
171 }
172 }
173 }
174
175 if ( modified )
176 {
177 this->ComputeIndexToPhysicalPointMatrices();
178 this->m_InverseDirection = m_Direction.GetInverse();
179 }
180 }
181
182
183 template< unsigned int VImageDimension >
184 void
185 ImageBase< VImageDimension >
ComputeIndexToPhysicalPointMatrices()186 ::ComputeIndexToPhysicalPointMatrices()
187 {
188 DirectionType scale;
189
190 for ( unsigned int i = 0; i < VImageDimension; i++ )
191 {
192 if ( this->m_Spacing[i] == 0.0 )
193 {
194 itkExceptionMacro("A spacing of 0 is not allowed: Spacing is " << this->m_Spacing);
195 }
196 scale[i][i] = this->m_Spacing[i];
197 }
198
199 if ( vnl_determinant( this->m_Direction.GetVnlMatrix() ) == 0.0 )
200 {
201 itkExceptionMacro(<< "Bad direction, determinant is 0. Direction is " << this->m_Direction);
202 }
203
204 this->m_IndexToPhysicalPoint = this->m_Direction * scale;
205 this->m_PhysicalPointToIndex = m_IndexToPhysicalPoint.GetInverse();
206
207 this->Modified();
208 }
209
210
211 template< unsigned int VImageDimension >
212 void
213 ImageBase< VImageDimension >
ComputeOffsetTable()214 ::ComputeOffsetTable()
215 {
216 // vxl_uint_64 num=1;
217 OffsetValueType num = 1;
218 const SizeType & bufferSize = this->GetBufferedRegion().GetSize();
219
220 // m_OffsetTable[0] = (OffsetValueType)num;
221 m_OffsetTable[0] = num;
222 for ( unsigned int i = 0; i < VImageDimension; i++ )
223 {
224 num *= bufferSize[i];
225 // m_OffsetTable[i+1] = (OffsetValueType)num;
226 m_OffsetTable[i + 1] = num;
227 }
228 // if( num > NumericTraits<SizeValueType>::max() )
229 // {
230 // itkExceptionMacro(<< "Requested number of pixels (" << num
231 // << ") is greater than the largest possible number of pixels (" <<
232 // NumericTraits<SizeValueType>::max() << ").");
233 // }
234 }
235
236
237 template< unsigned int VImageDimension >
238 void
239 ImageBase< VImageDimension >
UpdateOutputInformation()240 ::UpdateOutputInformation()
241 {
242 if ( this->GetSource() )
243 {
244 this->GetSource()->UpdateOutputInformation();
245 }
246 else
247 {
248 // If we don't have a source, we should set our Image to span our
249 // buffer (by setting our LargestPossibleRegion to equal our
250 // BufferedRegion). However, if the buffer is empty, we leave the
251 // LargestPossibleRegion at its prior value. This allows InPlace
252 // filters to overwrite their inputs safely (taking ownership of
253 // the pixel buffers), yet respond to subsequent requests for
254 // information.
255 if ( this->GetBufferedRegion().GetNumberOfPixels() > 0 )
256 {
257 this->SetLargestPossibleRegion( this->GetBufferedRegion() );
258 }
259 }
260
261 // Now we should know what our largest possible region is. If our
262 // requested region was not set yet, (or has been set to something
263 // invalid - with no data in it ) then set it to the largest possible
264 // region.
265 if ( this->GetRequestedRegion().GetNumberOfPixels() == 0 )
266 {
267 this->SetRequestedRegionToLargestPossibleRegion();
268 }
269 }
270
271
272 template< unsigned int VImageDimension >
273 void
274 ImageBase< VImageDimension >
UpdateOutputData()275 ::UpdateOutputData()
276 {
277 // If the requested region does not contain any pixels then there is
278 // no reason to Update the output data. This is needed so that
279 // filters don't need to update all inputs. This occours in
280 // ImageBase as oppose to DataObject, but cause this statement
281 // requires the specific GetNumberOfPixels methods ( as oppose to a
282 // generic Region::IsEmpty method ).
283 //
284 // Also note, the check of the largest possible region is needed so
285 // that an exception will be thrown in the process object when no
286 // input has been set. ( This part of the statement could be removed
287 // if this check happened earlier in the pipeline )
288 if ( this->GetRequestedRegion().GetNumberOfPixels() > 0
289 || this->GetLargestPossibleRegion().GetNumberOfPixels() == 0 )
290 {
291 this->Superclass::UpdateOutputData();
292 }
293 }
294
295
296 template< unsigned int VImageDimension >
297 void
298 ImageBase< VImageDimension >
SetRequestedRegionToLargestPossibleRegion()299 ::SetRequestedRegionToLargestPossibleRegion()
300 {
301 this->SetRequestedRegion( this->GetLargestPossibleRegion() );
302 }
303
304
305 template< unsigned int VImageDimension >
306 void
307 ImageBase< VImageDimension >
CopyInformation(const DataObject * data)308 ::CopyInformation(const DataObject *data)
309 {
310 // Standard call to the superclass' method
311 Superclass::CopyInformation(data);
312
313 if ( data )
314 {
315 // Attempt to cast data to an ImageBase
316 const auto * const imgData = dynamic_cast< const ImageBase< VImageDimension > * >( data );
317
318 if ( imgData != nullptr )
319 {
320 // Copy the meta data for this data type
321 this->SetLargestPossibleRegion( imgData->GetLargestPossibleRegion() );
322 this->SetSpacing( imgData->GetSpacing() );
323 this->SetOrigin( imgData->GetOrigin() );
324 this->SetDirection( imgData->GetDirection() );
325 this->SetNumberOfComponentsPerPixel(
326 imgData->GetNumberOfComponentsPerPixel() );
327 }
328 else
329 {
330 // pointer could not be cast back down
331 itkExceptionMacro( << "itk::ImageBase::CopyInformation() cannot cast "
332 << typeid( data ).name() << " to "
333 << typeid( const ImageBase * ).name() );
334 }
335 }
336 }
337
338
339 template< unsigned int VImageDimension >
340 void
341 ImageBase< VImageDimension >
Graft(const Self * image)342 ::Graft(const Self *image)
343 {
344 if ( !image )
345 {
346 return;
347 }
348
349 // Copy the meta-information
350 this->CopyInformation(image);
351
352 // Copy the remaining region information. Subclasses are
353 // responsible for copying the pixel container.
354 this->SetBufferedRegion( image->GetBufferedRegion() );
355 this->SetRequestedRegion( image->GetRequestedRegion() );
356 }
357
358
359 template< unsigned int VImageDimension >
360 void
361 ImageBase< VImageDimension >
Graft(const DataObject * data)362 ::Graft(const DataObject *data)
363 {
364 using ImageBaseType = ImageBase< VImageDimension >;
365
366 const auto * image = dynamic_cast< const ImageBaseType * >( data );
367
368 if ( !image )
369 {
370 return;
371 }
372
373 // Call Graft() with image input to actually perform the graft operation
374 this->Graft(image);
375 }
376
377
378 template< unsigned int VImageDimension >
379 bool
380 ImageBase< VImageDimension >
RequestedRegionIsOutsideOfTheBufferedRegion()381 ::RequestedRegionIsOutsideOfTheBufferedRegion()
382 {
383 unsigned int i;
384 const IndexType & requestedRegionIndex = this->GetRequestedRegion().GetIndex();
385 const IndexType & bufferedRegionIndex = this->GetBufferedRegion().GetIndex();
386
387 const SizeType & requestedRegionSize = this->GetRequestedRegion().GetSize();
388 const SizeType & bufferedRegionSize = this->GetBufferedRegion().GetSize();
389
390 for ( i = 0; i < VImageDimension; i++ )
391 {
392 if ( ( requestedRegionIndex[i] < bufferedRegionIndex[i] )
393 || ( ( requestedRegionIndex[i] + static_cast< OffsetValueType >( requestedRegionSize[i] ) )
394 > ( bufferedRegionIndex[i] + static_cast< OffsetValueType >( bufferedRegionSize[i] ) ) ) )
395 {
396 return true;
397 }
398 }
399
400 return false;
401 }
402
403
404 template< unsigned int VImageDimension >
405 bool
406 ImageBase< VImageDimension >
VerifyRequestedRegion()407 ::VerifyRequestedRegion()
408 {
409 bool retval = true;
410 unsigned int i;
411
412 // Is the requested region within the LargestPossibleRegion?
413 // Note that the test is indeed against the largest possible region
414 // rather than the buffered region; see DataObject::VerifyRequestedRegion.
415 const IndexType & requestedRegionIndex = this->GetRequestedRegion().GetIndex();
416 const IndexType & largestPossibleRegionIndex =
417 this->GetLargestPossibleRegion().GetIndex();
418
419 const SizeType & requestedRegionSize = this->GetRequestedRegion().GetSize();
420 const SizeType & largestPossibleRegionSize =
421 this->GetLargestPossibleRegion().GetSize();
422
423 for ( i = 0; i < VImageDimension; i++ )
424 {
425 if ( ( requestedRegionIndex[i] < largestPossibleRegionIndex[i] )
426 || ( ( requestedRegionIndex[i] + static_cast< OffsetValueType >( requestedRegionSize[i] ) )
427 > ( largestPossibleRegionIndex[i] + static_cast< OffsetValueType >( largestPossibleRegionSize[i] ) ) ) )
428 {
429 retval = false;
430 }
431 }
432
433 return retval;
434 }
435
436
437 template< unsigned int VImageDimension >
438 void
439 ImageBase< VImageDimension >
SetBufferedRegion(const RegionType & region)440 ::SetBufferedRegion(const RegionType & region)
441 {
442 if ( m_BufferedRegion != region )
443 {
444 m_BufferedRegion = region;
445 this->ComputeOffsetTable();
446 this->Modified();
447 }
448 }
449
450
451 template< unsigned int VImageDimension >
452 void
453 ImageBase< VImageDimension >
InitializeBufferedRegion()454 ::InitializeBufferedRegion()
455 {
456 //
457 // We don't modify ourselves because the "ReleaseData" methods depend upon
458 // no modification when initialized.
459 //
460 // Otherwise BUG: 8490 will reoccur
461
462 m_BufferedRegion = RegionType();
463 this->ComputeOffsetTable();
464 }
465
466
467 template< unsigned int VImageDimension >
468 void
469 ImageBase< VImageDimension >
SetRequestedRegion(const RegionType & region)470 ::SetRequestedRegion(const RegionType & region)
471 {
472 m_RequestedRegion = region;
473 }
474
475
476 template< unsigned int VImageDimension >
477 void
478 ImageBase< VImageDimension >
SetRequestedRegion(const DataObject * data)479 ::SetRequestedRegion( const DataObject *data )
480 {
481 const auto * const imgData = dynamic_cast< const ImageBase * >( data );
482
483 if ( imgData != nullptr )
484 {
485 // only copy the RequestedRegion if the parameter object is an image
486 this->SetRequestedRegion( imgData->GetRequestedRegion() );
487 }
488 }
489
490
491 template< unsigned int VImageDimension >
492 void
493 ImageBase< VImageDimension >
SetLargestPossibleRegion(const RegionType & region)494 ::SetLargestPossibleRegion(const RegionType & region)
495 {
496 if ( m_LargestPossibleRegion != region )
497 {
498 m_LargestPossibleRegion = region;
499 this->Modified();
500 }
501 }
502
503
504 template< unsigned int VImageDimension >
505 unsigned int
506 ImageBase< VImageDimension >
GetNumberOfComponentsPerPixel() const507 ::GetNumberOfComponentsPerPixel() const
508 {
509 // Returns the number of components in the image.
510 // base implementation defaults to 1
511 return 1;
512 }
513
514
515 template< unsigned int VImageDimension >
516 void
517 ImageBase< VImageDimension >
SetNumberOfComponentsPerPixel(unsigned int)518 ::SetNumberOfComponentsPerPixel(unsigned int)
519 {
520 // does nothing (always 1)
521 }
522
523
524 template< unsigned int VImageDimension >
525 void
526 ImageBase< VImageDimension >
PrintSelf(std::ostream & os,Indent indent) const527 ::PrintSelf(std::ostream & os, Indent indent) const
528 {
529 Superclass::PrintSelf(os, indent);
530
531 os << indent << "LargestPossibleRegion: " << std::endl;
532 this->GetLargestPossibleRegion().PrintSelf( os, indent.GetNextIndent() );
533
534 os << indent << "BufferedRegion: " << std::endl;
535 this->GetBufferedRegion().PrintSelf( os, indent.GetNextIndent() );
536
537 os << indent << "RequestedRegion: " << std::endl;
538 this->GetRequestedRegion().PrintSelf( os, indent.GetNextIndent() );
539
540 os << indent << "Spacing: " << this->GetSpacing() << std::endl;
541
542 os << indent << "Origin: " << this->GetOrigin() << std::endl;
543
544 os << indent << "Direction: " << std::endl << this->GetDirection() << std::endl;
545
546 os << indent << "IndexToPointMatrix: " << std::endl;
547 os << this->m_IndexToPhysicalPoint << std::endl;
548
549 os << indent << "PointToIndexMatrix: " << std::endl;
550 os << this->m_PhysicalPointToIndex << std::endl;
551
552 os << indent << "Inverse Direction: " << std::endl;
553 os << this->GetInverseDirection() << std::endl;
554 }
555
556 } // end namespace itk
557
558 #endif
559