gVirtualXRay  2.0.10
VirtualX-RayImagingLibraryonGPU
Image.inl
Go to the documentation of this file.
1 /*
2 
3 Copyright (c) 2016-2023, Dr Franck P. Vidal, Bangor University, All rights reserved.
4 Copyright (c) 2023-present, Prof Franck P. Vidal (franck.vidal@stfc.ac.uk),
5 UK Research and Innovation, All rights reserved.
6 
7 
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10 
11 1. Redistributions of source code must retain the above copyright notice,
12 this list of conditions and the following disclaimer.
13 
14 2. Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
17 
18 3. Neither the name of Bangor University, UK Research and Innovation nor the
19 names of its contributors may be used to endorse or promote products derived
20 from this software without specific prior written permission.
21 
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 */
34 
35 
61 //******************************************************************************
62 // Define
63 //******************************************************************************
64 #define LINE_SIZE 4096
65 
66 
67 //******************************************************************************
68 // Include
69 //******************************************************************************
70 #include <type_traits>
71 #include <sstream> // Header file for stringstream
72 #include <fstream> // Header file for filestream
73 #include <iomanip>
74 #include <algorithm> // Header file for min/max/fill
75 #include <cmath> // Header file for abs
76 #include <numeric> // Header file for accumulate
77 #include <limits> // Numeric limits per types
78 #include <vector>
79 #include <iostream>
80 #include <typeinfo>
81 
82 // C++ 11
83 #if __cplusplus > 199711L
84 #include <utility>
85 #endif
86 
87 #include <zlib.h>
88 
89 #ifdef HAS_ITK
90 #include <itkImage.h>
91 #include <itkImageSeriesReader.h>
92 #include <itkImageFileReader.h>
93 #include <itkImageFileWriter.h>
94 #include <itkNumericSeriesFileNames.h>
95 #include "itkGDCMImageIO.h"
96 #include "itkGDCMSeriesFileNames.h"
97 #include "itkImportImageFilter.h"
98 #include "itkDiscreteGaussianImageFilter.h"
99 #include "itkAntiAliasBinaryImageFilter.h"
100 #include "itkBinaryBallStructuringElement.h"
101 #include <itkBinaryMorphologicalOpeningImageFilter.h>
102 #include <itkBinaryMorphologicalClosingImageFilter.h>
103 #include <itkRecursiveGaussianImageFilter.h>
104 #include <itkResampleImageFilter.h>
105 #include <itkIdentityTransform.h>
106 #include <itkBSplineInterpolateImageFunction.h>
107 #include "itkMesh.h"
108 #include <itkRescaleIntensityImageFilter.h>
109 //#include "itkBinaryThresholdImageFilter.h"
110 #include "itkBinaryMask3DMeshSource.h"
111 //#include "itkMeshFileWriter.h"
112 #endif
113 
114 #ifdef HAS_GDCM
115 #include <gdcmGlobal.h>
116 
117 #include "gdcmImageReader.h"
118 #include "gdcmSequenceOfFragments.h"
119 #include "gdcmSystem.h"
120 #include "gdcmImageWriter.h"
121 #include "gdcmPixmapWriter.h"
122 #include <gdcmRescaler.h>
123 
124 #include <gdcmImage.h>
125 #include <gdcmBitmap.h>
126 #include <gdcmImageWriter.h>
127 #include <gdcmPixelFormat.h>
128 #include <gdcmPhotometricInterpretation.h>
129 #include <gdcmImageChangeTransferSyntax.h>
130 #endif
131 
132 #ifdef HAS_OPENMP
133 #include <omp.h>
134 #endif
135 
136 #ifdef HAS_TIFF
137 #include <tiffio.h>
138 #endif
139 
140 #ifndef __ConstantValues_h
142 #endif
143 
144 #ifndef __Utilities_h
145 #include "gVirtualXRay/Utilities.h"
146 #endif
147 
148 #ifndef __Exception_h
149 #include "gVirtualXRay/Exception.h"
150 #endif
151 
152 #ifndef __OutOfBoundsException_h
154 #endif
155 
156 #ifndef __OutOfMemoryException_h
158 #endif
159 
160 #ifndef __FileDoesNotExistException_h
162 #endif
163 
164 #ifndef __PolygonMesh_h
166 #endif
167 
168 #ifndef __Logger_h
169 #include "gVirtualXRay/Logger.h"
170 #endif
171 
172 #ifndef __Shader_h
173 #include "gVirtualXRay/Shader.h"
174 #endif
175 
176 
177 //******************************************************************************
178 // namespace
179 //******************************************************************************
180 namespace gVirtualXRay {
181 
182 
183 //-------------------------------------
184 template<typename T> Image<T>::Image():
185 //-------------------------------------
186  m_width(0),
187  m_height(0),
188  m_number_of_slices(0),
189  m_spacing(1, 1, 1),
190  m_p_image(0),
191  m_texture_id(0)
192 //-------------------------------------
193 {}
194 
195 
196 //----------------------------------------------
197 template<typename T> Image<T>::Image(const Image<T>& anImage):
198 //----------------------------------------------
199  m_width(anImage.m_width),
200  m_height(anImage.m_height),
201  m_number_of_slices(anImage.m_number_of_slices),
202  m_spacing(anImage.m_spacing),
203  m_p_image(new T[m_width * m_height * m_number_of_slices]),
204  m_texture_id(0)
205 //----------------------------------------------
206 {
207  // Out of memory
208  if (m_width && m_height && m_number_of_slices && !m_p_image)
209  {
210  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
211  }
212 
213  // Copy the data
214  std::copy(anImage.m_p_image, anImage.m_p_image + m_width * m_height * m_number_of_slices, m_p_image);
215 }
216 
217 
218 //----------------------------------------------------------
219 template<typename T> Image<T>::Image(const char* aFileName):
220 //----------------------------------------------------------
221  m_width(0),
222  m_height(0),
223  m_number_of_slices(0),
224  m_spacing(1, 1, 1),
225  m_p_image(0),
226  m_texture_id(0)
227 //----------------------------------------------------------
228 {
229  load(aFileName);
230 }
231 
232 
233 //-----------------------------------------------------------------
234 template<typename T> Image<T>::Image(const std::string& aFileName):
235 //-----------------------------------------------------------------
236  m_width(0),
237  m_height(0),
238  m_number_of_slices(0),
239  m_spacing(1, 1, 1),
240  m_p_image(0),
241  m_texture_id(0)
242 //-----------------------------------------------------------------
243 {
244  load(aFileName);
245 }
246 
247 
248 //----------------------------------------------
249 template<typename T> Image<T>::Image(const T* apData,
250  unsigned int aWidth,
251  unsigned int aHeight,
252  unsigned int aNumberOfSlices,
253  const VEC3& aVoxelSize):
254 //----------------------------------------------
255  m_width(aWidth),
256  m_height(aHeight),
257  m_number_of_slices(aNumberOfSlices),
258  m_spacing(aVoxelSize),
259  m_p_image(new T[m_width * m_height * m_number_of_slices]),
260  m_texture_id(0)
261 //----------------------------------------------
262 {
263  // Out of memeory
264  if (m_width && m_height && !m_p_image)
265  {
266  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
267  }
268 
269  // Copy the data
270  std::copy(apData, apData + m_width * m_height * m_number_of_slices, m_p_image);
271 }
272 
273 
274 //----------------------------------------------
275 template<typename T> Image<T>::Image(unsigned int aWidth,
276  unsigned int aHeight,
277  unsigned int aNumberOfSlices,
278  T aDefaultValue,
279  const VEC3& aVoxelSize):
280 //----------------------------------------------
281  m_width(aWidth),
282  m_height(aHeight),
283  m_number_of_slices(aNumberOfSlices),
284  m_spacing(aVoxelSize),
285  m_p_image(new T[m_width * m_height * m_number_of_slices]),
286  m_texture_id(0)
287 //----------------------------------------------
288 {
289  // Out of memeory
290  if (m_width && m_height && m_number_of_slices && !m_p_image)
291  {
292  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
293  }
294 
295  // Initialise the data
296  std::fill_n(m_p_image, m_width * m_height * m_number_of_slices, aDefaultValue);
297 }
298 
299 
300 //-------------
301 template<typename T> Image<T>::~Image()
302 //-------------
303 {
304  // Release memory
305  destroy();
306 }
307 
308 
309 //---------------------------------------------------------------------------
310 template<typename T> Image<T> gauss2D(unsigned int aSize, double aSigmaValue)
311 //---------------------------------------------------------------------------
312 {
313  Image<T> kernel(aSize, aSize, 1, 0.0);
314 
315  double sum = 0.0;
316 
317  double two_sigma_square = 2.0 * aSigmaValue * aSigmaValue;
318  int x0 = aSize/2;
319  int y0 = aSize/2;
320 
321  for (int i = 0; i < aSize; i++)
322  {
323  for (int j = 0; j < aSize; j++)
324  {
325  kernel(i, j, 0) = exp(-(pow(i - x0, 2) + pow(j - y0, 2)) / two_sigma_square);
326  sum += kernel(i, j, 0);
327  }
328  }
329 
330  for (int i = 0; i < aSize; i++)
331  {
332  for (int j = 0; j < aSize; j++)
333  {
334  kernel(i, j, 0) /= sum;
335  }
336  }
337 
338  return kernel;
339 }
340 
341 
342 //--------------------------------------------------------------------
343 template<typename T> void Image<T>::makeBlank(const T& aDefaultColour)
344 //--------------------------------------------------------------------
345 {
346  // Initialise the data
347  std::fill_n(m_p_image, m_width * m_height * m_number_of_slices, aDefaultColour);
348 }
349 
350 //------------------------------------------------------------------
351 template<typename T> void Image<T>::loadASCII(const char* aFileName)
352 //------------------------------------------------------------------
353 {
354  // Open the file
355  std::ifstream input_file (aFileName);
356 
357  // The file is not open
358  if (!input_file.is_open())
359  {
360  std::string error_message("The file (");
361  error_message += aFileName;
362  error_message += ") does not exist";
363 
364  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
365  }
366 
367  // Load the data into a vector
368  std::vector<T> p_data;
369  std::string line;
370  int number_of_rows(0);
371  int number_of_columns(0);
372 
373  // Read evely line
374  while (std::getline(input_file, line))
375  {
376  if (line.size())
377  {
378  if (line[0] != '#')
379  {
380  number_of_columns = 0;
381  T intensity;
382  std::stringstream line_parser;
383  line_parser << line;
384  while (line_parser >> intensity)
385  {
386  p_data.push_back(intensity);
387  ++number_of_columns;
388  }
389  ++number_of_rows;
390  }
391  }
392  }
393 
394  // Wrong number of pixels
395  if (number_of_rows * number_of_columns != p_data.size())
396  {
397  std::string error_message("The file (");
398  error_message += aFileName;
399  error_message += ") is invalid";
400 
401  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
402  }
403 
404  // Release the memory
405  destroy();
406 
407  // Allocate memory for file content
408  m_width = number_of_columns;
409  m_height = number_of_rows;
410  m_number_of_slices = 1;
411  m_p_image = new T[m_width * m_height];
412 
413  // Copy the data
414  std::copy(p_data.begin(), p_data.end(), m_p_image);
415 }
416 
417 
418 //-------------------------------------------------------------------------
419 template<typename T> void Image<T>::loadASCII(const std::string& aFileName)
420 //-------------------------------------------------------------------------
421 {
422  loadASCII(aFileName.data());
423 }
424 
425 
426 //-------------------------------------------------------------------
427 template<typename T> void Image<T>::loadASCII(const char* aFileName,
428  unsigned int aWidth,
429  unsigned int aHeight,
430  unsigned int aDepth,
431  const VEC3& aVoxelSize)
432 //-------------------------------------------------------------------
433 {
434  // Open the file
435  std::ifstream input_file(aFileName);
436 
437  // Release the memory
438  destroy();
439 
440  // The file is not open
441  if (!input_file.is_open())
442  {
443  std::string error_message("The file (");
444  error_message += aFileName;
445  error_message += ") does not exist";
446 
447  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
448  }
449 
450  // Allocate memory for file content
451  m_width = aWidth;
452  m_height = aHeight;
453  m_number_of_slices = aDepth;
454  unsigned int volume_size(m_width * m_height * m_number_of_slices);
455  m_p_image = new T[volume_size];
456  m_spacing = aVoxelSize;
457 
458  // Read evely line
459  unsigned int voxels(0);
460  T intensity;
461 
462  while (input_file >> intensity)
463  {
464  // Wrong number of pixels
465  if (voxels >= volume_size)
466  {
467  /*std::string error_message("The file (");
468  error_message += aFileName;
469  error_message += ") is invalid (too many voxels)";
470 
471  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);*/
472  }
473  else
474  {
475  m_p_image[voxels] = intensity;
476 
477  }
478  ++voxels;
479  }
480 
481  // Wrong number of pixels
482  /*if (voxels < volume_size)
483  {
484  std::string error_message("The file (");
485  error_message += aFileName;
486  error_message += ") is invalid (not enough voxels)";
487 
488  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
489  }*/
490 }
491 
492 
493 //-------------------------------------------------------------------------
494 template<typename T> void Image<T>::loadASCII(const std::string& aFileName,
495  unsigned int aWidth,
496  unsigned int aHeight,
497  unsigned int aDepth,
498  const VEC3& aVoxelSize)
499 //-------------------------------------------------------------------------
500 {
501  loadASCII(aFileName.data(), aWidth,
502  aHeight,
503  aDepth,
504  aVoxelSize);
505 }
506 
507 
508 //-----------------------------------------------------------------------
509 template<typename T> Image<T> Image<T>::getROI(unsigned int i,
510  unsigned int j,
511  unsigned int k,
512  unsigned int aWidth,
513  unsigned int aHeight,
514  unsigned int aDepth) const
515 //-----------------------------------------------------------------------
516 {
517  // Create a black image
518  Image<T> roi(aWidth, aHeight, aDepth);
519  roi.m_spacing = m_spacing;
520 
521  // Process every slice of the ROI
522 #ifdef NDEBUG
523 #ifdef WIN32
524  #pragma omp parallel for
525 #else
526  #pragma omp parallel for collapse(3)
527 #endif
528 #endif
529  for (int z = 0; z < aDepth; ++z)
530  {
531  // Process every row of the ROI
532  for (int y = 0; y < aHeight; ++y)
533  {
534  // Process every column of the ROI
535  for (int x = 0; x < aWidth; ++x)
536  {
537  unsigned int index_i(x + i);
538  unsigned int index_j(y + j);
539  unsigned int index_k(z + k);
540 
541  // The pixel index is not valid
542  if ((index_i >= m_width) ||
543  (index_j >= m_height) ||
544  (index_k >= m_number_of_slices))
545  {
546  throw OutOfBoundsException(__FILE__, __FUNCTION__, __LINE__);
547  }
548 
549  // Get the pixel intensity from the current instance
550  T intensity(getPixel(index_i, index_j, index_k));
551 
552  // Set the pixel of the ROI
553  roi.setPixel(x, y, z, intensity);
554  }
555  }
556  }
557 
558  return (roi);
559 }
560 
561 
562 //----------------------------------------------------------
563 template<typename T> void Image<T>::setPixel(unsigned int i,
564  unsigned int j,
565  unsigned int k,
566  T aValue)
567 //----------------------------------------------------------
568 {
569  // The pixel index is not valid
570  if ((i >= m_width) ||
571  (j >= m_height) ||
572  (k >= m_number_of_slices))
573  {
574  throw OutOfBoundsException(__FILE__, __FUNCTION__, __LINE__);
575  }
576 
577  m_p_image[k * m_width * m_height + j * m_width + i] = aValue;
578 }
579 
580 
581 //--------------------------------------------------------
582 template<typename T> T& Image<T>::getPixel(unsigned int i,
583  unsigned int j,
584  unsigned int k)
585 //--------------------------------------------------------
586 {
587  // The pixel index is not valid
588  if ((i >= m_width) ||
589  (j >= m_height) ||
590  (k >= m_number_of_slices))
591  {
592  throw OutOfBoundsException(__FILE__, __FUNCTION__, __LINE__);
593  }
594 
595  return (m_p_image[k * m_width * m_height + j * m_width + i]);
596 }
597 
598 
599 //--------------------------------------------------------------------
600 template<typename T> const T& Image<T>::getPixel(unsigned int i,
601  unsigned int j,
602  unsigned int k) const
603 //--------------------------------------------------------------------
604 {
605  // The pixel index is not valid
606  if ((i >= m_width) ||
607  (j >= m_height) ||
608  (k >= m_number_of_slices))
609  {
610  throw OutOfBoundsException(__FILE__, __FUNCTION__, __LINE__);
611  }
612 
613  return (m_p_image[k * m_width * m_height + j * m_width + i]);
614 }
615 
616 
617 //-------------------------------------------------------------
618 template<typename T> T& Image<T>::getPixelValue(unsigned int i,
619  unsigned int j,
620  unsigned int k)
621 //-------------------------------------------------------------
622 {
623  return (getPixel(i, j, k));
624 }
625 
626 
627 //-------------------------------------------------------------------------
628 template<typename T> const T& Image<T>::getPixelValue(unsigned int i,
629  unsigned int j,
630  unsigned int k) const
631 //-------------------------------------------------------------------------
632 {
633  return (getPixel(i, j, k));
634 }
635 
636 
637 //------------------------------------------------------------------------
638 template<typename T> VEC3 Image<T>::getPixelPosition(unsigned int i,
639  unsigned int j,
640  unsigned int k) const
641 //------------------------------------------------------------------------
642 {
643  return (VEC3(m_spacing[0] * i, m_spacing[1] * j, m_spacing[2] * k));
644 }
645 
646 
647 //------------------------------------------------------------
648 template<typename T> T& Image<T>::operator[](unsigned anIndex)
649 //------------------------------------------------------------
650 {
651  if (anIndex >= m_width * m_height * m_number_of_slices)
652  {
653  throw OutOfBoundsException(__FILE__, __FUNCTION__, __LINE__);
654  }
655 
656 return (m_p_image[anIndex]);
657 }
658 
659 
660 //------------------------------------------------------------------------
661 template<typename T> const T& Image<T>::operator[](unsigned anIndex) const
662 //------------------------------------------------------------------------
663 {
664  if (anIndex >= m_width * m_height * m_number_of_slices)
665  {
666  throw OutOfBoundsException(__FILE__, __FUNCTION__, __LINE__);
667  }
668 
669  return (m_p_image[anIndex]);
670 }
671 
672 
673 //----------------------------------------------------------
674 template<typename T> T& Image<T>::operator()(unsigned int i,
675  unsigned int j,
676  unsigned int k)
677 //----------------------------------------------------------
678 {
679  return (getPixel(i, j, k));
680 }
681 
682 
683 //----------------------------------------------------------------------
684 template<typename T> const T& Image<T>::operator()(unsigned int i,
685  unsigned int j,
686  unsigned int k) const
687 //----------------------------------------------------------------------
688 {
689  return (getPixel(i, j, k));
690 }
691 
692 
693 //-------------------------------------------
694 template<typename T> void Image<T>::destroy()
695 //-------------------------------------------
696 {
697  // Memory has been dynamically allocated
698  if (m_p_image)
699  {
700  // Release the memory
701  delete [] m_p_image;
702 
703  // Make sure the pointer is reset to NULL
704  m_p_image = 0;
705  }
706 
707  // There is no pixel in the image
708  m_width = 0;
709  m_height = 0;
710  m_number_of_slices = 0;
711 
712  destroyOpenGLTexture();
713 }
714 
715 
716 //--------------------------------------------
717 template<typename T> T* Image<T>::getRawData()
718 //--------------------------------------------
719 {
720  return (m_p_image);
721 }
722 
723 
724 //--------------------------------------------------------
725 template<typename T> const T* Image<T>::getRawData() const
726 //--------------------------------------------------------
727 {
728  return (m_p_image);
729 }
730 
731 
732 //-------------------------------------------
733 template<typename T> Image<T>& Image<T>::operator=(const Image<T>& anImage)
734 //-------------------------------------------
735 {
736  // The images different
737  if (this != &anImage)
738  {
739  // The total number of pixels/voxels has changed
740  if (m_width * m_height * m_number_of_slices != anImage.m_width * anImage.m_height * anImage.m_number_of_slices)
741  {
742  // Release memory
743  destroy();
744 
745  // Allocate the memory
746  m_p_image = new T[anImage.m_width * anImage.m_height * anImage.m_number_of_slices];
747 
748  // Out of memeory
749  if (m_width && m_height && m_number_of_slices && !m_p_image)
750  {
751  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
752  }
753  }
754 
755  // Copy the image properites
756  m_width = anImage.m_width;
757  m_height = anImage.m_height;
758  m_number_of_slices = anImage.m_number_of_slices;
759  m_spacing = anImage.m_spacing;
760 
761  // Copy the data
762  std::copy(anImage.m_p_image, anImage.m_p_image + m_width * m_height * m_number_of_slices, m_p_image);
763  }
764 
765  // Return the instance
766  return (*this);
767 }
768 
769 
770 template<typename T> Image<T>& Image<T>::operator=(const T* apImage)
771 {
772  // Release the texture if needed
773  destroyOpenGLTexture();
774 
775  // Copy the data
776  std::copy(apImage, apImage + m_width * m_height * m_number_of_slices, m_p_image);
777 
778  // Return the instance
779  return (*this);
780 }
781 
782 
783 //----------------------------------
784 template<typename T> unsigned int Image<T>::getWidth() const
785 //----------------------------------
786 {
787  return (m_width);
788 }
789 
790 
791 //-----------------------------------
792 template<typename T> unsigned int Image<T>::getHeight() const
793 //-----------------------------------
794 {
795  return (m_height);
796 }
797 
798 
799 //----------------------------------------------------------
800 template<typename T> unsigned int Image<T>::getDepth() const
801 //----------------------------------------------------------
802 {
803  return (m_number_of_slices);
804 }
805 
806 
807 //--------------------------------------------------------------------
808 template<typename T> void Image<T>::setSpacing(const VEC3& aPixelSize)
809 //--------------------------------------------------------------------
810 {
811  m_spacing = aPixelSize;
812 }
813 
814 
815 //------------------------------------------------------------------------
816 template<typename T> void Image<T>::setSpacing(const double& aPixelWidth,
817  const double& aPixelHeight,
818  const double& aPixelDepth)
819 //------------------------------------------------------------------------
820 {
821  m_spacing[0] = aPixelWidth;
822  m_spacing[1] = aPixelHeight;
823  m_spacing[2] = aPixelDepth;
824 }
825 
826 
827 //-----------------------------------------------------------
828 template<typename T> const VEC3& Image<T>::getSpacing() const
829 //-----------------------------------------------------------
830 {
831  return (m_spacing);
832 }
833 
834 
835 //--------------------------------------------------
836 template<typename T> T Image<T>::getMinValue() const
837 //--------------------------------------------------
838 {
839  // The image is empty
840  if (!m_p_image)
841  {
842  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Empty image");
843  }
844 
845  return (*std::min_element(&m_p_image[0], &m_p_image[m_width * m_height * m_number_of_slices]));
846 }
847 
848 
849 //--------------------------------------------------
850 template<typename T> T Image<T>::getMaxValue() const
851 //--------------------------------------------------
852 {
853  // The image is empty
854  if (!m_p_image)
855  {
856  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Empty image");
857  }
858 
859  return (*std::max_element(&m_p_image[0], &m_p_image[m_width * m_height * m_number_of_slices]));
860 }
861 
862 
863 //-----------------------------------------------------------------------
864 template<typename T> T Image<T>::getMinValue(unsigned int aSliceID) const
865 //-----------------------------------------------------------------------
866 {
867  // The image is empty
868  if (!m_p_image)
869  {
870  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Empty image");
871  }
872 
873  return (*std::min_element(&m_p_image[aSliceID * m_width * m_height], &m_p_image[(aSliceID + 1) * m_width * m_height]));
874 }
875 
876 
877 //-----------------------------------------------------------------------
878 template<typename T> T Image<T>::getMaxValue(unsigned int aSliceID) const
879 //-----------------------------------------------------------------------
880 {
881  // The image is empty
882  if (!m_p_image)
883  {
884  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Empty image");
885  }
886 
887  return (*std::max_element(&m_p_image[aSliceID * m_width * m_height], &m_p_image[(aSliceID + 1) * m_width * m_height]));
888 }
889 
890 
891 //--------------------------------------------------------------------------------
892 template<typename T> Image<T> Image<T>::shiftScaleFilter(double aShiftValue,
893  double aScaleValue,
894  bool aRunOnGPUFlag) const
895 //--------------------------------------------------------------------------------
896 {
897  // Copy the instance into a temporary variable
898  Image<T> temp(*this);
899  temp.m_spacing = m_spacing;
900 
901  bool use_gpu = false;
902 
903  if (aRunOnGPUFlag)
904  {
905  if (useOpenGLCompute())
906  {
907  const_cast<Image<T>&>(*this).transferHostToDevice();
908  const_cast<Image<T>&>(temp).transferHostToDevice();
909 
910  if (getTextureId() && temp.getTextureId())
911  use_gpu = true;
912  }
913  else
914  {
915  LOGGER.logWarning("GPU computation requested but not supported in the function as follows:") << std::endl <<
916  "\tFile: " << __FILE__ << std::endl <<
917  "\tFunction: " << __FUNCTION__ << std::endl <<
918  "\tLine: " << __LINE__ << std::endl;
919  }
920  }
921 
922  if (use_gpu)
923  {
924  // Instantiate a compute shader
925  Shader dummy_shader;
926 
927  // Compute shader
928  std::string g_compute_shader;
929  g_compute_shader += "#version 430\n";
930  g_compute_shader += "\n";
931  g_compute_shader += "layout(local_size_x=1, local_size_y=1, local_size_z=1) in;\n";
932  g_compute_shader += "layout(" + getShaderTypeID(typeid(T)) + ", binding=0) uniform " + getShaderImageType(typeid(T)) + " input_output_image;\n";
933  g_compute_shader += "\n";
934  g_compute_shader += "layout(location=0) uniform float shift;\n";
935  g_compute_shader += "layout(location=1) uniform float scale;\n";
936  g_compute_shader += "\n";
937  g_compute_shader += "void main(void)\n";
938  g_compute_shader += "{\n";
939  g_compute_shader += " ivec3 pixel_coordinates = ivec3(gl_GlobalInvocationID.xyz);\n";
940  g_compute_shader += " float intensity = imageLoad(input_output_image, pixel_coordinates).r;\n";
941  g_compute_shader += " " + getShaderPixelType(typeid(T)) + " pixel_value = (shift + intensity) * scale;\n";
942  g_compute_shader += "\n";
943  g_compute_shader += " imageStore(input_output_image, pixel_coordinates, " + getShaderRGBAPixelType(typeid(T)) + "(pixel_value, 0, 0, 0));\n";
944  g_compute_shader += "}\n";
945 
946  LOGGER.logNow(g_compute_shader) << std::endl;
947 
948  try
949  {
950  // Give a text ID to the compute shader, it can be useful when debuging the shader
951  dummy_shader.setLabel("g_compute_shader");
952 
953  // Load the source code of the shader onto the GPU
954  dummy_shader.loadSource(static_cast<const GLchar*>(g_compute_shader.c_str()));
955 
956  // Check that the compute shader is valid
957  if (dummy_shader.isValid())
958  {
959  // Bind the texture
960  glBindImageTexture(0, temp.getTextureId(), 0, GL_FALSE, 0, GL_READ_WRITE, getShaderOpenGLType(typeid(T)));
961  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
962 
963  // Enable the compute shader
964  dummy_shader.enable();
965  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
966 
967  // Load uniform variables to the GPU
968  GLint program_handle(0);
969  glGetIntegerv(GL_CURRENT_PROGRAM, &program_handle);
970  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
971 
972  GLint uniform_handle = glGetUniformLocation(program_handle, "shift");
973  glUniform1f(uniform_handle, aShiftValue);
974  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
975 
976  uniform_handle = glGetUniformLocation(program_handle, "scale");
977  glUniform1f(uniform_handle, aScaleValue);
978  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
979 
980 
981  // Dispatch the compute shader
982  glDispatchCompute(getWidth(), getHeight(), getDepth());
983  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
984 
985  // Make sure all the calculations are performed
986  glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
987  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
988 
989  const_cast<Image<T>&>(temp).transferDeviceToHost();
990  }
991  }
992  catch (const Exception& e)
993  {
994  LOGGER.logWarning(e.what()) << std::endl;
995 
996  use_gpu = false;
997  }
998  catch (...)
999  {
1000  LOGGER.logWarning("GPU computation requested and supported but failed to execute in the function as follows:") << std::endl <<
1001  "\tFile: " << __FILE__ << std::endl <<
1002  "\tFunction: " << __FUNCTION__ << std::endl <<
1003  "\tLine: " << __LINE__ << std::endl;
1004 
1005  use_gpu = false;
1006  }
1007  }
1008 
1009  if (use_gpu == false)
1010  {
1011  // Process every pixel of the image
1012 #ifdef NDEBUG
1013  #pragma omp parallel for
1014 #endif
1015  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
1016  {
1017  // Apply the shilft/scale filter
1018  temp[i] = (temp[i] + aShiftValue) * aScaleValue;
1019  }
1020  }
1021 
1022  // Return the result
1023  return (temp);
1024 }
1025 
1026 
1027 //--------------------------------------------------------
1028 template<typename T> void Image<T>::transferHostToDevice()
1029 //--------------------------------------------------------
1030 {
1031  // Generate the texture name
1032  if (!m_texture_id)
1033  {
1034  glGenTextures(1, &m_texture_id);
1035  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1036  }
1037 
1038  // Set the texture properties
1039  glBindTexture(GL_TEXTURE_3D, m_texture_id);
1040 
1041  if (typeid(T) == typeid(unsigned int) ||
1042  typeid(T) == typeid(int) ||
1043  typeid(T) == typeid(unsigned short) ||
1044  typeid(T) == typeid(short) ||
1045  typeid(T) == typeid(unsigned char) ||
1046  typeid(T) == typeid(char))
1047  {
1048  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1049  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1050  }
1051  else if (typeid(T) == typeid(float))
1052  {
1053  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1054  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1055  }
1056  else
1057  {
1058  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1059  "The data type of this image is not supported on GPU.");
1060  }
1061 
1062  glPixelStorei(GL_PACK_ALIGNMENT, 1);
1063  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1064 
1065  // Make sure the texture can be created using a texture proxy
1066  GLint texture_width(0);
1067 
1068  if (typeid(T) == typeid(unsigned int))
1069  {
1070  glTexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_R32UI, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
1071  }
1072  else if (typeid(T) == typeid(int))
1073  {
1074  glTexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_R32I, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_INT, 0);
1075  }
1076  else if (typeid(T) == typeid(unsigned short))
1077  {
1078  glTexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_R16UI, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, 0);
1079  }
1080  else if (typeid(T) == typeid(short))
1081  {
1082  glTexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_R16I, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_SHORT, 0);
1083  }
1084  else if (typeid(T) == typeid(unsigned char))
1085  {
1086  glTexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_R8UI, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0);
1087  }
1088  else if (typeid(T) == typeid(char))
1089  {
1090  glTexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_R8I, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_BYTE, 0);
1091  }
1092  else if (typeid(T) == typeid(float))
1093  {
1094  glTexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_R32F, m_width, m_height, m_number_of_slices, 0, GL_RED, GL_FLOAT, 0);
1095  }
1096  else
1097  {
1098  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1099  "The data type of this image is not supported on GPU.");
1100  }
1101 
1102  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1103 
1104  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &texture_width);
1105 
1106  if ((unsigned int)(texture_width) != m_width)
1107  {
1108  std::stringstream error_message;
1109  error_message << "Cannot create a " << "(" << m_width << " x " << m_height << " x " << m_number_of_slices << ") floating point render texture.";
1110  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message.str());
1111  }
1112 
1113  if (typeid(T) == typeid(unsigned int))
1114  {
1115  glTexImage3D(GL_TEXTURE_3D, 0, GL_R32UI, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, m_p_image);
1116  }
1117  else if (typeid(T) == typeid(int))
1118  {
1119  glTexImage3D(GL_TEXTURE_3D, 0, GL_R32I, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_INT, m_p_image);
1120  }
1121  else if (typeid(T) == typeid(unsigned short))
1122  {
1123  glTexImage3D(GL_TEXTURE_3D, 0, GL_R16UI, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, m_p_image);
1124  }
1125  else if (typeid(T) == typeid(short))
1126  {
1127  glTexImage3D(GL_TEXTURE_3D, 0, GL_R16I, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_SHORT, m_p_image);
1128  }
1129  else if (typeid(T) == typeid(unsigned char))
1130  {
1131  glTexImage3D(GL_TEXTURE_3D, 0, GL_R8UI, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, m_p_image);
1132  }
1133  else if (typeid(T) == typeid(char))
1134  {
1135  glTexImage3D(GL_TEXTURE_3D, 0, GL_R8I, m_width, m_height, m_number_of_slices, 0, GL_RED_INTEGER, GL_BYTE, m_p_image);
1136  }
1137  else if (typeid(T) == typeid(float))
1138  {
1139  glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, m_width, m_height, m_number_of_slices, 0, GL_RED, GL_FLOAT, m_p_image);
1140  }
1141  else
1142  {
1143  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1144  "The data type of this image is not supported on GPU.");
1145  }
1146  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1147 }
1148 
1149 
1150 //--------------------------------------------------------
1151 template<typename T> void Image<T>::transferDeviceToHost()
1152 //--------------------------------------------------------
1153 {
1154  // The texture exists
1155  if (m_texture_id)
1156  {
1157  glBindTexture(GL_TEXTURE_3D, m_texture_id);
1158 
1159  if (typeid(T) == typeid(unsigned int))
1160  {
1161  glGetTexImage(GL_TEXTURE_3D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, m_p_image);
1162  }
1163  else if (typeid(T) == typeid(int))
1164  {
1165  glGetTexImage(GL_TEXTURE_3D, 0, GL_RED_INTEGER, GL_INT, m_p_image);
1166  }
1167  else if (typeid(T) == typeid(unsigned short))
1168  {
1169  glGetTexImage(GL_TEXTURE_3D, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, m_p_image);
1170  }
1171  else if (typeid(T) == typeid(short))
1172  {
1173  glGetTexImage(GL_TEXTURE_3D, 0, GL_RED_INTEGER, GL_SHORT, m_p_image);
1174  }
1175  else if (typeid(T) == typeid(unsigned char))
1176  {
1177  glGetTexImage(GL_TEXTURE_3D, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, m_p_image);
1178  }
1179  else if (typeid(T) == typeid(char))
1180  {
1181  glGetTexImage(GL_TEXTURE_3D, 0, GL_RED_INTEGER, GL_BYTE, m_p_image);
1182  }
1183  else if (typeid(T) == typeid(float))
1184  {
1185  glGetTexImage(GL_TEXTURE_3D, 0, GL_RED, GL_FLOAT, m_p_image);
1186  }
1187  else
1188  {
1189  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1190  "The data type of this image is not supported on GPU.");
1191  }
1192  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1193  }
1194 }
1195 
1196 
1197 
1198 
1199 template<typename T> Image<unsigned char> Image<T>::threshold(const T& aLowerThreshold,
1200  const T& aUpperThreshold,
1201  unsigned char anInValue,
1202  unsigned char anOutValue,
1203  bool aRunOnGPUFlag) const
1204 {
1205  Image<unsigned char> segmentation(m_width, m_height, m_number_of_slices, anOutValue);
1206  segmentation.setSpacing(m_spacing);
1207 
1208 
1209  bool use_gpu = false;
1210 
1211  if (aRunOnGPUFlag)
1212  {
1213  if (useOpenGLCompute())
1214  {
1215  const_cast<Image<T>&>(*this).transferHostToDevice();
1216  const_cast<Image<unsigned char>&>(segmentation).transferHostToDevice();
1217 
1218  if (getTextureId() && segmentation.getTextureId())
1219  use_gpu = true;
1220  }
1221  else
1222  {
1223  LOGGER.logWarning("GPU computation requested but not supported in the function as follows:") << std::endl <<
1224  "\tFile: " << __FILE__ << std::endl <<
1225  "\tFunction: " << __FUNCTION__ << std::endl <<
1226  "\tLine: " << __LINE__ << std::endl;
1227  }
1228  }
1229 
1230  if (use_gpu)
1231  {
1232  // Instantiate a compute shader
1233  Shader dummy_shader;
1234 
1235  // Compute shader
1236  std::string g_compute_shader;
1237  g_compute_shader += "#version 430\n";
1238  g_compute_shader += "\n";
1239  g_compute_shader += "layout(local_size_x=1, local_size_y=1, local_size_z=1) in;\n";
1240  g_compute_shader += "layout(" + getShaderTypeID(typeid(T)) + ", binding=0) uniform " + getShaderImageType(typeid(T)) + " input_image;\n";
1241  g_compute_shader += "layout(" + getShaderTypeID(typeid(unsigned char)) + ", binding=1) uniform " + getShaderImageType(typeid(unsigned char)) + " output_image;\n";
1242  g_compute_shader += "\n";
1243  g_compute_shader += "layout(location=0) uniform float low_threshold;\n";
1244  g_compute_shader += "layout(location=1) uniform float high_threshold;\n";
1245  g_compute_shader += "layout(location=2) uniform uint in_value;\n";
1246  g_compute_shader += "layout(location=3) uniform uint out_value;\n";
1247  g_compute_shader += "\n";
1248  g_compute_shader += "void main(void)\n";
1249  g_compute_shader += "{\n";
1250  g_compute_shader += " ivec3 pixel_coordinates = ivec3(gl_GlobalInvocationID.xyz);\n";
1251  g_compute_shader += " float intensity = imageLoad(input_image, pixel_coordinates).r;\n";
1252  g_compute_shader += " int comparison_in = int(low_threshold <= intensity && intensity <= high_threshold);\n";
1253  g_compute_shader += " int comparison_out = int(!(low_threshold <= intensity && intensity <= high_threshold));\n";
1254  g_compute_shader += " " + getShaderPixelType(typeid(unsigned char)) + " pixel_value = comparison_in * in_value + comparison_out * out_value;\n";
1255  g_compute_shader += "\n";
1256  g_compute_shader += " imageStore(output_image, pixel_coordinates, " + getShaderRGBAPixelType(typeid(unsigned char)) + "(pixel_value, 0, 0, 0));\n";
1257  g_compute_shader += "}\n";
1258 
1259  try
1260  {
1261  // Give a text ID to the compute shader, it can be useful when debuging the shader
1262  dummy_shader.setLabel("g_compute_shader");
1263 
1264  // Load the source code of the shader onto the GPU
1265  dummy_shader.loadSource(static_cast<const GLchar*>(g_compute_shader.c_str()));
1266 
1267  // Check that the compute shader is valid
1268  if (dummy_shader.isValid())
1269  {
1270  // Bind the texture
1271  glBindImageTexture(0, getTextureId(), 0, GL_FALSE, 0, GL_READ_ONLY, getShaderOpenGLType(typeid(T)));
1272  glBindImageTexture(1, segmentation.getTextureId(), 0, GL_FALSE, 0, GL_WRITE_ONLY, getShaderOpenGLType(typeid(unsigned char)));
1273  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1274 
1275  // Enable the compute shader
1276  dummy_shader.enable();
1277  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1278 
1279  // Load uniform variables to the GPU
1280  GLint program_handle(0);
1281  glGetIntegerv(GL_CURRENT_PROGRAM, &program_handle);
1282  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1283 
1284  GLint uniform_handle = glGetUniformLocation(program_handle, "low_threshold");
1285  glUniform1f(uniform_handle, aLowerThreshold);
1286  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1287 
1288  uniform_handle = glGetUniformLocation(program_handle, "high_threshold");
1289  glUniform1f(uniform_handle, aUpperThreshold);
1290  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1291 
1292  uniform_handle = glGetUniformLocation(program_handle, "in_value");
1293  glUniform1ui(uniform_handle, anInValue);
1294  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1295 
1296  uniform_handle = glGetUniformLocation(program_handle, "out_value");
1297  glUniform1ui(uniform_handle, anOutValue);
1298  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1299 
1300 
1301  // Dispatch the compute shader
1302  glDispatchCompute(getWidth(), getHeight(), getDepth());
1303  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1304 
1305  // Make sure all the calculations are performed
1306  glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
1307  checkOpenGLErrorStatus(__FILE__, __FUNCTION__, __LINE__);
1308 
1309  const_cast<Image<unsigned char>&>(segmentation).transferDeviceToHost();
1310  }
1311  }
1312  catch (const Exception& e)
1313  {
1314  LOGGER.logWarning(e.what()) << std::endl;
1315 
1316  use_gpu = false;
1317  }
1318  catch (...)
1319  {
1320  LOGGER.logWarning("GPU computation requested and supported but failed to execute in the function as follows:") << std::endl <<
1321  "\tFile: " << __FILE__ << std::endl <<
1322  "\tFunction: " << __FUNCTION__ << std::endl <<
1323  "\tLine: " << __LINE__ << std::endl;
1324 
1325  use_gpu = false;
1326  }
1327  }
1328 
1329  if (use_gpu == false)
1330  {
1331 #ifdef NDEBUG
1332 #ifdef WIN32
1333  #pragma omp parallel for
1334 #else
1335  #pragma omp parallel for collapse(3)
1336 #endif
1337 #endif
1338  for (int k = 0; k < m_number_of_slices; ++k)
1339  {
1340  for (int j = 0; j < (m_height); ++j)
1341  {
1342  for (int i = 0; i < m_width; ++i)
1343  {
1344  unsigned int index(k * m_width * m_height + j * m_width + i);
1345 
1346  float voxel_value(m_p_image[index]);
1347  if (aLowerThreshold <= voxel_value && voxel_value <= aUpperThreshold)
1348  {
1349  segmentation.setPixel(i, j, k, anInValue);
1350  }
1351  }
1352  }
1353  }
1354  }
1355  return (segmentation);
1356 }
1357 
1358 
1359 //--------------------------------------------------------------------------------------------
1360 template<typename T> Image<T> Image<T>::convolution1D(const std::vector<float>& aKernel) const
1361 //--------------------------------------------------------------------------------------------
1362 {
1363  // Copy the instance into a temporary variable
1364  Image<T> temp(m_width, m_height, m_number_of_slices, 0);
1365  temp.m_spacing = m_spacing;
1366 
1367  int kernel_size = aKernel.size();
1368  int half_kernel_size = kernel_size / 2;
1369 
1370  // Process every pixel of the image
1371 #ifdef NDEBUG
1372  #pragma omp parallel for collapse(3)
1373 #endif
1374  for (int k = 0; k < m_number_of_slices; ++k)
1375  for (int j = 0; j < m_height; ++j)
1376  for (int i = 0; i < m_width; ++i)
1377  for (int u = -half_kernel_size; u <= half_kernel_size; ++u)
1378  {
1379  int index_i(std::max(0, i + u));
1380  index_i = std::min(index_i, int(m_width - 1));
1381 
1382  int index_u = u + half_kernel_size;
1383 
1384  if (index_u < kernel_size)
1385  {
1386  temp(i, j, k) += getPixel(index_i, j, k) * aKernel[index_u];
1387  }
1388  }
1389 
1390  // Return the result
1391  return (temp);
1392 }
1393 
1394 
1395 //--------------------------------------------------------------------------------------
1396 template<typename T> Image<T> Image<T>::convolution2D(const Image<float>& aKernel) const
1397 //--------------------------------------------------------------------------------------
1398 {
1399  // Copy the instance into a temporary variable
1400  Image<T> temp(m_width, m_height, m_number_of_slices, 0);
1401  temp.m_spacing = m_spacing;
1402 
1403  int kernel_size_u = aKernel.getWidth();
1404  int kernel_size_v = aKernel.getHeight();
1405  int half_kernel_size_u = kernel_size_u / 2;
1406  int half_kernel_size_v = kernel_size_v / 2;
1407 
1408  // Process every pixel of the image
1409 #ifdef NDEBUG
1410  #pragma omp parallel for collapse(4)
1411 #endif
1412  for (int k = 0; k < m_number_of_slices; ++k)
1413  for (int j = 0; j < m_height; ++j)
1414  for (int i = 0; i < m_width; ++i)
1415  for (int v = -half_kernel_size_v; v <= half_kernel_size_v; ++v)
1416  {
1417  int index_j(std::max(0, j + v));
1418  index_j = std::min(index_j, int(m_height - 1));
1419 
1420  int index_v = v + half_kernel_size_v;
1421 
1422  for (int u = -half_kernel_size_u; u <= half_kernel_size_u; ++u)
1423  {
1424  int index_i(std::max(0, i + u));
1425  index_i = std::min(index_i, int(m_width - 1));
1426 
1427  int index_u = u + half_kernel_size_u;
1428 
1429  if (index_u < kernel_size_u && index_v < kernel_size_v)
1430  {
1431  temp(i, j, k) += getPixel(index_i, index_j, k) * aKernel.getPixel(index_u, index_v);
1432  }
1433  }
1434  }
1435 
1436  // Return the result
1437  return (temp);
1438 }
1439 
1440 
1441 //---------------------------------------------------------------------------------------
1442 template<typename T> Image<T> Image<T>::convolution2D(const Image<double>& aKernel) const
1443 //---------------------------------------------------------------------------------------
1444 {
1445  // Copy the instance into a temporary variable
1446  Image<T> temp(m_width, m_height, m_number_of_slices, 0);
1447  temp.m_spacing = m_spacing;
1448 
1449  int kernel_size_u = aKernel.getWidth();
1450  int kernel_size_v = aKernel.getHeight();
1451  int half_kernel_size_u = kernel_size_u / 2;
1452  int half_kernel_size_v = kernel_size_v / 2;
1453 
1454  // Process every pixel of the image
1455 #ifdef NDEBUG
1456  #pragma omp parallel for collapse(4)
1457 #endif
1458  for (int k = 0; k < m_number_of_slices; ++k)
1459  for (int j = 0; j < m_height; ++j)
1460  for (int i = 0; i < m_width; ++i)
1461  for (int v = -half_kernel_size_v; v <= half_kernel_size_v; ++v)
1462  {
1463  int index_j(std::max(0, j + v));
1464  index_j = std::min(index_j, int(m_height - 1));
1465 
1466  int index_v = v + half_kernel_size_v;
1467 
1468  for (int u = -half_kernel_size_u; u <= half_kernel_size_u; ++u)
1469  {
1470  int index_i(std::max(0, i + u));
1471  index_i = std::min(index_i, int(m_width - 1));
1472 
1473  int index_u = u + half_kernel_size_u;
1474 
1475  if (index_u < kernel_size_u && index_v < kernel_size_v)
1476  {
1477  temp(i, j, k) += getPixel(index_i, index_j, k) * aKernel.getPixel(index_u, index_v);
1478  }
1479  }
1480  }
1481 
1482  // Return the result
1483  return (temp);
1484 }
1485 
1486 
1487 template<typename T> Image<unsigned char> Image<T>::opening(unsigned int aRadius) const
1488 {
1489 #ifndef HAS_ITK
1490  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1491  "gVirtualXRay has not been compiled with ITK support. As a consequence Image<T>::antiAliasBinaryImageFilter is not available.");
1492 #else
1493 
1494  typedef itk::Image< unsigned char, 3 > InputImageType;
1495  typedef itk::ImportImageFilter< unsigned char, 3 > ImportFilterType;
1496 
1497  ImportFilterType::Pointer importFilter = ImportFilterType::New();
1498 
1499  ImportFilterType::SizeType size;
1500  size[0] = m_width; // size along X
1501  size[1] = m_height; // size along Y
1502  size[2] = m_number_of_slices; // size along Z
1503 
1504  ImportFilterType::IndexType start;
1505  start.Fill( 0 );
1506 
1507  ImportFilterType::RegionType region;
1508  region.SetIndex( start );
1509  region.SetSize( size );
1510 
1511  importFilter->SetRegion( region );
1512 
1513  double origin[ 3 ];
1514  origin[0] = 0.0; // X coordinate
1515  origin[1] = 0.0; // Y coordinate
1516  origin[2] = 0.0; // Z coordinate
1517 
1518  importFilter->SetOrigin( origin );
1519 
1520  double spacing[ 3 ];
1521  spacing[0] = m_spacing[0]; // along X direction
1522  spacing[1] = m_spacing[1]; // along Y direction
1523  spacing[2] = m_spacing[2]; // along Z direction
1524 
1525  importFilter->SetSpacing( spacing );
1526 
1527  const bool importImageFilterWillOwnTheBuffer = false;
1528  importFilter->SetImportPointer( m_p_image, m_width * m_height * m_number_of_slices,
1529  importImageFilterWillOwnTheBuffer );
1530 
1531  typedef itk::BinaryBallStructuringElement<InputImageType::PixelType, InputImageType::ImageDimension>
1532  StructuringElementType;
1533  StructuringElementType structuringElement;
1534  structuringElement.SetRadius(aRadius);
1535  structuringElement.CreateStructuringElement();
1536 
1537  typedef itk::BinaryMorphologicalOpeningImageFilter <InputImageType, InputImageType, StructuringElementType>
1538  BinaryMorphologicalOpeningImageFilterType;
1539  BinaryMorphologicalOpeningImageFilterType::Pointer openingFilter
1540  = BinaryMorphologicalOpeningImageFilterType::New();
1541  openingFilter->SetInput(importFilter->GetOutput());
1542  openingFilter->SetKernel(structuringElement);
1543 
1544  openingFilter->SetBackgroundValue (getMinValue());
1545 
1546  openingFilter->SetForegroundValue (getMaxValue());
1547 
1548  openingFilter->Update();
1549 
1550  return (Image<unsigned char>(openingFilter->GetOutput()->GetBufferPointer(), m_width, m_height, m_number_of_slices, m_spacing));
1551 #endif
1552 }
1553 
1554 
1555 template<typename T> Image<unsigned char> Image<T>::closing(unsigned int aRadius) const
1556 {
1557 #ifndef HAS_ITK
1558  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1559  "gVirtualXRay has not been compiled with ITK support. As a consequence Image<T>::antiAliasBinaryImageFilter is not available.");
1560 #else
1561 
1562  typedef itk::Image< unsigned char, 3 > InputImageType;
1563  typedef itk::ImportImageFilter< unsigned char, 3 > ImportFilterType;
1564 
1565  ImportFilterType::Pointer importFilter = ImportFilterType::New();
1566 
1567  ImportFilterType::SizeType size;
1568  size[0] = m_width; // size along X
1569  size[1] = m_height; // size along Y
1570  size[2] = m_number_of_slices; // size along Z
1571 
1572  ImportFilterType::IndexType start;
1573  start.Fill( 0 );
1574 
1575  ImportFilterType::RegionType region;
1576  region.SetIndex( start );
1577  region.SetSize( size );
1578 
1579  importFilter->SetRegion( region );
1580 
1581  double origin[ 3 ];
1582  origin[0] = 0.0; // X coordinate
1583  origin[1] = 0.0; // Y coordinate
1584  origin[2] = 0.0; // Z coordinate
1585 
1586  importFilter->SetOrigin( origin );
1587 
1588  double spacing[ 3 ];
1589  spacing[0] = m_spacing[0]; // along X direction
1590  spacing[1] = m_spacing[1]; // along Y direction
1591  spacing[2] = m_spacing[2]; // along Z direction
1592 
1593  importFilter->SetSpacing( spacing );
1594 
1595  const bool importImageFilterWillOwnTheBuffer = false;
1596  importFilter->SetImportPointer( m_p_image, m_width * m_height * m_number_of_slices,
1597  importImageFilterWillOwnTheBuffer );
1598 
1599  typedef itk::BinaryBallStructuringElement<InputImageType::PixelType, InputImageType::ImageDimension>
1600  StructuringElementType;
1601  StructuringElementType structuringElement;
1602  structuringElement.SetRadius(aRadius);
1603  structuringElement.CreateStructuringElement();
1604 
1605  typedef itk::BinaryMorphologicalClosingImageFilter <InputImageType, InputImageType, StructuringElementType>
1606  BinaryMorphologicalClosingImageFilterType;
1607  BinaryMorphologicalClosingImageFilterType::Pointer closingFilter
1608  = BinaryMorphologicalClosingImageFilterType::New();
1609  closingFilter->SetInput(importFilter->GetOutput());
1610  closingFilter->SetKernel(structuringElement);
1611 
1612  // closingFilter->SetBackgroundValue (getMinValue());
1613 
1614  closingFilter->SetForegroundValue (getMaxValue());
1615 
1616  closingFilter->Update();
1617 
1618  return (Image<unsigned char>(closingFilter->GetOutput()->GetBufferPointer(), m_width, m_height, m_number_of_slices, m_spacing));
1619 #endif
1620 }
1621 
1622 
1623 template<typename T> Image<float> Image<T>::getIsoTropic() const
1624 {
1625 #ifndef HAS_ITK
1626  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1627  "gVirtualXRay has not been compiled with ITK support. As a consequence Image<T>::antiAliasBinaryImageFilter is not available.");
1628 #else
1629 
1630  typedef itk::Image< unsigned char, 3 > InputImageType;
1631  typedef itk::Image< float, 3 > OutputImageType;
1632  typedef itk::ImportImageFilter< unsigned char, 3 > ImportFilterType;
1633 
1634  ImportFilterType::Pointer importFilter = ImportFilterType::New();
1635 
1636  ImportFilterType::SizeType current_size;
1637  current_size[0] = m_width; // size along X
1638  current_size[1] = m_height; // size along Y
1639  current_size[2] = m_number_of_slices; // size along Z
1640 
1641  ImportFilterType::IndexType start;
1642  start.Fill( 0 );
1643 
1644  ImportFilterType::RegionType region;
1645  region.SetIndex( start );
1646  region.SetSize( current_size );
1647 
1648  importFilter->SetRegion( region );
1649 
1650  double origin[ 3 ];
1651  origin[0] = 0.0; // X coordinate
1652  origin[1] = 0.0; // Y coordinate
1653  origin[2] = 0.0; // Z coordinate
1654 
1655  importFilter->SetOrigin( origin );
1656 
1657  double current_spacing[ 3 ];
1658  current_spacing[0] = m_spacing[0]; // along X direction
1659  current_spacing[1] = m_spacing[1]; // along Y direction
1660  current_spacing[2] = m_spacing[2]; // along Z direction
1661 
1662  importFilter->SetSpacing( current_spacing );
1663 
1664  const bool importImageFilterWillOwnTheBuffer = false;
1665  importFilter->SetImportPointer( m_p_image, m_width * m_height * m_number_of_slices,
1666  importImageFilterWillOwnTheBuffer );
1667 
1668 
1669  // Software Guide : BeginLatex
1670  //
1671  // We instantiate the smoothing filter that will be used on the preprocessing
1672  // for subsampling the in-plane resolution of the dataset.
1673  //
1674  // Software Guide : std::endlatex
1675  // Software Guide : BeginCodeSnippet
1676  typedef itk::RecursiveGaussianImageFilter<
1677  InputImageType,
1678  OutputImageType > InputGaussianFilterType;
1679  typedef itk::RecursiveGaussianImageFilter<
1680  OutputImageType,
1681  OutputImageType > OutputGaussianFilterType;
1682  // Software Guide : EndCodeSnippet
1683  // Software Guide : BeginLatex
1684  //
1685  // We create two instances of the smoothing filter: one will smooth along the
1686  // $X$ direction while the other will smooth along the $Y$ direction. They are
1687  // connected in a cascade in the pipeline, while taking their input from the
1688  // intensity windowing filter. Note that you may want to skip the intensity
1689  // windowing scale and simply take the input directly from the reader.
1690  //
1691  // Software Guide : std::endlatex
1692  // Software Guide : BeginCodeSnippet
1693  InputGaussianFilterType::Pointer smootherX = InputGaussianFilterType::New();
1694  OutputGaussianFilterType::Pointer smootherY = OutputGaussianFilterType::New();
1695  smootherX->SetInput( importFilter->GetOutput() );
1696  smootherY->SetInput( smootherX->GetOutput() );
1697  // Software Guide : EndCodeSnippet
1698  // Software Guide : BeginLatex
1699  //
1700  // We must now provide the settings for the resampling itself. This is done by
1701  // searching for a value of isotropic resolution that will provide a trade-off
1702  // between the evil of subsampling and the evil of supersampling. We advance
1703  // here the conjecture that the geometrical mean between the in-plane and the
1704  // inter-slice resolutions should be a convenient isotropic resolution to use.
1705  // This conjecture is supported on nothing other than intuition and common
1706  // sense. You can rightfully argue that this choice deserves a more technical
1707  // consideration, but then, if you are so concerned about the technical integrity
1708  // of the image sampling process, you should not be using this code, and should
1709  // discuss these issues with the radiologist who
1710  // acquired this ugly anisotropic dataset.
1711  //
1712  // We take the image from the input and then request its array of pixel spacing
1713  // values.
1714  //
1715  // Software Guide : std::endlatex
1716  // Software Guide : BeginCodeSnippet
1717  //InputImageType::ConstPointer inputImage = reader->GetOutput();
1718  //const InputImageType::SpacingType& inputSpacing = inputImage->GetSpacing();
1719  // Software Guide : EndCodeSnippet
1720  // Software Guide : BeginLatex
1721  //
1722  // and apply our ad-hoc conjecture that the correct anisotropic resolution
1723  // to use is the geometrical mean of the in-plane and inter-slice resolutions.
1724  // Then set this spacing as the Sigma value to be used for the Gaussian
1725  // smoothing at the preprocessing stage.
1726  //
1727  // Software Guide : std::endlatex
1728  // Software Guide : BeginCodeSnippet
1729  const double isoSpacing = std::sqrt( current_spacing[2] * current_spacing[0] );
1730  smootherX->SetSigma( isoSpacing );
1731  smootherY->SetSigma( isoSpacing );
1732  // Software Guide : EndCodeSnippet
1733  // Software Guide : BeginLatex
1734  //
1735  // We instruct the smoothing filters to act along the $X$ and $Y$ direction
1736  // respectively.
1737  //
1738  // Software Guide : std::endlatex
1739  // Software Guide : BeginCodeSnippet
1740  smootherX->SetDirection( 0 );
1741  smootherY->SetDirection( 1 );
1742  // Software Guide : EndCodeSnippet
1743  // Software Guide : BeginLatex
1744  //
1745  // Now that we have taken care of the smoothing in-plane, we proceed to
1746  // instantiate the resampling filter that will reconstruct an isotropic image.
1747  // We start by declaring the pixel type to be used as the output of this filter,
1748  // then instantiate the image type and the type for the resampling filter.
1749  // Finally we construct an instantiation of the filter.
1750  //
1751  // Software Guide : std::endlatex
1752  // Software Guide : BeginCodeSnippet
1753  typedef itk::ResampleImageFilter<
1754  OutputImageType, OutputImageType > ResampleFilterType;
1755  ResampleFilterType::Pointer resampler = ResampleFilterType::New();
1756  // Software Guide : EndCodeSnippet
1757  // Software Guide : BeginLatex
1758  //
1759  // The resampling filter requires that we provide a Transform, which in this
1760  // particular case can simply be an identity transform.
1761  //
1762  // Software Guide : std::endlatex
1763  // Software Guide : BeginCodeSnippet
1764  typedef itk::IdentityTransform< double, 3 > TransformType;
1765  TransformType::Pointer transform = TransformType::New();
1766  transform->SetIdentity();
1767  resampler->SetTransform( transform );
1768  // Software Guide : EndCodeSnippet
1769  // Software Guide : BeginLatex
1770  //
1771  // The filter also requires an interpolator to be passed to it. In this case we
1772  // chose to use a linear interpolator.
1773  //
1774  // Software Guide : std::endlatex
1775  // Software Guide : BeginCodeSnippet
1776  typedef itk::LinearInterpolateImageFunction<
1777  //typedef itk::BSplineInterpolateImageFunction<
1778  OutputImageType, double > InterpolatorType;
1779  InterpolatorType::Pointer interpolator = InterpolatorType::New();
1780  resampler->SetInterpolator( interpolator );
1781  // Software Guide : EndCodeSnippet
1782  resampler->SetDefaultPixelValue( 0 ); // highlight regions without source
1783  // Software Guide : BeginLatex
1784  //
1785  // The pixel spacing of the resampled dataset is loaded in a \code{SpacingType}
1786  // and passed to the resampling filter.
1787  //
1788  // Software Guide : std::endlatex
1789  // Software Guide : BeginCodeSnippet
1790  OutputImageType::SpacingType spacing;
1791  spacing[0] = isoSpacing;
1792  spacing[1] = isoSpacing;
1793  spacing[2] = isoSpacing;
1794  resampler->SetOutputSpacing( spacing );
1795  // Software Guide : EndCodeSnippet
1796  // Software Guide : BeginLatex
1797  //
1798  // The origin and orientation of the output image is maintained, since we
1799  // decided to resample the image in the same physical extent of the input
1800  // anisotropic image.
1801  //
1802  // Software Guide : std::endlatex
1803  // Software Guide : BeginCodeSnippet
1804  resampler->SetOutputOrigin( origin );
1805  resampler->SetOutputDirection( importFilter->GetOutput()->GetDirection() );
1806  // Software Guide : EndCodeSnippet
1807  // Software Guide : BeginLatex
1808  //
1809  // The number of pixels to use along each dimension in the grid of the
1810  // resampled image is computed using the ratio between the pixel spacings of the
1811  // input image and those of the output image. Note that the computation of the
1812  // number of pixels along the $Z$ direction is slightly different with the
1813  // purpose of making sure that we don't attempt to compute pixels that are
1814  // outside of the original anisotropic dataset.
1815  //
1816  // Software Guide : std::endlatex
1817  // Software Guide : BeginCodeSnippet
1818  InputImageType::SizeType inputSize =
1819  importFilter->GetOutput()->GetLargestPossibleRegion().GetSize();
1820  typedef InputImageType::SizeType::SizeValueType SizeValueType;
1821  const double dx = current_size[0] * current_spacing[0] / isoSpacing;
1822  const double dy = current_size[1] * current_spacing[1] / isoSpacing;
1823  const double dz = (current_size[2] - 1 ) * current_spacing[2] / isoSpacing;
1824  // Software Guide : EndCodeSnippet
1825  // Software Guide : BeginLatex
1826  //
1827  // Finally the values are stored in a \code{SizeType} and passed to the
1828  // resampling filter. Note that this process requires a casting since the
1829  // computations are performed in \code{double}, while the elements of the
1830  // \code{SizeType} are integers.
1831  //
1832  // Software Guide : std::endlatex
1833  // Software Guide : BeginCodeSnippet
1834  InputImageType::SizeType size;
1835  size[0] = static_cast<SizeValueType>( dx );
1836  size[1] = static_cast<SizeValueType>( dy );
1837  size[2] = static_cast<SizeValueType>( dz );
1838  resampler->SetSize( size );
1839  // Software Guide : EndCodeSnippet
1840  // Software Guide : BeginLatex
1841  //
1842  // Our last action is to take the input for the resampling image filter from
1843  // the output of the cascade of smoothing filters, and then to trigger the
1844  // execution of the pipeline by invoking the \code{Update()} method on the
1845  // resampling filter.
1846  //
1847  // Software Guide : std::endlatex
1848  // Software Guide : BeginCodeSnippet
1849  resampler->SetInput( smootherY->GetOutput() );
1850  resampler->Update();
1851 
1852  return (Image<float>(resampler->GetOutput()->GetBufferPointer(), size[0], size[1], size[2], VEC3(isoSpacing, isoSpacing, isoSpacing)));
1853 #endif
1854 }
1855 
1856 
1857 template<typename T> Image<float> Image<T>::antiAliasBinaryImageFilter(double aMaximumRMSChange, unsigned int aNumberOfIterations, double aVariance) const
1858 {
1859 #ifndef HAS_ITK
1860  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1861  "gVirtualXRay has not been compiled with ITK support. As a consequence Image<T>::antiAliasBinaryImageFilter is not available.");
1862 #else
1863  if (aMaximumRMSChange >= 1.0)
1864  {
1865  throw Exception(__FILE__, __FUNCTION__, __LINE__,
1866  "aMaximumRMSChange should be less than 1.0. A value of 0.07 is a good starting estimate.");
1867  }
1868 
1869  typedef itk::Image< unsigned char, 3 > InputImageType;
1870  typedef itk::Image< float, 3 > OutputImageType;
1871 
1872  typedef itk::ImportImageFilter< unsigned char, 3 > ImportFilterType;
1873 
1874  ImportFilterType::Pointer importFilter = ImportFilterType::New();
1875 
1876  ImportFilterType::SizeType size;
1877  size[0] = m_width; // size along X
1878  size[1] = m_height; // size along Y
1879  size[2] = m_number_of_slices; // size along Z
1880 
1881  ImportFilterType::IndexType start;
1882  start.Fill( 0 );
1883 
1884  ImportFilterType::RegionType region;
1885  region.SetIndex( start );
1886  region.SetSize( size );
1887 
1888  importFilter->SetRegion( region );
1889 
1890  double origin[ 3 ];
1891  origin[0] = 0.0; // X coordinate
1892  origin[1] = 0.0; // Y coordinate
1893  origin[2] = 0.0; // Z coordinate
1894 
1895  importFilter->SetOrigin( origin );
1896 
1897  double spacing[ 3 ];
1898  spacing[0] = m_spacing[0]; // along X direction
1899  spacing[1] = m_spacing[1]; // along Y direction
1900  spacing[2] = m_spacing[2]; // along Z direction
1901 
1902  importFilter->SetSpacing( spacing );
1903 
1904  const bool importImageFilterWillOwnTheBuffer = false;
1905  importFilter->SetImportPointer( m_p_image, m_width * m_height * m_number_of_slices,
1906  importImageFilterWillOwnTheBuffer );
1907 
1908  typedef itk::DiscreteGaussianImageFilter< InputImageType, OutputImageType > GaussianBlurImageFilter;
1909  GaussianBlurImageFilter::Pointer p_gaussian_blur_filter(GaussianBlurImageFilter::New());
1910  p_gaussian_blur_filter->SetInput(importFilter->GetOutput());
1911  p_gaussian_blur_filter->SetVariance(aVariance);
1912  p_gaussian_blur_filter->Update();
1913 
1914  typedef itk::AntiAliasBinaryImageFilter< OutputImageType, OutputImageType > AntiAliasBinaryImageFilter;
1915  AntiAliasBinaryImageFilter::Pointer p_filter(AntiAliasBinaryImageFilter::New());
1916  p_filter->SetInput(p_gaussian_blur_filter->GetOutput());
1917  p_filter->SetNumberOfIterations(aNumberOfIterations);
1918  p_filter->SetMaximumRMSError(aMaximumRMSChange);
1919  p_filter->Update();
1920 
1921  return (Image<float>(p_filter->GetOutput()->GetBufferPointer(), m_width, m_height, m_number_of_slices, m_spacing));
1922 #endif
1923 }
1924 
1925 
1926 //---------------------------------------------------------------------------------
1927 template<typename T> Image<T> Image<T>::constantPadFilter(const T& aPadValue) const
1928 //---------------------------------------------------------------------------------
1929 {
1930  Image<T> temp_image(m_width + 2, m_height + 2, m_number_of_slices + 2, aPadValue, m_spacing);
1931 
1932 #ifdef NDEBUG
1933 #ifdef WIN32
1934  #pragma omp parallel for
1935 #else
1936  #pragma omp parallel for collapse(3)
1937 #endif
1938 #endif
1939  for (int k = 0; k < m_number_of_slices; ++k)
1940  {
1941  for (int j = 0; j < m_height; ++j)
1942  {
1943  for (int i = 0; i < m_width; ++i)
1944  {
1945  temp_image.setPixel(i + 1, j + 1, k + 1, getPixel(i, j, k));
1946  }
1947  }
1948  }
1949 
1950  return temp_image;
1951 }
1952 
1953 
1954 //------------------------------------------------------------------------------------------
1955 template<typename T> Image<unsigned char> Image<T>::threshold(const T& aThreshold,
1956  unsigned char aValueIn,
1957  unsigned char aValueOut) const
1958 //------------------------------------------------------------------------------------------
1959 {
1960  Image<unsigned char> temp_image(m_width, m_height, m_number_of_slices, aValueOut, m_spacing);
1961 
1962 #ifdef NDEBUG
1963 #ifdef WIN32
1964  #pragma omp parallel for
1965 #else
1966  #pragma omp parallel for collapse(3)
1967 #endif
1968 #endif
1969  for (int k = 0; k < m_number_of_slices; ++k)
1970  {
1971  for (int j = 0; j < m_height; ++j)
1972  {
1973  for (int i = 0; i < m_width; ++i)
1974  {
1975  if (aThreshold <= getPixel(i, j, k))
1976  {
1977  temp_image.setPixel(i, j, k, aValueIn);
1978  }
1979  }
1980  }
1981  }
1982 
1983  return temp_image;
1984 }
1985 
1986 
1988 //template<typename T> Image<unsigned char> Image<T>::threshold(const T& aLowerThreshold,
1989 // const T& aUpperThreshold,
1990 // unsigned char aValueIn,
1991 // unsigned char aValueOut) const
1993 //{
1994 // Image<unsigned char> temp_image(m_width, m_height, m_number_of_slices, aValueOut, m_spacing);
1995 //
1996 //#ifdef NDEBUG
1997 //#ifdef WIN32
1998 // #pragma omp parallel for
1999 //#else
2000 // #pragma omp parallel for collapse(3)
2001 //#endif
2002 //#endif
2003 // for (int k = 0; k < m_number_of_slices; ++k)
2004 // {
2005 // for (int j = 0; j < m_height; ++j)
2006 // {
2007 // for (int i = 0; i < m_width; ++i)
2008 // {
2009 // T pixel_value = getPixel(i, j, k);
2010 // if (aLowerThreshold <= pixel_value && pixel_value < aUpperThreshold)
2011 // {
2012 // temp_image.setPixel(i, j, k, aValueIn);
2013 // }
2014 // }
2015 // }
2016 // }
2017 //
2018 // return temp_image;
2019 //}
2020 
2021 
2022 //--------------------------------------------------------
2023 template<typename T> Image<T> Image<T>::normalised() const
2024 //--------------------------------------------------------
2025 {
2026  return (shiftScaleFilter(-getMinValue(), 1.0 / (getMaxValue() - getMinValue())));
2027 }
2028 
2029 
2030 //--------------------------------------------------------
2031 template<typename T> Image<T> Image<T>::normalized() const
2032 //--------------------------------------------------------
2033 {
2034  return (normalised());
2035 }
2036 
2037 
2038 //------------------------------------------------------------
2039 template<typename T> Image<T> Image<T>::flipVertically() const
2040 //------------------------------------------------------------
2041 {
2042  Image<T> temp(*this);
2043  temp.m_spacing = m_spacing;
2044 
2045 #ifdef NDEBUG
2046 #ifdef WIN32
2047  #pragma omp parallel for
2048 #else
2049  #pragma omp parallel for collapse(3)
2050 #endif
2051 #endif
2052  for (int k = 0; k < m_number_of_slices; ++k)
2053  {
2054  for (int j = 0; j < (m_height / 2); ++j)
2055  {
2056  for (int i = 0; i < m_width; ++i)
2057  {
2058  std::swap(
2059  temp[k * m_height * m_width + j * m_width + i],
2060  temp[k * m_height * m_width + (m_height - 1 - j) * m_width + i]
2061  );
2062  }
2063  }
2064  }
2065 
2066  return (temp);
2067 }
2068 
2069 
2070 //--------------------------------------------------------------
2071 template<typename T> Image<T> Image<T>::flipHorizontally() const
2072 //--------------------------------------------------------------
2073 {
2074  Image<T> temp(*this);
2075  temp.m_spacing = m_spacing;
2076 
2077 #ifdef NDEBUG
2078 #ifdef WIN32
2079  #pragma omp parallel for
2080 #else
2081  #pragma omp parallel for collapse(3)
2082 #endif
2083 #endif
2084  for (unsigned int k = 0; k < m_number_of_slices; ++k)
2085  {
2086  for (unsigned int j = 0; j < m_height; ++j)
2087  {
2088  for (unsigned int i = 0; i < (m_width / 2); ++i)
2089  {
2090  std::swap(
2091  temp[k * m_height * m_width + j * m_width + i ],
2092  temp[k * m_height * m_width + j * m_width + (m_width - 1 - i)]
2093  );
2094  }
2095  }
2096  }
2097 
2098  return (temp);
2099 }
2100 
2101 
2102 //-------------------------------------------------
2103 template<typename T> Image<T> Image<T>::abs() const
2104 //-------------------------------------------------
2105 {
2106  Image<T> temp(*this);
2107  temp.m_spacing = m_spacing;
2108 
2109 #ifdef NDEBUG
2110  #pragma omp parallel for
2111 #endif
2112  for (unsigned int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
2113  {
2114  temp[i] = std::abs(m_p_image[i]);
2115  }
2116 
2117  return (temp);
2118 }
2119 
2120 
2121 //----------------------
2122 template<typename T> Image<T> Image<T>::log() const
2123 //----------------------
2124 {
2125  Image<T> temp(*this);
2126  temp.m_spacing = m_spacing;
2127 
2128  double offset(0.0);
2129 
2130  if (std::abs(getMinValue()) < EPSILON)
2131  {
2132  offset = 0.000000157;
2133  }
2134 
2135 #ifdef NDEBUG
2136  #pragma omp parallel for
2137 #endif
2138  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
2139  {
2140  double pixel(m_p_image[i]);
2141 
2142  if (std::abs(pixel) < EPSILON)
2143  {
2144  pixel = offset;
2145  }
2146 
2147  temp[i] = std::log(pixel);
2148  }
2149 
2150  return (temp);
2151 }
2152 
2153 
2154 //-------------------------------------------------------------
2155 template<typename T> void Image<T>::load(const char* aFileName)
2156 //-------------------------------------------------------------
2157 {
2158 #ifdef HAS_ITK
2159  loadUsingITK(aFileName);
2160 #else
2161  if (isMAT(aFileName) || isDAT(aFileName) || isTXT(aFileName))
2162  {
2163  loadASCII(aFileName);
2164  }
2165  else if (isPGM(aFileName))
2166  {
2167  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2168  "Loading PGM files is not implemented, "
2169  "please contact Dr FP Vidal.");
2170  }
2171  else if (isRAW(aFileName))
2172  {
2173  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2174  "Loading RAW files is not implemented, "
2175  "please contact Dr FP Vidal.");
2176  }
2177  else if (isMHD(aFileName))
2178  {
2179  loadMHD(aFileName);
2180  }
2181  else if (isMHA(aFileName))
2182  {
2183  loadMHA(aFileName);
2184  }
2185  else if (isDCM(aFileName))
2186  {
2187  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2188  "Loading DICOM files is not implemented, "
2189  "please contact Dr FP Vidal.");
2190  }
2191  else if (isTIFF(aFileName))
2192  {
2193  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2194  "Loading TIFF files is not implemented, "
2195  "please contact Dr FP Vidal.");
2196  }
2197  else if (isJPEG(aFileName))
2198  {
2199  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2200  "Loading JPEG files is not implemented, "
2201  "please contact Dr FP Vidal.");
2202  }
2203  else
2204  {
2205  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2206  "Unknown file format, to get it added to the library, "
2207  "please contact Dr FP Vidal.");
2208  }
2209 #endif
2210 }
2211 
2212 
2213 //--------------------------------------------------------------------
2214 template<typename T> void Image<T>::load(const std::string& aFileName)
2215 //--------------------------------------------------------------------
2216 {
2217  load(aFileName.data());
2218 }
2219 
2220 
2221 //---------------------------------------------------------------------
2222 template<typename T> void Image<T>::loadUsingITK(const char* aFileName)
2223 //---------------------------------------------------------------------
2224 {
2225 #ifdef HAS_ITK
2226 
2227  // Set some types
2228  typedef T PixelType;
2229  const unsigned int Dimension = 3;
2230 
2231  typedef itk::Image< PixelType, Dimension > ImageType;
2232  typedef itk::ImageFileReader < ImageType > ReaderType;
2233 
2234  // Set the loader
2235  typename ReaderType::Pointer p_reader(ReaderType::New());
2236  p_reader->SetFileName(aFileName);
2237  p_reader->Update();
2238 
2239  // Get the image size
2240  typename ImageType::RegionType region(p_reader->GetOutput()->GetLargestPossibleRegion());
2241  typename ImageType::SizeType size(region.GetSize());
2242 
2243  m_width = size[0];
2244  m_height = size[1];
2245  m_number_of_slices = size[2];
2246 
2247  const typename ImageType::SpacingType spacing(p_reader->GetOutput()->GetSpacing());
2248  m_spacing[0] = spacing[0];
2249  m_spacing[1] = spacing[1];
2250  m_spacing[2] = spacing[2];
2251 
2252  // Release the memory if needed
2253  if (m_p_image)
2254  {
2255  delete [] m_p_image;
2256  m_p_image = 0;
2257  }
2258 
2259  // Allocate the memory
2260  int number_of_pixels(m_width * m_height * m_number_of_slices);
2261  m_p_image = new T[number_of_pixels];
2262 
2263  // Copy the pixel data
2264  std::copy(p_reader->GetOutput()->GetBufferPointer(),
2265  p_reader->GetOutput()->GetBufferPointer() + number_of_pixels,
2266  m_p_image);
2267 
2268 #else
2269  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2270  "ITK is not supported.");
2271 #endif
2272 
2273 }
2274 
2275 
2276 //----------------------------------------------------------------------------
2277 template<typename T> void Image<T>::loadUsingITK(const std::string& aFileName)
2278 //----------------------------------------------------------------------------
2279 {
2280  loadUsingITK(aFileName.data());
2281 }
2282 
2283 
2284 //------------------------------------------------------------------
2285 template<typename T> void Image<T>::loadSeries(const char* aPattern,
2286  int aFirstSliceIndex,
2287  int aLastSliceIndex,
2288  int anIncrementIndex)
2289 //------------------------------------------------------------------
2290 {
2291 #ifdef HAS_ITK
2292 
2293  // Set some types
2294  typedef T PixelType;
2295  const unsigned int Dimension = 3;
2296 
2297  typedef itk::Image< PixelType, Dimension > ImageType;
2298  typedef itk::ImageSeriesReader< ImageType > ReaderType;
2299  typedef itk::NumericSeriesFileNames NameGeneratorType;
2300 
2301  // Create the file names
2302  NameGeneratorType::Pointer p_name_generator(NameGeneratorType::New());
2303 
2304  p_name_generator->SetSeriesFormat(aPattern);
2305  p_name_generator->SetStartIndex(aFirstSliceIndex);
2306  p_name_generator->SetEndIndex(aLastSliceIndex);
2307  p_name_generator->SetIncrementIndex(anIncrementIndex);
2308  std::vector<std::string> p_name_set(p_name_generator->GetFileNames());
2309 
2310  // Set the loader
2311  typename ReaderType::Pointer p_reader(ReaderType::New());
2312  p_reader->SetFileNames(p_name_set);
2313  p_reader->Update();
2314 
2315  // Get the image size
2316  typename ImageType::RegionType region(p_reader->GetOutput()->GetLargestPossibleRegion());
2317  typename ImageType::SizeType size(region.GetSize());
2318 
2319  m_width = size[0];
2320  m_height = size[1];
2321  m_number_of_slices = size[2];
2322 
2323  const typename ImageType::SpacingType spacing(p_reader->GetOutput()->GetSpacing());
2324  m_spacing[0] = spacing[0];
2325  m_spacing[1] = spacing[1];
2326  m_spacing[2] = spacing[2];
2327 
2328  // Release the memory if needed
2329  if (m_p_image)
2330  {
2331  delete [] m_p_image;
2332  m_p_image = 0;
2333  }
2334 
2335  // Allocate the memory
2336  int number_of_pixels(m_width * m_height * m_number_of_slices);
2337  m_p_image = new T[number_of_pixels];
2338 
2339  // Copy the pixel data
2340  std::copy(p_reader->GetOutput()->GetBufferPointer(),
2341  p_reader->GetOutput()->GetBufferPointer() + number_of_pixels,
2342  m_p_image);
2343 
2344 #else
2345  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2346  "ITK is not supported.");
2347 #endif
2348 }
2349 
2350 
2351 //-------------------------------------------------------------------------
2352 template<typename T> void Image<T>::loadSeries(const std::string& aPattern,
2353  int aFirstSliceIndex,
2354  int aLastSliceIndex,
2355  int anIncrementIndex)
2356 //-------------------------------------------------------------------------
2357 {
2358  loadSeries(aPattern.data(), aFirstSliceIndex, aLastSliceIndex, anIncrementIndex);
2359 }
2360 
2361 
2362 //-------------------------------------------------------------------------
2363 template<typename T> void Image<T>::loadDicomSeries(const char* aDirectory)
2364 //-------------------------------------------------------------------------
2365 {
2366 #ifdef HAS_ITK
2367 
2368  // Set some types
2369  typedef T PixelType;
2370  const unsigned int Dimension = 3;
2371 
2372  typedef itk::Image< PixelType, Dimension > ImageType;
2373  typedef itk::ImageSeriesReader< ImageType > ReaderType;
2374  typedef itk::GDCMSeriesFileNames InputNamesGeneratorType;
2375 
2376  // Create the file names
2377  InputNamesGeneratorType::Pointer p_name_generator(InputNamesGeneratorType::New());
2378  p_name_generator->SetInputDirectory(aDirectory);
2379  const typename ReaderType::FileNamesContainer& p_name_set(p_name_generator->GetInputFileNames());
2380 
2381  // Set the loader
2382  typename ReaderType::Pointer p_reader(ReaderType::New());
2383  p_reader->SetFileNames(p_name_set);
2384  p_reader->Update();
2385 
2386  // Get the image size
2387  typename ImageType::RegionType region(p_reader->GetOutput()->GetLargestPossibleRegion());
2388  typename ImageType::SizeType size(region.GetSize());
2389 
2390  m_width = size[0];
2391  m_height = size[1];
2392  m_number_of_slices = size[2];
2393 
2394  const typename ImageType::SpacingType spacing(p_reader->GetOutput()->GetSpacing());
2395  m_spacing[0] = spacing[0];
2396  m_spacing[1] = spacing[1];
2397  m_spacing[2] = spacing[2];
2398 
2399  // Release the memory if needed
2400  if (m_p_image)
2401  {
2402  delete [] m_p_image;
2403  m_p_image = 0;
2404  }
2405 
2406  // Allocate the memory
2407  int number_of_pixels(m_width * m_height * m_number_of_slices);
2408  m_p_image = new T[number_of_pixels];
2409 
2410  // Copy the pixel data
2411  std::copy(p_reader->GetOutput()->GetBufferPointer(),
2412  p_reader->GetOutput()->GetBufferPointer() + number_of_pixels,
2413  m_p_image);
2414 
2415 #else
2416  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2417  "ITK is not supported.");
2418 #endif
2419 }
2420 
2421 
2422 //--------------------------------------------------------------------------------
2423 template<typename T> void Image<T>::loadDicomSeries(const std::string& aDirectory)
2424 //--------------------------------------------------------------------------------
2425 {
2426  loadDicomSeries(aDirectory.data());
2427 }
2428 
2429 
2430 
2431 //------------------------------------------------------------------
2432 template<typename T> void Image<T>::save(const char* aFileName,
2433  bool anInvertLUTFlag,
2434  const char* aComment,
2435  bool useDeflateCompressionIfPossible) const
2436 //------------------------------------------------------------------
2437 {
2438  bool image_saved = false;
2439 
2440  if (isMAT(aFileName) || isDAT(aFileName) || isTXT(aFileName))
2441  {
2442  saveASCII(aFileName);
2443  image_saved = true;
2444  }
2445 
2446  else if (isPGM(aFileName))
2447  {
2448  savePGM(aFileName);
2449  image_saved = true;
2450  }
2451 
2452  else if (isRAW(aFileName))
2453  {
2454  saveRAW(aFileName, useDeflateCompressionIfPossible);
2455  image_saved = true;
2456  }
2457 
2458  else if (isMHD(aFileName))
2459  {
2460  saveMHD(aFileName, useDeflateCompressionIfPossible);
2461  image_saved = true;
2462  }
2463 
2464  else if (isMHA(aFileName))
2465  {
2466  saveMHA(aFileName, useDeflateCompressionIfPossible);
2467  image_saved = true;
2468  }
2469 
2470 #ifdef HAS_TIFF
2471  else if (isTIFF(aFileName))
2472  {
2473  saveTIFF(aFileName, anInvertLUTFlag);
2474  image_saved = true;
2475  }
2476 #endif
2477 
2478 #ifdef HAS_JPEG
2479  else if (isJPEG(aFileName))
2480  {
2481  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2482  "Saving JPEG files is not implemented, "
2483  "please contact Dr FP Vidal.");
2484  image_saved = true;
2485  }
2486 #endif
2487 
2488 #ifdef HAS_GDCM
2489  else if (isDCM(aFileName))
2490  {
2491  saveDCM(aFileName, anInvertLUTFlag, aComment);
2492  image_saved = true;
2493  }
2494 #endif
2495 
2496 #ifdef HAS_ITK
2497  if (!image_saved)
2498  {
2499  saveUsingITK(aFileName, useDeflateCompressionIfPossible);
2500  image_saved = true;
2501  }
2502 #endif
2503 
2504  if (!image_saved)
2505  {
2506  throw Exception(__FILE__, __FUNCTION__, __LINE__,
2507  "Unknown file format, to get it added to the library, "
2508  "please contact Dr FP Vidal.");
2509  }
2510 }
2511 
2512 
2513 //-----------------------------------------------------------------------
2514 template<typename T> void Image<T>::loadMHD(const std::string& aFileName)
2515 //-----------------------------------------------------------------------
2516 {
2517  loadMHD(aFileName.data());
2518 }
2519 
2520 
2521 //----------------------------------------------------------------
2522 template<typename T> void Image<T>::loadMHD(const char* aFileName)
2523 //----------------------------------------------------------------
2524 {
2525  // Load the meta header
2526  loadMetaHeader(aFileName);
2527 }
2528 
2529 
2530 //-----------------------------------------------------------------------
2531 template<typename T> void Image<T>::loadMHA(const std::string& aFileName)
2532 //-----------------------------------------------------------------------
2533 {
2534  loadMHA(aFileName.data());
2535 }
2536 
2537 
2538 //----------------------------------------------------------------
2539 template<typename T> void Image<T>::loadMHA(const char* aFileName)
2540 //----------------------------------------------------------------
2541 {
2542  // Load the meta header
2543  loadMetaHeader(aFileName);
2544 }
2545 
2546 
2547 //-------------------------------------------------------------------------
2548 template<typename T> void Image<T>::loadRAW(const char* aFileName,
2549  unsigned int aWidth,
2550  unsigned int aHeight,
2551  unsigned int aNumberOfSlices,
2552  bool aChangeEndianessFlag,
2553  const char* anElementType)
2554 //------------------------------------------------------------------------
2555 {
2556  // Release memory if any
2557  destroy();
2558 
2559  // Open the file
2560  std::ifstream input(aFileName, std::ios::binary|std::ios::in );
2561 
2562  // The file cannot be opened
2563  if (!input.is_open())
2564  {
2565  // Throw an error
2566  throw FileDoesNotExistException(__FILE__, __FUNCTION__, __LINE__, aFileName);
2567  }
2568 
2569  loadRAW(input, aWidth, aHeight, aNumberOfSlices, aChangeEndianessFlag, aFileName, anElementType);
2570 }
2571 
2572 //-----------------------------------------------------------------------
2573 template<typename T> void Image<T>::loadRAW(const std::string& aFileName,
2574  unsigned int aWidth,
2575  unsigned int aHeight,
2576  unsigned int aNumberOfSlices,
2577  bool aChangeEndianessFlag,
2578  const std::string& anElementType)
2579 //-----------------------------------------------------------------------
2580 {
2581  loadRAW(aFileName.data(), aWidth, aHeight, aNumberOfSlices, aChangeEndianessFlag, anElementType.data());
2582 }
2583 
2584 
2585 //-------------------------------------------------------------------------
2586 template<typename T> void Image<T>::save(const std::string& aFileName,
2587  bool anInvertLUTFlag,
2588  const std::string& aComment,
2589  bool useDeflateCompressionIfPossible) const
2590 //-------------------------------------------------------------------------
2591 {
2592  save(aFileName.data(), anInvertLUTFlag, aComment.data());
2593 }
2594 
2595 
2596 //----------------------------------------------
2597 template<typename T> void Image<T>::savePGM(const char* aFileName) const
2598 //----------------------------------------------
2599 {
2600  // Open the file
2601  std::ofstream output_file(aFileName);
2602 
2603  // The file does not exist
2604  if (!output_file.is_open())
2605  {
2606  // Build the error message
2607  std::stringstream error_message;
2608  error_message << "Cannot create the file \"" << aFileName << "\"";
2609 
2610  // Throw an error
2611  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message.str());
2612  }
2613  // The file is open
2614  else
2615  {
2616  // Get the min/max/range values
2617  T min_value(getMinValue());
2618  T max_value(getMaxValue());
2619  T range_value(max_value - min_value);
2620 
2621  // Set the image type
2622  output_file << "P2" << std::endl;
2623 
2624  // Print a comment
2625  output_file << "gVirtualXRay" << std::endl;
2626 
2627  // The image size
2628  output_file << m_width << " " << m_height * m_number_of_slices << std::endl;
2629 
2630  // The get the max value
2631  output_file << "65535" << std::endl;
2632 
2633  // Process every line
2634  for (unsigned int k = 0; k < m_number_of_slices; ++k)
2635  {
2636  for (unsigned int j = 0; j < m_height; ++j)
2637  {
2638  // Process every column
2639  for (unsigned int i = 0; i < m_width; ++i)
2640  {
2641  // Process the pixel
2642  int pixel_value;
2643  if (typeid(T) == typeid(unsigned char))
2644  {
2645  pixel_value = m_p_image[k * m_width * m_height + j * m_width + i];
2646  }
2647  else
2648  {
2649  pixel_value = 65535.0 * (m_p_image[k * m_width * m_height + j * m_width + i] - min_value) / range_value;
2650  }
2651 
2652  pixel_value = std::max(0, pixel_value);
2653  pixel_value = std::min(65535, pixel_value);
2654 
2655  output_file << pixel_value;
2656 
2657  // It is not the last pixel of the line
2658  if (i < (m_width - 1))
2659  {
2660  output_file << " ";
2661  }
2662  }
2663 
2664  // It is not the last line of the image
2665  if (j < (m_height - 1))
2666  {
2667  output_file << std::endl;
2668  }
2669  }
2670  }
2671  }
2672 }
2673 
2674 
2675 //-----------------------------------------------------
2676 template<typename T> void Image<T>::savePGM(const std::string& aFileName) const
2677 //-----------------------------------------------------
2678 {
2679  savePGM(aFileName.data());
2680 }
2681 
2682 
2683 //----------------------------------------------
2684 template<typename T> void Image<T>::saveRaw(const char* aFileName, bool useDeflateCompressionIfPossible) const
2685 //----------------------------------------------
2686 {
2687  // Open the file in binary
2688  if (!useDeflateCompressionIfPossible)
2689  {
2690  std::ofstream output_file (aFileName, std::ifstream::binary);
2691 
2692  // The file is not open
2693  if (!output_file.is_open())
2694  {
2695  std::string error_message("The file (");
2696  error_message += aFileName;
2697  error_message += ") cannot be created";
2698 
2699  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2700  }
2701 
2702  // Write content to file
2703  output_file.write(reinterpret_cast<char*>(m_p_image), m_width * m_height * m_number_of_slices * sizeof(T));
2704  }
2705  else
2706  {
2707  gzFile output_file = gzopen(aFileName,"wb");
2708 
2709  // The file is not open
2710  if (!output_file)
2711  {
2712  std::string error_message("The file (");
2713  error_message += aFileName;
2714  error_message += ") cannot be created";
2715 
2716  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2717  }
2718 
2719  int write_size = gzwrite( output_file, reinterpret_cast<char*>(m_p_image), m_width * m_height * m_number_of_slices * sizeof(T));
2720  int close_err = gzclose( output_file );
2721 
2722  if (write_size == 0)
2723  {
2724  std::string error_message("The file (");
2725  error_message += aFileName;
2726  error_message += ") cannot be written";
2727 
2728  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2729  }
2730 
2731  if (close_err == Z_STREAM_ERROR)
2732  {
2733  std::string error_message("The file (");
2734  error_message += aFileName;
2735  error_message += ") is not valid";
2736 
2737  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2738  }
2739 
2740  if (close_err == Z_ERRNO)
2741  {
2742  std::string error_message("File operation error on the file (");
2743  error_message += aFileName;
2744  error_message += ").";
2745 
2746  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2747  }
2748 
2749  if (close_err == Z_MEM_ERROR)
2750  {
2751  std::string error_message("File operation error on the file (");
2752  error_message += aFileName;
2753  error_message += ").";
2754 
2755  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
2756  }
2757 
2758  if (close_err == Z_BUF_ERROR)
2759  {
2760  std::string error_message("The last read ended in the middle of a gzip stream when writing the file (");
2761  error_message += aFileName;
2762  error_message += ").";
2763 
2764  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2765  }
2766 
2767  if (close_err != Z_OK)
2768  {
2769  std::string error_message("Unspecified error when writing the file (");
2770  error_message += aFileName;
2771  error_message += ").";
2772 
2773  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2774  }
2775  }
2776 }
2777 
2778 
2779 //----------------------------------------------
2780 template<typename T> void Image<T>::saveRAW(const char* aFileName, bool useDeflateCompressionIfPossible) const
2781 //----------------------------------------------
2782 {
2783  saveRaw(aFileName, useDeflateCompressionIfPossible);
2784 }
2785 
2786 
2787 //-----------------------------------------------------
2788 template<typename T> void Image<T>::saveRaw(const std::string& aFileName,
2789 bool useDeflateCompressionIfPossible) const
2790 //-----------------------------------------------------
2791 {
2792  saveRaw(aFileName.data(), useDeflateCompressionIfPossible);
2793 }
2794 
2795 
2796 //-----------------------------------------------------
2797 template<typename T> void Image<T>::saveRAW(const std::string& aFileName, bool useDeflateCompressionIfPossible) const
2798 //-----------------------------------------------------
2799 {
2800  saveRaw(aFileName, useDeflateCompressionIfPossible);
2801 }
2802 
2803 
2804 //------------------------------------------------
2805 template<typename T> void Image<T>::saveASCII(const char* aFileName) const
2806 //------------------------------------------------
2807 {
2808  // Open the file
2809  std::ofstream output_file (aFileName);
2810 
2811  // The file is not open
2812  if (!output_file.is_open())
2813  {
2814  std::string error_message("The file (");
2815  error_message += aFileName;
2816  error_message += ") cannot be created";
2817 
2818  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
2819  }
2820 
2821  // Write content to file
2822  T* p_data(m_p_image);
2823  for (unsigned int k = 0; k < m_number_of_slices; ++k)
2824  {
2825  for (unsigned int j(0); j < m_height; ++j)
2826  {
2827  for (unsigned int i(0); i < m_width; ++i)
2828  {
2829  output_file << *p_data++;
2830 
2831  // This is not the last pixel of the line
2832  if (i < m_width - 1)
2833  {
2834  output_file << " ";
2835  }
2836  }
2837 
2838  // This is not the last line
2839  if (j < m_height - 1 || k < m_number_of_slices - 1)
2840  {
2841  output_file << std::endl;
2842  }
2843  }
2844  }
2845 }
2846 
2847 
2848 //-------------------------------------------------------
2849 template<typename T> void Image<T>::saveASCII(const std::string& aFileName) const
2850 //-------------------------------------------------------
2851 {
2852  saveASCII(aFileName.data());
2853 }
2854 
2855 
2856 //----------------------------------------------------------------------
2857 template<typename T> void Image<T>::saveMHD(const char* aFileName, bool useDeflateCompressionIfPossible) const
2858 //----------------------------------------------------------------------
2859 {
2860  // Save the header
2861  saveMetaHeader(aFileName, useDeflateCompressionIfPossible);
2862 }
2863 
2864 
2865 template<typename T> void Image<T>::saveMHD(const std::string& aFileName, bool useDeflateCompressionIfPossible) const
2866 {
2867  saveMHD(aFileName.data(), useDeflateCompressionIfPossible);
2868 }
2869 
2870 
2871 template<typename T> void Image<T>::saveMHA(const char* aFileName, bool useDeflateCompressionIfPossible) const
2872 {
2873  saveMetaHeader(aFileName, useDeflateCompressionIfPossible);
2874 }
2875 
2876 
2877 template<typename T> void Image<T>::saveMHA(const std::string& aFileName, bool useDeflateCompressionIfPossible) const
2878 {
2879  saveMHA(aFileName.data(), useDeflateCompressionIfPossible);
2880 }
2881 
2882 
2883 //---------------------------------------------------------------------
2884 template<typename T> void Image<T>::saveDCM(const char* aFileName,
2885  bool anInvertLUTFlag,
2886  const char* aComment) const
2887 //---------------------------------------------------------------------
2888 {
2889 #ifdef HAS_GDCM
2890  T min_input_value(getMinValue());
2891  T max_input_value(getMaxValue());
2892  T difference_input(max_input_value - min_input_value);
2893 
2894  unsigned short min_output_value(((gdcm::PixelFormat)gdcm::PixelFormat::UINT16).GetMin());
2895  unsigned short max_output_value(((gdcm::PixelFormat)gdcm::PixelFormat::UINT16).GetMax());
2896  unsigned short difference_output(max_output_value - min_output_value);
2897 
2898  double shift(min_input_value);
2899  double scale(double(max_input_value - min_input_value) / max_output_value);
2900 
2901  if (m_number_of_slices == 1)
2902  {
2903  // Write the modified DataSet back to disk
2904  gdcm::ImageWriter writer;
2905  //writer.CheckFileMetaInformationOff(); // Do not attempt to reconstruct the file meta to preserve the file
2906  // as close to the original as possible.
2907 
2908  // Create the image (GDCM format)
2909  gdcm::Image& gdcm_image(writer.GetImage());
2910 
2911  // Set the size of the image
2912  unsigned int dims[3] = {};
2913  dims[0] = m_width;
2914  dims[1] = m_height;
2915 
2916  double spacing[3] = {};
2917  spacing[0] = m_spacing[0];
2918  spacing[1] = m_spacing[1];
2919 
2920  if (m_number_of_slices == 1)
2921  {
2922  gdcm_image.SetNumberOfDimensions(2);
2923  }
2924  else
2925  {
2926  dims[2] = m_number_of_slices;
2927  gdcm_image.SetNumberOfDimensions(3);
2928 
2929  spacing[2] = 1;//m_spacing[2];
2930  }
2931 
2932  gdcm_image.SetSpacing (spacing);
2933 
2934  gdcm_image.SetDimensions( dims );
2935 
2936 
2937  unsigned short* p_temp(new unsigned short[m_width * m_height * m_number_of_slices]);
2938 
2939 #ifdef NDEBUG
2940  #pragma omp parallel for
2941 #endif
2942  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
2943  {
2944  p_temp[i] = min_output_value + difference_output * (double(m_p_image[i] - min_input_value) / double(difference_input));
2945  }
2946 
2947  unsigned int input_length(m_width * m_height * m_number_of_slices * sizeof(T));
2948  unsigned int output_length(m_width * m_height * m_number_of_slices * sizeof(unsigned short));
2949 
2950  gdcm::PixelFormat input_pixel_format(gdcm::PixelFormat::UINT16);
2951  input_pixel_format.SetSamplesPerPixel(1);
2952 
2953  gdcm_image.SetSlope(scale);
2954  gdcm_image.SetIntercept(shift);
2955 
2956  //gdcm_image.SetPixelFormat(pixel_format.ComputeInterceptSlopePixelType());
2957  gdcm_image.SetPixelFormat(input_pixel_format);
2958  gdcm::PhotometricInterpretation photometric_interpretation;
2959 
2960  if (anInvertLUTFlag)
2961  {
2962  photometric_interpretation = gdcm::PhotometricInterpretation::MONOCHROME2;
2963  }
2964  else
2965  {
2966  photometric_interpretation = gdcm::PhotometricInterpretation::MONOCHROME1;
2967  }
2968  gdcm_image.SetPhotometricInterpretation(photometric_interpretation);
2969  gdcm_image.SetTransferSyntax(gdcm::TransferSyntax::ExplicitVRLittleEndian);
2970 
2971 
2972 
2973  // Disable compression
2974  gdcm_image.SetLossyFlag(false);
2975 
2976 
2977  // Window / Level
2978  addTag(difference_input,
2979  0x0028, 0x1051,
2980  writer.GetFile());
2981 
2982  addTag(min_input_value + difference_input / 2.0,
2983  0x0028,0x1050,
2984  writer.GetFile());
2985 
2986  // Study description
2987  addTag("X-ray simulation",
2988  0x0008,0x1030,
2989  writer.GetFile());
2990 
2991  // Patient
2992  addTag("Patient",
2993  0x0010,0x0010,
2994  writer.GetFile());
2995 
2996  // Modality
2997  addTag(aComment,
2998  0x0008,0x0060,
2999  writer.GetFile());
3000 
3001  // Manufacturer
3002  addTag("gVirtualXRay",
3003  0x0008,0x0070,
3004  writer.GetFile());
3005 
3006  // InstitutionName
3007  addTag("Bangor University",
3008  0x0008,0x0080,
3009  writer.GetFile());
3010 
3011  // InstitutionAddress
3012  addTag("http://gvirtualxray.sourceforge.net/",
3013  0x0008,0x0081,
3014  writer.GetFile());
3015 
3016  // DICOM fields for X-rays:
3017  // See http://dicomlookup.com/lookup.asp?sw=Ttable&q=C.8-27
3018 
3019 
3020  // Set pixel data
3021  gdcm::DataElement pixel_data(gdcm::Tag(0x7fe0,0x0010));
3022  pixel_data.SetByteValue((const char*)p_temp, uint32_t(output_length));
3023  gdcm_image.SetDataElement(pixel_data);
3024 
3025  // Set the file name
3026  writer.SetFileName( aFileName );
3027 
3028  // Save the data
3029  if( !writer.Write() )
3030  {
3031  delete [] p_temp;
3032  p_temp = 0;
3033 
3034  std::string error_message("Could not write the DICOM file (");
3035  error_message += aFileName;
3036  error_message += ")";
3037  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3038  }
3039  delete [] p_temp;
3040  p_temp = 0;
3041  }
3042 
3043 #else
3044 
3045  throw Exception(__FILE__, __FUNCTION__, __LINE__,
3046  "Saving DICOM files is not implemented, "
3047  "please contact Dr FP Vidal.");
3048 
3049 #endif
3050 }
3051 
3052 
3053 //----------------------------------------------------------------------------
3054 template<typename T> void Image<T>::saveDCM(const std::string& aFileName,
3055  bool anInvertLUTFlag,
3056  const std::string& aComment) const
3057 //----------------------------------------------------------------------------
3058 {
3059  saveDCM(aFileName.data(), anInvertLUTFlag, aComment.data());
3060 }
3061 
3062 
3063 //----------------------------------------------------------------------
3064 template<typename T> void Image<T>::saveTIFF(const char* aFileName,
3065  bool anInvertLUTFlag) const
3066 //----------------------------------------------------------------------
3067 {
3068 #ifdef HAS_TIFF
3069 
3070  float* p_float_data(0);
3071  unsigned int number_of_pixels(m_width * m_height);
3072 
3073 
3074  TIFF_UINT16_T number_of_bits_per_pixel(0);
3075 
3076  if (typeid(T) == typeid(char) || typeid(T) == typeid(unsigned char))
3077  {
3078  number_of_bits_per_pixel = 8;
3079  }
3080  else if (typeid(T) == typeid(short) || typeid(T) == typeid(unsigned short))
3081  {
3082  number_of_bits_per_pixel = 16;
3083  }
3084  else if (typeid(T) == typeid(float))
3085  {
3086  number_of_bits_per_pixel = 32;
3087  }
3088  else
3089  {
3090  number_of_bits_per_pixel = 32;
3091  p_float_data = new float[number_of_pixels];
3092  }
3093 
3094  // Open the file
3095  TIFF* p_file = TIFFOpen(aFileName, "w");
3096 
3097  // The file is not open
3098  if (!p_file)
3099  {
3100  std::string error_message("The file (");
3101  error_message += aFileName;
3102  error_message += ") cannot be created";
3103 
3104  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3105  }
3106 
3107  // Write all the slices
3108  for (unsigned int k(0); k < m_number_of_slices; k++)
3109  {
3110  // Get the min/max/range values
3111  T min_value(getMinValue(k));
3112  T max_value(getMaxValue(k));
3113  T value_range(max_value - min_value);
3114 
3115  // Convert the image if necessary
3116  if (p_float_data)
3117  {
3118 #ifdef NDEBUG
3119  #pragma omp parallel for
3120 #endif
3121  for (int i = 0; i < number_of_pixels; ++i)
3122  {
3123  float temp(double(m_p_image[k * m_width * m_height + i] - min_value) / value_range);
3124  p_float_data[i] = temp;
3125  }
3126  }
3127 
3128  // Create a new directory
3129  if (m_number_of_slices > 1)
3130  {
3131  if (TIFFWriteDirectory(p_file) != 1)
3132  {
3133  std::string error_message("Could not write the new directory in the TIFF file (");
3134  error_message += aFileName;
3135  error_message += ")";
3136  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3137  }
3138  }
3139 
3140  // Set the image width
3141  TIFF_UINT32_T image_width(m_width);
3142 
3143  if (TIFFSetField(p_file, TIFFTAG_IMAGEWIDTH, image_width) != 1)
3144  {
3145  std::string error_message("Could not set the image width in the TIFF file (");
3146  error_message += aFileName;
3147  error_message += ")";
3148  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3149  }
3150 
3151  // Set the image height
3152  TIFF_UINT32_T image_height(m_height);
3153 
3154  if (TIFFSetField(p_file, TIFFTAG_IMAGELENGTH, image_height) != 1)
3155  {
3156  std::string error_message("Could not set the image height in the TIFF file (");
3157  error_message += aFileName;
3158  error_message += ")";
3159  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3160  }
3161 
3162  // Set the number of bits per pixel
3163  if (TIFFSetField(p_file, TIFFTAG_BITSPERSAMPLE, number_of_bits_per_pixel) != 1)
3164  {
3165  std::string error_message("Could not set the number of bits per pixel in the TIFF file (");
3166  error_message += aFileName;
3167  error_message += ")";
3168  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3169  }
3170 
3171  // Set the compression
3172  TIFF_UINT16_T compression(COMPRESSION_LZW);
3173 
3174  if (TIFFSetField(p_file, TIFFTAG_COMPRESSION, compression) != 1)
3175  {
3176  std::string error_message("Could not set the compression in the TIFF file (");
3177  error_message += aFileName;
3178  error_message += ")";
3179  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3180  }
3181 
3182  // Set the photometric interpretation
3183  TIFF_UINT16_T photometric_interpretation;
3184  if (anInvertLUTFlag)
3185  {
3186  photometric_interpretation = PHOTOMETRIC_MINISWHITE;
3187  }
3188  else
3189  {
3190  photometric_interpretation = PHOTOMETRIC_MINISBLACK;
3191  }
3192 
3193  if (TIFFSetField(p_file, TIFFTAG_PHOTOMETRIC, photometric_interpretation) != 1)
3194  {
3195  std::string error_message("Could not set the photometric interpretation in the TIFF file (");
3196  error_message += aFileName;
3197  error_message += ")";
3198  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3199  }
3200 
3201  // Set the orientation
3202  TIFF_UINT16_T orientation(ORIENTATION_TOPLEFT);
3203 
3204  if (TIFFSetField(p_file, TIFFTAG_ORIENTATION, orientation) != 1)
3205  {
3206  std::string error_message("Could not set the orientation in the TIFF file (");
3207  error_message += aFileName;
3208  error_message += ")";
3209  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3210  }
3211 
3212  // Set the number of samples per pixel
3213  TIFF_UINT16_T number_of_samples_per_pixel(1);
3214 
3215  if (TIFFSetField(p_file, TIFFTAG_SAMPLESPERPIXEL, number_of_samples_per_pixel) != 1)
3216  {
3217  std::string error_message("Could not set the number of samples per pixel in the TIFF file (");
3218  error_message += aFileName;
3219  error_message += ")";
3220  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3221  }
3222 
3223  // Set the min sample value
3224  if (TIFFSetField(p_file, TIFFTAG_MINSAMPLEVALUE, min_value) != 1)
3225  {
3226  std::string error_message("Could not set the min sample value in the TIFF file (");
3227  error_message += aFileName;
3228  error_message += ")";
3229  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3230  }
3231 
3232  // Set the max sample value
3233  if (TIFFSetField(p_file, TIFFTAG_MAXSAMPLEVALUE, max_value) != 1)
3234  {
3235  std::string error_message("Could not set the max sample value in the TIFF file (");
3236  error_message += aFileName;
3237  error_message += ")";
3238  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3239  }
3240 
3241  // Set the planar configuration
3242  TIFF_UINT16_T planar_configuration(PLANARCONFIG_CONTIG);
3243 
3244  if (TIFFSetField(p_file, TIFFTAG_PLANARCONFIG, planar_configuration) != 1)
3245  {
3246  std::string error_message("Could not set the planar configuration in the TIFF file (");
3247  error_message += aFileName;
3248  error_message += ")";
3249  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3250  }
3251 
3252  std::string software;
3253  software += "gVirtualXRay-";
3254  software += gVirtualXRay_VERSION_MAJOR;
3255  software += ".";
3256  software += gVirtualXRay_VERSION_MINOR;
3257  software += ".";
3258  software += gVirtualXRay_VERSION_PATCH;
3259 
3260  if (TIFFSetField(p_file, TIFFTAG_COPYRIGHT, software.data()) != 1)
3261  {
3262  std::string error_message("Could not set the software value in the TIFF file (");
3263  error_message += aFileName;
3264  error_message += ")";
3265  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3266  }
3267 
3268  // Set the format of a sample
3269  TIFF_UINT16_T format_of_a_sample;
3270 
3271  if (typeid(T) == typeid(unsigned char) || typeid(T) == typeid(unsigned short))
3272  {
3273  format_of_a_sample = SAMPLEFORMAT_UINT;
3274  }
3275  else if (typeid(T) == typeid(char) || typeid(T) == typeid(short))
3276  {
3277  format_of_a_sample = SAMPLEFORMAT_INT;
3278  }
3279  else
3280  {
3281  format_of_a_sample = SAMPLEFORMAT_IEEEFP;
3282  }
3283 
3284  if (TIFFSetField(p_file, TIFFTAG_SAMPLEFORMAT, format_of_a_sample) != 1)
3285  {
3286  std::string error_message("Could not set the compression in the TIFF file (");
3287  error_message += aFileName;
3288  error_message += ")";
3289  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3290  }
3291 
3292  // Length in memory of one row of pixel in the image
3293  tsize_t number_of_bytes_per_line(number_of_samples_per_pixel * m_width);
3294 
3295  // We set the strip size of the file to be size of one row of pixels
3296  TIFFSetField(p_file, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(p_file, number_of_bytes_per_line));
3297 
3298 
3299  // Write the image data, line by line
3300  int error_code(0);
3301  for (unsigned int j(0); j < m_height; ++j)
3302  {
3303  if (p_float_data)
3304  {
3305  error_code = TIFFWriteScanline(p_file, p_float_data + m_width * j, j, 0);
3306  }
3307  else
3308  {
3309  error_code = TIFFWriteScanline(p_file, m_p_image + m_width * m_height * k + m_width * j, j, 0);
3310  }
3311 
3312  if (error_code != 1)
3313  {
3314  // Close the file
3315  TIFFClose(p_file);
3316 
3317  // Release the memory if necessary
3318  if (p_float_data)
3319  {
3320  delete [] p_float_data;
3321  p_float_data = 0;
3322  }
3323 
3324  // Generate an error
3325  std::stringstream error_message;
3326  error_message << "Could not write the pixel data (Line " << j << ") in the TIFF file (";
3327  error_message << aFileName;
3328  error_message << ")";
3329  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message.str());
3330  }
3331  }
3332 
3333  // Flush the data in the TIFF file
3334  if (m_number_of_slices > 1)
3335  {
3336  if (TIFFFlush(p_file) != 1)
3337  {
3338  std::string error_message("Could not flush the data in the TIFF file (");
3339  error_message += aFileName;
3340  error_message += ")";
3341  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
3342  }
3343  }
3344  }
3345 
3346  // Release the memory if necessary
3347  if (p_float_data)
3348  {
3349  delete [] p_float_data;
3350  p_float_data = 0;
3351  }
3352 
3353  // Close the file
3354  TIFFClose(p_file);
3355 
3356 #else
3357 
3358  throw Exception(__FILE__, __FUNCTION__, __LINE__,
3359  "Saving TIFF files is not implemented, "
3360  "please contact Dr FP Vidal.");
3361 
3362 #endif
3363 }
3364 
3365 
3366 //------------------------------------------------------------------------
3367 template<typename T> void Image<T>::saveTIFF(const std::string& aFileName,
3368  bool anInvertLUTFlag) const
3369 //------------------------------------------------------------------------
3370 {
3371  saveTIFF(aFileName.data(), anInvertLUTFlag);
3372 }
3373 
3374 
3375 //------------------------------------------------------------------------------------------
3376 template<typename T> void Image<T>::saveUsingITK(const char* aFileName,
3377  bool useDeflateCompressionIfPossible) const
3378 //------------------------------------------------------------------------------------------
3379 {
3380 #ifdef HAS_ITK
3381  // 3D volume
3382  if (m_number_of_slices > 1 || isDCM(aFileName))
3383  {
3384  typedef itk::Image< T, 3 > InputImageType;
3385 
3386  typename InputImageType::RegionType region;
3387  typename InputImageType::IndexType start;
3388  start[0] = 0;
3389  start[1] = 0;
3390  start[2] = 0;
3391 
3392  typename InputImageType::SizeType size;
3393  size[0] = m_width;
3394  size[1] = m_height;
3395  size[2] = m_number_of_slices;
3396 
3397  region.SetSize(size);
3398  region.SetIndex(start);
3399 
3400  typename InputImageType::Pointer p_image(InputImageType::New());
3401  p_image->SetRegions(region);
3402  p_image->Allocate();
3403 
3404 
3405  typename InputImageType::SpacingType spacing;
3406  spacing[0] = m_spacing[0];
3407  spacing[1] = m_spacing[1];
3408  spacing[2] = m_spacing[2];
3409  p_image->SetSpacing(spacing);
3410 
3411  // Copy the pixel data
3412  int number_of_pixels(m_width * m_height * m_number_of_slices);
3413  std::copy(m_p_image,
3414  m_p_image + number_of_pixels,
3415  p_image->GetBufferPointer());
3416 
3417  // Save the data in SHORT
3418  if (isDCM(aFileName))
3419  {
3420  typedef itk::Image< short, 3 > ShortImageType;
3421  typedef itk::ImageFileWriter< ShortImageType > ShortWriterType;
3422 
3423  typedef itk::RescaleIntensityImageFilter< InputImageType, ShortImageType > RescaleType;
3424  typename RescaleType::Pointer rescale = RescaleType::New();
3425  rescale->SetInput( p_image );
3426  rescale->Update();
3427 
3428  typename ShortWriterType::Pointer p_writer(ShortWriterType::New());
3429  p_writer->SetFileName( aFileName );
3430  p_writer->SetInput( rescale->GetOutput() );
3431  p_writer->SetUseCompression(useDeflateCompressionIfPossible);
3432  p_writer->Update();
3433  }
3434  // Save the data in its native format
3435  else
3436  {
3437  typedef itk::ImageFileWriter< InputImageType > InputWriterType;
3438  typename InputWriterType::Pointer p_writer(InputWriterType::New());
3439  p_writer->SetFileName( aFileName );
3440  p_writer->SetInput( p_image );
3441  p_writer->SetUseCompression(useDeflateCompressionIfPossible);
3442  p_writer->Update();
3443  }
3444  }
3445  // 2D image
3446  else
3447  {
3448  typedef itk::Image< T, 2 > InputImageType;
3449 
3450  typename InputImageType::RegionType region;
3451  typename InputImageType::IndexType start;
3452  start[0] = 0;
3453  start[1] = 0;
3454 
3455  typename InputImageType::SizeType size;
3456  size[0] = m_width;
3457  size[1] = m_height;
3458 
3459  region.SetSize(size);
3460  region.SetIndex(start);
3461 
3462  typename InputImageType::Pointer p_image(InputImageType::New());
3463  p_image->SetRegions(region);
3464  p_image->Allocate();
3465 
3466 
3467  typename InputImageType::SpacingType spacing;
3468  spacing[0] = m_spacing[0];
3469  spacing[1] = m_spacing[1];
3470  p_image->SetSpacing(spacing);
3471 
3472  // Copy the pixel data
3473  int number_of_pixels(m_width * m_height);
3474  std::copy(m_p_image,
3475  m_p_image + number_of_pixels,
3476  p_image->GetBufferPointer());
3477 
3478 
3479  // Save the data in UCHAR
3480  if (isJPEG(aFileName) ||
3481  checkExtension(aFileName, "PNG") ||
3482  checkExtension(aFileName, "BMP"))
3483  {
3484  typedef itk::Image< unsigned char, 2 > UByteImageType;
3485  typedef itk::ImageFileWriter< UByteImageType > UByteWriterType;
3486 
3487  typedef itk::RescaleIntensityImageFilter< InputImageType, UByteImageType > RescaleType;
3488  typename RescaleType::Pointer rescale = RescaleType::New();
3489  rescale->SetInput( p_image );
3490  rescale->SetOutputMinimum( 0 );
3491  rescale->SetOutputMaximum( 255 );
3492  rescale->Update();
3493 
3494  typename UByteWriterType::Pointer p_writer(UByteWriterType::New());
3495  p_writer->SetFileName( aFileName );
3496  p_writer->SetInput( rescale->GetOutput() );
3497  p_writer->SetUseCompression(useDeflateCompressionIfPossible);
3498  p_writer->Update();
3499  }
3500  // Save the data in its native format
3501  else
3502  {
3503  typedef itk::ImageFileWriter< InputImageType > InputWriterType;
3504  typename InputWriterType::Pointer p_writer(InputWriterType::New());
3505  p_writer->SetFileName( aFileName );
3506  p_writer->SetInput( p_image );
3507  p_writer->SetUseCompression(useDeflateCompressionIfPossible);
3508  p_writer->Update();
3509  }
3510  }
3511 #else
3512  throw Exception(__FILE__, __FUNCTION__, __LINE__,
3513  "ITK is not supported.");
3514 #endif
3515 }
3516 
3517 
3518 //------------------------------------------------------------------------------------------
3519 template<typename T> void Image<T>::saveUsingITK(const std::string& aFileName,
3520  bool useDeflateCompressionIfPossible) const
3521 //------------------------------------------------------------------------------------------
3522 {
3523  saveUsingITK(aFileName.data(), useDeflateCompressionIfPossible);
3524 }
3525 
3526 
3527 //------------------------------------------------
3528 template<typename T> bool Image<T>::operator==(const Image<T>& anImage) const
3529 //------------------------------------------------
3530 {
3531  if (m_width != anImage.m_width)
3532  {
3533  return (false);
3534  }
3535 
3536  if (m_height != anImage.m_height)
3537  {
3538  return (false);
3539  }
3540 
3541  if (m_number_of_slices != anImage.m_number_of_slices)
3542  {
3543  return (false);
3544  }
3545 
3546  T const * p_data1(m_p_image);
3547  T const * p_data2(anImage.m_p_image);
3548  for (unsigned int i(0); i < m_width * m_height * m_number_of_slices; ++i)
3549  {
3550  if (std::abs(*p_data1++ - *p_data2++) > EPSILON)
3551  {
3552  return (false);
3553  }
3554  }
3555 
3556  return (true);
3557 }
3558 
3559 
3560 //------------------------------------------------
3561 template<typename T> bool Image<T>::operator!=(const Image<T>& anImage) const
3562 //------------------------------------------------
3563 {
3564  return (!(operator==(anImage)));
3565 }
3566 
3567 
3568 //--------------------------
3569 template<typename T> double Image<T>::getSum() const
3570 //--------------------------
3571 {
3572  // The image is empty
3573  if (!m_p_image)
3574  {
3575  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Empty image");
3576  }
3577 
3578  return (std::accumulate(&m_p_image[0],
3579  &m_p_image[m_width * m_height * m_number_of_slices],
3580  0.0));
3581 }
3582 
3583 
3584 //------------------------------
3585 template<typename T> double Image<T>::getAverage() const
3586 //------------------------------
3587 {
3588  return (getSum() / (m_width * m_height));
3589 }
3590 
3591 
3592 //-------------------------------
3593 template<typename T> double Image<T>::getVariance() const
3594 //-------------------------------
3595 {
3596  double variance(0.0);
3597  double average(getAverage());
3598 
3599 #ifdef NDEBUG
3600  #pragma omp parallel for reduction( + : variance )
3601 #endif
3602  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
3603  {
3604  variance += std::pow(m_p_image[i] - average, 2);
3605  }
3606 
3607  return (variance / (m_width * m_height));
3608 }
3609 
3610 
3611 //----------------------------------------
3612 template<typename T> double Image<T>::getStandardDeviation() const
3613 //----------------------------------------
3614 {
3615  return (std::sqrt(getVariance()));
3616 }
3617 
3618 
3619 //-----------------------------------------------------------------------------
3620 template<typename T> double Image<T>::computeSAE(const Image<T>& anImage) const
3621 //-----------------------------------------------------------------------------
3622 {
3623  if (m_width != anImage.m_width || m_height != anImage.m_height || m_number_of_slices != anImage.m_number_of_slices)
3624  {
3625  throw Exception(__FILE__, __FUNCTION__, __LINE__, "The images have different sizes");
3626  }
3627 
3628  double sae(0.0);
3629 
3630 #ifdef NDEBUG
3631  #pragma omp parallel for reduction( + : sae )
3632 #endif
3633  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
3634  {
3635  sae += std::abs(m_p_image[i] - anImage.m_p_image[i]);
3636  }
3637 
3638  return (sae);
3639 }
3640 
3641 
3642 //-----------------------------------------------------------------------------
3643 template<typename T> double Image<T>::computeMAE(const Image<T>& anImage) const
3644 //-----------------------------------------------------------------------------
3645 {
3646  return (computeSAE(anImage) / double(m_width * m_height * m_number_of_slices));
3647 }
3648 
3649 
3650 //-----------------------------------------------------------------------------
3651 template<typename T> double Image<T>::computeSSE(const Image<T>& anImage) const
3652 //-----------------------------------------------------------------------------
3653 {
3654  if (m_width != anImage.m_width || m_height != anImage.m_height || m_number_of_slices != anImage.m_number_of_slices)
3655  {
3656  throw Exception(__FILE__, __FUNCTION__, __LINE__, "The images have different sizes");
3657  }
3658 
3659  double sse(0.0);
3660  double temp;
3661 
3662 #ifdef NDEBUG
3663  #pragma omp parallel for reduction( + : sse ) private(temp)
3664 #endif
3665  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
3666  {
3667  temp = m_p_image[i] - anImage.m_p_image[i];
3668  temp *= temp;
3669 
3670  sse += temp;
3671  }
3672 
3673  return (sse);
3674 }
3675 
3676 
3677 //-----------------------------------------------------------------------------
3678 template<typename T> double Image<T>::computeMSE(const Image<T>& anImage) const
3679 //-----------------------------------------------------------------------------
3680 {
3681  return (computeSSE(anImage) / double(m_width * m_height * m_number_of_slices));
3682 }
3683 
3684 
3685 //------------------------------------------------------------------------------
3686 template<typename T> double Image<T>::computeRMSE(const Image<T>& anImage) const
3687 //------------------------------------------------------------------------------
3688 {
3689  return (std::sqrt(computeMSE(anImage) / double(m_width * m_height * m_number_of_slices)));
3690 }
3691 
3692 
3693 //--------------------------------------------
3694 template<typename T> double Image<T>::computeNCC(const Image<T>& anImage) const
3695 //--------------------------------------------
3696 {
3697  if (m_width != anImage.m_width || m_height != anImage.m_height || m_number_of_slices != anImage.m_number_of_slices)
3698  {
3699  throw Exception(__FILE__, __FUNCTION__, __LINE__, "The images have different sizes");
3700  }
3701 
3702  double average1(getAverage());
3703  double average2(anImage.getAverage());
3704 
3705  double standard_deviation_product(getStandardDeviation() * anImage.getStandardDeviation());
3706  double ncc(0.0);
3707 
3708 #ifdef NDEBUG
3709  #pragma omp parallel for reduction( + : ncc )
3710 #endif
3711  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
3712  {
3713  ncc += (m_p_image[i] - average1) * (anImage.m_p_image[i] - average2);
3714  }
3715 
3716  return (ncc / (m_width * m_height * standard_deviation_product));
3717 }
3718 
3719 
3720 
3721 template<typename T> FFT<T> Image<T>::getFFT() const
3722 {
3723  return (FFT<T>::computeFFT(*this));
3724 }
3725 
3726 
3727 template<typename T> Image<T> Image<T>::transpose() const
3728 {
3729  Image<T> temp(m_height, m_width, m_number_of_slices);
3730  temp.m_spacing = m_spacing; // Debatable
3731 
3732 #ifdef NDEBUG
3733 #ifdef WIN32
3734  #pragma omp parallel for
3735 #else
3736  #pragma omp parallel for collapse(3)
3737 #endif
3738 #endif
3739  for (int k = 0; k < m_number_of_slices; ++k)
3740  {
3741  for (int j = 0; j < m_height; ++j)
3742  {
3743  for (int i = 0; i < m_width; ++i)
3744  {
3745  temp(j, i, k) = getPixel(i, j, k);
3746  }
3747  }
3748  }
3749 
3750  return (temp);
3751 }
3752 
3753 
3754 #ifdef HAS_GDCM
3755 //-----------------------------------------------------------------
3756 template<typename T> void Image<T>::addTag(double aValue,
3757  unsigned int anAddress1,
3758  unsigned int anAddress2,
3759  gdcm::File& aFile) const
3760 //-----------------------------------------------------------------
3761 {
3762  std::stringstream message;
3763  message << aValue;
3764 
3765  addTag(message.str(), anAddress1, anAddress2, aFile);
3766 }
3767 
3768 
3769 //-----------------------------------------------------------------
3770 template<typename T> void Image<T>::addTag(const char* aValue,
3771  unsigned int anAddress1,
3772  unsigned int anAddress2,
3773  gdcm::File& aFile) const
3774 //-----------------------------------------------------------------
3775 {
3776  std::stringstream message;
3777  message << aValue;
3778 
3779  addTag(std::string(aValue), anAddress1, anAddress2, aFile);
3780 }
3781 
3782 
3783 //-------------------------------------------------------------------
3784 template<typename T> void Image<T>::addTag(const std::string& aValue,
3785  unsigned int anAddress1,
3786  unsigned int anAddress2,
3787  gdcm::File& aFile) const
3788 //-------------------------------------------------------------------
3789 {
3790  gdcm::DataElement data_element(gdcm::Tag(anAddress1, anAddress2));
3791 
3792 
3793  data_element.SetByteValue((const char*)aValue.data(), uint32_t(aValue.size()));
3794 
3795  aFile.GetDataSet().Insert(data_element);
3796 }
3797 #endif
3798 
3799 
3800 //------------------------------------------------------------------------------
3801 template<typename T> Image<T> Image<T>::operator+(const Image<T>& anImage) const
3802 //------------------------------------------------------------------------------
3803 {
3804  // Deal with images of different sizes
3805  unsigned int min_width( std::min(m_width, anImage.m_width));
3806  unsigned int min_height(std::min(m_height, anImage.m_height));
3807  unsigned int min_depth( std::min(m_number_of_slices, anImage.m_number_of_slices));
3808 
3809  // Copy the instance into a temporary variable
3810  Image<T> temp(getROI(0, 0, 0, min_width, min_height, min_depth));
3811  temp.m_spacing = m_spacing;
3812 
3813  // Copy the data
3814 #ifdef NDEBUG
3815 #ifdef WIN32
3816  #pragma omp parallel for
3817 #else
3818  #pragma omp parallel for collapse(3)
3819 #endif
3820 #endif
3821  for (int k = 0; k < min_depth; ++k)
3822  {
3823  for (int j = 0; j < min_height; ++j)
3824  {
3825  for (int i = 0; i < min_width; ++i)
3826  {
3827  temp(i, j, k) += anImage(i, j, k);
3828  }
3829  }
3830  }
3831 
3832  // Return the result
3833  return (temp);
3834 }
3835 
3836 
3837 //------------------------------------------------------------------------------
3838 template<typename T> Image<T> Image<T>::operator-(const Image<T>& anImage) const
3839 //------------------------------------------------------------------------------
3840 {
3841  // Deal with images of different sizes
3842  unsigned int min_width( std::min(m_width, anImage.m_width));
3843  unsigned int min_height(std::min(m_height, anImage.m_height));
3844  unsigned int min_depth( std::min(m_number_of_slices, anImage.m_number_of_slices));
3845 
3846  // Copy the instance into a temporary variable
3847  Image<T> temp(getROI(0, 0, 0, min_width, min_height, min_depth));
3848  temp.m_spacing = m_spacing;
3849 
3850  // Copy the data
3851 #ifdef NDEBUG
3852 #ifdef WIN32
3853  #pragma omp parallel for
3854 #else
3855  #pragma omp parallel for collapse(3)
3856 #endif
3857 #endif
3858  for (int k = 0; k < min_depth; ++k)
3859  {
3860  for (int j = 0; j < min_height; ++j)
3861  {
3862  for (int i = 0; i < min_width; ++i)
3863  {
3864  temp(i, j, k) -= anImage(i, j, k);
3865  }
3866  }
3867  }
3868 
3869  // Return the result
3870  return (temp);
3871 }
3872 
3873 
3874 //------------------------------------------------------------------------------
3875 template<typename T> Image<T> Image<T>::operator*(const Image<T>& anImage) const
3876 //------------------------------------------------------------------------------
3877 {
3878  // Deal with images of different sizes
3879  unsigned int min_width( std::min(m_width, anImage.m_width));
3880  unsigned int min_height(std::min(m_height, anImage.m_height));
3881  unsigned int min_depth( std::min(m_number_of_slices, anImage.m_number_of_slices));
3882 
3883  // Copy the instance into a temporary variable
3884  Image<T> temp(getROI(0, 0, 0, min_width, min_height, min_depth));
3885  temp.m_spacing = m_spacing;
3886 
3887  // Copy the data
3888 #ifdef NDEBUG
3889 #ifdef WIN32
3890  #pragma omp parallel for
3891 #else
3892  #pragma omp parallel for collapse(3)
3893 #endif
3894 #endif
3895  for (int k = 0; k < min_depth; ++k)
3896  {
3897  for (int j = 0; j < min_height; ++j)
3898  {
3899  for (int i = 0; i < min_width; ++i)
3900  {
3901  temp(i, j, k) *= anImage(i, j, k);
3902  }
3903  }
3904  }
3905 
3906  // Return the result
3907  return (temp);
3908 }
3909 
3910 
3911 //------------------------------------------------------------------------------
3912 template<typename T> Image<T> Image<T>::operator/(const Image<T>& anImage) const
3913 //------------------------------------------------------------------------------
3914 {
3915  // Deal with images of different sizes
3916  unsigned int min_width( std::min(m_width, anImage.m_width));
3917  unsigned int min_height(std::min(m_height, anImage.m_height));
3918  unsigned int min_depth( std::min(m_number_of_slices, anImage.m_number_of_slices));
3919 
3920  // Copy the instance into a temporary variable
3921  Image<T> temp(getROI(0, 0, 0, min_width, min_height, min_depth));
3922  temp.m_spacing = m_spacing;
3923 
3924  // Copy the data
3925  // #pragma omp parallel for collapse(3)
3926  // Not parallel due to the exception
3927  for (unsigned int k = 0; k < min_depth; ++k)
3928  {
3929  for (unsigned int j = 0; j < min_height; ++j)
3930  {
3931  for (unsigned int i = 0; i < min_width; ++i)
3932  {
3933  double pixel_value(anImage(i, j, k));
3934 
3935  if (std::abs(pixel_value) < EPSILON)
3936  {
3937  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Division by zero.");
3938  }
3939  else
3940  {
3941  temp(i, j, k) /= pixel_value;
3942  }
3943  }
3944  }
3945  }
3946 
3947  // Return the result
3948  return (temp);
3949 }
3950 
3951 
3952 //--------------------------------------------------------------------------
3953 template<typename T> Image<T>& Image<T>::operator+=(const Image<T>& anImage)
3954 //--------------------------------------------------------------------------
3955 {
3956  // Re-use operator+
3957  *this = *this + anImage;
3958 
3959  // Return the result
3960  return (*this);
3961 }
3962 
3963 
3964 //--------------------------------------------------------------------------
3965 template<typename T> Image<T>& Image<T>::operator-=(const Image<T>& anImage)
3966 //--------------------------------------------------------------------------
3967 {
3968  // Re-use operator-
3969  *this = *this - anImage;
3970 
3971  // Return the result
3972  return (*this);
3973 }
3974 
3975 
3976 //--------------------------------------------------------------------------
3977 template<typename T> Image<T>& Image<T>::operator*=(const Image<T>& anImage)
3978 //--------------------------------------------------------------------------
3979 {
3980  // Re-use operator*
3981  *this = *this * anImage;
3982 
3983  // Return the result
3984  return (*this);
3985 }
3986 
3987 
3988 //--------------------------------------------------------------------------
3989 template<typename T> Image<T>& Image<T>::operator/=(const Image<T>& anImage)
3990 //--------------------------------------------------------------------------
3991 {
3992  // Re-use operator/
3993  *this = *this / anImage;
3994 
3995  // Return the result
3996  return (*this);
3997 }
3998 
3999 
4000 //---------------------------------------------------------------
4001 template<typename T> Image<T> Image<T>::operator+(T aValue) const
4002 //---------------------------------------------------------------
4003 {
4004  // Copy the instance into a temporary variable
4005  Image<T> temp(*this);
4006  temp.m_spacing = m_spacing;
4007 
4008 #ifdef NDEBUG
4009  #pragma omp parallel for
4010 #endif
4011  for (unsigned int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4012  {
4013  temp.m_p_image[i] += aValue;
4014  }
4015 
4016  // Return the result
4017  return (temp);
4018 }
4019 
4020 
4021 //---------------------------------------------------------------
4022 template<typename T> Image<T> Image<T>::operator-(T aValue) const
4023 //---------------------------------------------------------------
4024 {
4025  // Copy the instance into a temporary variable
4026  Image<T> temp(*this);
4027  temp.m_spacing = m_spacing;
4028 
4029 #ifdef NDEBUG
4030  #pragma omp parallel for
4031 #endif
4032  for (unsigned int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4033  {
4034  temp.m_p_image[i] -= aValue;
4035  }
4036 
4037  // Return the result
4038  return (temp);
4039 }
4040 
4041 
4042 //---------------------------------------------------------------
4043 template<typename T> Image<T> Image<T>::operator*(T aValue) const
4044 //---------------------------------------------------------------
4045 {
4046  // Copy the instance into a temporary variable
4047  Image<T> temp(*this);
4048  temp.m_spacing = m_spacing;
4049 
4050 #ifdef NDEBUG
4051  #pragma omp parallel for
4052 #endif
4053  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4054  {
4055  temp.m_p_image[i] *= aValue;
4056  }
4057 
4058  // Return the result
4059  return (temp);
4060 }
4061 
4062 
4063 //---------------------------------------------------------------
4064 template<typename T> Image<T> Image<T>::operator/(T aValue) const
4065 //---------------------------------------------------------------
4066 {
4067  // Division by zero
4068  if (std::abs(aValue) < EPSILON)
4069  {
4070  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Division by zero.");
4071  }
4072 
4073  // Copy the instance into a temporary variable
4074  Image<T> temp(*this);
4075  temp.m_spacing = m_spacing;
4076 
4077 #ifdef NDEBUG
4078  #pragma omp parallel for
4079 #endif
4080  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4081  {
4082  temp.m_p_image[i] /= aValue;
4083  }
4084 
4085  // Return the result
4086  return (temp);
4087 }
4088 
4089 
4090 //-----------------------------------------------------------
4091 template<typename T> Image<T>& Image<T>::operator+=(T aValue)
4092 //-----------------------------------------------------------
4093 {
4094 #ifdef NDEBUG
4095  #pragma omp parallel for
4096 #endif
4097  for (unsigned int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4098  {
4099  m_p_image[i] += aValue;
4100  }
4101 
4102  // Return the result
4103  return (*this);
4104 }
4105 
4106 
4107 //-----------------------------------------------------------
4108 template<typename T> Image<T>& Image<T>::operator-=(T aValue)
4109 //-----------------------------------------------------------
4110 {
4111 #ifdef NDEBUG
4112  #pragma omp parallel for
4113 #endif
4114  for (unsigned int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4115  {
4116  m_p_image[i] -= aValue;
4117  }
4118 
4119  // Return the result
4120  return (*this);
4121 }
4122 
4123 
4124 //-----------------------------------------------------------
4125 template<typename T> Image<T>& Image<T>::operator*=(T aValue)
4126 //-----------------------------------------------------------
4127 {
4128 #ifdef NDEBUG
4129  #pragma omp parallel for
4130 #endif
4131  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4132  {
4133  m_p_image[i] *= aValue;
4134  }
4135 
4136  // Return the result
4137  return (*this);
4138 }
4139 
4140 
4141 //-----------------------------------------------------------
4142 template<typename T> Image<T>& Image<T>::operator/=(T aValue)
4143 //-----------------------------------------------------------
4144 {
4145  // Division by zero
4146  if (std::abs(aValue) < EPSILON)
4147  {
4148  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Division by zero.");
4149  }
4150 
4151 #ifdef NDEBUG
4152  #pragma omp parallel for
4153 #endif
4154  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
4155  {
4156  m_p_image[i] /= aValue;
4157  }
4158 
4159  // Return the result
4160  return (*this);
4161 }
4162 
4163 
4164 //-------------------------------------------------------
4165 template<typename T> Image<T> Image<T>::operator!() const
4166 //-------------------------------------------------------
4167 {
4168  // Copy the instance into a temporary variable
4169  Image<T> temp(*this);
4170  temp.m_spacing = m_spacing;
4171 
4172  T min_value(getMinValue());
4173  T max_value(getMaxValue());
4174  T range(max_value - min_value);
4175 
4176  // Process every pixel
4177 #ifdef NDEBUG
4178  #pragma omp parallel for
4179 #endif
4180  for (unsigned int i = 0;
4181  i < m_width * m_height * m_number_of_slices;
4182  ++i)
4183  {
4184  // Take care to preserve the dynamic of the image
4185  temp.m_p_image[i] = min_value + range * (1.0 - (m_p_image[i] - min_value) / range);
4186  }
4187 
4188  // Return the result
4189  return (temp);
4190 }
4191 
4192 
4193 //------------------------------------------------------------------------------------------
4194 template<typename T> Image<T> Image<T>::setCanvasSize(unsigned int aNewWidth,
4195  unsigned int aNewHeight,
4196  unsigned int aNewNumberOfSlices) const
4197 //------------------------------------------------------------------------------------------
4198 {
4199  Image<T> temp(aNewWidth, aNewHeight, aNewNumberOfSlices);
4200  temp.m_spacing = m_spacing;
4201 
4202  int x_offset((int(aNewWidth) - int(m_width)) / 2);
4203  int y_offset((int(aNewHeight) - int(m_height)) / 2);
4204  int z_offset((int(aNewNumberOfSlices) - int(m_number_of_slices)) / 2);
4205 
4206 #ifdef NDEBUG
4207 #ifdef WIN32
4208  #pragma omp parallel for
4209 #else
4210  #pragma omp parallel for collapse(3)
4211 #endif
4212 #endif
4213  for (int k = 0; k < m_number_of_slices; ++k)
4214  {
4215  for (int j = 0; j < m_height; ++j)
4216  {
4217  for (int i = 0; i < m_width; ++i)
4218  {
4219  if (i + x_offset >= 0 && i + x_offset < aNewWidth &&
4220  j + y_offset >= 0 && j + y_offset < aNewHeight &&
4221  k + z_offset >= 0 && k + z_offset < aNewNumberOfSlices)
4222  {
4223  temp(i + x_offset, j + y_offset, k + z_offset) = getPixel(i, j, k);
4224  }
4225  }
4226  }
4227  }
4228 
4229  return (temp);
4230 }
4231 
4232 
4233 //-----------------------------------------------------------------------------------
4234 template<typename T> Image<T> Image<T>::resize(unsigned int aNewWidth,
4235  unsigned int aNewHeight,
4236  unsigned int aNewNumberOfSlices) const
4237 //-----------------------------------------------------------------------------------
4238 {
4239  Image temp(aNewWidth, aNewHeight, aNewNumberOfSlices);
4240  temp.m_spacing = m_spacing;
4241  temp.m_spacing[0] = m_spacing[0] * RATIONAL_NUMBER(m_width) / RATIONAL_NUMBER(aNewWidth);
4242  temp.m_spacing[1] = m_spacing[1] * RATIONAL_NUMBER(m_height) / RATIONAL_NUMBER(aNewHeight);
4243  temp.m_spacing[2] = m_spacing[2] * RATIONAL_NUMBER(m_number_of_slices) / RATIONAL_NUMBER(aNewNumberOfSlices);
4244 
4245 #ifdef NDEBUG
4246 #ifdef WIN32
4247  #pragma omp parallel for
4248 #else
4249  #pragma omp parallel for collapse(3)
4250 #endif
4251 #endif
4252  for (int k = 0; k < aNewNumberOfSlices; ++k)
4253  {
4254  for (unsigned j = 0; j < aNewHeight; ++j)
4255  {
4256  for (unsigned i = 0; i < aNewWidth; ++i)
4257  {
4258 
4259  double x = double(i) * (m_width - 1) / (aNewWidth - 1);
4260  double y = double(j) * (m_height - 1) / (aNewHeight - 1);
4261  double z = double(k) * (m_number_of_slices - 1) / (aNewNumberOfSlices - 1);
4262 
4263  int x1 = floor(x);
4264  int y1 = floor(y);
4265  int z1 = floor(z);
4266 
4267  if (x1 < 0) x1 = 0;
4268  if (y1 < 0) y1 = 0;
4269  if (z1 < 0) z1 = 0;
4270 
4271  int x2 = x1 + 1.0;
4272  int y2 = y1 + 1.0;
4273  int z2 = z1 + 1.0;
4274 
4275  if (x2 >= m_width)
4276  {
4277  x2 = m_width - 1;
4278  }
4279 
4280  if (y2 >= m_height)
4281  {
4282  y2 = m_height - 1;
4283  }
4284 
4285  if (z2 >= m_number_of_slices)
4286  {
4287  z2 = m_number_of_slices - 1;
4288  }
4289 
4290 
4291 
4292  double c111 = getPixel(x1, y1, z1);
4293  double c121 = getPixel(x1, y2, z1);
4294  double c221 = getPixel(x2, y2, z1);
4295  double c211 = getPixel(x2, y1, z1);
4296 
4297  double cx11 = c111;
4298  double cx21 = c121;
4299  if (x1 != x2)
4300  {
4301  cx11 += (c211 - c111) * (x - x1) / (x2 - x1);
4302  cx21 += (c221 - c121) * (x - x1) / (x2 - x1);
4303  }
4304 
4305  double cy1 = cx11;
4306  if (y1 != y2)
4307  {
4308  cy1 += (cx21 - cx11) * (y - y1) / (y2 - y1);
4309  }
4310 
4311 
4312  double c112 = getPixel(x1, y1, z2);
4313  double c122 = getPixel(x1, y2, z2);
4314  double c222 = getPixel(x2, y2, z2);
4315  double c212 = getPixel(x2, y1, z2);
4316 
4317  double cx12 = c112;
4318  double cx22 = c122;
4319  if (x1 != x2)
4320  {
4321  cx12 += (c212 - c112) * (x - x1) / (x2 - x1);
4322  cx22 += (c222 - c122) * (x - x1) / (x2 - x1);
4323  }
4324 
4325  double cy2 = cx12;
4326  if (y1 != y2)
4327  {
4328  cy2 += (cx22 - cx12) * (y - y1) / (y2 - y1);
4329  }
4330 
4331  double cz = cy1;
4332  if (z1 != z2)
4333  {
4334  cz += (cy2 - cy1) * (z - z1) / (z2 - z1);
4335  }
4336 
4337  temp(i, j, k) = cz;
4338  }
4339  }
4340  }
4341 
4342  return (temp);
4343 }
4344 
4345 
4346 //--------------------------------------------------------------------------
4347 template<typename T> Image<T> Image<T>::rotate(float anAngleInDegrees) const
4348 //--------------------------------------------------------------------------
4349 {
4350  Image<T> temp(m_width, m_height, m_number_of_slices);
4351  temp.m_spacing = m_spacing;
4352 
4353  float angle_in_radian(anAngleInDegrees * Pi / 180);
4354 
4355  //double center_x(0.5 * (m_width + 1) /*- 1*/);
4356  //double center_y(0.5 * (m_height + 1) /*- 1*/);
4357 
4358 #ifdef NDEBUG
4359 #ifdef WIN32
4360  #pragma omp parallel for
4361 #else
4362  #pragma omp parallel for collapse(3)
4363 #endif
4364 #endif
4365  for (int k = 0; k < m_number_of_slices; ++k)
4366  {
4367  for (unsigned j = 0; j < m_height; ++j)
4368  {
4369  for (unsigned i = 0; i < m_width; ++i)
4370  {
4371  double temp_x(i);
4372  double temp_y(j);
4373 
4374  temp_x += 0.5;
4375  temp_y += 0.5;
4376 
4377  temp_x -= (m_width - 1.0) / 2.0;
4378  temp_y -= (m_height - 1.0) / 2.0;
4379 
4380  double x = temp_x * std::cos(angle_in_radian) + temp_y * std::sin(angle_in_radian);
4381  double y = temp_y * std::cos(angle_in_radian) - temp_x * std::sin(angle_in_radian);
4382 
4383  x += (m_width - 1.0) / 2.0;
4384  y += (m_height - 1.0) / 2.0;
4385 
4386  int x1 = floor(x);
4387  int y1 = floor(y);
4388 
4389  int x2 = x1 + 1;
4390  int y2 = y1 + 1;
4391 
4392  if (x1 >= 0 && x1 < m_width &&
4393  y1 >= 0 && y1 < m_height)
4394  {
4395  temp(i, j, k) = getPixel(x1, y1, k);
4396 
4397  if (x2 >= 0 && x2 < m_width &&
4398  y2 >= 0 && y2 < m_height)
4399  {
4400  double c11 = getPixel(x1, y1, k);
4401  double c12 = getPixel(x1, y2, k);
4402  double c22 = getPixel(x2, y2, k);
4403  double c21 = getPixel(x2, y1, k);
4404 
4405  double cx1 = c11 + (c21 - c11) * (x - double(x1)) / double(x2 - x1);
4406  double cx2 = c12 + (c22 - c12) * (x - double(x1)) / double(x2 - x1);
4407 
4408  double cy = cx1 + (cx2 - cx1) * double(y - y1) / double(y2 - y1);
4409 
4410  temp(i, j, k) = cy;
4411  }
4412  }
4413  }
4414  }
4415  }
4416 
4417  return (temp);
4418 }
4419 
4420 
4421 //--------------------------------------------------------------------------------
4422 template<typename T> Sinogram<T> Image<T>::radonTransform(double aFirstAngle,
4423  double anAngleStep,
4424  double aLastAngle) const
4425 //--------------------------------------------------------------------------------
4426 {
4427  unsigned int square_width(4 + std::max(m_width, m_height));
4428  square_width *= square_width;
4429 
4430  unsigned int sinogram_width(floor(std::sqrt(2.0 * square_width)));
4431 
4432 
4433  //unsigned int sinogram_width(floor(double(std::max(m_width, m_height)) / 2.0 * (2.0 * std::sqrt(2.0))));
4434  unsigned int sinogram_height(1);
4435  if (std::abs(anAngleStep) > EPSILON && std::abs(aLastAngle - aFirstAngle) > EPSILON)
4436  {
4437  sinogram_height += floor((aLastAngle - aFirstAngle) / anAngleStep);
4438  }
4439 
4440  double scaling_factor(double(sinogram_width) / double(m_width));
4441 
4442  Sinogram<T> sinogram(sinogram_width, sinogram_height, m_number_of_slices);
4443 
4444  Image<T> padded_image(setCanvasSize(sinogram_width, scaling_factor * m_height, m_number_of_slices));
4445 
4446 #ifndef NDEBUG
4447  padded_image.save("padded.mhd");
4448 #endif
4449 
4450  double angle(aFirstAngle);
4451 
4452  for (unsigned int angle_id(0); angle_id < sinogram_height; ++angle_id, angle += anAngleStep)
4453  {
4454  Image<T> rotated_image(padded_image.rotate(angle));
4455 #ifndef NDEBUG
4456  {
4457  std::stringstream file_name;
4458  file_name << "rotated_" << angle << ".mhd";
4459  rotated_image.save(file_name.str());
4460  }
4461 #endif
4462 
4463 #ifdef NDEBUG
4464 #ifdef WIN32
4465  #pragma omp parallel for
4466 #else
4467  #pragma omp parallel for collapse(3)
4468 #endif
4469 #endif
4470  for (int j = 0; j < rotated_image.m_height; ++j)
4471  {
4472  for (int i = 0; i < rotated_image.m_width; ++i)
4473  {
4474  for (int k = 0; k < m_number_of_slices; ++k)
4475  {
4476  sinogram(i, angle_id, k) += rotated_image(i, j, k);
4477  }
4478  }
4479  }
4480 
4481 #ifndef NDEBUG
4482  {
4483  std::stringstream file_name;
4484  file_name << "sinogram_" << angle << ".mhd";
4485  sinogram.save(file_name.str());
4486  }
4487 #endif
4488 
4489  }
4490 
4491  return (sinogram);
4492 }
4493 
4494 
4495 //-----------------------------------------------------------------------------------------
4496 template<typename T> PolygonMesh Image<T>::marchingCubes(const T& aFirstThesholdValue,
4497  const T& aSecondThresholdValue,
4498  bool aTwoThresholdFlag,
4499  unsigned char aVerboseLevel) const
4500 //-----------------------------------------------------------------------------------------
4501 {
4502  // We use a temporary copy of the volume that we pad.
4503  // This is to make sure the iso-surface is closed.
4504  Image<unsigned char> padded_image;
4505 
4506  unsigned char value_in = 255;
4507  unsigned char value_out = 0;
4508  unsigned char iso_value = 128;
4509 
4510  if (!aTwoThresholdFlag)
4511  {
4512  padded_image = this->threshold(aFirstThesholdValue, value_in, value_out).constantPadFilter(value_out);
4513  }
4514  else
4515  {
4516  padded_image = this->threshold(aFirstThesholdValue, aSecondThresholdValue, value_in, value_out, true).constantPadFilter(value_out);
4517  }
4518 
4519 
4520  PolygonMesh iso_surface;
4521 
4522  {
4523 
4524  struct GLvector
4525  {
4526  float fX;
4527  float fY;
4528  float fZ;
4529  };
4530 
4531  //a2fEdgeDirection lists the direction vector (vertex1-vertex0) for each edge in the cube
4532  static const float a2fEdgeDirection[12][3] =
4533  {
4534  {1.0, 0.0, 0.0},{0.0, 1.0, 0.0},{-1.0, 0.0, 0.0},{0.0, -1.0, 0.0},
4535  {1.0, 0.0, 0.0},{0.0, 1.0, 0.0},{-1.0, 0.0, 0.0},{0.0, -1.0, 0.0},
4536  {0.0, 0.0, 1.0},{0.0, 0.0, 1.0},{ 0.0, 0.0, 1.0},{0.0, 0.0, 1.0}
4537  };
4538 
4539  //a2iEdgeConnection lists the index of the endpoint vertices for each of the 12 edges of the cube
4540  static const int a2iEdgeConnection[12][2] =
4541  {
4542  {0,1}, {1,2}, {2,3}, {3,0},
4543  {4,5}, {5,6}, {6,7}, {7,4},
4544  {0,4}, {1,5}, {2,6}, {3,7}
4545  };
4546 
4547  //a2fVertexOffset lists the positions, relative to vertex0, of each of the 8 vertices of a cube
4548  static const unsigned int a2fVertexOffset[8][3] =
4549  {
4550  {0, 0, 0},{1, 0, 0},{1, 1, 0},{0, 1, 0},
4551  {0, 0, 1},{1, 0, 1},{1, 1, 1},{0, 1, 1}
4552  };
4553 
4554  // For any edge, if one vertex is inside of the surface and the other is outside of the surface
4555  // then the edge intersects the surface
4556  // For each of the 8 vertices of the cube can be two possible states : either inside or outside of the surface
4557  // For any cube the are 2^8=256 possible sets of vertex states
4558  // This table lists the edges intersected by the surface for all 256 possible vertex states
4559  // There are 12 edges. For each entry in the table, if edge #n is intersected, then bit #n is set to 1
4560 
4561  static const int aiCubeEdgeFlags[256]=
4562  {
4563  0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
4564  0x190, 0x099, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
4565  0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
4566  0x3a0, 0x2a9, 0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
4567  0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
4568  0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
4569  0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x055, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
4570  0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
4571  0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0x0cc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
4572  0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
4573  0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
4574  0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663, 0x569, 0x460,
4575  0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0,
4576  0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x033, 0x339, 0x230,
4577  0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190,
4578  0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
4579  };
4580 
4581  // For each of the possible vertex states listed in aiCubeEdgeFlags there is a specific triangulation
4582  // of the edge intersection points. a2iTriangleConnectionTable lists all of them in the form of
4583  // 0-5 edge triples with the list terminated by the invalid value -1.
4584  // For example: a2iTriangleConnectionTable[3] list the 2 triangles formed when corner[0]
4585  // and corner[1] are inside of the surface, but the rest of the cube is not.
4586  //
4587  // I found this table in an example program someone wrote long ago. It was probably generated by hand
4588 
4589  static const int a2iTriangleConnectionTable[256][16] =
4590  {
4591  {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4592  {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4593  {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4594  {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4595  {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4596  {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4597  {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4598  {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
4599  {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4600  {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4601  {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4602  {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
4603  {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4604  {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
4605  {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
4606  {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4607  {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4608  {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4609  {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4610  {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
4611  {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4612  {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
4613  {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
4614  {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
4615  {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4616  {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
4617  {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
4618  {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
4619  {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
4620  {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
4621  {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
4622  {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
4623  {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4624  {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4625  {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4626  {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
4627  {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4628  {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
4629  {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
4630  {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
4631  {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4632  {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
4633  {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
4634  {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
4635  {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
4636  {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
4637  {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
4638  {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
4639  {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4640  {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
4641  {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
4642  {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4643  {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
4644  {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
4645  {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
4646  {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
4647  {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
4648  {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
4649  {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
4650  {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
4651  {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
4652  {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
4653  {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
4654  {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4655  {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4656  {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4657  {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4658  {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
4659  {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4660  {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
4661  {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
4662  {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
4663  {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4664  {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
4665  {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
4666  {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
4667  {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
4668  {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
4669  {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
4670  {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
4671  {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4672  {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
4673  {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
4674  {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
4675  {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
4676  {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
4677  {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
4678  {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
4679  {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
4680  {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
4681  {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
4682  {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
4683  {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
4684  {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
4685  {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
4686  {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
4687  {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4688  {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
4689  {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
4690  {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
4691  {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
4692  {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
4693  {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4694  {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
4695  {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
4696  {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
4697  {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
4698  {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
4699  {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
4700  {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
4701  {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
4702  {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4703  {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
4704  {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
4705  {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
4706  {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
4707  {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
4708  {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
4709  {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
4710  {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4711  {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
4712  {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
4713  {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
4714  {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
4715  {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
4716  {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4717  {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
4718  {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4719  {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4720  {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4721  {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4722  {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
4723  {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4724  {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
4725  {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
4726  {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
4727  {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4728  {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
4729  {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
4730  {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
4731  {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
4732  {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
4733  {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
4734  {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
4735  {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4736  {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
4737  {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
4738  {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
4739  {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
4740  {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
4741  {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
4742  {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
4743  {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
4744  {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4745  {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
4746  {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
4747  {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
4748  {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
4749  {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
4750  {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4751  {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4752  {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
4753  {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
4754  {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
4755  {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
4756  {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
4757  {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
4758  {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
4759  {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
4760  {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
4761  {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
4762  {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
4763  {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
4764  {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
4765  {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
4766  {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
4767  {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
4768  {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
4769  {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
4770  {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
4771  {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
4772  {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
4773  {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
4774  {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
4775  {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
4776  {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
4777  {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
4778  {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4779  {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
4780  {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
4781  {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4782  {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4783  {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4784  {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
4785  {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
4786  {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
4787  {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
4788  {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
4789  {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
4790  {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
4791  {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
4792  {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
4793  {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
4794  {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
4795  {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4796  {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
4797  {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
4798  {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4799  {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
4800  {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
4801  {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
4802  {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
4803  {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
4804  {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
4805  {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
4806  {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4807  {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
4808  {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
4809  {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
4810  {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
4811  {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
4812  {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4813  {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
4814  {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4815  {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
4816  {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
4817  {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
4818  {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
4819  {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
4820  {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
4821  {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
4822  {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
4823  {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
4824  {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
4825  {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
4826  {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4827  {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
4828  {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
4829  {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4830  {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4831  {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4832  {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
4833  {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
4834  {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4835  {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
4836  {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
4837  {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4838  {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4839  {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
4840  {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4841  {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
4842  {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4843  {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4844  {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4845  {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
4846  {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
4847  };
4848 
4849 
4850  if (padded_image.getWidth() && padded_image.getHeight() && padded_image.getDepth() && padded_image.getRawData())
4851  {
4852  unsigned int max_thread(1);
4853 #ifdef HAS_OPENMP
4854  max_thread = omp_get_max_threads();
4855 #endif
4856 
4857  std::vector<std::vector<float> > p_vertex_set(max_thread);
4858  std::vector<std::vector<float> > p_normal_vector_set(max_thread);
4859 
4860  if (aVerboseLevel)
4861  {
4862  LOGGER.logNow("Create the iso-surface using Marching Cubes") << std::endl;
4863 
4864  if (aTwoThresholdFlag)
4865  LOGGER.logNow("use two thresholds") << std::endl;
4866  else
4867  LOGGER.logNow("use a single threshold") << std::endl;
4868  }
4869 
4870  unsigned int number_of_voxels((padded_image.getWidth() - 1) * (padded_image.getHeight() - 1) * (padded_image.getDepth() - 1));
4871  unsigned int thread_progress = 0;
4872 
4873 #ifdef NDEBUG
4874 #ifdef WIN32
4875  #pragma omp parallel for
4876 #else
4877  #pragma omp parallel for collapse(3)
4878 #endif
4879 #endif
4880  for (int k = 0; k < padded_image.getDepth() - 1; ++k)
4881  {
4882  for (int j = 0; j < padded_image.getHeight() - 1; ++j)
4883  {
4884  for (int i = 0; i < padded_image.getWidth() - 1; ++i)
4885  {
4886 #ifdef HAS_OPENMP
4887  int thread_id = omp_get_thread_num();
4888 #else
4889  int thread_id = 0;
4890 #endif
4891 
4892  // Deal with the progress bar
4893  if (thread_id == 0)
4894  {
4895  thread_progress++;
4896 
4897  if (aVerboseLevel)
4898  {
4899  int number_of_threads = omp_get_num_threads();
4900  unsigned int tasks_per_thread = number_of_voxels / number_of_threads;
4901 
4902  if (thread_progress == 1)
4903  {
4904  LOGGER.logNow("Number of threads: ") << number_of_threads << std::endl;
4905  LOGGER.logNow("Number of voxels per thread: ") << tasks_per_thread << std::endl << std::flush;
4906  }
4907 
4908  if (aVerboseLevel > 1)
4909  {
4910  LOGGER.progressBar(thread_progress, tasks_per_thread);
4911  }
4912  }
4913  }
4914 
4915  int iCorner, iVertex, iVertexTest, iEdge, iTriangle, iFlagIndex, iEdgeFlags;
4916  float fOffset;
4917  GLvector sColor;
4918  float afCubeValue[8];
4919  GLvector asEdgeVertex[12];
4920  GLvector asEdgeNorm[12];
4921 
4922  VEC3 voxel_position(padded_image.getPixelPosition(i, j, k));
4923 
4924  //Make a local copy of the values at the cube's corners
4925  for(iVertex = 0; iVertex < 8; iVertex++)
4926  {
4927  afCubeValue[iVertex] = padded_image.getPixelValue(i + a2fVertexOffset[iVertex][0],
4928  j + a2fVertexOffset[iVertex][1],
4929  k + a2fVertexOffset[iVertex][2]);
4930  }
4931 
4932  //Find which vertices are inside of the surface and which are outside
4933  iFlagIndex = 0;
4934  for(iVertexTest = 0; iVertexTest < 8; iVertexTest++)
4935  {
4936  if(!(iso_value <= afCubeValue[iVertexTest]))
4937  {
4938  iFlagIndex |= 1<<iVertexTest;
4939  }
4940  }
4941 
4942  //Find which edges are intersected by the surface
4943  iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];
4944 
4945  //If the cube is not entirely inside or outside of the surface, then there will be no intersections
4946  if(iEdgeFlags != 0)
4947  {
4948  //Find the point of intersection of the surface with each edge
4949  //Then find the normal to the surface at those points
4950  for(iEdge = 0; iEdge < 12; iEdge++)
4951  {
4952  //if there is an intersection on this edge
4953  if(iEdgeFlags & (1<<iEdge))
4954  {
4955  double fDelta = afCubeValue[ a2iEdgeConnection[iEdge][1] ] - afCubeValue[ a2iEdgeConnection[iEdge][0] ];
4956 
4957  if (std::abs(fDelta) < EPSILON)
4958  {
4959  fOffset = 0.5;
4960  }
4961  else
4962  {
4963  fOffset = (iso_value - afCubeValue[ a2iEdgeConnection[iEdge][0] ])/fDelta;
4964  }
4965 
4966  asEdgeVertex[iEdge].fX = voxel_position.getX() + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0] + fOffset * a2fEdgeDirection[iEdge][0]) * padded_image.getSpacing()[0];
4967  asEdgeVertex[iEdge].fY = voxel_position.getY() + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1] + fOffset * a2fEdgeDirection[iEdge][1]) * padded_image.getSpacing()[1];
4968  asEdgeVertex[iEdge].fZ = voxel_position.getZ() + (a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2] + fOffset * a2fEdgeDirection[iEdge][2]) * padded_image.getSpacing()[2];
4969 
4970  }
4971  }
4972 
4973  //Draw the triangles that were found. There can be up to five per cube
4974  for(iTriangle = 0; iTriangle < 5; iTriangle++)
4975  {
4976  if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0)
4977  break;
4978 
4979  for(iCorner = 0; iCorner < 3; iCorner++)
4980  {
4981  iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner];
4982 
4983  p_vertex_set[thread_id].push_back(asEdgeVertex[iVertex].fX);
4984  p_vertex_set[thread_id].push_back(asEdgeVertex[iVertex].fY);
4985  p_vertex_set[thread_id].push_back(asEdgeVertex[iVertex].fZ);
4986 
4987  VEC3 normal(
4988  padded_image.getPixelValue(std::max(i - 1, 0), j, k) - padded_image.getPixelValue(std::min(i + 1, int(padded_image.getWidth()) - 1), j, k),
4989  padded_image.getPixelValue(i, std::max(j - 1, 0), k) - padded_image.getPixelValue(i, std::min(j + 1, int(padded_image.getHeight()) - 1), k),
4990  padded_image.getPixelValue(i, j, std::max(k - 1, 0)) - padded_image.getPixelValue(i, j, std::min(k + 1, int(padded_image.getDepth()) - 1)));
4991 
4992  normal.normalise();
4993 
4994  p_normal_vector_set[thread_id].push_back(normal.getX());
4995  p_normal_vector_set[thread_id].push_back(normal.getY());
4996  p_normal_vector_set[thread_id].push_back(normal.getZ());
4997  }
4998  }
4999  }
5000  }
5001  }
5002  }
5003 
5004  if (aVerboseLevel > 1)
5005  {
5006  LOGGER.progressBar(1.0, true);
5007  }
5008 
5009  // Store the results from all the threads if needed
5010  if (max_thread > 1)
5011  {
5012  std::vector<float> p_combined_vertex_set;
5013  std::vector<float> p_combined_normal_vector_set;
5014 
5015  for (int i = 0; i < max_thread; ++i)
5016  {
5017  p_combined_vertex_set.insert(p_combined_vertex_set.end(), p_vertex_set[i].begin(), p_vertex_set[i].end());
5018  p_combined_normal_vector_set.insert(p_combined_normal_vector_set.end(), p_normal_vector_set[i].begin(), p_normal_vector_set[i].end());
5019  }
5020 
5021  iso_surface.setInternalData(
5022  g_mesh_type,
5023  &p_combined_vertex_set,
5024  &p_combined_normal_vector_set,
5025  false,
5026  g_vbo_type);
5027  }
5028  else
5029  {
5030  iso_surface.setInternalData(
5031  g_mesh_type,
5032  &(p_vertex_set[0]),
5033  &(p_normal_vector_set[0]),
5034  false,
5035  g_vbo_type);
5036  }
5037  }
5038 
5039  }
5040 
5041  return (iso_surface);
5042 }
5043 
5044 
5045 //--------------------------------------------------------
5046 template<typename T> void Image<T>::destroyOpenGLTexture()
5047 //--------------------------------------------------------
5048 {
5049  if (m_texture_id)
5050  {
5051  glDeleteTextures(1, &m_texture_id);
5052  m_texture_id = 0;
5053  }
5054 }
5055 
5056 
5057 //--------------------------------------------------------------
5058 template<typename T> unsigned int Image<T>::getTextureId() const
5059 //--------------------------------------------------------------
5060 {
5061  return m_texture_id;
5062 }
5063 
5064 
5065 //----------------------------------------------------
5066 inline std::string getPixelType(const char* aFileName)
5067 //----------------------------------------------------
5068 {
5069 #ifdef HAS_ITK
5070  // Create a type
5071  typedef itk::ImageIOBase::IOComponentType ScalarPixelType;
5072 
5073  // Create an imageIO based on the first file
5074  itk::ImageIOBase::Pointer imageIO(itk::ImageIOFactory::CreateImageIO(aFileName, itk::ImageIOFactory::ReadMode));
5075 
5076  // Cannot create the imageIO
5077  if (!imageIO)
5078  {
5079  std::stringstream error_message;
5080  error_message << "Could not CreateImageIO for \"" << aFileName << "\"";
5081  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message.str());
5082  }
5083  imageIO->SetFileName(aFileName);
5084  imageIO->ReadImageInformation();
5085 
5086  const ScalarPixelType pixelType = imageIO->GetComponentType();
5087 
5088  return (std::string(imageIO->GetComponentTypeAsString(pixelType)));
5089 #else
5090  return (std::string("unknown"));
5091 #endif
5092 }
5093 
5094 
5095 //-----------------------------------------------------------
5096 inline std::string getPixelType(const std::string& aFileName)
5097 //-----------------------------------------------------------
5098 {
5099  return (getPixelType(aFileName.data()));
5100 }
5101 
5102 
5103 //--------------------------------------------------------------
5104 template<typename T> Image<T> operator*(const T& aValue,
5105  const Image<T>& anImage)
5106 //--------------------------------------------------------------
5107 {
5108  return (anImage * aValue);
5109 }
5110 
5111 
5112 //--------------------------------------------------------------
5113 template<typename T> Image<T> operator/(const T& aValue,
5114  const Image<T>& anImage)
5115 //--------------------------------------------------------------
5116 {
5117  Image<T> temp_image(anImage);
5118  temp_image.m_spacing = anImage.m_spacing;
5119 
5120  unsigned int number_of_voxels(temp_image.getWidth() * temp_image.getHeight() * temp_image.getDepth());
5121  T* p_temp(temp_image.getRawData());
5122  for (unsigned int i(0); i < number_of_voxels; ++i, ++p_temp)
5123  {
5124  if (std::abs(*p_temp) < EPSILON)
5125  {
5126  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Division by zero.");
5127  }
5128 
5129  *p_temp = aValue / *p_temp;
5130  }
5131 
5132  return (temp_image);
5133 }
5134 
5135 
5136 //--------------------------------------------------------------
5137 template<typename T> Image<T> operator+(const T& aValue,
5138  const Image<T>& anImage)
5139 //--------------------------------------------------------------
5140 {
5141  return (anImage + aValue);
5142 }
5143 
5144 
5145 //--------------------------------------------------------------
5146 template<typename T> Image<T> operator-(const T& aValue,
5147  const Image<T>& anImage)
5148 //--------------------------------------------------------------
5149 {
5150  Image<T> temp_image(anImage);
5151  temp_image.m_spacing = anImage.m_spacing;
5152 
5153  unsigned int number_of_voxels(temp_image.getWidth() * temp_image.getHeight() * temp_image.getDepth());
5154  T* p_temp(temp_image.getRawData());
5155 
5156 #ifdef NDEBUG
5157  #pragma omp parallel for
5158 #endif
5159  for (int i = 0; i < number_of_voxels; ++i)
5160  {
5161  p_temp[i] = aValue - p_temp[i];
5162  }
5163 
5164  return (temp_image);
5165 }
5166 
5167 
5168 //--------------------------------------------------------
5169 template<typename T> Image<T> log(const Image<T>& anImage)
5170 //--------------------------------------------------------
5171 {
5172  return (anImage.log());
5173 }
5174 
5175 
5176 //--------------------------------------------------------
5177 template<typename T> Image<T> abs(const Image<T>& anImage)
5178 //--------------------------------------------------------
5179 {
5180  return (anImage.abs());
5181 }
5182 
5183 
5184 //------------------------------------------------------------------------
5185 template<typename T> VEC3 Image<T>::VertexInterp(const T& aThresholdValue,
5186  const VEC3& p0,
5187  const VEC3& p1,
5188  const T& v0,
5189  const T& v1) const
5190 //------------------------------------------------------------------------
5191 {
5192  double ratio((v1 - aThresholdValue) / (v1 - v0));
5193 
5194  return (p1 + (p0 - p1) * ratio);
5195 }
5196 
5197 
5198 //------------------------------------------------------------------------------
5199 template<typename T> void Image<T>::loadMetaHeader(const std::string& aFileName)
5200 //------------------------------------------------------------------------------
5201 {
5202  loadMetaHeader(aFileName.data());
5203 }
5204 
5205 
5206 //-----------------------------------------------------------------------
5207 template<typename T> void Image<T>::loadMetaHeader(const char* aFileName)
5208 //-----------------------------------------------------------------------
5209 {
5210  // Open the meta header
5211  std::ifstream meta_header(aFileName, std::ios::binary|std::ios::in);
5212 
5213  // The file cannot be opened
5214  if (!meta_header.is_open())
5215  {
5216  // Throw an error
5217  throw FileDoesNotExistException(__FILE__, __FUNCTION__, __LINE__, aFileName);
5218  }
5219 
5220  // Parse the meta header
5221  unsigned int number_of_dimensions(0);
5222  double spacing[3] = {0.0, 0.0, 0.0};
5223  unsigned int dimensions[3] = {1, 1, 1};
5224  std::string element_type;
5225  std::string raw_file_name;
5226  bool change_endianess(false);
5227  bool deflate_compression(false);
5228 
5229  std::string info_type;
5230  while (meta_header >> info_type)
5231  {
5232  std::string equal_sign;
5233  meta_header >> equal_sign;
5234 
5235  // Remove the equal sign
5236  if (equal_sign != "=")
5237  {
5238  std::string error_message("The program was expecting \"=\" but received \"");
5239  error_message += equal_sign;
5240  error_message += "\".";
5241  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5242  }
5243 
5244  if (info_type == "ObjectType")
5245  {
5246  std::string object_type;
5247  meta_header >> object_type;
5248 
5249  if (object_type != "Image")
5250  {
5251  std::string error_message("The object type \"");
5252  error_message += object_type;
5253  error_message += "\" is not known. It should have been \"Image\".";
5254  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5255  }
5256  }
5257  else if (info_type == "NDims")
5258  {
5259  meta_header >> number_of_dimensions;
5260 
5261  // number_of_dimensions is always positive as
5262  // it is an unsigned int
5263  if (/*number_of_dimensions < 0 ||*/ 3 < number_of_dimensions)
5264  {
5265  std::stringstream error_message;
5266  error_message << "The number of dimensions \"" << number_of_dimensions <<
5267  "\" is not valid. It should have been in the range [0, 3].";
5268  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message.str());
5269  }
5270  }
5271  else if (info_type == "BinaryData")
5272  {
5273  std::string data_type;
5274  meta_header >> data_type;
5275 
5276  if (data_type != "True")
5277  {
5278  std::string error_message("The data type is not binary.");
5279  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5280  }
5281  }
5282  else if (info_type == "BinaryDataByteOrderMSB" || info_type == "ElementByteOrderMSB")
5283  {
5284  std::string big_endian;
5285  meta_header >> big_endian;
5286 
5287  if (big_endian == "False" && isBigEndian())
5288  {
5289  change_endianess = true;
5290  }
5291 
5292  if (big_endian == "True" && isLittleEndian())
5293  {
5294  change_endianess = true;
5295  }
5296  }
5297  else if (info_type == "CompressedData")
5298  {
5299  std::string compression;
5300  meta_header >> compression;
5301 
5302  if (compression == "True")
5303  {
5304  deflate_compression = true;
5305  }
5306  }
5307  else if (info_type == "TransformMatrix")
5308  {
5309  for (unsigned int j(0); j < number_of_dimensions; ++j)
5310  {
5311  for (unsigned int i(0); i < number_of_dimensions; ++i)
5312  {
5313  double temp;
5314  meta_header >> temp;
5315  }
5316  }
5317  }
5318  else if (info_type == "Offset")
5319  {
5320  for (unsigned int i(0); i < number_of_dimensions; ++i)
5321  {
5322  double temp;
5323  meta_header >> temp;
5324  }
5325  }
5326  else if (info_type == "CenterOfRotation")
5327  {
5328  for (unsigned int i(0); i < number_of_dimensions; ++i)
5329  {
5330  double temp;
5331  meta_header >> temp;
5332  }
5333  }
5334  else if (info_type == "AnatomicalOrientation")
5335  {
5336  std::string anatomical_orientation;
5337  meta_header >> anatomical_orientation;
5338  }
5339  else if (info_type == "ElementSpacing")
5340  {
5341  for (unsigned int i(0); i < number_of_dimensions; ++i)
5342  {
5343  meta_header >> spacing[i];
5344  }
5345  }
5346  else if (info_type == "DimSize")
5347  {
5348  for (unsigned int i(0); i < number_of_dimensions; ++i)
5349  {
5350  meta_header >> dimensions[i];
5351  }
5352  }
5353  else if (info_type == "ElementType")
5354  {
5355  std::string data_type_string;
5356  meta_header >> data_type_string;
5357 
5358  if (data_type_string == "MET_DOUBLE")
5359  {
5360  element_type = typeid(double).name();
5361  }
5362  else if (data_type_string == "MET_FLOAT")
5363  {
5364  element_type = typeid(float).name();
5365  }
5366  else if (data_type_string == "MET_CHAR")
5367  {
5368  element_type = typeid(char).name();
5369  }
5370  else if (data_type_string == "MET_UCHAR")
5371  {
5372  element_type = typeid(unsigned char).name();
5373  }
5374  else if (data_type_string == "MET_SHORT")
5375  {
5376  element_type = typeid(short).name();
5377  }
5378  else if (data_type_string == "MET_USHORT")
5379  {
5380  element_type = typeid(unsigned short).name();
5381  }
5382  else if (data_type_string == "MET_INT")
5383  {
5384  element_type = typeid(int).name();
5385  }
5386  else if (data_type_string == "MET_UINT")
5387  {
5388  element_type = typeid(unsigned int).name();
5389  }
5390  else
5391  {
5392  std::string error_message("The data type \"");
5393  error_message += data_type_string;
5394  error_message += "\" is not known.";
5395  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5396  }
5397  }
5398  else if (info_type == "HeaderSize")
5399  {
5400  int header_size;
5401  meta_header >> header_size;
5402  }
5403  else if (info_type == "ElementSize")
5404  {
5405  int element_size;
5406 
5407  for (unsigned int i(0); i < number_of_dimensions; ++i)
5408  {
5409  meta_header >> element_size;
5410  }
5411  }
5412  else if (info_type == "ElementDataFile")
5413  {
5414  meta_header >> raw_file_name;
5415 
5416  if (raw_file_name == "LOCAL")
5417  {
5418  loadRAW(meta_header,
5419  dimensions[0], dimensions[1], dimensions[2],
5420  change_endianess,
5421  aFileName,
5422  element_type.data()/*,
5423  deflate_compression*/);
5424  }
5425  else
5426  {
5427  std::string meta_header_file_name(aFileName);
5428  size_t index1(meta_header_file_name.find_last_of('\\'));
5429  size_t index2(meta_header_file_name.find_last_of('/'));
5430  size_t index(0);
5431 
5432  if (index1 == std::string::npos)
5433  {
5434  index1 = 0;
5435  }
5436 
5437  if (index2 == std::string::npos)
5438  {
5439  index2 = 0;
5440  }
5441 
5442  index = std::max(index1, index2);
5443 
5444  if (index)
5445  {
5446  raw_file_name = meta_header_file_name.substr(0, index) + "/" + raw_file_name;
5447  }
5448 
5449  loadRAW(raw_file_name,
5450  dimensions[0], dimensions[1], dimensions[2],
5451  change_endianess,
5452  element_type/*,
5453  deflate_compression*/);
5454  }
5455 
5456  setSpacing(spacing[0], spacing[1], spacing[2]);
5457  }
5458  else
5459  {
5460  std::string error_message("The field \"");
5461  error_message += info_type;
5462  error_message += "\" is not known.";
5463  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5464  }
5465  }
5466 }
5467 
5468 
5469 template<typename T> unsigned int Image<T>::sizeOf(const char* anElementType) const
5470 {
5471  return (sizeOf(std::string(anElementType)));
5472 }
5473 
5474 
5475 template<typename T> unsigned int Image<T>::sizeOf(const std::string& anElementType) const
5476 {
5477  // Double
5478  if (anElementType == typeid(double).name())
5479  return (sizeof(double));
5480 
5481  // Float
5482  else if (anElementType == typeid(float).name())
5483  return (sizeof(float));
5484 
5485  // char
5486  else if (anElementType == typeid(char).name())
5487  return (sizeof(char));
5488 
5489  // unsigned char
5490  else if (anElementType == typeid(unsigned char).name())
5491  return (sizeof(unsigned char));
5492 
5493  // short
5494  else if (anElementType == typeid(short).name())
5495  return (sizeof(short));
5496 
5497  // unsigned short
5498  else if (anElementType == typeid(unsigned short).name())
5499  return (sizeof(unsigned short));
5500 
5501  // int
5502  else if (anElementType == typeid(int).name())
5503  return (sizeof(int));
5504 
5505  // unsigned int
5506  else if (anElementType == typeid(unsigned int).name())
5507  return (sizeof(unsigned int));
5508 
5509 
5510  std::string error_message("The data type \"");
5511  error_message += anElementType;
5512  error_message += "\" is not known.";
5513  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5514 }
5515 
5516 
5517 //-----------------------------------------------------------------------
5518 template<typename T> void Image<T>::loadRAW(std::ifstream& anInputStream,
5519  unsigned int aWidth,
5520  unsigned int aHeight,
5521  unsigned int aNumberOfSlices,
5522  bool aChangeEndianessFlag,
5523  const char* aFileName,
5524  const char* anElementType/*,
5525  bool aCompressionFlag*/)
5526 //-----------------------------------------------------------------------
5527 {
5528  // Release memory if needed
5529  destroy();
5530 
5531  // Store the image size
5532  m_width = aWidth;
5533  m_height = aHeight;
5534  m_number_of_slices = aNumberOfSlices;
5535 
5536  unsigned int number_of_voxels(m_width * m_height * m_number_of_slices);
5537 
5538  // No data to read
5539  if (!number_of_voxels)
5540  {
5541  return;
5542  }
5543 
5544  // Data size in native file format (in bytes)
5545  unsigned int requested_length_in_bytes(
5546  number_of_voxels *
5547  sizeOf(anElementType));
5548 
5549  // Allocate the memory
5550  char* p_temp_buffer(new char[requested_length_in_bytes + sizeOf(anElementType)]); // + sizeOf(anElementType) in case there is an offset
5551 
5552  // Out of memory
5553  if (!p_temp_buffer)
5554  {
5555  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
5556  }
5557 
5558  // Get file size
5559  int offset_current_position(anInputStream.tellg());
5560  anInputStream.seekg (0, std::ios::end);
5561  unsigned int length(int(anInputStream.tellg()) - offset_current_position);
5562  anInputStream.seekg (offset_current_position, std::ios::beg);
5563 
5564  if (length < requested_length_in_bytes)
5565  {
5566  // release the memory
5567  delete [] p_temp_buffer;
5568 
5569  std::string error_message("The file size of \"");
5570  error_message += aFileName;
5571  error_message += "\" is too small to contained the specified raw data.";
5572  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5573  }
5574 
5575 
5576  // Allocate the memory
5577  /*m_p_image = new T[m_width * m_height * m_number_of_slices];
5578 
5579  // Out of memory
5580  if (m_width && m_height && m_number_of_slices && !m_p_image)
5581  {
5582  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
5583  }*/
5584 
5585 
5586 
5587  // Skip the first byte if needed
5588  int offset_in_bytes(0);
5589  anInputStream.read(reinterpret_cast<char*>(p_temp_buffer), 1);
5590  if (*reinterpret_cast<char*>(p_temp_buffer) != 10)
5591  {
5592  offset_in_bytes = 1;
5593  }
5594 
5595  // Read the data
5596  anInputStream.read(reinterpret_cast<char*>(p_temp_buffer) + offset_in_bytes, requested_length_in_bytes);
5597 
5598  // Close the file
5599  anInputStream.close();
5600 
5601  // Change endianess
5602  if (aChangeEndianessFlag && sizeOf(anElementType) > 1)
5603  {
5604 #ifdef NDEBUG
5605  #pragma omp parallel for
5606 #endif
5607  for (int i = 0; i < m_width * m_height * m_number_of_slices; ++i)
5608  {
5609  swapBytes(p_temp_buffer[i]);
5610  }
5611  }
5612 
5613  // Decompress the data
5614  /*if (aCompressionFlag)
5615  {
5616  // Array to store the decompressed data
5617  char* p_dest(new char[requested_length_in_bytes]);
5618 
5619  // Decompress the data
5620  int z_lib_return_code = inflate(p_temp_buffer,
5621  requested_length_in_bytes,
5622  &p_dest);
5623 
5624  // It does not exist
5625  if (!z_lib_return_code)
5626  {
5627  delete [] p_dest;
5628  throw Exception(__FILE__, __FUNCTION__, __LINE__, "No image data was decoded.");
5629  }
5630 
5631  // Release the compressed data
5632  delete [] p_temp_buffer;
5633  p_temp_buffer = 0;
5634 
5635  // Keep the decompressed data
5636  p_temp_buffer = p_dest;
5637  }*/
5638 
5639 
5640  // Same native file format is the same as T
5641  if (anElementType == typeid(T).name())
5642  {
5643  // Reassign the pointer
5644  m_p_image = reinterpret_cast<T*>(p_temp_buffer);
5645  }
5646  // Conversion is needed
5647  else
5648  {
5649  // Allocate the memory
5650  m_p_image = new T[number_of_voxels];
5651 
5652  // Out of memory
5653  if (!m_p_image)
5654  {
5655  // release the memory
5656  delete [] p_temp_buffer;
5657 
5658  throw OutOfMemoryException(__FILE__, __FUNCTION__, __LINE__);
5659  }
5660 
5661  // Double
5662  if (std::string(anElementType) == typeid(double).name())
5663  {
5664  double* p_temp = reinterpret_cast<double*> (p_temp_buffer);
5665  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5666  }
5667 
5668  // Float
5669  else if (std::string(anElementType) == typeid(float).name())
5670  {
5671  float* p_temp = reinterpret_cast<float*> (p_temp_buffer);
5672  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5673  }
5674 
5675  // char
5676  else if (std::string(anElementType) == typeid(char).name())
5677  {
5678  char* p_temp = reinterpret_cast<char*> (p_temp_buffer);
5679  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5680  }
5681 
5682  // unsigned char
5683  else if (std::string(anElementType) == typeid(unsigned char).name())
5684  {
5685  unsigned char* p_temp = reinterpret_cast<unsigned char*> (p_temp_buffer);
5686  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5687  }
5688 
5689  // short
5690  else if (std::string(anElementType) == typeid(short).name())
5691  {
5692  short* p_temp = reinterpret_cast<short*> (p_temp_buffer);
5693  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5694  }
5695 
5696  // unsigned short
5697  else if (std::string(anElementType) == typeid(unsigned short).name())
5698  {
5699  unsigned short* p_temp = reinterpret_cast<unsigned short*> (p_temp_buffer);
5700  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5701  }
5702 
5703  // int
5704  else if (std::string(anElementType) == typeid(int).name())
5705  {
5706  int* p_temp = reinterpret_cast<int*> (p_temp_buffer);
5707  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5708  }
5709 
5710  // unsigned int
5711  else if (std::string(anElementType) == typeid(unsigned int).name())
5712  {
5713  unsigned int* p_temp = reinterpret_cast<unsigned int*> (p_temp_buffer);
5714  std::copy(p_temp, p_temp + number_of_voxels, m_p_image);
5715  }
5716 
5717  else
5718  {
5719  // release the memory
5720  delete [] p_temp_buffer;
5721 
5722  std::string error_message("The data type \"");
5723  error_message += anElementType;
5724  error_message += "\" is not known.";
5725  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5726  }
5727 
5728  // release the memory
5729  delete [] p_temp_buffer;
5730  }
5731 }
5732 
5733 
5734 //------------------------------------------------------------------------------------
5735 template<typename T> void Image<T>::saveMetaHeader(const std::string& aFileName, bool useDeflateCompressionIfPossible) const
5736 //------------------------------------------------------------------------------------
5737 {
5738  saveMetaHeader(aFileName.data(), useDeflateCompressionIfPossible);
5739 }
5740 
5741 
5742 //-----------------------------------------------------------------------------
5743 template<typename T> void Image<T>::saveMetaHeader(const char* aFileName, bool useDeflateCompressionIfPossible) const
5744 //-----------------------------------------------------------------------------
5745 {
5746  // Open the file which will contain the meta header
5747  std::ofstream meta_header(aFileName);
5748 
5749  // The file is not open
5750  if (!meta_header.is_open())
5751  {
5752  std::string error_message;
5753  error_message = "Cannot create the file (\"";
5754  error_message += aFileName;
5755  error_message += "\")";
5756 
5757  throw Exception(__FILE__, __FUNCTION__, __LINE__, error_message);
5758  }
5759 
5760  meta_header << "ObjectType = Image" << std::endl;
5761  if (m_number_of_slices == 1)
5762  {
5763  meta_header << "NDims = 2" << std::endl;
5764  meta_header << "DimSize = " << m_width << " " << m_height << std::endl;
5765  }
5766  else
5767  {
5768  meta_header << "NDims = 3" << std::endl;
5769  meta_header << "DimSize = " << m_width << " " << m_height << " " << m_number_of_slices << std::endl;
5770  }
5771 
5772  std::string data_type_string;
5773  if (typeid(T) == typeid(double))
5774  {
5775  data_type_string = "MET_DOUBLE";
5776  }
5777  else if (typeid(T) == typeid(float))
5778  {
5779  data_type_string = "MET_FLOAT";
5780  }
5781  else if (typeid(T) == typeid(char))
5782  {
5783  data_type_string = "MET_CHAR";
5784  }
5785  else if (typeid(T) == typeid(unsigned char))
5786  {
5787  data_type_string = "MET_UCHAR";
5788  }
5789  else if (typeid(T) == typeid(short))
5790  {
5791  data_type_string = "MET_SHORT";
5792  }
5793  else if (typeid(T) == typeid(unsigned short))
5794  {
5795  data_type_string = "MET_USHORT";
5796  }
5797  else if (typeid(T) == typeid(int))
5798  {
5799  data_type_string = "MET_INT";
5800  }
5801  else if (typeid(T) == typeid(unsigned int))
5802  {
5803  data_type_string = "MET_UINT";
5804  }
5805  else
5806  {
5807  throw Exception(__FILE__, __FUNCTION__, __LINE__, "Unsupported data type");
5808  }
5809 
5810  meta_header << "ElementType = " << data_type_string << std::endl;
5811 
5812  if (m_number_of_slices == 1)
5813  {
5814  meta_header << "ElementSize = 1 1" << std::endl;
5815  meta_header << "ElementSpacing = " << m_spacing[0] << " " << m_spacing[1] << std::endl;
5816  }
5817  else
5818  {
5819  meta_header << "ElementSize = 1 1 1" << std::endl;
5820  meta_header << "ElementSpacing = " << m_spacing[0] << " " << m_spacing[1] << " " << m_spacing[2] << std::endl;
5821  }
5822 
5823  meta_header << "ElementByteOrderMSB = " <<
5824  (isBigEndian()? "True":"False") << std::endl;
5825 
5826  meta_header << "CompressedData = " <<
5827  (useDeflateCompressionIfPossible? "True":"False") << std::endl;
5828 
5829  std::string raw_file_name(aFileName);
5830  if (isMHD(aFileName))
5831  {
5832  raw_file_name[raw_file_name.size() - 3] = 'r';
5833  raw_file_name[raw_file_name.size() - 2] = 'a';
5834  raw_file_name[raw_file_name.size() - 1] = 'w';
5835  }
5836  else
5837  {
5838  raw_file_name += ".raw";
5839  }
5840  if (useDeflateCompressionIfPossible)
5841  {
5842  raw_file_name += ".gz";
5843  }
5844 
5845  size_t index1(raw_file_name.find_last_of('\\'));
5846  size_t index2(raw_file_name.find_last_of('/'));
5847  size_t index(0);
5848 
5849  if (index1 != std::string::npos)
5850  {
5851  ++index1;
5852  }
5853  else
5854  {
5855  index1 = 0;
5856  }
5857 
5858  if (index2 != std::string::npos)
5859  {
5860  ++index2;
5861  }
5862  else
5863  {
5864  index2 = 0;
5865  }
5866 
5867  index = std::max(index1, index2);
5868 
5869 
5870  if (isMHD(aFileName))
5871  {
5872  meta_header << "ElementDataFile = " << raw_file_name.substr(index, raw_file_name.size() - index) << std::endl;
5873 
5874  saveRAW(raw_file_name, useDeflateCompressionIfPossible);
5875  }
5876  else
5877  {
5878  meta_header << "ElementDataFile = " << "LOCAL\n";// << std::endl;
5879 
5880  // Write content to file
5881  // Compressed data
5882  if (useDeflateCompressionIfPossible)
5883  {
5884  char* compressed_data = new char[m_width * m_height * m_number_of_slices * sizeof(T)];
5885  int data_size = deflate(
5886  reinterpret_cast<char*>(m_p_image),
5887  m_width * m_height * m_number_of_slices * sizeof(T),
5888  &compressed_data);
5889 
5890  meta_header.write(compressed_data, data_size);
5891  delete [] compressed_data;
5892  }
5893  // Uncompressed data
5894  else
5895  {
5896  meta_header.write(reinterpret_cast<char*>(m_p_image), m_width * m_height * m_number_of_slices * sizeof(T));
5897  }
5898  }
5899 }
5900 
5901 
5902 } // namespace gVirtualXRay
bool isRAW(const char *aFileName)
Check if the extension of a file name is RAW.
Definition: Utilities.inl:145
bool isMHD(const char *aFileName)
Check if the extension of a file name is MHD.
Definition: Utilities.inl:177
Class to handle GLSL shaders.
Image< T > operator+(const T &aValue, const Image< T > &anImage)
Definition: Image.inl:5137
bool isBigEndian()
Definition: Utilities.inl:334
T & getPixel(unsigned int i, unsigned int j, unsigned int k=0)
Accessor on a pixel value.
Definition: Image.inl:582
bool isDAT(const char *aFileName)
Check if the extension of a file name is DAT.
Definition: Utilities.inl:97
bool isTIFF(const char *aFileName)
Check if the extension of a file name is TIFF.
Definition: Utilities.inl:227
Class to handle exceptions when memory cannot be allocated dynamically.
void checkOpenGLErrorStatus(const char *aFileName, const char *aFunctionName, int aLineNumber)
Check OpenGL&#39;s error status.
bool isMHA(const char *aFileName)
Check if the extension of a file name is MHA.
Definition: Utilities.inl:161
Image< T > log(const Image< T > &anImage)
Definition: Image.inl:5169
void setLabel(const std::string &aComputeShaderLabel)
Set the compute shader label. This is useful for debugging.
Definition: Shader.inl:79
bool isPGM(const char *aFileName)
Check if the extension of a file name is PGM.
Definition: Utilities.inl:129
Image< T > rotate(float anAngleInDegrees) const
Definition: Image.inl:4347
void normalise()
Normalise the current vector so that its length is 1.
Definition: Vec3.inl:166
OutOfMemoryException is a class to handle exceptions when accessing an array cell that is not accessi...
T getX() const
Accessor on the position along the x-axis.
Definition: Vec3.inl:115
bool isValid() const
Accessor on the validity status of the shader.
Definition: Shader.inl:95
VEC3 m_spacing
The space between two successive pixels.
Definition: Image.h:1158
Image()
Default constructor.
Definition: Image.inl:184
FileDoesNotExistException is a class to handle exceptions when trying to open a read-only file that i...
T getZ() const
Accessor on the position along the z-axis.
Definition: Vec3.inl:131
Exception is a class to handle exceptions.
Definition: Exception.h:109
void transferHostToDevice()
Definition: Image.inl:1028
Sinogram is a class to reconstruct images from a sinogram.
Definition: Image.h:94
bool isTXT(const char *aFileName)
Check if the extension of a file name is TXT.
Definition: Utilities.inl:113
std::string getShaderPixelType(const std::type_info &aTypeID)
bool isJPEG(const char *aFileName)
Check if the extension of a file name is JPEG.
Definition: Utilities.inl:209
Image< T > operator/(const T &aValue, const Image< T > &anImage)
Definition: Image.inl:5113
Class to handle exceptions when trying to open a read-only file that is not accessible.
#define gVirtualXRay_VERSION_MAJOR
#define TIFF_UINT16_T
Definition: Image.h:99
#define EPSILON
Smallest value that can be stored with a real number.
bool isLittleEndian()
Definition: Utilities.inl:343
Generic class to handle exceptions.
#define gVirtualXRay_VERSION_PATCH
Image< T > log() const
Definition: Image.inl:2122
T * getRawData()
Accessor on the raw data.
Definition: Image.inl:717
bool isDCM(const char *aFileName)
Check if the extension of a file name is DCM.
Definition: Utilities.inl:193
void save(const char *aFileName, bool anInvertLUTFlag=false, const char *aComment="", bool useDeflateCompressionIfPossible=false) const
Save the image in a file.
Definition: Image.inl:2432
std::string getShaderRGBAPixelType(const std::type_info &aTypeID)
T getY() const
Accessor on the position along the y-axis.
Definition: Vec3.inl:123
const VEC3 & getSpacing() const
Definition: Image.inl:828
#define gVirtualXRay_VERSION_MINOR
void swapBytes(T &aValue)
Definition: Utilities.inl:384
PolygonMesh is a class to handle polygon (triangles) meshes.
Definition: PolygonMesh.h:114
Image is a class to manage a greyscale image.
Definition: Image.h:92
std::string getShaderImageType(const std::type_info &aTypeID)
float RATIONAL_NUMBER
Type of data used to store real numbers.
Definition: Types.h:107
unsigned int getHeight() const
Number of pixels along the vertical axis.
Definition: Image.inl:792
void setPixel(unsigned int i, unsigned int j, unsigned int k, T aValue)
Set a pixel.
Definition: Image.inl:563
Image< T > abs() const
Definition: Image.inl:2103
Image constantPadFilter(const T &aPadValue) const
Definition: Image.inl:1927
Vec3< RATIONAL_NUMBER > VEC3
Type of data used to store 3D vectors.
Definition: Types.h:115
VEC3 getPixelPosition(unsigned int i, unsigned int j, unsigned int k=0) const
Accessor on a pixel position.
Definition: Image.inl:638
std::ostream & logWarning(const std::string &aMessage="")
unsigned int getDepth() const
Number of pixels along the Z axis.
Definition: Image.inl:800
Shader is a class to handle shaders written in GLSL.
Definition: Shader.h:87
unsigned int m_number_of_slices
Number of slices.
Definition: Image.h:1154
Some utility functions that do not fit anywhere else.
unsigned int m_width
Number of pixel along the horizontal axis.
Definition: Image.h:1146
std::ostream & logNow(const std::string &aMessage="")
bool checkExtension(const char *aFileName, const char *anExtension)
Definition: Utilities.inl:325
Image< T > operator-(const T &aValue, const Image< T > &anImage)
Definition: Image.inl:5146
unsigned int getWidth() const
Number of pixels along the horizontal axis.
Definition: Image.inl:784
#define TIFF_UINT32_T
Definition: Image.h:103
int deflate(const void *src, int srcLen, char **apDst)
Compress data using the Zlib.
Image< T > abs(const Image< T > &anImage)
Definition: Image.inl:5177
virtual const char * what() const
Accessor on the error message.
Definition: Exception.inl:143
std::string getShaderTypeID(const std::type_info &aTypeID)
void setSpacing(const VEC3 &aPixelSize)
Definition: Image.inl:808
void enable() const
Enable the current shader.
Image< T > gauss2D(unsigned int aSize, double aSigmaValue)
Definition: Image.inl:310
void setInternalData(int aTypeOfPrimitive, const void *aVertexArray, unsigned int aNumberOfVertices, int aVertexDataType, const void *anIndexArray, unsigned int aNumberOfIndices, int anIndexDataType, bool aCreateVBOFlag, int aTypeOfVBO)
Class to handle exceptions when accessing an array cell that is not accessible, i.e. out of bounds memory access.
void progressBar(float aPercentage, bool aNewLineFlag=false)
const double Pi
Pi.
int getShaderOpenGLType(const std::type_info &aTypeID)
Constant values, such as the Z number of different atoms, etc.
unsigned int getTextureId() const
Definition: Image.inl:5058
FFT is a class to compute the FFT of a greyscale image.
Definition: FFT.h:78
bool isMAT(const char *aFileName)
Check if the extension of a file name is MAT.
Definition: Utilities.inl:81
unsigned int m_height
Number of pixel along the vertical axis.
Definition: Image.h:1150
void loadSource(const std::string &aVertexShaderSourceCode, const std::string &aFragmentShaderSourceCode)
Load the shader source code.
Class to handle polygon (triangles) meshes.
Image< T > operator*(const T &aValue, const Image< T > &anImage)
Definition: Image.inl:5104
std::string getPixelType(const std::string &aFileName)
Definition: Image.inl:5096
T * m_p_image
The pixel data.
Definition: Image.h:1162
T & getPixelValue(unsigned int i, unsigned int j, unsigned int k=0)
Accessor on a pixel value.
Definition: Image.inl:618
bool useOpenGLCompute()
Check if OpenGL Compute Shaders are supported by the current OpenGL context.