1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 /**
19  * \author Don C. Bigler
20  *         The Pennsylvania State University 2005
21  *
22  * This implementation was contributed as a paper to the Insight Journal
23  * http://insight-journal.org/midas/handle.php?handle=1926/1381
24  *
25  */
26 
27 #include "itkPhilipsRECImageIO.h"
28 #include "itkPhilipsPAR.h"
29 #include "itkIOCommon.h"
30 #include "itkByteSwapper.h"
31 #include "itkMetaDataObject.h"
32 #include "itkSpatialOrientationAdapter.h"
33 #include "itksys/SystemTools.hxx"
34 #include "itk_zlib.h"
35 #include <fstream>
36 
37 namespace itk
38 {
39 const char *const PAR_Version = "PAR_Version";
40 const char *const PAR_SliceOrientation = "PAR_SliceOrientation";
41 const char *const PAR_ExaminationName = "PAR_ExaminationName";
42 const char *const PAR_ProtocolName = "PAR_ProtocolName";
43 const char *const PAR_SeriesType = "PAR_SeriesType";
44 const char *const PAR_AcquisitionNr = "PAR_AcquisitionNr";
45 const char *const PAR_ReconstructionNr = "PAR_ReconstructionNr";
46 const char *const PAR_ScanDuration = "PAR_ScanDuration";
47 const char *const PAR_MaxNumberOfCardiacPhases = "PAR_MaxNumberOfCardiacPhases";
48 const char *const PAR_TriggerTimes = "PAR_TriggerTimes";
49 const char *const PAR_MaxNumberOfEchoes = "PAR_MaxNumberOfEchoes";
50 const char *const PAR_EchoTimes = "PAR_EchoTimes";
51 const char *const PAR_MaxNumberOfDynamics = "PAR_MaxNumberOfDynamics";
52 const char *const PAR_MaxNumberOfMixes = "PAR_MaxNumberOfMixes";
53 const char *const PAR_PatientPosition = "PAR_PatientPosition";
54 const char *const PAR_PreparationDirection = "PAR_PreparationDirection";
55 const char *const PAR_Technique = "PAR_Technique";
56 const char *const PAR_ScanMode = "PAR_ScanMode";
57 const char *const PAR_NumberOfAverages = "PAR_NumberOfAverages";
58 const char *const PAR_ScanResolution = "PAR_ScanResolution";
59 const char *const PAR_RepetitionTimes = "PAR_RepetitionTimes";
60 const char *const PAR_ScanPercentage = "PAR_ScanPercentage";
61 const char *const PAR_FOV = "PAR_FOV";
62 const char *const PAR_WaterFatShiftPixels = "PAR_WaterFatShiftPixels";
63 const char *const PAR_AngulationMidSlice = "PAR_AngulationMidSlice";
64 const char *const PAR_OffCentreMidSlice = "PAR_OffCentreMidSlice";
65 const char *const PAR_FlowCompensation = "PAR_FlowCompensation";
66 const char *const PAR_Presaturation = "PAR_Presaturation";
67 const char *const PAR_CardiacFrequency = "PAR_CardiacFrequency";
68 const char *const PAR_MinRRInterval = "PAR_MinRRInterval";
69 const char *const PAR_MaxRRInterval = "PAR_MaxRRInterval";
70 const char *const PAR_PhaseEncodingVelocity = "PAR_PhaseEncodingVelocity";
71 const char *const PAR_MTC = "PAR_MTC";
72 const char *const PAR_SPIR = "PAR_SPIR";
73 const char *const PAR_EPIFactor = "PAR_EPIFactor";
74 const char *const PAR_TurboFactor = "PAR_TurboFactor";
75 const char *const PAR_DynamicScan = "PAR_DynamicScan";
76 const char *const PAR_Diffusion = "PAR_Diffusion";
77 const char *const PAR_DiffusionEchoTime = "PAR_DiffusionEchoTime";
78 const char *const PAR_MaxNumberOfDiffusionValues =
79   "PAR_MaxNumberOfDiffusionValues";
80 const char *const PAR_GradientBValues = "PAR_GradientBValues";
81 const char *const PAR_MaxNumberOfGradientOrients =
82   "PAR_MaxNumberOfGradientOrients";
83 const char *const PAR_GradientDirectionValues = "PAR_GradientDirectionValues";
84 const char *const PAR_InversionDelay = "PAR_InversionDelay";
85 const char *const PAR_NumberOfImageTypes = "PAR_NumberOfImageTypes";
86 const char *const PAR_ImageTypes = "PAR_ImageTypes";
87 const char *const PAR_NumberOfScanningSequences =
88   "PAR_NumberOfScanningSequences";
89 const char *const PAR_ScanningSequences = "PAR_ScanningSequences";
90 const char *const PAR_ScanningSequenceImageTypeRescaleValues =
91   "PAR_ScanningSequenceImageTypeRescaleValues";
92 const char *const PAR_NumberOfASLLabelTypes = "PAR_NumberOfASLLabelTypes";
93 const char *const PAR_ASLLabelTypes = "PAR_ASLLabelTypes";
94 
95 static std::string
GetExtension(const std::string & filename)96 GetExtension(const std::string & filename)
97 {
98   std::string fileExt( itksys::SystemTools::GetFilenameLastExtension(filename) );
99 
100   // If the last extension is .gz, then need to pull off 2 extensions.
101   //.gz is the only valid compression extension.
102   if ( fileExt == std::string(".gz") )
103     {
104     fileExt = itksys::SystemTools::GetFilenameLastExtension(
105       itksys::SystemTools::GetFilenameWithoutLastExtension(filename) );
106     fileExt += ".gz";
107     }
108   // Check that a valid extension was found
109   // Will check for either all caps or all lower-case.
110   // By default the Philips Pride Workstation outputs
111   // the filenames as all caps, but a user may change the
112   // filenames to lowercase.  This will allow one or the
113   // other.  Mixed caps/lower-case will always (with the
114   // exception of the lower-case gz on the end which is
115   // always assumed to be lower-case) fail on an OS with
116   // a case sensitive file system.
117   if ( fileExt != ".REC.gz"
118        && fileExt != ".REC"
119        && fileExt != ".PAR"
120        && fileExt != ".rec.gz"
121        && fileExt != ".rec"
122        && fileExt != ".par" )
123     {
124     return ( "" );
125     }
126 
127   return ( fileExt );
128 }
129 
130 static std::string
GetRootName(const std::string & filename)131 GetRootName(const std::string & filename)
132 {
133   const std::string fileExt = GetExtension(filename);
134 
135   // Create a base filename
136   // i.e Image.PAR --> Image
137   if ( fileExt.length() > 0
138        && filename.length() > fileExt.length() )
139     {
140     const std::string::size_type it = filename.find_last_of(fileExt);
141     std::string                  baseName( filename, 0, it - ( fileExt.length() - 1 ) );
142     return ( baseName );
143     }
144   //Default to return same as input when the extension is nothing.
145   return ( filename );
146 }
147 
148 static std::string
GetHeaderFileName(const std::string & filename)149 GetHeaderFileName(const std::string & filename)
150 {
151   std::string       ImageFileName(filename);
152   const std::string fileExt = GetExtension(filename);
153 
154   // Accommodate either all caps or all lower-case filenames.
155   if ( ( fileExt == ".REC" ) || ( fileExt == ".REC.gz" ) )
156     {
157     ImageFileName = GetRootName(filename);
158     ImageFileName += ".PAR";
159     }
160   else if ( ( fileExt == ".rec" ) || ( fileExt == ".rec.gz" ) )
161     {
162     ImageFileName = GetRootName(filename);
163     ImageFileName += ".par";
164     }
165   return ( ImageFileName );
166 }
167 
168 //Returns the base image filename.
GetImageFileName(const std::string & filename)169 static std::string GetImageFileName(const std::string & filename)
170 {
171   std::string       ImageFileName(filename);
172   const std::string fileExt = GetExtension(filename);
173 
174   // Default to uncompressed .REC if .PAR is given as file name.
175   if ( fileExt == ".PAR" )
176     {
177     ImageFileName = GetRootName(filename);
178     ImageFileName += ".REC";
179     }
180   else if ( fileExt == ".par" )
181     {
182     ImageFileName = GetRootName(filename);
183     ImageFileName += ".rec";
184     }
185   return ( ImageFileName );
186 }
187 
188 //----------------------------------------------------------------------------
189 // This generates the correct offset to the desired image type and
190 // scanning sequence (randomly ordered in the REC).
PhilipsRECImageIOGetImageTypeOffset(int imageType,int scanSequence,int volumeIndex,int slice,int numSlices,struct par_parameter parParam,PhilipsPAR::PARSliceIndexImageTypeVector sliceImageTypesIndex,PhilipsPAR::PARSliceIndexScanSequenceVector sliceScanSequenceIndex)191 int PhilipsRECImageIOGetImageTypeOffset(int imageType, int scanSequence,
192                                         int volumeIndex, int slice, int numSlices, struct par_parameter parParam,
193                                         PhilipsPAR::PARSliceIndexImageTypeVector sliceImageTypesIndex,
194                                         PhilipsPAR::PARSliceIndexScanSequenceVector sliceScanSequenceIndex)
195 {
196   int index = volumeIndex * parParam.num_slice_repetitions * numSlices
197               + slice * parParam.num_slice_repetitions;
198   int i;
199 
200   for ( i = 0; i < parParam.num_slice_repetitions; i++ )
201     {
202     if ( ( sliceImageTypesIndex[index + i].second == imageType )
203          && ( sliceScanSequenceIndex[index + i].second == scanSequence ) )
204       {
205       break;
206       }
207     }
208   return i;
209 }
210 
211 //----------------------------------------------------------------------------
212 // This creates the desired slice order index (slice or image block).
PhilipsRECImageIOSetupSliceIndex(PhilipsRECImageIO::SliceIndexType * indexMatrix,int sortBlock,struct par_parameter parParam,PhilipsPAR::PARImageTypeScanSequenceVector imageTypesScanSequenceIndex,PhilipsPAR::PARSliceIndexImageTypeVector sliceImageTypesIndex,PhilipsPAR::PARSliceIndexScanSequenceVector sliceScanSequenceIndex)213 void PhilipsRECImageIOSetupSliceIndex(
214   PhilipsRECImageIO::SliceIndexType *indexMatrix, int sortBlock,
215   struct par_parameter parParam,
216   PhilipsPAR::PARImageTypeScanSequenceVector imageTypesScanSequenceIndex,
217   PhilipsPAR::PARSliceIndexImageTypeVector sliceImageTypesIndex,
218   PhilipsPAR::PARSliceIndexScanSequenceVector sliceScanSequenceIndex)
219 {
220   int index = 0;
221   int actualSlices = parParam.slice;
222   int remainingVolumes = parParam.image_blocks / parParam.num_slice_repetitions;
223 
224   if ( indexMatrix->size() !=
225        (PhilipsRECImageIO::SliceIndexType::size_type)parParam.dim[2] )
226     {
227     std::ostringstream message;
228     message << "indexMatrix->size(): "
229             << indexMatrix->size()
230             << " != parParam.dim[2]: "
231             << parParam.dim[2];
232     ExceptionObject exception(__FILE__, __LINE__,
233                               message.str(),
234                               ITK_LOCATION);
235     throw exception;
236     }
237   if ( parParam.dim[2] != ( parParam.slice * parParam.image_blocks ) )
238     {
239     std::ostringstream message;
240     message << "parParam.dim[2]: "
241             << parParam.dim[2]
242             << " != (parParam.slice*parParam.image_blocks): "
243             << parParam.slice * parParam.image_blocks;
244     ExceptionObject exception(__FILE__, __LINE__,
245                               message.str(),
246                               ITK_LOCATION);
247     throw exception;
248     }
249   if ( !parParam.slicessorted && (imageTypesScanSequenceIndex.size() !=
250        (PhilipsRECImageIO::SliceIndexType::size_type)parParam.num_slice_repetitions) )
251     {
252     std::ostringstream message;
253     message << "imageTypesScanSequenceIndex.size(): "
254             << imageTypesScanSequenceIndex.size()
255             << " != parParam.num_slice_repetitions "
256             << parParam.num_slice_repetitions;
257     ExceptionObject exception(__FILE__, __LINE__,
258                               message.str(),
259                               ITK_LOCATION);
260     throw exception;
261     }
262 
263   // Different index depending on the desired slice sort and the REC
264   // slice order.
265   if ( ( sortBlock && parParam.slicessorted )
266        || ( !sortBlock && !parParam.slicessorted ) )
267     {
268     // No sorting necessary for these cases.
269     for ( int i = 0; i < parParam.dim[2]; i++ )
270       {
271       ( *indexMatrix )[i] = i;
272       }
273     }
274   // This case is the real problematic one.
275   else if ( sortBlock && !parParam.slicessorted
276             && ( parParam.num_slice_repetitions > 1 ) )
277     {
278     // Ok, need to figure out where all of the images are located
279     // using sliceImageTypesIndex and sliceScanSequenceIndex.
280     for ( int i = 0; i < parParam.num_slice_repetitions; i++ )
281       {
282       for ( int j = 0; j < remainingVolumes; j++ )
283         {
284         for ( int k = 0; k < actualSlices; k++ )
285           {
286           ( *indexMatrix )[index] = j * parParam.num_slice_repetitions * actualSlices
287                                     + k * parParam.num_slice_repetitions
288                                     + PhilipsRECImageIOGetImageTypeOffset(
289             imageTypesScanSequenceIndex[i].first,
290             imageTypesScanSequenceIndex[i].second, j, k, actualSlices, parParam,
291             sliceImageTypesIndex, sliceScanSequenceIndex);
292           index++;
293           }
294         }
295       }
296     }
297   else
298     {
299     // Unsort image block or sort by image block.
300     for ( int i = 0; i < parParam.image_blocks; i++ )
301       {
302       for ( int j = 0; j < actualSlices; j++ )
303         {
304         ( *indexMatrix )[index] = j * parParam.image_blocks + i;
305         index++;
306         }
307       }
308     }
309 }
310 
311 void
SwapBytesIfNecessary(void * buffer,SizeValueType numberOfPixels)312 PhilipsRECImageIO::SwapBytesIfNecessary(void *buffer,
313                                         SizeValueType numberOfPixels)
314 {
315   if ( m_ByteOrder == LittleEndian )
316     {
317     switch ( this->m_ComponentType )
318       {
319       case CHAR:
320         ByteSwapper< char >::SwapRangeFromSystemToLittleEndian
321           ( (char *)buffer, numberOfPixels );
322         break;
323       case UCHAR:
324         ByteSwapper< unsigned char >::SwapRangeFromSystemToLittleEndian
325           ( (unsigned char *)buffer, numberOfPixels );
326         break;
327       case SHORT:
328         ByteSwapper< short >::SwapRangeFromSystemToLittleEndian
329           ( (short *)buffer, numberOfPixels );
330         break;
331       case USHORT:
332         ByteSwapper< unsigned short >::SwapRangeFromSystemToLittleEndian
333           ( (unsigned short *)buffer, numberOfPixels );
334         break;
335       case INT:
336         ByteSwapper< int >::SwapRangeFromSystemToLittleEndian
337           ( (int *)buffer, numberOfPixels );
338         break;
339       case UINT:
340         ByteSwapper< unsigned int >::SwapRangeFromSystemToLittleEndian
341           ( (unsigned int *)buffer, numberOfPixels );
342         break;
343       case LONG:
344         ByteSwapper< long >::SwapRangeFromSystemToLittleEndian
345           ( (long *)buffer, numberOfPixels );
346         break;
347       case ULONG:
348         ByteSwapper< unsigned long >::SwapRangeFromSystemToLittleEndian
349           ( (unsigned long *)buffer, numberOfPixels );
350         break;
351       case FLOAT:
352         ByteSwapper< float >::SwapRangeFromSystemToLittleEndian
353           ( (float *)buffer, numberOfPixels );
354         break;
355       case DOUBLE:
356         ByteSwapper< double >::SwapRangeFromSystemToLittleEndian
357           ( (double *)buffer, numberOfPixels );
358         break;
359       default:
360         ExceptionObject exception(__FILE__, __LINE__,
361                                   "Component Type Unknown",
362                                   ITK_LOCATION);
363         throw exception;
364       }
365     }
366   else
367     {
368     switch ( this->m_ComponentType )
369       {
370       case CHAR:
371         ByteSwapper< char >::SwapRangeFromSystemToBigEndian
372           ( (char *)buffer, numberOfPixels );
373         break;
374       case UCHAR:
375         ByteSwapper< unsigned char >::SwapRangeFromSystemToBigEndian
376           ( (unsigned char *)buffer, numberOfPixels );
377         break;
378       case SHORT:
379         ByteSwapper< short >::SwapRangeFromSystemToBigEndian
380           ( (short *)buffer, numberOfPixels );
381         break;
382       case USHORT:
383         ByteSwapper< unsigned short >::SwapRangeFromSystemToBigEndian
384           ( (unsigned short *)buffer, numberOfPixels );
385         break;
386       case INT:
387         ByteSwapper< int >::SwapRangeFromSystemToBigEndian
388           ( (int *)buffer, numberOfPixels );
389         break;
390       case UINT:
391         ByteSwapper< unsigned int >::SwapRangeFromSystemToBigEndian
392           ( (unsigned int *)buffer, numberOfPixels );
393         break;
394       case LONG:
395         ByteSwapper< long >::SwapRangeFromSystemToBigEndian
396           ( (long *)buffer, numberOfPixels );
397         break;
398       case ULONG:
399         ByteSwapper< unsigned long >::SwapRangeFromSystemToBigEndian
400           ( (unsigned long *)buffer, numberOfPixels );
401         break;
402       case FLOAT:
403         ByteSwapper< float >::SwapRangeFromSystemToBigEndian
404           ( (float *)buffer, numberOfPixels );
405         break;
406       case DOUBLE:
407         ByteSwapper< double >::SwapRangeFromSystemToBigEndian
408           ( (double *)buffer, numberOfPixels );
409         break;
410       default:
411         ExceptionObject exception(__FILE__, __LINE__,
412                                   "Component Type Unknown",
413                                   ITK_LOCATION);
414         throw exception;
415       }
416     }
417 }
418 
PhilipsRECImageIO()419 PhilipsRECImageIO::PhilipsRECImageIO()
420 {
421   //by default, have 4 dimensions
422   this->SetNumberOfDimensions(4);
423   this->m_PixelType         = SCALAR;
424   this->m_ComponentType     = CHAR;
425 
426   // Set m_MachineByteOrder to the ByteOrder of the machine
427   // Start out with file byte order == system byte order
428   // this will be changed if we're reading a file to whatever
429   // the file actually contains.
430   if ( ByteSwapper< int >::SystemIsBigEndian() )
431     {
432     this->m_MachineByteOrder = this->m_ByteOrder = BigEndian;
433     }
434   else
435     {
436     this->m_MachineByteOrder = this->m_ByteOrder = LittleEndian;
437     }
438   this->m_SliceIndex = new SliceIndexType();
439 }
440 
~PhilipsRECImageIO()441 PhilipsRECImageIO::~PhilipsRECImageIO()
442 {
443   delete this->m_SliceIndex;
444 }
445 
PrintSelf(std::ostream & os,Indent indent) const446 void PhilipsRECImageIO::PrintSelf(std::ostream & os, Indent indent) const
447 {
448   Superclass::PrintSelf(os, indent);
449 }
450 
451 PhilipsRECImageIO::IndexValueType
GetSliceIndex(IndexValueType index) const452 PhilipsRECImageIO::GetSliceIndex(IndexValueType index) const
453 {
454   IndexValueType maximumSliceNumber =
455     Math::CastWithRangeCheck< IndexValueType, size_t >( this->m_SliceIndex->size() ) - 1;
456 
457   if ( ( index < 0 ) || ( index > maximumSliceNumber ) )
458     {
459     return -1;
460     }
461   return ( *this->m_SliceIndex )[index];
462 }
463 
Read(void * buffer)464 void PhilipsRECImageIO::Read(void *buffer)
465 {
466   unsigned int       dim;
467   const unsigned int dimensions = this->GetNumberOfDimensions();
468   unsigned int       numberOfPixels = 1;
469 
470   for ( dim = 0; dim < dimensions; dim++ )
471     {
472     numberOfPixels *= this->m_Dimensions[dim];
473     }
474 
475   auto *const p = static_cast< char * >( buffer );
476   //8 cases to handle
477   //1: given .PAR and image is .REC
478   //2: given .PAR and image is .REC.gz
479   //3: given .REC
480   //4: given .REC.gz
481   //5: given .par and image is .rec
482   //6: given .par and image is .rec.gz
483   //7: given .rec
484   //8: given .rec.gz
485 
486   /* Returns proper name for cases 1,2,3,4,5,6 */
487   std::string ImageFileName = GetImageFileName(this->m_FileName);
488   //NOTE: gzFile operations act just like FILE * operations when the files
489   // are not in gzip fromat.
490   // This greatly simplifies the following code, and gzFile types are used
491   // everywhere.
492   // In addition, it has the added benefit of reading gzip compressed image
493   // files that do not have a .gz ending.
494   gzFile file_p = gzopen(ImageFileName.c_str(), "rb");
495   if ( file_p == nullptr )
496     {
497     ImageFileName += ".gz";
498     file_p = gzopen(ImageFileName.c_str(), "rb");
499     if ( file_p == nullptr )
500       {
501       std::ostringstream message;
502       message << "Philips REC Data File can not be opened. "
503               << "The following files were attempted:" << std::endl
504               << GetImageFileName(this->m_FileName) << std::endl
505               << ImageFileName;
506       ExceptionObject exception(__FILE__, __LINE__,
507                               message.str(),
508                               ITK_LOCATION);
509       throw exception;
510       }
511     }
512 
513   // read image a slice at a time (sorted).
514   SizeType numberOfSlices = this->m_Dimensions[2];
515   if ( dimensions > 3 )
516     {
517     numberOfSlices *= this->m_Dimensions[3];
518     }
519 
520   SizeType imageSliceSizeInBytes = this->GetImageSizeInBytes() / numberOfSlices;
521 
522   for ( IndexValueType slice = 0; slice < numberOfSlices; slice++ )
523     {
524     IndexValueType realIndex = this->GetSliceIndex( (int)slice );
525     if ( realIndex < 0 )
526       {
527       std::ostringstream message;
528       message << "Philips REC Data File can not be read. "
529               << "The following files were attempted:" << std::endl
530               << GetImageFileName(this->m_FileName) << std::endl
531               << ImageFileName;
532       ExceptionObject exception(__FILE__, __LINE__,
533                                 message.str(),
534                                 ITK_LOCATION);
535       throw exception;
536       }
537     const auto offset =  Math::CastWithRangeCheck< z_off_t, SizeType >(realIndex * imageSliceSizeInBytes);
538     gzseek(file_p, offset, SEEK_SET);
539     gzread( file_p, p + ( slice * imageSliceSizeInBytes ),
540               Math::CastWithRangeCheck< unsigned int, SizeType >(imageSliceSizeInBytes) );
541     }
542   gzclose(file_p);
543   SwapBytesIfNecessary(buffer, numberOfPixels);
544 }
545 
CanReadFile(const char * FileNameToRead)546 bool PhilipsRECImageIO::CanReadFile(const char *FileNameToRead)
547 {
548   std::string filename(FileNameToRead);
549 
550   // we check that the correct extension is given by the user
551   std::string filenameext = GetExtension(filename);
552 
553   if ( filenameext != std::string(".PAR")
554        && filenameext != std::string(".REC")
555        && filenameext != std::string(".REC.gz")
556        && filenameext != std::string(".par")
557        && filenameext != std::string(".rec")
558        && filenameext != std::string(".rec.gz") )
559     {
560     return false;
561     }
562 
563   const std::string HeaderFileName = GetHeaderFileName(filename);
564 
565   // Try to read the par file.
566   struct par_parameter par;
567   // Zero out par_parameter.
568   memset( &par, 0, sizeof( struct par_parameter ) );
569 
570   PhilipsPAR::Pointer philipsPAR = PhilipsPAR::New();
571   try
572     {
573     philipsPAR->ReadPAR(HeaderFileName, &par);
574     // Check to see if there were any problems reading
575     // the par file.
576     if ( par.problemreading )
577       {
578       return false;
579       }
580     }
581   catch ( ExceptionObject & )
582     {
583     return false;
584     }
585 
586   return true;
587 }
588 
ReadImageInformation()589 void PhilipsRECImageIO::ReadImageInformation()
590 {
591   const std::string    HeaderFileName = GetHeaderFileName(this->m_FileName);
592   struct par_parameter par;
593 
594   // Zero out par_parameter.
595   memset( &par, 0, sizeof( struct par_parameter ) );
596 
597   // Read PAR file.
598   PhilipsPAR::Pointer philipsPAR = PhilipsPAR::New();
599   try
600     {
601     philipsPAR->ReadPAR(HeaderFileName, &par);
602     }
603   catch ( itk::ExceptionObject & )
604     {
605     throw;
606     }
607   if ( par.problemreading )
608     {
609     ExceptionObject exception(__FILE__, __LINE__,
610                               "Problem reading PAR file",
611                               ITK_LOCATION);
612     throw exception;
613     }
614 
615   // Get all the diffusion info, rescale, etc.
616   GradientBvalueContainerType::Pointer diffusionBvalueVector =
617     GradientBvalueContainerType::New();
618   GradientDirectionContainerType::Pointer diffusionGradientOrientationVector =
619     GradientDirectionContainerType::New();
620   if ( !philipsPAR->GetDiffusionGradientOrientationAndBValues(HeaderFileName,
621                                                               diffusionGradientOrientationVector, diffusionBvalueVector) )
622     {
623     ExceptionObject exception(__FILE__, __LINE__,
624                               "Problem reading diffusion gradients and b values from PAR file",
625                               ITK_LOCATION);
626     throw exception;
627     }
628 
629   // Get ASL label types.
630   LabelTypesASLContainerType::Pointer labelTypesASLVector =
631     LabelTypesASLContainerType::New();
632   if ( !philipsPAR->GetLabelTypesASL(HeaderFileName, labelTypesASLVector) )
633     {
634     ExceptionObject exception(__FILE__, __LINE__,
635                               "Problem reading ASL label types from PAR file",
636                               ITK_LOCATION);
637     throw exception;
638     }
639 
640   // Get rescale values associated with each scanning sequence.
641   ScanningSequenceImageTypeRescaleValuesContainerType::Pointer
642     scanningSequenceImageTypeRescaleVector =
643     ScanningSequenceImageTypeRescaleValuesContainerType::New();
644   scanningSequenceImageTypeRescaleVector->clear();
645   // Must match number of scanning sequences.
646   scanningSequenceImageTypeRescaleVector->resize(par.num_scanning_sequences);
647   for ( int scanIndex = 0; scanIndex < par.num_scanning_sequences; scanIndex++ )
648     {
649     ImageTypeRescaleValuesContainerType::Pointer imageTypeRescaleValuesVector =
650       ImageTypeRescaleValuesContainerType::New();
651     if ( !philipsPAR->GetRECRescaleValues(HeaderFileName, imageTypeRescaleValuesVector,
652                                           par.scanning_sequences[scanIndex]) )
653       {
654       ExceptionObject exception(__FILE__, __LINE__,
655                                 "Problem reading recale values for each scanning sequence from PAR file",
656                                 ITK_LOCATION);
657       throw exception;
658       }
659     ( *scanningSequenceImageTypeRescaleVector )[scanIndex] =
660       imageTypeRescaleValuesVector;
661     }
662 
663   // Setup the slice index matrix.
664   this->m_SliceIndex->clear();
665   this->m_SliceIndex->resize(par.dim[2]);
666   PhilipsPAR::PARSliceIndexImageTypeVector sliceImageTypesIndexes =
667     philipsPAR->GetRECSliceIndexImageTypes(HeaderFileName);
668   PhilipsPAR::PARSliceIndexScanSequenceVector sliceScanSequencesIndexes =
669     philipsPAR->GetRECSliceIndexScanningSequence(HeaderFileName);
670   PhilipsPAR::PARImageTypeScanSequenceVector imageTypesScanSequencesIndexes =
671     philipsPAR->GetImageTypesScanningSequence(HeaderFileName);
672   PhilipsRECImageIOSetupSliceIndex(this->m_SliceIndex, 1, par,
673                                    imageTypesScanSequencesIndexes, sliceImageTypesIndexes,
674                                    sliceScanSequencesIndexes);
675 
676   // As far as I know all Philips REC files are littleEndian.
677   this->m_ByteOrder = LittleEndian;
678 
679   // Set dimensions.
680   unsigned int numberOfDimensions = 4;
681   // In reality PAR/REC files can have more dimensions
682   // but it is very difficult to sort out all of the
683   // possibilities.  The reader will sort the images
684   // by block and the different types of images
685   // stored in the blocks may be determined using the
686   // MetaDataDictionary.
687   // NOTE: ImageFileReader checks the number of dimensions to see if they
688   // are larger than the template dimensions.  If so, it sets the direction
689   // cosines to the default value.  In my view this is a bug, but in order
690   // to get around this problem I need to set the number of dimensions to 3
691   // if the number of image blocks is less than 2.
692   if ( par.image_blocks < 2 )
693     {
694     numberOfDimensions = 3;
695     }
696 
697   this->SetNumberOfDimensions(numberOfDimensions);
698 
699   // As far as I know, Philips REC files are only
700   // 8-bit or 16-bit signed integers.
701   switch ( par.bit )
702     {
703     case 8:
704       m_ComponentType = CHAR;
705       m_PixelType = SCALAR;
706       break;
707     case 16:
708       m_ComponentType = SHORT;
709       m_PixelType = SCALAR;
710       break;
711     default:
712       std::ostringstream message;
713       message << "Unknown data type. par.bit must be 8 or 16. "
714               << "par.bit is "
715               << par.bit;
716       ExceptionObject exception(__FILE__, __LINE__,
717                                 message.str(),
718                                 ITK_LOCATION);
719       throw exception;
720     }
721   //
722   // set up the dimension stuff
723   this->SetDimensions(0, par.dim[0]);
724   this->SetDimensions(1, par.dim[1]);
725   this->SetDimensions(2, par.slice);
726   this->SetSpacing(0, par.vox[0]);
727   this->SetSpacing(1, par.vox[1]);
728   this->SetSpacing(2, par.vox[2]);
729   if ( numberOfDimensions > 3 )
730     {
731     this->SetDimensions(3,par.image_blocks);
732     // Just 1 for the fourth dimension.
733     this->SetSpacing(3,1.0f);
734     }
735 
736   //
737   // figure out re-orientation required if not in Coronal
738   this->ComputeStrides();
739 
740   //Get Dictionary Information
741   //Important hk fields.
742   MetaDataDictionary & thisDic = this->GetMetaDataDictionary();
743   // Necessary to clear dict if ImageIO object is re-used
744   thisDic.Clear();
745   std::string          classname( this->GetNameOfClass() );
746   EncapsulateMetaData< std::string >(thisDic, ITK_InputFilterName, classname);
747 
748   EncapsulateMetaData< std::string >( thisDic, ITK_ImageFileBaseName,
749                                       GetRootName(this->m_FileName) );
750 
751   //Important dime fields
752   EncapsulateMetaData< std::string >( thisDic, ITK_VoxelUnits, std::string("mm") );
753   EncapsulateMetaData< short int >(thisDic, ITK_OnDiskBitPerPixel, par.bit);
754   EncapsulateMetaData< int >(thisDic, ITK_NumberOfDimensions, numberOfDimensions);
755 
756   switch ( par.bit )
757     {
758     case 8:
759       EncapsulateMetaData< std::string >( thisDic, ITK_OnDiskStorageTypeName,
760                                           std::string( typeid( char ).name() ) );
761       break;
762     case 16:
763       EncapsulateMetaData< std::string >( thisDic, ITK_OnDiskStorageTypeName,
764                                           std::string( typeid( short ).name() ) );
765       break;
766     default:
767       break;
768     }
769 
770   //Important hist fields
771   EncapsulateMetaData< std::string >( thisDic, ITK_FileNotes,
772                                       std::string(par.series_type) );
773 
774   using AffineMatrix = Matrix< double, 4, 4 >;
775   AffineMatrix spacing;
776   spacing.SetIdentity();
777 
778   SpatialOrientation::ValidCoordinateOrientationFlags coord_orient;
779 
780   switch ( par.sliceorient )
781     {
782     case PAR_SLICE_ORIENTATION_TRANSVERSAL:
783       // Transverse - the REC data appears to be stored as right-left,
784       // anterior-posterior, and inferior-superior.
785       // Verified using a marker on right side of brain.
786       coord_orient = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI;
787       spacing[0][0] = par.vox[0];
788       spacing[1][1] = par.vox[1];
789       spacing[2][2] = par.vox[2];
790       break;
791     case PAR_SLICE_ORIENTATION_SAGITTAL:
792       // Sagittal - the REC data appears to be stored as anterior-posterior,
793       // superior-inferior, and right-left.
794       // Verified using marker on right side of brain.
795       coord_orient = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASL;
796       spacing[0][0] = par.vox[2];
797       spacing[1][1] = par.vox[0];
798       spacing[2][2] = par.vox[1];
799       break;
800     case PAR_SLICE_ORIENTATION_CORONAL:
801     // Coronal - the REC data appears to be stored as right-left,
802     // superior-inferior, and anterior-posterior.
803     // Verified using marker on right side of brain.
804     // fall thru
805     default:
806       coord_orient = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSA;
807       spacing[0][0] = par.vox[0];
808       spacing[1][1] = par.vox[2];
809       spacing[2][2] = par.vox[1];
810     }
811 
812   using OrientAdapterType = SpatialOrientationAdapter;
813   SpatialOrientationAdapter::DirectionType dir =
814     OrientAdapterType().ToDirectionCosines(coord_orient);
815 
816   AffineMatrix direction;
817   direction.SetIdentity();
818   int rows, columns;
819   for(rows=0; rows<3; rows++)
820     {
821     for(columns=0; columns<3; columns++)
822       {
823       direction[columns][rows] = dir[columns][rows];
824       }
825     }
826 //#define DEBUG_ORIENTATION
827 #ifdef DEBUG_ORIENTATION
828   std::cout << "Direction cosines = " << direction << std::endl;
829   std::cout << "Spacing = " << spacing << std::endl;
830 #endif
831   // Create right/left rotation matrix (about x axis).
832   AffineMatrix r1;
833   r1.SetIdentity();
834   r1[1][1] = std::cos(par.angRL*Math::pi/180.0);
835   r1[2][1] = -std::sin(par.angRL*Math::pi/180.0);
836   r1[1][2] = std::sin(par.angRL*Math::pi/180.0);
837   r1[2][2] = std::cos(par.angRL*Math::pi/180.0);
838   // Create anterior/posterior rotation matrix (about y axis).
839   AffineMatrix r2;
840   r2.SetIdentity();
841   r2[0][0] = std::cos(par.angAP*Math::pi/180.0);
842   r2[2][0] = std::sin(par.angAP*Math::pi/180.0);
843   r2[0][2] = -std::sin(par.angAP*Math::pi/180.0);
844   r2[2][2] = std::cos(par.angAP*Math::pi/180.0);
845   // Create foot/head rotation matrix (about z axis).
846   AffineMatrix r3;
847   r3.SetIdentity();
848   r3[0][0] = std::cos(par.angFH*Math::pi/180.0);
849   r3[1][0] = -std::sin(par.angFH*Math::pi/180.0);
850   r3[0][1] = std::sin(par.angFH*Math::pi/180.0);
851   r3[1][1] = std::cos(par.angFH*Math::pi/180.0);
852   // Total rotation matrix.
853   AffineMatrix rtotal = r1*r2*r3;
854 #ifdef DEBUG_ORIENTATION
855   std::cout << "Right-Left rotation = " << r1 << std::endl;
856   std::cout << "Anterior-Posterior rotation = " << r2 << std::endl;
857   std::cout << "Foot-Head rotation = " << r3 << std::endl;
858   std::cout << "Total = " << rtotal << std::endl;
859 #endif
860 
861   // Find and set origin
862   AffineMatrix final = rtotal*spacing*direction;
863 #ifdef DEBUG_ORIENTATION
864   std::cout << "Final transformation = " << final << std::endl;
865 #endif
866   using PointVector = Vector< double, 4 >;
867   PointVector center;
868   center[0] = (par.dim[0]-1)/2.0;
869   center[1] = (par.dim[0]-1)/2.0;
870   center[2] = (par.slice-1)/2.0;
871   center[3] = 1;
872   PointVector origin = final*center;
873   origin = -origin;
874 #ifdef DEBUG_ORIENTATION
875   std::cout << "Origin before offset = " << origin << std::endl;
876 #endif
877   PointVector offset;
878   offset[0] = par.offRL;
879   offset[1] = par.offAP;
880   offset[2] = par.offFH;
881   offset[3] = 1;
882 #ifdef DEBUG_ORIENTATION
883   std::cout << "Offset = " << offset << std::endl;
884 #endif
885   origin[0] = origin[0]+offset[0];
886   origin[1] = origin[1]+offset[1];
887   origin[2] = origin[2]+offset[2];
888 #ifdef DEBUG_ORIENTATION
889   std::cout << "Origin after offset = " << origin << std::endl;
890 #endif
891 
892   this->SetOrigin(0, origin[0]);
893   this->SetOrigin(1, origin[1]);
894   this->SetOrigin(2, origin[2]);
895 
896   // Find true direction cosines after taking rotations into account.
897   direction = rtotal*direction;
898 #ifdef DEBUG_ORIENTATION
899   std::cout << "Final direction cosines after rotation = " << direction << std::endl;
900 #endif
901 
902   std::vector<double> dirx(numberOfDimensions,0),
903   diry(numberOfDimensions,0),dirz(numberOfDimensions,0),
904   dirBlock(numberOfDimensions,0);
905   dirBlock[numberOfDimensions-1] = 1;
906   dirx[0] = direction[0][0];
907   dirx[1] = direction[1][0];
908   dirx[2] = direction[2][0];
909   diry[0] = direction[0][1];
910   diry[1] = direction[1][1];
911   diry[2] = direction[2][1];
912   dirz[0] = direction[0][2];
913   dirz[1] = direction[1][2];
914   dirz[2] = direction[2][2];
915 
916   this->SetDirection(0,dirx);
917   this->SetDirection(1,diry);
918   this->SetDirection(2,dirz);
919   if ( numberOfDimensions > 3 )
920     {
921     this->SetDirection(3,dirBlock);
922     }
923 
924   EncapsulateMetaData< std::string >( thisDic, ITK_PatientID,
925                                       std::string(par.patient_name) );
926   EncapsulateMetaData< std::string >( thisDic, ITK_ExperimentDate,
927                                       std::string(par.exam_date) );
928   EncapsulateMetaData< std::string >( thisDic, ITK_ExperimentTime,
929                                       std::string(par.exam_time) );
930 
931   // Encapsulate remaining PAR parameters
932   EncapsulateMetaData< int >(thisDic, PAR_SliceOrientation, par.sliceorient);
933   switch ( par.ResToolsVersion )
934     {
935     case RESEARCH_IMAGE_EXPORT_TOOL_V3:
936       EncapsulateMetaData< std::string >( thisDic, PAR_Version, std::string("V3") );
937       break;
938     case RESEARCH_IMAGE_EXPORT_TOOL_V4:
939       EncapsulateMetaData< std::string >( thisDic, PAR_Version, std::string("V4") );
940       break;
941     case RESEARCH_IMAGE_EXPORT_TOOL_V4_1:
942       EncapsulateMetaData< std::string >( thisDic, PAR_Version,
943                                           std::string("V4.1") );
944       break;
945     case RESEARCH_IMAGE_EXPORT_TOOL_V4_2:
946       EncapsulateMetaData< std::string >( thisDic, PAR_Version,
947                                           std::string("V4.2") );
948       break;
949     }
950 
951   EncapsulateMetaData< std::string >( thisDic, PAR_ExaminationName,
952                                       std::string(par.exam_name) );
953   EncapsulateMetaData< std::string >( thisDic, PAR_ProtocolName,
954                                       std::string(par.protocol_name) );
955   EncapsulateMetaData< std::string >( thisDic, PAR_SeriesType,
956                                       std::string(par.series_type) );
957   EncapsulateMetaData< int >(thisDic, PAR_AcquisitionNr, par.scno);
958   EncapsulateMetaData< int >(thisDic, PAR_ReconstructionNr, par.recno);
959   EncapsulateMetaData< int >(thisDic, PAR_ScanDuration, par.scan_duration);
960   EncapsulateMetaData< int >(thisDic, PAR_MaxNumberOfCardiacPhases,
961                              par.cardiac_phases);
962   TriggerTimesContainerType::Pointer triggerTimes =
963     TriggerTimesContainerType::New();
964   triggerTimes->resize(par.cardiac_phases);
965 
966   for ( unsigned int ttime_index = 0; ttime_index < (unsigned int)par.cardiac_phases;
967         ttime_index++ )
968     {
969     triggerTimes->SetElement(ttime_index, (double)par.trigger_times[ttime_index]);
970     }
971 
972   EncapsulateMetaData< TriggerTimesContainerType::Pointer >(thisDic,
973                                                             PAR_TriggerTimes, triggerTimes);
974   EncapsulateMetaData< int >(thisDic, PAR_MaxNumberOfEchoes, par.echoes);
975   EchoTimesContainerType::Pointer echoTimes = EchoTimesContainerType::New();
976   echoTimes->resize(par.echoes);
977 
978   for ( unsigned int echo_index = 0; echo_index < (unsigned int)par.echoes;
979         echo_index++ )
980     {
981     echoTimes->SetElement(echo_index, (double)par.echo_times[echo_index]);
982     }
983 
984   EncapsulateMetaData< EchoTimesContainerType::Pointer >(thisDic, PAR_EchoTimes,
985                                                          echoTimes);
986   EncapsulateMetaData< int >(thisDic, PAR_MaxNumberOfDynamics, par.dyn);
987   EncapsulateMetaData< int >(thisDic, PAR_MaxNumberOfMixes, par.mixes);
988   EncapsulateMetaData< std::string >( thisDic, PAR_PatientPosition,
989                                       std::string(par.patient_position) );
990   EncapsulateMetaData< std::string >( thisDic, PAR_PreparationDirection,
991                                       std::string(par.prep_direction) );
992   EncapsulateMetaData< std::string >( thisDic, PAR_Technique,
993                                       std::string(par.technique) );
994   EncapsulateMetaData< std::string >( thisDic, PAR_ScanMode,
995                                       std::string(par.scan_mode) );
996   EncapsulateMetaData< int >(thisDic, PAR_NumberOfAverages, par.num_averages);
997   EncapsulateMetaData< ScanResolutionType >( thisDic, PAR_ScanResolution,
998                                              ScanResolutionType(par.scan_resolution) );
999   RepetitionTimesContainerType::Pointer repTimes =
1000     RepetitionTimesContainerType::New();
1001   repTimes->resize(par.mixes); // This has only been verified using a
1002                                // Look-Locker sequence and may not be valid.
1003 
1004   for ( unsigned int rep_index = 0; rep_index < (unsigned int)par.mixes; rep_index++ )
1005     {
1006     repTimes->SetElement(rep_index, (double)par.repetition_time[rep_index]);
1007     }
1008 
1009   EncapsulateMetaData< RepetitionTimesContainerType::Pointer >(thisDic,
1010                                                                PAR_RepetitionTimes, repTimes);
1011   EncapsulateMetaData< int >(thisDic, PAR_ScanPercentage, par.scan_percent);
1012   EncapsulateMetaData< FOVType >( thisDic, PAR_FOV, FOVType(par.fov) );
1013   EncapsulateMetaData< float >(thisDic, PAR_WaterFatShiftPixels,
1014                                par.water_fat_shift);
1015   AngulationMidSliceType tempAngulation;
1016   tempAngulation[0] = (double)par.angAP;
1017   tempAngulation[1] = (double)par.angFH;
1018   tempAngulation[2] = (double)par.angRL;
1019   EncapsulateMetaData< AngulationMidSliceType >(thisDic, PAR_AngulationMidSlice,
1020                                                 tempAngulation);
1021   OffCentreMidSliceType tempOffcentre;
1022   tempOffcentre[0] = (double)par.offAP;
1023   tempOffcentre[1] = (double)par.offFH;
1024   tempOffcentre[2] = (double)par.offRL;
1025   EncapsulateMetaData< OffCentreMidSliceType >(thisDic, PAR_OffCentreMidSlice,
1026                                                tempOffcentre);
1027   EncapsulateMetaData< int >(thisDic, PAR_FlowCompensation, par.flow_comp);
1028   EncapsulateMetaData< int >(thisDic, PAR_Presaturation, par.presaturation);
1029   EncapsulateMetaData< int >(thisDic, PAR_CardiacFrequency, par.cardiac_freq);
1030   EncapsulateMetaData< int >(thisDic, PAR_MinRRInterval, par.min_rr_int);
1031   EncapsulateMetaData< int >(thisDic, PAR_MaxRRInterval, par.max_rr_int);
1032   EncapsulateMetaData< PhaseEncodingVelocityType >( thisDic,
1033                                                     PAR_PhaseEncodingVelocity,
1034                                                     PhaseEncodingVelocityType(par.phase_encode_vel) );
1035   EncapsulateMetaData< int >(thisDic, PAR_MTC, par.mtc);
1036   EncapsulateMetaData< int >(thisDic, PAR_SPIR, par.spir);
1037   EncapsulateMetaData< int >(thisDic, PAR_EPIFactor, par.epi);
1038   EncapsulateMetaData< int >(thisDic, PAR_TurboFactor, par.turbo);
1039   EncapsulateMetaData< int >(thisDic, PAR_DynamicScan, par.dynamic_scan);
1040   EncapsulateMetaData< int >(thisDic, PAR_Diffusion, par.diffusion);
1041   EncapsulateMetaData< float >(thisDic, PAR_DiffusionEchoTime, par.diff_echo);
1042   EncapsulateMetaData< int >(thisDic, PAR_MaxNumberOfDiffusionValues,
1043                              par.max_num_diff_vals);
1044   EncapsulateMetaData< GradientBvalueContainerType::Pointer >(thisDic,
1045                                                               PAR_GradientBValues, diffusionBvalueVector);
1046   EncapsulateMetaData< int >(thisDic, PAR_MaxNumberOfGradientOrients,
1047                              par.max_num_grad_orient);
1048   EncapsulateMetaData< GradientDirectionContainerType::Pointer >(thisDic,
1049                                                                  PAR_GradientDirectionValues,
1050                                                                  diffusionGradientOrientationVector);
1051   EncapsulateMetaData< float >(thisDic, PAR_InversionDelay, par.inversion_delay);
1052   EncapsulateMetaData< int >(thisDic, PAR_NumberOfImageTypes, par.num_image_types);
1053   EncapsulateMetaData< ImageTypesType >( thisDic, PAR_ImageTypes,
1054                                          ImageTypesType(par.image_types) );
1055   EncapsulateMetaData< int >(thisDic, PAR_NumberOfScanningSequences,
1056                              par.num_scanning_sequences);
1057   EncapsulateMetaData< ScanningSequencesType >( thisDic, PAR_ScanningSequences,
1058                                                 ScanningSequencesType(par.scanning_sequences) );
1059   using ScanningSequenceImageTypeRescaleValuesContainerTypePtr = ScanningSequenceImageTypeRescaleValuesContainerType::Pointer;
1060   EncapsulateMetaData< ScanningSequenceImageTypeRescaleValuesContainerTypePtr >(
1061     thisDic, PAR_ScanningSequenceImageTypeRescaleValues,
1062     scanningSequenceImageTypeRescaleVector);
1063   EncapsulateMetaData< int >(thisDic, PAR_NumberOfASLLabelTypes,
1064                              par.num_label_types);
1065   EncapsulateMetaData< LabelTypesASLContainerType::Pointer >(thisDic,
1066                                                              PAR_ASLLabelTypes, labelTypesASLVector);
1067 }
1068 } // end namespace itk
1069