1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2008-2015 Imperial College London
5  * Copyright 2008-2013 Daniel Rueckert, Julia Schnabel
6  * Copyright 2013-2015 Andreas Schuh
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #ifndef MIRTK_ImageAttributes_H
22 #define MIRTK_ImageAttributes_H
23 
24 #include "mirtk/Indent.h"
25 #include "mirtk/Matrix.h"
26 #include "mirtk/Point.h"
27 #include "mirtk/Array.h"
28 
29 
30 namespace mirtk {
31 
32 
33 /**
34  * Class which defines the attributes of the imaging geometry
35  */
36 struct ImageAttributes
37 {
38   // ---------------------------------------------------------------------------
39   // Attributes
40   int _x; ///< Image x-dimension (in voxels)
41   int _y; ///< Image y-dimension (in voxels)
42   int _z; ///< Image z-dimension (in voxels)
43   int _t; ///< Image t-dimension (in voxels)
44 
45   double _dx; ///< Voxel x-dimensions (in mm)
46   double _dy; ///< Voxel y-dimensions (in mm)
47   double _dz; ///< Voxel z-dimensions (in mm)
48   double _dt; ///< Voxel t-dimensions (in ms)
49 
50   double _xorigin; ///< Image x-origin (in mm)
51   double _yorigin; ///< Image y-origin (in mm)
52   double _zorigin; ///< Image z-origin (in mm)
53   double _torigin; ///< Image t-origin (in ms)
54 
55   double _xaxis[3]; ///< Direction of x-axis
56   double _yaxis[3]; ///< Direction of y-axis
57   double _zaxis[3]; ///< Direction of z-axis
58 
59   Matrix _smat; ///< Affine transformation matrix
60 
61   const Matrix *_w2i;  ///< Pointer to pre-computed world to image matrix (cf. BaseImage::_matW2I)
62   const Matrix *_i2w;  ///< Pointer to pre-computed image to world matrix (cf. BaseImage::_matI2W)
63 
64   /// Number of lattice points in image domain
65   int NumberOfLatticePoints() const;
66 
67   /// Number of spatial lattice points
68   int NumberOfSpatialPoints() const;
69 
70   /// Number of lattice points
71   /// (i.e., NumberOfLatticePoints if _dt != 0, otherwise NumberOfSpatialPoints)
72   int NumberOfPoints() const;
73 
74   /// Get number of lattice points in x dimension
75   int X() const;
76 
77   /// Get number of lattice points in y dimension
78   int Y() const;
79 
80   /// Get number of lattice points in z dimension
81   int Z() const;
82 
83   /// Get number of lattice points in t dimension
84   int T() const;
85 
86   /// Get number of lattice points in i-th dimension
87   int N(int) const;
88 
89   /// Get spacing of lattice points in i-th dimension
90   double Spacing(int) const;
91 
92   /// Get spacing of lattice points in x dimension
93   double XSpacing() const;
94 
95   /// Get spacing of lattice points in y dimension
96   double YSpacing() const;
97 
98   /// Get spacing of lattice points in z dimension
99   double ZSpacing() const;
100 
101   /// Get spacing of lattice points in t dimension
102   double TSpacing() const;
103 
104   /// Get voxel size/spacing of lattice points in x dimension
105   double XSize() const;
106 
107   /// Get voxel size/spacing of lattice points in y dimension
108   double YSize() const;
109 
110   /// Get voxel size/spacing of lattice points in z dimension
111   double ZSize() const;
112 
113   /// Get voxel size/spacing of lattice points in t dimension
114   double TSize() const;
115 
116   // ---------------------------------------------------------------------------
117   // Construction/Destruction
118 
119   /// Constructor
120   ImageAttributes();
121 
122   /// Constructor
123   ImageAttributes(int, int, double = 1.0, double = 1.0);
124 
125   /// Constructor
126   ImageAttributes(int, int, int, double = 1.0, double = 1.0, double = 1.0);
127 
128   /// Constructor
129   ImageAttributes(int, int, int, int, double = 1.0, double = 1.0, double = 1.0, double = 1.0);
130 
131   /// Copy constructor
132   ImageAttributes(const ImageAttributes &);
133 
134   /// Copy operator
135   ImageAttributes& operator= (const ImageAttributes &);
136 
137   /// Check if attributes are valid
138   operator bool() const;
139 
140   // ---------------------------------------------------------------------------
141   // Comparison
142 
143   /// Whether given lattice is fully contained by this image lattice
144   bool ContainsInSpace(const ImageAttributes &attr) const;
145 
146   /// Whether spatial attributes are equal
147   bool EqualInSpace(const ImageAttributes &attr) const;
148 
149   /// Whether temporal attributes are equal
150   bool EqualInTime(const ImageAttributes &attr) const;
151 
152   /// Equality operator
153   bool operator==(const ImageAttributes &attr) const;
154 
155   /// Inequality operator
156   bool operator!=(const ImageAttributes &attr) const;
157 
158   // ---------------------------------------------------------------------------
159   // Coordinate conversion
160 
161   /// Get Index from Lattice
162   int LatticeToIndex(int, int, int = 0, int = 0) const;
163 
164   /// Get Index from Lattice
165   void IndexToLattice(int, int *, int *, int * = NULL, int * = NULL) const;
166 
167   /// Get Index from Lattice
168   void IndexToLattice(int, int &, int &) const;
169 
170   /// Get Index from Lattice
171   void IndexToLattice(int, int &, int &, int &) const;
172 
173   /// Get Index from Lattice
174   void IndexToLattice(int, int &, int &, int &, int &) const;
175 
176   /// Get world coordinates (in mm) of lattice point
177   void IndexToWorld(int, double &, double &) const;
178 
179   /// Get world coordinates (in mm) of lattice point
180   void IndexToWorld(int, double &, double &, double &) const;
181 
182   /// Get world coordinates (in mm) of lattice point
183   void IndexToWorld(int, Point &) const;
184 
185   /// Get world coordinates (in mm) of lattice point
186   Point IndexToWorld(int) const;
187 
188   /// Convert lattice to world coordinate
189   void LatticeToWorld(Point &) const;
190 
191   /// Convert lattice to world coordinate
192   void LatticeToWorld(double &, double &, double &) const;
193 
194   /// Compute spatial world coordinates of lattice points (_x * _y * _z)
195   void LatticeToWorld(double *, double *, double *) const;
196 
197   /// Compute world coordinates of all lattice points (_x * _y * _z * _t)
198   void LatticeToWorld(double *, double *, double *, double *) const;
199 
200   /// Convert world to lattice coordinate
201   void WorldToLattice(double &, double &, double &) const;
202 
203   /// Convert lattice to world coordinate
204   void WorldToLattice(Point &) const;
205 
206   /// Convert lattice to time coordinate
207   double LatticeToTime(double) const;
208 
209   /// Convert time to lattice coordinate
210   double TimeToLattice(double) const;
211 
212   /// Put affine world coordinate transformation which is applied
213   /// after the image to world coordinate transformation derived from the
214   /// imaging geometry when mapping voxel indices to world coordinates.
215   /// This transformation can be the inverse of the affine transformation
216   /// obtained by an affine registration with this image as source.
217   ///
218   /// \param[in] m     Homogeneous transformation matrix.
219   /// \param[in] apply Whether to apply the translation, rotation, and
220   ///                  scaling directly to the image attributes and store
221   ///                  only the shearing (if any) as additional transformation.
222   void PutAffineMatrix(const Matrix &m, bool apply = false);
223 
224   /// Return transformation matrix for lattice to world coordinates
225   Matrix GetLatticeToWorldMatrix() const;
226 
227   /// Return transformation matrix for world to lattice coordinates
228   Matrix GetWorldToLatticeMatrix() const;
229 
230   /// Return orientation part of lattice to world coordinate transformation
231   Matrix GetLatticeToWorldOrientation() const;
232 
233   /// Return orientation part of lattice to world coordinate transformation
234   Matrix GetWorldToLatticeOrientation() const;
235 
236   /// Alias for GetLatticeToWorldMatrix
GetImageToWorldMatrixImageAttributes237   inline Matrix GetImageToWorldMatrix()      const { return GetLatticeToWorldMatrix(); }
238   /// Alias for GetWorldToLatticeMatrix
GetWorldToImageMatrixImageAttributes239   inline Matrix GetWorldToImageMatrix()      const { return GetWorldToLatticeMatrix(); }
240   /// Alias for GetLatticeToWorldOrientation
GetImageToWorldOrientationImageAttributes241   inline Matrix GetImageToWorldOrientation() const { return GetLatticeToWorldOrientation(); }
242   /// Alias for GetWorldToLatticeOrientation
GetWorldToImageOrientationImageAttributes243   inline Matrix GetWorldToImageOrientation() const { return GetWorldToLatticeOrientation(); }
244 
245   // ---------------------------------------------------------------------------
246   // Voxel index checks
247 
248   /// Whether voxel index is within finite image domain
249   bool IsInside(int) const;
250 
251   /// Whether voxel indices are within finite 2D image domain
252   bool IsInside(int, int) const;
253 
254   /// Whether voxel indices are within finite 3D image domain
255   bool IsInside(int, int, int) const;
256 
257   /// Whether voxel indices are within finite 4D image domain
258   bool IsInside(int, int, int, int) const;
259 
260   /// Whether voxel is index is outside finite image domain
261   bool IsOutside(int) const;
262 
263   /// Whether voxel indices are outside finite 4D image domain
264   bool IsOutside(int, int) const;
265 
266   /// Whether voxel indices are outside finite 4D image domain
267   bool IsOutside(int, int, int) const;
268 
269   /// Whether voxel indices are outside finite 4D image domain
270   bool IsOutside(int, int, int, int) const;
271 
272   /// Whether voxel index is at boundary of finite image domain
273   bool IsBoundary(int) const;
274 
275   /// Whether voxel indices are at boundary of finite 2D image domain
276   bool IsBoundary(int, int) const;
277 
278   /// Whether voxel indices are at boundary of finite 3D image domain
279   bool IsBoundary(int, int, int) const;
280 
281   /// Whether voxel indices are at boundary of finite 4D image domain
282   bool IsBoundary(int, int, int, int) const;
283 
284   // ---------------------------------------------------------------------------
285   // Output
286 
287   /// Image slice area in world space
288   double Area() const;
289 
290   /// Image volume in world space
291   double Volume() const;
292 
293   /// Amount of space occupied by the image in n-D world space (excluding time dimension)
294   double Space() const;
295 
296   /// Print attributes
297   void Print(ostream &, Indent = 0) const;
298 
299   /// Print attributes
300   void Print(Indent = 0) const;
301 
302 };
303 
304 ////////////////////////////////////////////////////////////////////////////////
305 // Inline definitions
306 ////////////////////////////////////////////////////////////////////////////////
307 
308 // -----------------------------------------------------------------------------
X()309 inline int ImageAttributes::X() const
310 {
311   return _x;
312 }
313 
314 // -----------------------------------------------------------------------------
Y()315 inline int ImageAttributes::Y() const
316 {
317   return _y;
318 }
319 
320 // -----------------------------------------------------------------------------
Z()321 inline int ImageAttributes::Z() const
322 {
323   return _z;
324 }
325 
326 // -----------------------------------------------------------------------------
T()327 inline int ImageAttributes::T() const
328 {
329   return _t;
330 }
331 
332 // -----------------------------------------------------------------------------
N(int i)333 inline int ImageAttributes::N(int i) const
334 {
335   if (i == 0) return _x;
336   if (i == 1) return _y;
337   if (i == 2) return _z;
338   if (i == 3) return _t;
339   return 0;
340 }
341 
342 // -----------------------------------------------------------------------------
Spacing(int i)343 inline double ImageAttributes::Spacing(int i) const
344 {
345   if (i == 0) return _dx;
346   if (i == 1) return _dy;
347   if (i == 2) return _dz;
348   if (i == 3) return _dt;
349   return 0.;
350 }
351 
352 // -----------------------------------------------------------------------------
XSpacing()353 inline double ImageAttributes::XSpacing() const
354 {
355   return _dx;
356 }
357 
358 // -----------------------------------------------------------------------------
YSpacing()359 inline double ImageAttributes::YSpacing() const
360 {
361   return _dy;
362 }
363 
364 // -----------------------------------------------------------------------------
ZSpacing()365 inline double ImageAttributes::ZSpacing() const
366 {
367   return _dz;
368 }
369 
370 // -----------------------------------------------------------------------------
TSpacing()371 inline double ImageAttributes::TSpacing() const
372 {
373   return _dt;
374 }
375 
376 // -----------------------------------------------------------------------------
XSize()377 inline double ImageAttributes::XSize() const
378 {
379   return _dx;
380 }
381 
382 // -----------------------------------------------------------------------------
YSize()383 inline double ImageAttributes::YSize() const
384 {
385   return _dy;
386 }
387 
388 // -----------------------------------------------------------------------------
ZSize()389 inline double ImageAttributes::ZSize() const
390 {
391   return _dz;
392 }
393 
394 // -----------------------------------------------------------------------------
TSize()395 inline double ImageAttributes::TSize() const
396 {
397   return _dt;
398 }
399 
400 // -----------------------------------------------------------------------------
401 inline ImageAttributes::operator bool() const
402 {
403   // Note: _dz may be zero for 2D images, _dt may even be negative!
404   return _x > 0 && _y > 0 && _z > 0 && _t > 0 && _dx > .0 && _dy > .0 && _dz >= .0;
405 }
406 
407 // -----------------------------------------------------------------------------
408 inline bool ImageAttributes::operator==(const ImageAttributes &attr) const
409 {
410   return EqualInSpace(attr) && EqualInTime(attr);
411 }
412 
413 // -----------------------------------------------------------------------------
414 inline bool ImageAttributes::operator!=(const ImageAttributes &attr) const
415 {
416   return !(*this == attr);
417 }
418 
419 // -----------------------------------------------------------------------------
LatticeToIndex(int i,int j,int k,int l)420 inline int ImageAttributes::LatticeToIndex(int i, int j, int k, int l) const
421 {
422   return i + _x * (j + _y * (k + _z * l));
423 }
424 
425 // -----------------------------------------------------------------------------
IndexToLattice(int index,int * i,int * j,int * k,int * l)426 inline void ImageAttributes::IndexToLattice(int index, int *i, int *j, int *k, int *l) const
427 {
428   int n = _x * _y * _z;
429   if (l) *l = index / n;
430   index = index % n;
431   n = _x * _y;
432   if (k) *k = index / n;
433   *j = index % n / _x;
434   *i = index % n % _x;
435 }
436 
437 // -----------------------------------------------------------------------------
NumberOfLatticePoints()438 inline int ImageAttributes::NumberOfLatticePoints() const
439 {
440   return _x * _y * _z * _t;
441 }
442 
443 // -----------------------------------------------------------------------------
NumberOfSpatialPoints()444 inline int ImageAttributes::NumberOfSpatialPoints() const
445 {
446   return _x * _y * _z;
447 }
448 
449 // -----------------------------------------------------------------------------
NumberOfPoints()450 inline int ImageAttributes::NumberOfPoints() const
451 {
452   return _dt ? NumberOfLatticePoints() : NumberOfSpatialPoints();
453 }
454 
455 // -----------------------------------------------------------------------------
IndexToLattice(int index,int & i,int & j)456 inline void ImageAttributes::IndexToLattice(int index, int &i, int &j) const
457 {
458   IndexToLattice(index, &i, &j);
459 }
460 
461 // -----------------------------------------------------------------------------
IndexToLattice(int index,int & i,int & j,int & k)462 inline void ImageAttributes::IndexToLattice(int index, int &i, int &j, int &k) const
463 {
464   IndexToLattice(index, &i, &j, &k);
465 }
466 
467 // -----------------------------------------------------------------------------
IndexToLattice(int index,int & i,int & j,int & k,int & l)468 inline void ImageAttributes::IndexToLattice(int index, int &i, int &j, int &k, int &l) const
469 {
470   IndexToLattice(index, &i, &j, &k, &l);
471 }
472 
473 // -----------------------------------------------------------------------------
IndexToWorld(int idx,double & x,double & y)474 inline void ImageAttributes::IndexToWorld(int idx, double &x, double &y) const
475 {
476   int i, j, k;
477   IndexToLattice(idx, i, j, k);
478   x = i, y = j;
479   double z = k;
480   LatticeToWorld(x, y, z);
481 }
482 
483 // -----------------------------------------------------------------------------
IndexToWorld(int idx,double & x,double & y,double & z)484 inline void ImageAttributes::IndexToWorld(int idx, double &x, double &y, double &z) const
485 {
486   int i, j, k;
487   IndexToLattice(idx, i, j, k);
488   x = i, y = j, z = k;
489   LatticeToWorld(x, y, z);
490 }
491 
492 // -----------------------------------------------------------------------------
IndexToWorld(int idx,Point & p)493 inline void ImageAttributes::IndexToWorld(int idx, Point &p) const
494 {
495   IndexToWorld(idx, p._x, p._y, p._z);
496 }
497 
498 // -----------------------------------------------------------------------------
IndexToWorld(int idx)499 inline Point ImageAttributes::IndexToWorld(int idx) const
500 {
501   Point p;
502   IndexToWorld(idx, p);
503   return p;
504 }
505 
506 // -----------------------------------------------------------------------------
LatticeToWorld(double & x,double & y,double & z)507 inline void ImageAttributes::LatticeToWorld(double &x, double &y, double &z) const
508 {
509   Transform(_i2w ? *_i2w : GetImageToWorldMatrix(), x, y, z);
510 }
511 
512 // -----------------------------------------------------------------------------
LatticeToWorld(Point & p)513 inline void ImageAttributes::LatticeToWorld(Point &p) const
514 {
515   LatticeToWorld(p._x, p._y, p._z);
516 }
517 
518 // -----------------------------------------------------------------------------
LatticeToTime(double t)519 inline double ImageAttributes::LatticeToTime(double t) const
520 {
521   return _torigin + t * _dt;
522 }
523 
524 // -----------------------------------------------------------------------------
WorldToLattice(double & x,double & y,double & z)525 inline void ImageAttributes::WorldToLattice(double &x, double &y, double &z) const
526 {
527   Transform(_w2i ? *_w2i : GetWorldToImageMatrix(), x, y, z);
528 }
529 
530 // -----------------------------------------------------------------------------
WorldToLattice(Point & p)531 inline void ImageAttributes::WorldToLattice(Point &p) const
532 {
533   WorldToLattice(p._x, p._y, p._z);
534 }
535 
536 // -----------------------------------------------------------------------------
TimeToLattice(double t)537 inline double ImageAttributes::TimeToLattice(double t) const
538 {
539   return (_dt ? ((t - _torigin) / _dt) : .0);
540 }
541 
542 // -----------------------------------------------------------------------------
GetLatticeToWorldOrientation()543 inline Matrix ImageAttributes::GetLatticeToWorldOrientation() const
544 {
545   Matrix R(3, 3);
546   R(0, 0) = _xaxis[0];
547   R(1, 0) = _xaxis[1];
548   R(2, 0) = _xaxis[2];
549   R(0, 1) = _yaxis[0];
550   R(1, 1) = _yaxis[1];
551   R(2, 1) = _yaxis[2];
552   R(0, 2) = _zaxis[0];
553   R(1, 2) = _zaxis[1];
554   R(2, 2) = _zaxis[2];
555   return R;
556 }
557 
558 // -----------------------------------------------------------------------------
GetWorldToLatticeOrientation()559 inline Matrix ImageAttributes::GetWorldToLatticeOrientation() const
560 {
561   Matrix R(3, 3);
562   R(0, 0) = _xaxis[0];
563   R(0, 1) = _xaxis[1];
564   R(0, 2) = _xaxis[2];
565   R(1, 0) = _yaxis[0];
566   R(1, 1) = _yaxis[1];
567   R(1, 2) = _yaxis[2];
568   R(2, 0) = _zaxis[0];
569   R(2, 1) = _zaxis[1];
570   R(2, 2) = _zaxis[2];
571   return R;
572 }
573 
574 // -----------------------------------------------------------------------------
IsInside(int idx)575 inline bool ImageAttributes::IsInside(int idx) const
576 {
577   return (0 <= idx && idx < _x * _y * _z * _t);
578 }
579 
580 // -----------------------------------------------------------------------------
IsInside(int i,int j)581 inline bool ImageAttributes::IsInside(int i, int j) const
582 {
583   return (0 <= i && i < _x) && (0 <= j && j < _y);
584 }
585 
586 // -----------------------------------------------------------------------------
IsInside(int i,int j,int k)587 inline bool ImageAttributes::IsInside(int i, int j, int k) const
588 {
589   return IsInside(i, j) && (0 <= k && k < _z);
590 }
591 
592 // -----------------------------------------------------------------------------
IsInside(int i,int j,int k,int l)593 inline bool ImageAttributes::IsInside(int i, int j, int k, int l) const
594 {
595   return IsInside(i, j, k) && (0 <= l && l < _t);
596 }
597 
598 // -----------------------------------------------------------------------------
IsOutside(int idx)599 inline bool ImageAttributes::IsOutside(int idx) const
600 {
601   return !IsInside(idx);
602 }
603 
604 // -----------------------------------------------------------------------------
IsOutside(int i,int j)605 inline bool ImageAttributes::IsOutside(int i, int j) const
606 {
607   return !IsInside(i, j);
608 }
609 
610 // -----------------------------------------------------------------------------
IsOutside(int i,int j,int k)611 inline bool ImageAttributes::IsOutside(int i, int j, int k) const
612 {
613   return !IsInside(i, j, k);
614 }
615 
616 // -----------------------------------------------------------------------------
IsOutside(int i,int j,int k,int l)617 inline bool ImageAttributes::IsOutside(int i, int j, int k, int l) const
618 {
619   return !IsInside(i, j, k, l);
620 }
621 
622 // -----------------------------------------------------------------------------
IsBoundary(int i,int j)623 inline bool ImageAttributes::IsBoundary(int i, int j) const
624 {
625   return (i == 0 || i == _x - 1) || (j == 0 || j == _y - 1);
626 }
627 
628 // -----------------------------------------------------------------------------
IsBoundary(int i,int j,int k)629 inline bool ImageAttributes::IsBoundary(int i, int j, int k) const
630 {
631   return IsBoundary(i, j) || (k == 0 || k == _z - 1);
632 }
633 
634 // -----------------------------------------------------------------------------
IsBoundary(int i,int j,int k,int l)635 inline bool ImageAttributes::IsBoundary(int i, int j, int k, int l) const
636 {
637   return IsBoundary(i, j, k) || (l == 0 || l == _t - 1);
638 }
639 
640 // -----------------------------------------------------------------------------
IsBoundary(int idx)641 inline bool ImageAttributes::IsBoundary(int idx) const
642 {
643   if (!IsInside(idx)) return false;
644   int i, j, k, l;
645   IndexToLattice(idx, i, j, k, l);
646   if (_t == 1) {
647     if (_z == 1) return IsBoundary(i, j);
648     else         return IsBoundary(i, j, k);
649   }
650   return IsBoundary(i, j, k, l);
651 }
652 
653 ////////////////////////////////////////////////////////////////////////////////
654 // Image domain helpers
655 ////////////////////////////////////////////////////////////////////////////////
656 
657 /// Ensure that image domain has orthogonal basis vectors, i.e., no additional
658 /// affine transformation which contains any shearing
659 ///
660 /// \returns Image grid which fully contains the input image domain but without
661 ///          additional affine transformation (_smat is identity matrix).
662 ///
663 /// \remarks The returned image domain need not be an axis-aligned bounding box!
664 ///          When the Geometric Tools Engine (GTEngine) library is available,
665 ///          the minimum-volume bounding box is returned.
666 ImageAttributes OrthogonalFieldOfView(const ImageAttributes &);
667 
668 /// This method implements a method to find a common image grid which fully
669 /// contains all given image grids in world space
670 ///
671 /// The voxel size of the resulting grid corresponds to the average voxel size
672 /// of the input images. The orientation and origin are chosen such that the
673 /// resulting image domain overlaps all input images in world space with a near
674 /// to minimal image volume. The current implementation only uses an heuristic
675 /// approach to find such minimal oriented bounding box of the input domains.
676 /// When the Geometric Tools Engine (GTEngine) library is available, the
677 /// minimum-volume bounding box is returned.
678 ///
679 /// Additional information regarding algorithms to find the optimal
680 /// minimum-volume oriented bounding box (OBB) can be found at:
681 /// - http://stackoverflow.com/questions/7282805/where-can-i-find-a-c-c-implementation-of-the-minimum-bound-box-algorithm
682 /// - http://www.geometrictools.com/LibMathematics/Containment/Containment.html
683 /// - http://link.springer.com/article/10.1007%2FBF00991005?LI=true
684 ///
685 /// \note The final overall image grid computed by this function must be
686 ///       independent of the order in which the input attributes are given
687 ///       in the input list. Otherwise an inverse consistent registration
688 ///       might still depend on the order of the input images.
689 ImageAttributes OverallFieldOfView(const Array<ImageAttributes> &);
690 
691 
692 } // namespace mirtk
693 
694 #endif // MIRTK_ImageAttributes_H
695