CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
code/src/image/oInterfileIO.cc
Go to the documentation of this file.
1 
8 #include "oInterfileIO.hh"
9 #include <iomanip>
10 
11 #ifdef _WIN32
12 // Avoid compilation errors due to mix up between std::min()/max() and
13 // min max macros
14 #undef min
15 #undef max
16 #endif
17 
18 int User_Endianness = -1;
19 // =====================================================================
20 // ---------------------------------------------------------------------
21 // ---------------------------------------------------------------------
22 // =====================================================================
23 /*
24  \fn IntfKeyGetValueFromFile
25  \param a_pathToHeader : path to the interfile header
26  \param a_key : the key to recover
27  \param T* ap_return : template array in which the data will be recovered
28  \param int a_nbElts : number of elements to recover
29  \param int a_mandatoryFlag : flag indicating if the data to recover if mandatory (true) or optionnal (false)
30  \brief Look for "a_nbElts" elts in the "a_pathToHeader" interfile header matching the "a_keyword" key
31  passed as parameter and return the corresponding value(s) in the "ap_return" templated array.
32  \details If more than one elements are to be recovered, the function first check that
33  the key has a correct Interfile key layout (brackets and commas : {,,})
34  Depending on the mandatoryFlag, the function will return an error (flag > 0)
35  or a warning (flag = 0) if the key is not found
36  \return 0 if success, and positive value otherwise (1 if error, 2 if key not found).
37 */
38 template<class T>
39 int IntfKeyGetValueFromFile(const string& a_pathToHeader,
40  const string& a_key,
41  T* ap_return,
42  int a_nbElts,
43  int a_mandatoryFlag)
44 {
45  ifstream input_file(a_pathToHeader.c_str(), ios::in);
46  string line;
47 
48  // Check file
49  if (input_file)
50  {
51  while(!input_file.eof())
52  {
53  getline(input_file, line);
54 
55  if(line.empty())
56  continue;
57 
58  Intf_key Key;
59 
60  // Process the Key at this line
61  if (IntfRecoverKey(&Key, line) )
62  {
63  Cerr("***** IntfKeyGetValueFromFile()-> Error : couldn't correctly read interfile key '"<< line << "' !" << endl);
64  return 1;
65  }
66  //if (line.find(a_keyword) != string::npos)
67  if(IntfCheckKeyMatch(Key, a_key))
68  {
69 
70  if(a_nbElts == 1) // 1 elt is required, just return the result
71  {
72  if (ConvertFromString(Key.kvalue, &ap_return[0]))
73  {
74  Cerr("***** IntfKeyGetValueFromFile()-> Exception when trying to read tag '" << a_key << "' in file '" << a_pathToHeader << "'." << endl);
75  return 1;
76  }
77 
78  return 0;
79  }
80  else // Several elements required
81  {
82  // First check we have an array
83  if (!IntfKeyIsArray(Key))
84  {
85  Cerr("***** IntfKeyGetValueFromFile() -> " << a_nbElts << " requested for interfile key " << a_key << " , but this key is not an array !" << endl);
86  return 1;
87  }
88  else
89  {
90  // Check the number of elements in the key.
91  if(IntfKeyGetArrayNbElts(Key) != a_nbElts)
92  {
93  Cerr("***** IntfKeyGetValueFromFile() -> Nb of elements to recover (=" << a_nbElts << ") does not correspond to the number of elements found in the key '"
94  << a_key << "' (" << IntfKeyGetArrayNbElts(Key) << ") !" << endl);
95  return 1;
96  }
97 
98  // Read array key
99  if (IntfKeyGetArrayElts(Key, ap_return) )
100  {
101  Cerr("***** IntfKeyGetValueFromFile() -> " << a_nbElts << " requested for interfile key " << a_key << " , but this key is not an array !" << endl);
102  return 1;
103  }
104 
105  return 0;
106  }
107  }
108  }
109  }
110 
111  // Tag not found, throw an error message if the tag is mandatory
112  if (a_mandatoryFlag > 0)
113  {
114  Cerr("***** IntfKeyGetValueFromFile()-> Error when reading Interfile '" << a_pathToHeader << "'. Key '" << a_key << "' was not found." << endl);
116  }
117  else
118  {
120  }
121 
122  }
123  else
124  {
125  Cerr("***** IntfKeyGetValueFromFile()-> Couldn't find or read data-file '"<< a_pathToHeader << "' !" << endl);
126  return 1;
127  }
128 }
129 
130 // Templated functions definitions
131 template int IntfKeyGetValueFromFile<string>(const string& a_pathToHeader, const string& a_key, string* ap_return, int a_nbElts, int a_mandatoryFlag);
132 template int IntfKeyGetValueFromFile<int>(const string& a_pathToHeader, const string& a_key, int* ap_return, int a_nbElts, int a_mandatoryFlag);
133 template int IntfKeyGetValueFromFile<int64_t>(const string& a_pathToHeader, const string& a_key, int64_t* ap_return, int a_nbElts, int a_mandatoryFlag);
134 template int IntfKeyGetValueFromFile<float>(const string& a_pathToHeader, const string& a_key, float* ap_return, int a_nbElts, int a_mandatoryFlag);
135 template int IntfKeyGetValueFromFile<double>(const string& a_pathToHeader, const string& a_key, double* ap_return, int a_nbElts, int a_mandatoryFlag);
136 template int IntfKeyGetValueFromFile<long double>(const string& a_pathToHeader, const string& a_key, long double* ap_return, int a_nbElts, int a_mandatoryFlag);
137 template int IntfKeyGetValueFromFile<uint8_t>(const string& a_pathToHeader, const string& a_key, uint8_t* ap_return, int a_nbElts, int a_mandatoryFlag);
138 template int IntfKeyGetValueFromFile<uint16_t>(const string& a_pathToHeader, const string& a_key, uint16_t* ap_return, int a_nbElts, int a_mandatoryFlag);
139 template int IntfKeyGetValueFromFile<uint32_t>(const string& a_pathToHeader, const string& a_key, uint32_t* ap_return, int a_nbElts, int a_mandatoryFlag);
140 template int IntfKeyGetValueFromFile<bool>(const string& a_pathToHeader, const string& a_key, bool* ap_return, int a_nbElts, int a_mandatoryFlag);
141 
142 
143 /*
144  \fn IntfKeyGetValueFromFile(const string& a_pathToHeader, const string& a_key, T* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
145  \param a_pathToHeader : path to the interfile header
146  \param a_key : the key to recover
147  \param T* ap_return : template array in which the data will be recovered
148  \param int a_nbElts : number of elements to recover
149  \param int a_mandatoryFlag : flag indicating if the data to recover if mandatory (true) or optionnal (false)
150  \param int a_nbOccurences : number of occurences of the field before recovering the value
151  \brief Look for "a_nbElts" elts in the "a_pathToHeader" interfile header matching the "a_keyword" key passed as parameter and return the corresponding value(s) in the "ap_return" templated array.\n
152  Parameter "a_nbOccurences" can be set to recover a specific occurrence of a recurring value
153  \details If more than one elements are to be recovered, the function first check the key has a correct Interfile kay layout (brackets and commas : {,,})\n
154  Depending on the mandatoryFlag, the function will return an error (flag > 0) or a warning (flag = 0) if the key is not found
155  \return 0 if success, and positive value otherwise (1 if error, 2 if key not found).
156 */
157 template <typename T> int IntfKeyGetRecurringValueFromFile(const string& a_pathToHeader,
158  const string& a_key,
159  T* ap_return,
160  int a_nbElts,
161  int a_mandatoryFlag,
162  uint16_t a_nbOccurrences)
163 {
164  ifstream input_file(a_pathToHeader.c_str(), ios::in);
165  string line;
166  uint16_t nb_occurences_cur =0;
167 
168  // Check file
169  if (input_file)
170  {
171  while(!input_file.eof())
172  {
173  getline(input_file, line);
174 
175  if(line.empty())
176  continue;
177 
178  Intf_key Key;
179 
180  // Process the Key at this line
181  if (IntfRecoverKey(&Key, line) )
182  {
183  Cerr("***** IntfKeyGetValueFromFile()-> Error : couldn't correctly read interfile key '"<< line << "' !" << endl);
184  return 1;
185  }
186 
187 
188 
189  //if (line.find(a_keyword) != string::npos)
190  if(IntfCheckKeyMatch(Key, a_key))
191  {
192  // Check if we reached the correct number of occurence of the key
193  // Skip otherwise
194  if(nb_occurences_cur < a_nbOccurrences)
195  {
196  nb_occurences_cur++;
197  continue;
198  }
199 
200  if(a_nbElts == 1) // 1 elt is required, just return the result
201  {
202  if (ConvertFromString(Key.kvalue, &ap_return[0]))
203  {
204  Cerr("***** IntfKeyGetValueFromFile()-> Exception when trying to read tag '" << a_key << "' in file '" << a_pathToHeader << "'." << endl);
205  return 1;
206  }
207 
208  return 0;
209  }
210  else // Several elements required
211  {
212  // First check we have an array
213  if (!IntfKeyIsArray(Key))
214  {
215  Cerr("***** IntfKeyGetValueFromFile() -> " << a_nbElts << " requested for interfile key " << a_key << " , but this key is not an array !" << endl);
216  return 1;
217  }
218  else
219  {
220  // Check the number of elements in the key.
221  if(IntfKeyGetArrayNbElts(Key) != a_nbElts)
222  {
223  Cerr("***** IntfKeyGetValueFromFile() -> Nb of elements to recover (=" << a_nbElts << ") does not correspond to the number of elements found in the key '"
224  << a_key << "' (" << IntfKeyGetArrayNbElts(Key) << ") !" << endl);
225  return 1;
226  }
227 
228  // Read array key
229  if (IntfKeyGetArrayElts(Key, ap_return) )
230  {
231  Cerr("***** IntfKeyGetValueFromFile() -> " << a_nbElts << " requested for interfile key " << a_key << " , but this key is not an array !" << endl);
232  return 1;
233  }
234 
235  return 0;
236  }
237  }
238  }
239  }
240 
241  // Tag not found, throw an error message if the tag is mandatory
242  if (a_mandatoryFlag > 0)
243  {
244  Cerr("***** IntfKeyGetValueFromFile()-> Error when reading Interfile '" << a_pathToHeader << "'. Key '" << a_key << "' was not found." << endl);
246  }
247  else
248  {
250  }
251 
252  }
253  else
254  {
255  Cerr("***** IntfKeyGetValueFromFile()-> Couldn't find or read data-file '"<< a_pathToHeader << "' !" << endl);
256  return 1;
257  }
258 }
259 
260 // Templated functions definitions
261 template int IntfKeyGetRecurringValueFromFile<string>(const string& a_pathToHeader, const string& a_key, string* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
262 template int IntfKeyGetRecurringValueFromFile<int>(const string& a_pathToHeader, const string& a_key, int* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
263 template int IntfKeyGetRecurringValueFromFile<int64_t>(const string& a_pathToHeader, const string& a_key, int64_t* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
264 template int IntfKeyGetRecurringValueFromFile<float>(const string& a_pathToHeader, const string& a_key, float* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
265 template int IntfKeyGetRecurringValueFromFile<double>(const string& a_pathToHeader, const string& a_key, double* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
266 template int IntfKeyGetRecurringValueFromFile<long double>(const string& a_pathToHeader, const string& a_key, long double* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
267 template int IntfKeyGetRecurringValueFromFile<uint8_t>(const string& a_pathToHeader, const string& a_key, uint8_t* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
268 template int IntfKeyGetRecurringValueFromFile<uint16_t>(const string& a_pathToHeader, const string& a_key, uint16_t* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
269 template int IntfKeyGetRecurringValueFromFile<uint32_t>(const string& a_pathToHeader, const string& a_key, uint32_t* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
270 template int IntfKeyGetRecurringValueFromFile<bool>(const string& a_pathToHeader, const string& a_key, bool* ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences);
271 
272 
273 
274 
275 // =====================================================================
276 // ---------------------------------------------------------------------
277 // ---------------------------------------------------------------------
278 // =====================================================================
279 
280 int IntfReadProjectionImage( const string& a_pathToHeaderFile,
281  FLTNB* ap_ImgMatrix,
282  Intf_fields* ap_IF,
283  int vb,
284  bool a_lerpFlag)
285 {
286  if(vb >= 3) Cout("IntfReadProjectionImage()-> Read Interfile header : "<< a_pathToHeaderFile << endl);
287 
288  // Init Interfile Key structure
289  IntfKeyInitFields(ap_IF);
290 
291  // Recover image infos from the header file
292  if(IntfReadHeader(a_pathToHeaderFile, ap_IF, vb) )
293  {
294  Cerr("***** IntfReadProjectionImage()-> Error : while trying to read the interfile header '"<< a_pathToHeaderFile << "' !" << endl);
295  return 1;
296  }
297 
298  // Specific changes for projection data. //TODO : deal with that in IntfReadHeader, specification regarding the nature of the data
299  ap_IF->mtx_size[2] = ap_IF->nb_total_imgs;
300 
301  int nb_tot_pixels = ap_IF->mtx_size[0]
302  *ap_IF->mtx_size[1]
303  *ap_IF->mtx_size[2];
304 
305  // Read image data
306  ifstream img_file(ap_IF->path_to_image.c_str(), ios::binary | ios::in);
307  if(img_file)
308  {
309  // Get the position of the beginning of the image data
310  uint32_t offset = ap_IF->data_offset;
311 
312  // Call the right IntfReadData() function according to the pixel type, and read data
313  IntfGetPixelTypeAndReadData(*ap_IF, &img_file, ap_ImgMatrix, NULL, &offset, nb_tot_pixels, vb);
314  }
315  else
316  {
317  Cerr("***** IntfReadProjectionImage()-> Error occurred while trying to read the image file at the path: '"<< ap_IF->path_to_image << "' !" << endl);
318  return 1;
319  }
320 
321  return 0;
322 }
323 
324 // =====================================================================
325 // ---------------------------------------------------------------------
326 // ---------------------------------------------------------------------
327 // =====================================================================
328 
330 {
331 /*
332  // Check that the originating systems are the same
333  if (ImgFields1.originating_system != ImgFields2.originating_system)
334  {
335  Cerr("***** IntfCheckDimensionsConsistency() -> Originating systems are not the same !" << endl);
336  return 1;
337  }
338 */
339  // Check that the numbers of dimensions are the same
340  if (ImgFields1.nb_dims != ImgFields2.nb_dims)
341  {
342  Cerr("***** IntfCheckDimensionsConsistency() -> Numbers of dimensions are not the same !" << endl);
343  return 1;
344  }
345  // Loop over all dimensions
346  for (int dim=0; dim<((int)ImgFields1.nb_dims); dim++)
347  {
348  // Check the size of this dimension
349  if (ImgFields1.mtx_size[dim] != ImgFields2.mtx_size[dim])
350  {
351  Cerr("***** IntfCheckDimensionsConsistency() -> The sizes of the dimension " << dim+1 << " are not the same !" << endl);
352  return 1;
353  }
354  }
355  // For the first 3 dimensions, check the voxel size
356  for (int dim=0; dim<std::min(3,((int)ImgFields1.nb_dims)); dim++)
357  {
358  // Check voxel sizes
359  if (ImgFields1.vox_size[dim] != ImgFields2.vox_size[dim])
360  {
361  Cerr("***** IntfCheckDimensionsConsistency() -> Voxel sizes of dimension " << dim+1 << " are not the same !" << endl);
362  return 1;
363  }
364  }
365  // Check the slice thickness
366  if (ImgFields1.slice_thickness_mm != ImgFields2.slice_thickness_mm)
367  {
368  Cerr("***** IntfCheckDimensionsConsistency() -> Slice thicknesses are not the same !" << endl);
369  return 1;
370  }
371  // Normal end
372  return 0;
373 }
374 
375 // =====================================================================
376 // ---------------------------------------------------------------------
377 // ---------------------------------------------------------------------
378 // =====================================================================
379 
380 FLTNB* IntfLoadImageFromScratch( const string& a_pathToHeaderFile,
381  Intf_fields* ap_ImgFields,
382  int vb )
383 {
384  if (vb>=2) Cout("IntfLoadImageFromScratch() -> Read Interfile image '" << a_pathToHeaderFile << "'" << endl);
385 
386  // Init Interfile Key structure
387  IntfKeyInitFields(ap_ImgFields);
388 
389  // Recover image infos from the header file
390  if (IntfReadHeader(a_pathToHeaderFile, ap_ImgFields, vb))
391  {
392  Cerr("***** IntfLoadImageFromScratch() -> A problem occurred while trying to read the interfile header '" << a_pathToHeaderFile << "' !" << endl);
393  return NULL;
394  }
395 
396  // Error if number of dimensions is more than 3
397  if (ap_ImgFields->nb_dims>3)
398  {
399  Cerr("***** IntfLoadImageFromScratch() -> Cannot handle a number of dimensions higher than 3 !" << endl);
400  return NULL;
401  }
402 
403  // Check that the numbers of voxels have been read successfully (they are initialized to 0 by the IntfKeyInitFields functions
404  for (int d=0; d<ap_ImgFields->nb_dims; d++) if (ap_ImgFields->mtx_size[d]==0)
405  {
406  Cerr("***** IntfLoadImageFromScratch() -> Number of voxels for dimension #" << d << " is 0, so has not been read correctly in header file '" << a_pathToHeaderFile << "' !" << endl);
407  return NULL;
408  }
409  // Same for voxel sizes, initialized to -1 by default
410  for (int d=0; d<ap_ImgFields->nb_dims; d++) if (ap_ImgFields->vox_size[d]<=0.)
411  {
412  Cerr("***** IntfLoadImageFromScratch() -> Voxel size for dimension #" << d << " is negative, so has not been read correctly in header file '" << a_pathToHeaderFile << "' !" << endl);
413  return NULL;
414  }
415 
416  // Compute total number of voxels
417  INTNB dim_tot = ap_ImgFields->mtx_size[0] * ap_ImgFields->mtx_size[1] * ap_ImgFields->mtx_size[2];
418 
419  // Allocate image
420  FLTNB* p_image = (FLTNB*)malloc(dim_tot*sizeof(FLTNB));
421 
422  // Open image data file
423  ifstream img_file(ap_ImgFields->path_to_image.c_str(), ios::binary | ios::in);
424  if (!img_file)
425  {
426  Cerr("***** IntfLoadImageFromScratch() -> Input image file '" << ap_ImgFields->path_to_image << "' is missing or corrupted !" << endl);
427  return NULL;
428  }
429 
430  // Get the position of the beginning of the image data
431  uint32_t offset = ap_ImgFields->data_offset;
432 
433  // Call the right IntfReadData() function according to the pixel type, and read data
434  IntfGetPixelTypeAndReadData(*ap_ImgFields, &img_file, p_image, NULL, &offset, dim_tot, vb);
435 
436  // Return the pointer to the image in memory
437  return p_image;
438 }
439 
440 // =====================================================================
441 // ---------------------------------------------------------------------
442 // ---------------------------------------------------------------------
443 // =====================================================================
444 
445 int IntfWriteImageFromIntfFields(const string& a_pathToImg, FLTNB* ap_ImgMatrix, Intf_fields Img_fields, int vb)
446 {
447  if (vb>=2) Cout("IntfWriteImageFromIntfFields() -> Write 3D image with output base name '" << a_pathToImg << "'" << endl);
448 
449  // Write Interfile header
450  if (IntfWriteHeaderMainData(a_pathToImg, Img_fields, vb) )
451  {
452  Cerr("***** IntfWriteImageFromIntfFields() -> Error : while trying to write the interfile header '"<< a_pathToImg << "' !" << endl);
453  return 1;
454  }
455 
456  // Binary data file
457  string path_to_image = a_pathToImg;
458  path_to_image.append(".img");
459 
460  // Total number of voxels
461  uint32_t dim_tot = Img_fields.mtx_size[0] * Img_fields.mtx_size[1] * Img_fields.mtx_size[2];
462 
463  // Read Interfile image
464  if (IntfWriteImage(path_to_image, ap_ImgMatrix, dim_tot, vb) )
465  {
466  Cerr("***** IntfWriteImageFromIntfFields() -> Error : while trying to write the interfile image '"<< path_to_image << "' !" << endl);
467  return 1;
468  }
469 
470  return 0;
471 }
472 
473 
474 // =====================================================================
475 // ---------------------------------------------------------------------
476 // ---------------------------------------------------------------------
477 // =====================================================================
478 
479 int IntfReadImage( const string& a_pathToHeaderFile,
480  FLTNB* ap_ImgMatrix,
482  int a_verbose,
483  bool a_lerpFlag)
484 {
485  // Verbose
486  if (a_verbose>=VERBOSE_DETAIL) Cout("oInterfileIO::IntfReadImage() -> Read image from interfile header '" << a_pathToHeaderFile << "'" << endl);
487 
488  // Init Interfile Key structure
489  Intf_fields Img_fields;
490  IntfKeyInitFields(&Img_fields);
491 
492  // Recover image infos from the header file
493  if (IntfReadHeader(a_pathToHeaderFile, &Img_fields, a_verbose) )
494  {
495  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to read the interfile header '" << a_pathToHeaderFile << "' !" << endl);
496  return 1;
497  }
498 
499  // Check consistency between image interfile data and the algorithm data
500  if (IntfCheckConsistency(&Img_fields, ap_ID, a_verbose, a_lerpFlag) )
501  {
502  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while checking consistencies between reconstruction parameters and interfile keys from header '"
503  << a_pathToHeaderFile << "' !" << endl);
504  return 1;
505  }
506 
507  // If interpolation required, allocate matrix with original image dimensions.
508  FLTNB* pimg_erp = NULL;
509  IntfAllocInterpImg(&pimg_erp, Img_fields);
510  // TODO SS: if the input image is wider than the reconstructed image, then we will remove some content, so we should print a warning here
511 
512  // Read image data
513  ifstream img_file(Img_fields.path_to_image.c_str(), ios::binary | ios::in);
514  if (img_file)
515  {
516  // Get the position of the beginning of the image data
517  uint32_t offset = Img_fields.data_offset;
518  // Call the right IntfReadData() function according to the pixel type, and read data
519  if (IntfGetPixelTypeAndReadData(Img_fields, &img_file, ap_ImgMatrix, pimg_erp, &offset, ap_ID->GetNbVoxXYZ(), a_verbose))
520  {
521  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while reading data and eventually interpolating from the IntfGetPixelTypeAndReadData() function !" << endl);
522  return 1;
523  }
524  }
525  else
526  {
527  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to open the image file '" << Img_fields.path_to_image << "' !" << endl);
528  // If interpolation was required, deallocate image matrix
529  if (pimg_erp) delete[] pimg_erp;
530  return 1;
531  }
532 
533  // If interpolation was required, deallocate image matrix
534  if (pimg_erp) delete[] pimg_erp;
535 
536  return 0;
537 }
538 
539 // =====================================================================
540 // ---------------------------------------------------------------------
541 // ---------------------------------------------------------------------
542 // =====================================================================
543 
544 int IntfReadImage( const string& a_pathToHeaderFile,
545  FLTNB**** a4p_ImgMatrix,
547  int a_verbose,
548  bool a_lerpFlag)
549 {
550  if (a_verbose >= 5) Cout("oInterfileIO::IntfReadImage() -> Read image from interfile header '" << a_pathToHeaderFile << "'" << endl);
551 
552  // Init Interfile Key structure
553  Intf_fields Img_fields;
554  IntfKeyInitFields(&Img_fields);
555 
556  // List containing single/multiple interfile headers of the images to read
557  vector<string> lpath_to_headers;
558 
559  // First check if the provided file is a metaheader with multiple files
560  // and recover the list of header file
561  if (IntfIsMHD(a_pathToHeaderFile, lpath_to_headers) < 0 ) // error
562  {
563  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to read the interfile header '" << a_pathToHeaderFile << "' in IntfIsMHD() function !" << endl);
564  return 1;
565  }
566 
567  // --- Case 1: We have one single data file containing all the data --- //
568  if (lpath_to_headers.size() == 1)
569  {
570  // Recover image infos from either metaheader or single header file
571  if (IntfReadHeader(a_pathToHeaderFile, &Img_fields, a_verbose) )
572  {
573  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to read the interfile header '" << a_pathToHeaderFile << "' !" << endl);
574  return 1;
575  }
576  // Check consistency between image interfile data and the algorithm data
577  if (IntfCheckConsistency(&Img_fields, ap_ID, a_verbose, a_lerpFlag) )
578  {
579  Cerr("***** oInterfileIO::IntfReadImage() -> A error occurred while checking consistencies between reconstruction parameters and interfile keys in the header '" << a_pathToHeaderFile << "' !" << endl);
580  return 1;
581  }
582  // If interpolation required, instanciate matrix with original image dimensions.
583  FLTNB* pimg_erp = NULL;
584  IntfAllocInterpImg(&pimg_erp, Img_fields);
585  // TODO SS: if the input image is wider than the reconstructed image, then we will remove some content, so we should print a warning here
586  // Open file
587  ifstream img_file(Img_fields.path_to_image.c_str(), ios::binary | ios::in);
588  if (img_file)
589  {
590  // Get the position of the beginning of the image data
591  uint32_t offset = Img_fields.data_offset;
592  // Call the right IntfReadData() function according to the pixel type, and read data.
593  // offset is updated with the called functions within the loops
594  // TODO : RAJOUTER DES CHECKS SUR LES DIMENSIONS DYNAMIQUES DANS CHECKCONSISTENCY (gérer différences sensisitivityGenerator & reconstruction) !!!
595  for (int d1=0 ; d1<Img_fields.nb_time_frames ; d1++)
596  for (int d2=0 ; d2<Img_fields.nb_resp_gates ; d2++)
597  for (int d3=0 ; d3<Img_fields.nb_card_gates ; d3++)
598  {
599  if (IntfGetPixelTypeAndReadData(Img_fields, &img_file, a4p_ImgMatrix[d1][d2][d3], pimg_erp, &offset, ap_ID->GetNbVoxXYZ(), a_verbose))
600  {
601  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while reading data and eventually interpolating from the IntfGetPixelTypeAndReadData() function !" << endl);
602  // If interpolation was required, deallocate image matrix with original image dimensions
603  if (pimg_erp) delete[] pimg_erp;
604  return 1;
605  }
606  }
607  }
608  else
609  {
610  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to read the image file '"<< Img_fields.path_to_image << "' !" << endl);
611  // If interpolation was required, deallocate image matrix with original image dimensions
612  if (pimg_erp) delete[] pimg_erp;
613  return 1;
614  }
615  // If interpolation was required, deallocate image matrix with original image dimensions
616  if (pimg_erp) delete[] pimg_erp;
617  }
618 
619  // --- Case 2 : We have a number of data file equal to the number of image to load --- //
620  else
621  {
622  // Recover image infos from the metaheader
623  if (IntfReadHeader(a_pathToHeaderFile, &Img_fields, a_verbose) )
624  {
625  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to read the interfile metaheader '" << a_pathToHeaderFile << "' !" << endl);
626  return 1;
627  }
628  // Get dimensions from ImageDimensions object
629  int dims[3];
630  dims[0] = ap_ID->GetNbTimeFrames();
631  dims[1] = ap_ID->GetNbRespGates();
632  dims[2] = ap_ID->GetNbCardGates();
633  // Check we have a number of file corresponding to the number of images to load
634  if (lpath_to_headers.size() != (uint32_t)(dims[0]*dims[1]*dims[2]+ap_ID->GetNbFramesToSkip()))
635  {
636  Cerr("***** oInterfileIO::IntfReadImage() -> Number of interfile images (" << lpath_to_headers.size() <<
637  ") not consistent with the number of images to load (" << dims[0]*dims[1]*dims[2]<< ") !" << endl);
638  return 1;
639  }
640  // If interpolation required, instanciate matrix with original image dimensions.
641  FLTNB* pimg_erp = NULL;
642  IntfAllocInterpImg(&pimg_erp, Img_fields);
643  // TODO SS: if the input image is wider than the reconstructed image, then we will remove some content, so we should print a warning here
644  // Loop on dynamic image files
645  for(int d1=(ap_ID->GetNbFramesToSkip()) ; d1<(ap_ID->GetNbFramesToSkip()+dims[0]) ; d1++)
646  for(int d2=0 ; d2<dims[1] ; d2++)
647  for(int d3=0 ; d3<dims[2] ; d3++)
648  {
649  int idx_img = d1*dims[1]*dims[2] + d2*dims[2] + d3;
650  // Recover image infos from the specific interfile header
651  // Todo : Check if image 3D dimensions are different from an image to another ?
652  // (very unlikely, but it would cause segfault if interpolation is enabled)
653  if (IntfReadHeader(lpath_to_headers[idx_img], &Img_fields, a_verbose) )
654  {
655  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to read the interfile header '" << lpath_to_headers[idx_img] << "' !" << endl);
656  return 1;
657  }
658  // Check consistency between image interfile data and the algorithm data
659  if (IntfCheckConsistency(&Img_fields, ap_ID, a_verbose, a_lerpFlag) )
660  {
661  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while checking consistencies between reconstruction parameters and interfile keys in the header '"
662  << lpath_to_headers[idx_img] << "' !" << endl);
663  return 1;
664  }
665  // Open file
666  ifstream img_file(Img_fields.path_to_image.c_str(), ios::binary | ios::in);
667  if (img_file)
668  {
669  // Get the position of the beginning of the image data (assuming that offset is the same for each file (if any) )
670  uint32_t offset = Img_fields.data_offset;
671  // Call the right IntfReadData() function according to the pixel type, and read data (offset not updated)
672  if (IntfGetPixelTypeAndReadData(Img_fields, &img_file, a4p_ImgMatrix[d1-(ap_ID->GetNbFramesToSkip())][d2][d3], pimg_erp, &offset, ap_ID->GetNbVoxXYZ(), a_verbose))
673  {
674  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while reading data and eventually interpolating from the IntfGetPixelTypeAndReadData() function !" << endl);
675  // If interpolation was required, deallocate image matrix with original image dimensions
676  if (pimg_erp) delete[] pimg_erp;
677  return 1;
678  }
679  }
680  else
681  {
682  Cerr("***** oInterfileIO::IntfReadImage() -> An error occurred while trying to read the image file '"<< Img_fields.path_to_image << "' !" << endl);
683  // If interpolation was required, deallocate image matrix with original image dimensions
684  if (pimg_erp) delete[] pimg_erp;
685  return 1;
686  }
687  }
688  // If interpolation was required, deallocate image matrix with original image dimensions
689  if (pimg_erp) delete[] pimg_erp;
690  }
691 
692  /* If gating is enabled with separate 3D gated images, the image_duration key may be in each header
693  The following check will be broken in this case.
694  // Check some dynamic recovered infos
695  if(Img_fields.nb_time_frames > 1 &&
696  Img_fields.nb_time_frames != Img_fields.image_duration.size())
697  {
698  Cerr("***** IntfReadImage()-> Error : the number of provided image duration: '"<< Img_fields.image_duration.size()
699  << "' does not match the number of frames '"<< Img_fields.nb_time_frames <<"' !" << endl);
700  return 1;
701  }
702  */
703  return 0;
704 }
705 
706 // =====================================================================
707 // ---------------------------------------------------------------------
708 // ---------------------------------------------------------------------
709 // =====================================================================
710 
711 int IntfReadImgDynCoeffFile(const string& a_pathToHeaderFile,
712  FLTNB** a2p_ImgMatrix,
714  int a_nbFbasis,
715  int a_verbose,
716  bool a_lerpFlag)
717 {
718  if (a_verbose >= 5) Cout("IntfReadImgDynCoeffFile" << endl);
719 
720  // Init Interfile Key structure
721  Intf_fields Img_fields;
722  IntfKeyInitFields(&Img_fields);
723 
724  // List containing single/multiple interfile headers of the images to read
725  vector<string> lpath_to_headers;
726 
727  // First check if the provided file is a metaheader with multiple files
728  // and recover the list of image header file
729  if(IntfIsMHD(a_pathToHeaderFile, lpath_to_headers) <0) // error
730  {
731  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> an error occurred while trying to read the interfile header '" << a_pathToHeaderFile << "' !" << endl);
732  return 1;
733  }
734 
735  // --- Case 1: We have one single data file containing all the data --- //
736  if(lpath_to_headers.size() == 1)
737  {
738  // Recover image infos from either metaheader or single header file
739  if (IntfReadHeader(a_pathToHeaderFile, &Img_fields, a_verbose) )
740  {
741  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while trying to read the interfile header '" << a_pathToHeaderFile << "' !" << endl);
742  return 1;
743  }
744  // Check consistency between image interfile data and the algorithm data
745  if (IntfCheckConsistency(&Img_fields, ap_ID, a_verbose, a_lerpFlag) )
746  {
747  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while checking consistencies between reconstruction parameters and interfile keys in the header '" << a_pathToHeaderFile << "' !" << endl);
748  return 1;
749  }
750  // If interpolation required, instanciate matrix with original image dimensions.
751  FLTNB* pimg_erp = NULL;
752  IntfAllocInterpImg(&pimg_erp, Img_fields);
753  // Open file
754  ifstream img_file(Img_fields.path_to_image.c_str(), ios::binary | ios::in);
755  if (img_file)
756  {
757  // Get the position of the beginning of the image data
758  uint32_t offset = Img_fields.data_offset;
759  // Call the right IntfReadData() function according to the pixel type, and read data
760  // offset is updated with the called functions within the loops
761  for (int bf=0 ; bf<a_nbFbasis ; bf++)
762  {
763  if (IntfGetPixelTypeAndReadData(Img_fields, &img_file, a2p_ImgMatrix[bf], pimg_erp, &offset, ap_ID->GetNbVoxXYZ(), a_verbose))
764  {
765  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while reading from file stream and eventually interpolating !" << endl);
766  // If interpolation was required, deallocate image matrix with original image dimensions
767  if (pimg_erp) delete[] pimg_erp;
768  return 1;
769  }
770  }
771  }
772  else
773  {
774  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while trying to read the image file '" << Img_fields.path_to_image << "' !" << endl);
775  // If interpolation was required, deallocate image matrix with original image dimensions
776  if (pimg_erp) delete[] pimg_erp;
777  return 1;
778  }
779  // If interpolation was required, deallocate image matrix with original image dimensions
780  if (pimg_erp) delete[] pimg_erp;
781  }
782 
783  // --- Case 2: We have a number of data file equal to the number of image to load --- //
784  else
785  {
786  // Recover image infos from the metaheader
787  if (IntfReadHeader(a_pathToHeaderFile, &Img_fields, a_verbose) )
788  {
789  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while trying to read the interfile metaheader '" << a_pathToHeaderFile << "' !" << endl);
790  return 1;
791  }
792  // Check we have a number of file corresponding to the number of images to load
793  if (lpath_to_headers.size() != (uint32_t)a_nbFbasis)
794  {
795  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> Number of interfile images (" << lpath_to_headers.size() <<
796  ") not consistent with the number of parametric images (" << a_nbFbasis<< ") !" << endl);
797  return 1;
798  }
799  // If interpolation required, instanciate matrix with original image dimensions.
800  FLTNB* pimg_erp = NULL;
801  IntfAllocInterpImg(&pimg_erp, Img_fields);
802  // Loop
803  for (int bf=0 ; bf<a_nbFbasis ; bf++)
804  {
805  // Recover image infos from the specific interfile header
806  if (IntfReadHeader(lpath_to_headers[bf], &Img_fields, a_verbose) )
807  {
808  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> an error occurred while trying to read interfile header '" << a_pathToHeaderFile << "' !" << endl);
809  // If interpolation was required, deallocate image matrix with original image dimensions
810  if (pimg_erp) delete[] pimg_erp;
811  return 1;
812  }
813  // Check consistency between image interfile data and the algorithm data
814  if (IntfCheckConsistency(&Img_fields, ap_ID, a_verbose, a_lerpFlag) )
815  {
816  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while checking consistencies between reconstruction parameters and interfile keys in the header '"<< a_pathToHeaderFile << "' !" << endl);
817  // If interpolation was required, deallocate image matrix with original image dimensions
818  if (pimg_erp) delete[] pimg_erp;
819  return 1;
820  }
821  // Open file stream
822  ifstream img_file(Img_fields.path_to_image.c_str(), ios::binary | ios::in);
823  if (img_file)
824  {
825  // Get the position of the beginning of the image data (assuming that offset is the same for each file (if any) )
826  uint32_t offset = Img_fields.data_offset;
827  // Call the right IntfReadData() function according to the pixel type, and read data (offset not updated)
828  if (IntfGetPixelTypeAndReadData(Img_fields, &img_file, a2p_ImgMatrix[bf], pimg_erp, &offset, ap_ID->GetNbVoxXYZ(), a_verbose))
829  {
830  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while reading image stream and eventually interpolating !" << endl);
831  // If interpolation was required, deallocate image matrix with original image dimensions
832  if (pimg_erp) delete[] pimg_erp;
833  return 1;
834  }
835  }
836  else
837  {
838  Cerr("***** oInterfileIO::IntfReadImgDynCoeffFile() -> An error occurred while trying to read the image file '" << Img_fields.path_to_image << "' !" << endl);
839  // If interpolation was required, deallocate image matrix with original image dimensions
840  if (pimg_erp) delete[] pimg_erp;
841  return 1;
842  }
843  }
844  // If interpolation was required, deallocate image matrix with original image dimensions
845  if (pimg_erp) delete[] pimg_erp;
846  }
847 
848  return 0;
849 }
850 
851 
852 
853 
854 // =====================================================================
855 // ---------------------------------------------------------------------
856 // ---------------------------------------------------------------------
857 // =====================================================================
858 /*
859  \fn IntfAllocInterpImg
860  \param a2p_img : Pointer to 1 dimensional image matrix to recover the image to interpolate
861  \param ap_IF : Structure containing the interfile fields read in a interfile header
862  \brief Allocate memory for an image matrix to recover an image to interpolate
863 */
864 void IntfAllocInterpImg(FLTNB **a2p_img, Intf_fields a_IF)
865 {
866  if(a_IF.is_mtx_size_different == true)
867  {
868  uint32_t nb_vox = a_IF.mtx_size[0] *
869  a_IF.mtx_size[1] *
870  a_IF.mtx_size[2];
871 
872  // Allocate image and
873  *a2p_img = new FLTNB[ nb_vox ];
874  ::memset(*a2p_img, 0, sizeof(FLTNB) * nb_vox);
875  }
876 }
877 
878 
879 
880 
881 // =====================================================================
882 // ---------------------------------------------------------------------
883 // ---------------------------------------------------------------------
884 // =====================================================================
885 /*
886  \fn IntfCheckConsistency
887  \param ap_IF : Structure containing the interfile fields read in a interfile header
888  \param ap_ID : Provide the Image dimensions object containing reconstruction dimensions
889  \param vb : Verbosity level
890  \param a_lerpFlag : if true, enable linear interpolation of the image if img dimensions differ from the reconstruction dimensions
891  \brief Check if the mandatory fields have been initialize in the ap_IF structure, and check consistencies with the reconstruction parameters
892  \details This function also checks if the matrix size of the original image is different to the reconstruction matrix size.
893  In this case a boolean is set up to enable interpolation during image reading
894  \todo Add check for all mandatory parameters, and temporal image dimensions
895  \todo Float comparison ?
896  \return 0 if success, positive value otherwise.
897 */
898 int IntfCheckConsistency(Intf_fields* ap_IF, oImageDimensionsAndQuantification* ap_ID, int vb, int a_lerpFlag)
899 {
900  if(vb >= 5) Cout("IntfCheckConsistency()" << endl);
901 
902  // Check if main keys have been initialized
903  if(ap_IF->path_to_image.size()<1 ||
904  ap_IF->nb_format == "" ||
905  ap_IF->mtx_size[0]==0 || ap_IF->mtx_size[1]==0 || ap_IF->mtx_size[2]==0 )
906  {
907  Cerr("***** IntfCheckConsistency()-> Error : some mandatory keys not initialized. Cannot read the interfile image !" << endl);
908  // Print the related key
909  if(ap_IF->path_to_image.size()<1)
910  Cerr(" Error when trying to read path to image data" << endl);
911  if(ap_IF->nb_format == "")
912  Cerr(" Error when trying to read data voxel type " << endl);
913  if(ap_IF->mtx_size[0]==0 || ap_IF->mtx_size[1]==0 || ap_IF->mtx_size[2]==0 )
914  Cerr(" Error when trying to read matrix size (image dimensions) : x= " << ap_IF->mtx_size[0] << ", y= " << ap_IF->mtx_size[1] << ", z= " << ap_IF->mtx_size[2]<< endl);
915  return 1;
916  }
917 
918  // If voxel size not found, throw warning
919  if( ap_IF->vox_size[0]<0 || ap_IF->vox_size[1]<0 || ap_IF->vox_size[2]<0)
920  {
921  if(vb == 5)
922  Cerr("***** IntfCheckConsistency()-> WARNING : No information found about voxel size ('scaling factor (mm/pixel)' tags.). Missing voxel sizes will be set to 1mm !" << endl);
923 
924  if(ap_IF->vox_size[0]<0) ap_IF->vox_size[0] = 1.;
925  if(ap_IF->vox_size[1]<0) ap_IF->vox_size[1] = 1.;
926  if(ap_IF->vox_size[2]<0) ap_IF->vox_size[2] = 1.;
927  if(vb == 5)
928  Cerr(" Voxel sizes : x= " << ap_IF->vox_size[0] << ", y= " << ap_IF->vox_size[1] << ", z= " << ap_IF->vox_size[2]<< endl);
929  }
930 
931 
932  // If original dimensions don't match reconstructions dimensions/voxel sizes,
933  // recover this data in Intf_fields object (if a_lerpFlag==true) or throw error (if a_lerpFlag==false)
934  if( ((INTNB)(ap_IF->mtx_size[0])) != ap_ID->GetNbVoxX() ||
935  ((INTNB)(ap_IF->mtx_size[1])) != ap_ID->GetNbVoxY() ||
936  ((INTNB)(ap_IF->mtx_size[2])) != ap_ID->GetNbVoxZ() ||
937  !FLTNBIsEqual(ap_IF->vox_size[0], ap_ID->GetVoxSizeX(), pow(0.1, std::numeric_limits<FLTNB>::digits10-1) ) || // Check for difference ~e-05
938  !FLTNBIsEqual(ap_IF->vox_size[1], ap_ID->GetVoxSizeY(), pow(0.1, std::numeric_limits<FLTNB>::digits10-1) ) ||
939  !FLTNBIsEqual(ap_IF->vox_size[2], ap_ID->GetVoxSizeZ(), pow(0.1, std::numeric_limits<FLTNB>::digits10-1) ) )
940  {
941  if(a_lerpFlag)
942  {
943  ap_IF->cmtx_size[0] = (uint32_t)(ap_ID->GetNbVoxX());
944  ap_IF->cmtx_size[1] = (uint32_t)(ap_ID->GetNbVoxY());
945  ap_IF->cmtx_size[2] = (uint32_t)(ap_ID->GetNbVoxZ());
946  ap_IF->cvox_size[0] = ap_ID->GetVoxSizeX();
947  ap_IF->cvox_size[1] = ap_ID->GetVoxSizeY();
948  ap_IF->cvox_size[2] = ap_ID->GetVoxSizeZ();
949  ap_IF->cvox_offset[0] = ap_ID->GetOffsetX();
950  ap_IF->cvox_offset[1] = ap_ID->GetOffsetY();
951  ap_IF->cvox_offset[2] = ap_ID->GetOffsetZ();
952  ap_IF->is_mtx_size_different = true; // Set this boolean to true to indicate that an interpolation step will be required
953  }
954  else
955  {
956  Cerr("***** IntfCheckConsistency()-> Error : Image dimensions don't match reconstructions dimensions/voxel sizes" << endl);
957  Cerr(" and linear interpolation is disabled (a_lerpFlag is false) !" << endl);
958  Cerr("***** Recovered image dimensions (x;y;z): "<< ap_IF->mtx_size[0] <<" ; "<< ap_IF->mtx_size[1] << " ; " << ap_IF->mtx_size[2] << endl);
959  Cerr("***** Reconstruction dimensions (x;y;z) : "<< ap_ID->GetNbVoxX() <<" ; "<< ap_ID->GetNbVoxY() << " ; " << ap_ID->GetNbVoxZ() << endl);
960  Cerr("***** Image voxel sizes (x;y;z) : "<< ap_IF->vox_size[0] <<" ; "<< ap_IF->vox_size[1] << " ; " << ap_IF->vox_size[2] << endl);
961  Cerr("***** Reconstruction voxel sizes (x;y;z): "<< ap_ID->GetVoxSizeX() <<" ; "<< ap_ID->GetVoxSizeY() << " ; " << ap_ID->GetVoxSizeZ() << endl);
962  return 1;
963  }
964  }
965 
966  return 0;
967 }
968 
969 // =====================================================================
970 // ---------------------------------------------------------------------
971 // ---------------------------------------------------------------------
972 // =====================================================================
973 
975  ifstream* ap_iFile,
976  FLTNB* ap_outImgMatrix,
977  FLTNB* ap_inImgMatrix,
978  uint32_t* ap_offset,
979  int a_nbVox,
980  int a_verbose)
981 {
982  if (a_verbose >= 5) Cout("oInterfileIO::IntfGetPixelTypeAndReadData() " << endl);
983 
984  // To check if an error occurred in one of the called functions
985  int error = 0;
986 
987  // Check the image data voxel size according to the Interfile fields 'number_format' and 'number of bytes per pixel'
988  if (a_IF.nb_format == FLT32_str || a_IF.nb_format == FLT32_str2) // Float data
989  {
990  float flt;
991  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &flt);
992  }
993  else if (a_IF.nb_format == FLT64_str) // Double data
994  {
995  double db;
996  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &db);
997  }
998  else if (a_IF.nb_format == LONGDOUBLE_str) //long double data
999  {
1000  if (a_IF.nb_bytes_pixel != sizeof(long double))
1001  {
1002  Cerr("***** oInterfileIO::IntfGetPixelTypeAndReadData() -> The long double format is not the same for this image file and for this platform !" << endl);
1003  return 1;
1004  }
1005  long double db;
1006  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &db);
1007  }
1008  else if (a_IF.nb_format == INT32_str) // Signed integer data
1009  {
1010  if(a_IF.nb_bytes_pixel == 1)
1011  {
1012  int8_t s_int;
1013  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &s_int);
1014  }
1015  else if(a_IF.nb_bytes_pixel == 2)
1016  {
1017  int16_t s_int;
1018  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &s_int);
1019  }
1020  else if(a_IF.nb_bytes_pixel == 8)
1021  {
1022  int64_t s_int;
1023  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &s_int);
1024  }
1025  else // default : 4 bytes
1026  {
1027  int32_t s_int;
1028  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &s_int);
1029  }
1030  }
1031  else // Unsigned integer data (default)
1032  {
1033  if(a_IF.nb_bytes_pixel == 1)
1034  {
1035  uint8_t u_int;
1036  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &u_int);
1037  }
1038  else if(a_IF.nb_bytes_pixel == 2)
1039  {
1040  uint16_t u_int;
1041  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &u_int);
1042  }
1043  else if(a_IF.nb_bytes_pixel == 8)
1044  {
1045  uint64_t u_int;
1046  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &u_int);
1047  }
1048  else // default : 4 bytes
1049  {
1050  uint32_t u_int;
1051  error = IntfReadData(a_IF, ap_iFile, ap_outImgMatrix, ap_inImgMatrix, ap_offset, a_nbVox, a_verbose, &u_int);
1052  }
1053  }
1054 
1055  if (error!=0)
1056  {
1057  Cerr("***** oInterfileIO::IntfGetPixelTypeAndReadData() -> An error occurred when trying to read data through the IntfReadData() function !" << endl);
1058  return 1;
1059  }
1060 
1061  return 0;
1062 }
1063 
1064 // =====================================================================
1065 // ---------------------------------------------------------------------
1066 // ---------------------------------------------------------------------
1067 // =====================================================================
1068 
1069 template <class T>
1071  ifstream* ap_iFile,
1072  FLTNB* ap_outImgMatrix,
1073  FLTNB* ap_inImgMatrix,
1074  uint32_t* a_offset,
1075  int a_nbVox,
1076  int a_verbose,
1077  T* bytes)
1078 {
1079  if (a_verbose >= 5) Cout("oInterfileIO::IntfReadData() -> Read data from file stream and eventually interpolate" << endl);
1080 
1081  int nb_vox = 0; // number of voxels in the matrix which will recover the image data
1082  FLTNB* pimg = NULL; // pointer to the matrix which will recover the image data
1083 
1084  // Check if interpolation is required
1085  if(a_IF.is_mtx_size_different)
1086  {
1087  // Set the number of voxels in the image to the original image dimensions as they differ from reconstruction dimensions
1088  nb_vox = a_IF.mtx_size[0]*a_IF.mtx_size[1]*a_IF.mtx_size[2];
1089  pimg = ap_inImgMatrix; // recover the image into the image matrix initialized with interfile dimensions
1090  }
1091  else
1092  {
1093  nb_vox = a_nbVox;
1094  pimg = ap_outImgMatrix; // directly recover the image into the CASToR image matrix
1095  }
1096 
1097  // Allocate temporary reading buffer
1098  bytes = (T*)malloc(nb_vox*sizeof(T));
1099  // Seek to the provided position
1100  ap_iFile->seekg(*a_offset);
1101  // Read the data
1102  ap_iFile->read((char*)bytes, nb_vox*sizeof(T));
1103  // Check reading status
1104  if (!ap_iFile->good())
1105  {
1106  if (ap_iFile->eof()) Cerr("***** oInterfileIO::IntfReadData() -> Not enough data in the file stream !" << endl);
1107  else Cerr("***** oInterfileIO::IntfReadData() -> An error occurred while reading from file stream !" << endl);
1108  free(bytes);
1109  return 1;
1110  }
1111 
1112  // Loop over all voxels
1113  for (int v=0; v<nb_vox; v++)
1114  {
1115  // Todo Re-orient image data from the original orientation to the default orientation (transaxial/supine/headin)
1116  //int voxel_idx = IntfGetVoxIdxSHTOrientation(a_IF, v);
1117  int voxel_idx = v; // For now no orientations
1118 
1119  // Swap data if endianness is not the same as user processor
1120  if (a_IF.endianness != User_Endianness) SwapBytes(&(bytes[v]));
1121  // Cast to CASToR image format
1122  FLTNB buffer = (FLTNB)(bytes[v]);
1123  // Calibrate data using rescale slope and intercept if needed.
1124  // Then write in the image matrix
1125  pimg[voxel_idx] = buffer * a_IF.rescale_slope * a_IF.quant_units
1126  + a_IF.rescale_intercept;
1127 
1128  // set the offset to the next position
1129  *a_offset += sizeof(T);
1130  }
1131 
1132  free(bytes);
1133 
1134  // END OF NEW CODE
1135 
1136  // Call interpolation function if required
1137  if (a_IF.is_mtx_size_different
1138  || a_IF.vox_offset[0] != a_IF.vox_offset[0]
1139  || a_IF.vox_offset[1] != a_IF.vox_offset[1]
1140  || a_IF.vox_offset[2] != a_IF.vox_offset[2] )
1141  {
1142  if (ImageInterpolation(ap_inImgMatrix, ap_outImgMatrix,
1143  a_IF.mtx_size, a_IF.cmtx_size,
1144  a_IF.vox_size, a_IF.cvox_size,
1145  a_IF.vox_offset, a_IF.cvox_offset) )
1146  {
1147  Cerr("***** oInterfileIO::IntfReadData() -> An error occurred while interpolating the input image to the reconstruction dimensions !" << endl);
1148  return 1;
1149  }
1150  }
1151 
1152  return 0;
1153 }
1154 
1155 
1156 
1157 
1158 // =====================================================================
1159 // ---------------------------------------------------------------------
1160 // ---------------------------------------------------------------------
1161 // =====================================================================
1162 /*
1163  \fn ImageInterpolation
1164  \param ap_iImg : Image matrix to interpolate, with dimensions of the original interfile
1165  \param ap_oImg : Image matrix to recover the interpolated image to the reconstruction dimensions
1166  \param ap_iDimVox[3] : X,Y,Z dimensions of the image to interpolate
1167  \param ap_oDimVox[3] : X,Y,Z dimensions for the reconstruction
1168  \param ap_iSizeVox[3] : X,Y,Z voxel size of the image to interpolate
1169  \param ap_oSizeVox[3] : X,Y,Z voxel size for the reconstruction
1170  \param ap_iOffVox[3] : X,Y,Z offset position of the image to interpolate (mm)
1171  \param ap_oOffVox[3] : X,Y,Z offset position for the reconstruction (mm)
1172  \brief Trilinear interpolation
1173  \return 0 if success, positive value otherwise.
1174 */
1175 int ImageInterpolation(FLTNB *ap_iImg, FLTNB *ap_oImg,
1176  const uint32_t ap_iDimVox[3], const uint32_t ap_oDimVox[3],
1177  const FLTNB ap_iSizeVox[3], const FLTNB ap_oSizeVox[3],
1178  const FLTNB ap_iOffVox[3], const FLTNB ap_oOffVox[3] )
1179 {
1180  // Assuming the two images are registered
1181  // todo : deal with any potential offset if the input data contains following keys
1182  // "origin (mm) [x], offset [x], first pixel offset (mm) [x] "
1183 
1184 /* ORIGINAL FUNCTIONS IMPLEMENTED ONLY WITH FLOATS
1185  float const posOldImage[] = {-(float)ap_iDimVox[0]*ap_iSizeVox[0]/2 ,
1186  -(float)ap_iDimVox[1]*ap_iSizeVox[1]/2 ,
1187  -(float)ap_iDimVox[2]*ap_iSizeVox[2]/2 };
1188 
1189  float const posNewImage[] = {-(float)ap_oDimVox[0]*ap_oSizeVox[0]/2 ,
1190  -(float)ap_oDimVox[1]*ap_oSizeVox[1]/2 ,
1191  -(float)ap_oDimVox[2]*ap_oSizeVox[2]/2 };
1192 
1193  // Padding the old image in each direction
1194  uint32_t const iDimVoxPad[]
1195  {
1196  ap_iDimVox[ 0 ] + 2,
1197  ap_iDimVox[ 1 ] + 2,
1198  ap_iDimVox[ 2 ] + 2
1199  };
1200 
1201  uint32_t const nElts = iDimVoxPad[ 0 ] *
1202  iDimVoxPad[ 1 ] *
1203  iDimVoxPad[ 2 ];
1204 
1205  // Allocating a new buffer storing the padded image
1206  float *pPadIData = new float[ nElts ];
1207  ::memset( pPadIData, 0, sizeof( float ) * nElts );
1208 
1209  for( uint32_t k = 0; k < ap_iDimVox[ 2 ]; ++k )
1210  for( uint32_t j = 0; j < ap_iDimVox[ 1 ]; ++j )
1211  for( uint32_t i = 0; i < ap_iDimVox[ 0 ]; ++i )
1212  {
1213  pPadIData[ ( i + 1 ) + ( j + 1 ) * iDimVoxPad[ 0 ]
1214  + ( k + 1 ) * iDimVoxPad[ 0 ] * iDimVoxPad[ 1 ] ] =
1215  ap_iImg[ i + j * ap_iDimVox[ 0 ]
1216  + k * ap_iDimVox[ 0 ] * ap_iDimVox[ 1 ] ];
1217  }
1218 
1219 
1220  // Computing the bounds in each direction depending on the pad
1221  float const boundMin[]
1222  {
1223  posOldImage[ 0 ] - ap_iSizeVox[ 0 ] / 2.0f,
1224  posOldImage[ 1 ] - ap_iSizeVox[ 1 ] / 2.0f,
1225  posOldImage[ 2 ] - ap_iSizeVox[ 2 ] / 2.0f
1226  };
1227 
1228  float const boundMax[]
1229  {
1230  posOldImage[ 0 ] + (float)ap_iDimVox[ 0 ] * ap_iSizeVox[ 0 ]
1231  + ap_iSizeVox[ 0 ] / 2.0f,
1232  posOldImage[ 1 ] + (float)ap_iDimVox[ 1 ] * ap_iSizeVox[ 1 ]
1233  + ap_iSizeVox[ 1 ] / 2.0f,
1234  posOldImage[ 2 ] + (float)ap_iDimVox[ 2 ] * ap_iSizeVox[ 2 ]
1235  + ap_iSizeVox[ 2 ] / 2.0f
1236  };
1237 
1238  // Computing and storing the position of the center of the pixel in each
1239  // direction for the initial image
1240  float **pOldCoordCenter = new float*[ 3 ];
1241 
1242  for( uint32_t i = 0; i < 3; ++i )
1243  {
1244  pOldCoordCenter[ i ] = new float[ iDimVoxPad[ i ] ];
1245  // Set the values
1246  for( uint32_t j = 0; j < iDimVoxPad[ i ]; ++j )
1247  {
1248  pOldCoordCenter[ i ][ j ] = posOldImage[ i ] - ap_iSizeVox[ i ] / 2.0f
1249  + j * ap_iSizeVox[ i ];
1250  }
1251  }
1252 
1253  // Computing and storing the position of the center of the pixel in each
1254  // direction of the resampled image
1255  float **pNewCoordCenter = new float*[ 3 ];
1256  for( uint32_t i = 0; i < 3; ++i )
1257  {
1258  pNewCoordCenter[ i ] = new float[ ap_oDimVox[ i ] ];
1259  // Set the values
1260  for( uint32_t j = 0; j < ap_oDimVox[ i ]; ++j )
1261  {
1262  pNewCoordCenter[ i ][ j ] = posNewImage[ i ] + ap_oSizeVox[ i ] / 2.0f
1263  + j * ap_oSizeVox[ i ];
1264  }
1265  }
1266 
1267  // Defining parameters
1268  float const invSizeX = 1.0f / ap_iSizeVox[ 0 ];
1269  float const invSizeY = 1.0f / ap_iSizeVox[ 1 ];
1270  float const invSizeZ = 1.0f / ap_iSizeVox[ 2 ];
1271 
1272  // Allocating memory for the 8 pixels kernel
1273  float *pKernelData = new float[ 8 ];
1274 
1275  // Loop over the elements of the new images
1276  for( uint32_t k = 0; k < ap_oDimVox[ 2 ]; ++k )
1277  {
1278  // Get the coordinate in Z
1279  float const z = pNewCoordCenter[ 2 ][ k ];
1280  if( z < boundMin[ 2 ] || z > boundMax[ 2 ] ) continue;
1281 
1282  // Find the bin in the old image
1283  int32_t const zBin = ( z - boundMin[ 2 ] ) * invSizeZ;
1284 
1285  // Computing the 2 z composants
1286  float const zComposantI0 = invSizeZ * ( pOldCoordCenter[ 2 ][ zBin + 1 ] - z );
1287  float const zComposantI1 = invSizeZ * ( z - pOldCoordCenter[ 2 ][ zBin ] );
1288 
1289  for( uint32_t j = 0; j < ap_oDimVox[ 1 ]; ++j )
1290  {
1291  // Get the coordinate in Y
1292  float const y = pNewCoordCenter[ 1 ][ j ];
1293  if( y < boundMin[ 1 ] || y > boundMax[ 1 ] ) continue;
1294 
1295  // Find the bin in the old image
1296  int32_t const yBin = ( y - boundMin[ 1 ] ) * invSizeY;
1297 
1298  // Computing the 2 y composants
1299  float const yComposantI0 = invSizeY * ( pOldCoordCenter[ 1 ][ yBin + 1 ]
1300  - y );
1301  float const yComposantI1 = invSizeY * ( y
1302  - pOldCoordCenter[ 1 ][ yBin ] );
1303 
1304  for( uint32_t i = 0; i < ap_oDimVox[ 0 ]; ++i )
1305  {
1306  // Get the coordinate in X
1307  float const x = pNewCoordCenter[ 0 ][ i ];
1308  if( x < boundMin[ 0 ] || x > boundMax[ 0 ] ) continue;
1309 
1310  // Find the bin in the old image
1311  int32_t const xBin = ( x - boundMin[ 0 ] ) * invSizeX;
1312 
1313  // Computing the 2 x composants
1314  float const xComposantI0 = invSizeX * (
1315  pOldCoordCenter[ 0 ][ xBin + 1 ] - x );
1316  float const xComposantI1 = invSizeX * ( x
1317  - pOldCoordCenter[ 0 ][ xBin ] );
1318 
1319  // Fill the buffer storing the data
1320  for( uint32_t kk = 0; kk < 2; ++kk )
1321  {
1322  for( uint32_t jj = 0; jj < 2; ++jj )
1323  {
1324  for( uint32_t ii = 0; ii < 2; ++ii )
1325  {
1326  pKernelData[ ii + jj * 2 + kk * 2 * 2 ] =
1327  pPadIData[
1328  ( xBin + ii ) +
1329  ( yBin + jj ) * iDimVoxPad[ 0 ] +
1330  ( zBin + kk ) * iDimVoxPad[ 0 ] * iDimVoxPad[ 1 ]
1331  ];
1332  }
1333  }
1334  }
1335 
1336  // Computing the interpolation
1337  // In X
1338  float const xInterpVal0 = pKernelData[ 0 ] * xComposantI0 +
1339  pKernelData[ 1 ] * xComposantI1;
1340 
1341  float const xInterpVal1 = pKernelData[ 2 ] * xComposantI0 +
1342  pKernelData[ 3 ] * xComposantI1;
1343 
1344  float const xInterpVal2 = pKernelData[ 4 ] * xComposantI0 +
1345  pKernelData[ 5 ] * xComposantI1;
1346 
1347  float const xInterpVal3 = pKernelData[ 6 ] * xComposantI0 +
1348  pKernelData[ 7 ] * xComposantI1;
1349 
1350  // In Y
1351  float const yInterpVal0 = xInterpVal0 * yComposantI0 +
1352  xInterpVal1 * yComposantI1;
1353 
1354  float const yInterpVal1 = xInterpVal2 * yComposantI0 +
1355  xInterpVal3 * yComposantI1;
1356 
1357  // Final interpolation
1358  float const interpValTot = yInterpVal0 * zComposantI0 +
1359  yInterpVal1 * zComposantI1;
1360 
1361  ap_oImg[ i + j * ap_oDimVox[ 0 ]
1362  + k * ap_oDimVox[ 0 ] * ap_oDimVox[ 1 ] ] = interpValTot;
1363  }
1364  }
1365  }
1366 
1367  // Freeing the memory
1368  for( uint32_t i = 0; i < 3; ++i )
1369  {
1370  delete[] pOldCoordCenter[ i ];
1371  delete[] pNewCoordCenter[ i ];
1372  }
1373  delete[] pOldCoordCenter;
1374  delete[] pNewCoordCenter;
1375  delete[] pPadIData;
1376  delete[] pKernelData;
1377 
1378  return 0;
1379 */
1380 
1381 
1382 
1383  FLTNB const posOldImage[] = {-((FLTNB)(ap_iDimVox[0]))*ap_iSizeVox[0]*((FLTNB)0.5) ,
1384  -((FLTNB)(ap_iDimVox[1]))*ap_iSizeVox[1]*((FLTNB)0.5) ,
1385  -((FLTNB)(ap_iDimVox[2]))*ap_iSizeVox[2]*((FLTNB)0.5) };
1386 
1387  FLTNB const posNewImage[] = {-((FLTNB)(ap_oDimVox[0]))*ap_oSizeVox[0]*((FLTNB)0.5) ,
1388  -((FLTNB)(ap_oDimVox[1]))*ap_oSizeVox[1]*((FLTNB)0.5) ,
1389  -((FLTNB)(ap_oDimVox[2]))*ap_oSizeVox[2]*((FLTNB)0.5) };
1390 
1391  /*
1392  FLTNB const posOldImage[] = {-((FLTNB)(ap_iDimVox[0]))*ap_iSizeVox[0]*((FLTNB)0.5) + ap_iOffVox[0] ,
1393  -((FLTNB)(ap_iDimVox[1]))*ap_iSizeVox[1]*((FLTNB)0.5) + ap_iOffVox[1] ,
1394  -((FLTNB)(ap_iDimVox[2]))*ap_iSizeVox[2]*((FLTNB)0.5) + ap_iOffVox[2] };
1395 
1396  FLTNB const posNewImage[] = {-((FLTNB)(ap_oDimVox[0]))*ap_oSizeVox[0]*((FLTNB)0.5) + ap_oOffVox[0],
1397  -((FLTNB)(ap_oDimVox[1]))*ap_oSizeVox[1]*((FLTNB)0.5) + ap_oOffVox[1],
1398  -((FLTNB)(ap_oDimVox[2]))*ap_oSizeVox[2]*((FLTNB)0.5) + ap_oOffVox[2]};*/
1399 
1400  // Padding the old image in each direction
1401  uint32_t const iDimVoxPad[]
1402  {
1403  ap_iDimVox[ 0 ] + 2,
1404  ap_iDimVox[ 1 ] + 2,
1405  ap_iDimVox[ 2 ] + 2
1406  };
1407 
1408  uint32_t const nElts = iDimVoxPad[ 0 ] *
1409  iDimVoxPad[ 1 ] *
1410  iDimVoxPad[ 2 ];
1411 
1412  // Allocating a new buffer storing the padded image
1413  FLTNB *pPadIData = new FLTNB[ nElts ];
1414  ::memset( pPadIData, 0, sizeof( FLTNB ) * nElts );
1415 
1416  for( uint32_t k = 0; k < ap_iDimVox[ 2 ]; ++k )
1417  for( uint32_t j = 0; j < ap_iDimVox[ 1 ]; ++j )
1418  for( uint32_t i = 0; i < ap_iDimVox[ 0 ]; ++i )
1419  {
1420  pPadIData[ ( i + 1 ) + ( j + 1 ) * iDimVoxPad[ 0 ]
1421  + ( k + 1 ) * iDimVoxPad[ 0 ] * iDimVoxPad[ 1 ] ] =
1422  ap_iImg[ i + j * ap_iDimVox[ 0 ]
1423  + k * ap_iDimVox[ 0 ] * ap_iDimVox[ 1 ] ];
1424  }
1425 
1426 
1427  // Computing the bounds in each direction depending on the pad
1428  FLTNB const boundMin[]
1429  {
1430  posOldImage[ 0 ] - ap_iSizeVox[ 0 ] * ((FLTNB)0.5),
1431  posOldImage[ 1 ] - ap_iSizeVox[ 1 ] * ((FLTNB)0.5),
1432  posOldImage[ 2 ] - ap_iSizeVox[ 2 ] * ((FLTNB)0.5)
1433  };
1434 
1435  FLTNB const boundMax[]
1436  {
1437  posOldImage[ 0 ] + ((FLTNB)ap_iDimVox[ 0 ]) * ap_iSizeVox[ 0 ]
1438  + ap_iSizeVox[ 0 ] * ((FLTNB)0.5),
1439  posOldImage[ 1 ] + ((FLTNB)ap_iDimVox[ 1 ]) * ap_iSizeVox[ 1 ]
1440  + ap_iSizeVox[ 1 ] * ((FLTNB)0.5),
1441  posOldImage[ 2 ] + ((FLTNB)ap_iDimVox[ 2 ]) * ap_iSizeVox[ 2 ]
1442  + ap_iSizeVox[ 2 ] * ((FLTNB)0.5)
1443  };
1444 
1445  // Computing and storing the position of the center of the pixel in each
1446  // direction for the initial image
1447  FLTNB **pOldCoordCenter = new FLTNB*[ 3 ];
1448 
1449  for( uint32_t i = 0; i < 3; ++i )
1450  {
1451  pOldCoordCenter[ i ] = new FLTNB[ iDimVoxPad[ i ] ];
1452  // Set the values
1453  for( uint32_t j = 0; j < iDimVoxPad[ i ]; ++j )
1454  {
1455  pOldCoordCenter[ i ][ j ] = posOldImage[ i ] - ap_iSizeVox[ i ] / 2.0
1456  + j * ap_iSizeVox[ i ];
1457  }
1458  }
1459 
1460  // Computing and storing the position of the center of the pixel in each
1461  // direction of the resampled image
1462  FLTNB **pNewCoordCenter = new FLTNB*[ 3 ];
1463  for( uint32_t i = 0; i < 3; ++i )
1464  {
1465  pNewCoordCenter[ i ] = new FLTNB[ ap_oDimVox[ i ] ];
1466  // Set the values
1467  for( uint32_t j = 0; j < ap_oDimVox[ i ]; ++j )
1468  {
1469  pNewCoordCenter[ i ][ j ] = posNewImage[ i ] + ap_oSizeVox[ i ] / 2.0
1470  + j * ap_oSizeVox[ i ];
1471  }
1472  }
1473 
1474  // Defining parameters
1475  FLTNB const invSizeX = 1.0 / ap_iSizeVox[ 0 ];
1476  FLTNB const invSizeY = 1.0 / ap_iSizeVox[ 1 ];
1477  FLTNB const invSizeZ = 1.0 / ap_iSizeVox[ 2 ];
1478 
1479  // Allocating memory for the 8 pixels kernel
1480  FLTNB *pKernelData = new FLTNB[ 8 ];
1481 
1482  // Loop over the elements of the new images
1483  for( uint32_t k = 0; k < ap_oDimVox[ 2 ]; ++k )
1484  {
1485  // Get the coordinate in Z
1486  FLTNB const z = pNewCoordCenter[ 2 ][ k ];
1487  if( z < boundMin[ 2 ] || z > boundMax[ 2 ] ) continue;
1488 
1489  // Find the bin in the old image
1490  int32_t const zBin = ( z - boundMin[ 2 ] ) * invSizeZ;
1491 
1492  // Computing the 2 z composants
1493  FLTNB const zComposantI0 = invSizeZ * ( pOldCoordCenter[ 2 ][ zBin + 1 ] - z );
1494  FLTNB const zComposantI1 = invSizeZ * ( z - pOldCoordCenter[ 2 ][ zBin ] );
1495 
1496  for( uint32_t j = 0; j < ap_oDimVox[ 1 ]; ++j )
1497  {
1498  // Get the coordinate in Y
1499  FLTNB const y = pNewCoordCenter[ 1 ][ j ];
1500  if( y < boundMin[ 1 ] || y > boundMax[ 1 ] ) continue;
1501 
1502  // Find the bin in the old image
1503  int32_t const yBin = ( y - boundMin[ 1 ] ) * invSizeY;
1504 
1505  // Computing the 2 y composants
1506  FLTNB const yComposantI0 = invSizeY * ( pOldCoordCenter[ 1 ][ yBin + 1 ]
1507  - y );
1508  FLTNB const yComposantI1 = invSizeY * ( y
1509  - pOldCoordCenter[ 1 ][ yBin ] );
1510 
1511  for( uint32_t i = 0; i < ap_oDimVox[ 0 ]; ++i )
1512  {
1513  // Get the coordinate in X
1514  FLTNB const x = pNewCoordCenter[ 0 ][ i ];
1515  if( x < boundMin[ 0 ] || x > boundMax[ 0 ] ) continue;
1516 
1517  // Find the bin in the old image
1518  int32_t const xBin = ( x - boundMin[ 0 ] ) * invSizeX;
1519 
1520  // Computing the 2 x composants
1521  FLTNB const xComposantI0 = invSizeX * (
1522  pOldCoordCenter[ 0 ][ xBin + 1 ] - x );
1523  FLTNB const xComposantI1 = invSizeX * ( x
1524  - pOldCoordCenter[ 0 ][ xBin ] );
1525 
1526  // TODO : check for potential segfaults
1527  // Fill the buffer storing the data
1528  for( uint32_t kk = 0; kk < 2; ++kk )
1529  {
1530  for( uint32_t jj = 0; jj < 2; ++jj )
1531  {
1532  for( uint32_t ii = 0; ii < 2; ++ii )
1533  {
1534  pKernelData[ ii + jj * 2 + kk * 2 * 2 ] =
1535  pPadIData[
1536  ( xBin + ii ) +
1537  ( yBin + jj ) * iDimVoxPad[ 0 ] +
1538  ( zBin + kk ) * iDimVoxPad[ 0 ] * iDimVoxPad[ 1 ]
1539  ];
1540  }
1541  }
1542  }
1543 
1544  // Computing the interpolation
1545  // In X
1546  FLTNB const xInterpVal0 = pKernelData[ 0 ] * xComposantI0 +
1547  pKernelData[ 1 ] * xComposantI1;
1548 
1549  FLTNB const xInterpVal1 = pKernelData[ 2 ] * xComposantI0 +
1550  pKernelData[ 3 ] * xComposantI1;
1551 
1552  FLTNB const xInterpVal2 = pKernelData[ 4 ] * xComposantI0 +
1553  pKernelData[ 5 ] * xComposantI1;
1554 
1555  FLTNB const xInterpVal3 = pKernelData[ 6 ] * xComposantI0 +
1556  pKernelData[ 7 ] * xComposantI1;
1557 
1558  // In Y
1559  FLTNB const yInterpVal0 = xInterpVal0 * yComposantI0 +
1560  xInterpVal1 * yComposantI1;
1561 
1562  FLTNB const yInterpVal1 = xInterpVal2 * yComposantI0 +
1563  xInterpVal3 * yComposantI1;
1564 
1565  // Final interpolation
1566  FLTNB const interpValTot = yInterpVal0 * zComposantI0 +
1567  yInterpVal1 * zComposantI1;
1568 
1569  ap_oImg[ i + j * ap_oDimVox[ 0 ]
1570  + k * ap_oDimVox[ 0 ] * ap_oDimVox[ 1 ] ] = interpValTot;
1571  }
1572  }
1573  }
1574 
1575  // Freeing the memory
1576  for( uint32_t i = 0; i < 3; ++i )
1577  {
1578  delete[] pOldCoordCenter[ i ];
1579  delete[] pNewCoordCenter[ i ];
1580  }
1581  delete[] pOldCoordCenter;
1582  delete[] pNewCoordCenter;
1583  delete[] pPadIData;
1584  delete[] pKernelData;
1585 
1586  return 0;
1587 
1588 
1589 }
1590 
1591 
1592 
1593 
1594 // =====================================================================
1595 // ---------------------------------------------------------------------
1596 // ---------------------------------------------------------------------
1597 // =====================================================================
1598 /*
1599  \fn IntfWriteImgFile
1600  \param a_pathToImg : path to image basename
1601  \param ap_ImgMatrix : 1 dimensional image matrix which contains the image to write
1602  \param ap_IntfF : Intf_fields structure containing image metadata
1603  \param vb : verbosity
1604  \brief Main function dedicated to Interfile 3D image writing. \n
1605  Recover image information from a provided Intf_fields structure.
1606  \details Call the main functions dedicated to Interfile header and data writing :
1607  IntfWriteHeaderMainData() and then IntfWriteImage()
1608  \return 0 if success, positive value otherwise.
1609 */
1610 int IntfWriteImgFile(const string& a_pathToImg, FLTNB* ap_ImgMatrix, const Intf_fields& ap_IntfF, int vb)
1611 {
1612  if (vb>=3) Cout("IntfWriteImgFile (with Intf_fields)" << endl);
1613 
1614  // Write Interfile header
1615  if(IntfWriteHeaderMainData(a_pathToImg, ap_IntfF, vb) )
1616  {
1617  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile header for'"<< a_pathToImg << "' !" << endl);
1618  return 1;
1619  }
1620 
1621  string path_to_image = a_pathToImg;
1622  path_to_image.append(".img");
1623 
1624  // Read Interfile image
1625  if(IntfWriteImage(path_to_image, ap_ImgMatrix, ap_IntfF.mtx_size[0] * ap_IntfF.mtx_size[1] * ap_IntfF.mtx_size[2], vb) )
1626  {
1627  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile image '"<< path_to_image << "' !" << endl);
1628  return 1;
1629  }
1630 
1631  return 0;
1632 }
1633 
1634 
1635 
1636 
1637 // =====================================================================
1638 // ---------------------------------------------------------------------
1639 // ---------------------------------------------------------------------
1640 // =====================================================================
1641 /*
1642  \fn IntfWriteImgFile
1643  \param a_pathToImg : path to image basename
1644  \param ap_ImgMatrix : 1 dimensional image matrix which contains the image to write
1645  \param ap_ID : Provide the Image dimensions object containing reconstruction dimensions
1646  \param vb : verbosity
1647  \brief Main function dedicated to Interfile 3D image writing
1648  \details Call the main functions dedicated to Interfile header and data writing :
1649  IntfWriteHeaderMainData() and then IntfWriteImage()
1650  \todo Get metadata from a Intf_fields object ?
1651  (useful to transfer keys from read images to written images)
1652  \return 0 if success, positive value otherwise.
1653 */
1654 int IntfWriteImgFile(const string& a_pathToImg, FLTNB* ap_ImgMatrix, oImageDimensionsAndQuantification* ap_ID, int vb)
1655 {
1656  if (vb>=3) Cout("IntfWriteImgFile (3D image)" << endl);
1657 
1658  Intf_fields Img_fields;
1659  IntfKeySetFieldsOutput(&Img_fields, ap_ID);
1660 
1661  // Write Interfile header
1662  if(IntfWriteHeaderMainData(a_pathToImg, Img_fields, vb) )
1663  {
1664  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile header for'"<< a_pathToImg << "' !" << endl);
1665  return 1;
1666  }
1667 
1668  string path_to_image = a_pathToImg;
1669  path_to_image.append(".img");
1670 
1671  // Read Interfile image
1672  if(IntfWriteImage(path_to_image, ap_ImgMatrix, ap_ID->GetNbVoxXYZ(), vb) )
1673  {
1674  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile image '"<< path_to_image << "' !" << endl);
1675  return 1;
1676  }
1677 
1678  return 0;
1679 }
1680 
1681 
1682 
1683 
1684 // =====================================================================
1685 // ---------------------------------------------------------------------
1686 // ---------------------------------------------------------------------
1687 // =====================================================================
1688 /*
1689  \fn IntfWriteProjFile
1690  \param a_pathToImg : string containing the path to the image basename
1691  \param a2p_ImgMatrix : 2 dimensional image matrix which contains the image to write.
1692  \param ap_ID : Provide the Image dimensions object containing reconstruction dimensions
1693  \param a_Imgfields: Structure containing information about the projected data
1694  \param vb : verbosity
1695  \brief Function dedicated to Interfile image writing for projected data
1696  \details Call the main functions dedicated to Interfile header and data writing
1697  Currently work for SPECT projected data
1698  The total number of projections should be provided in parameters
1699  Depending on the output writing mode (stored in sOutputManager),
1700  \todo Get metadata from a Intf_fields object ?
1701  (useful to transfer keys from read images to written images)
1702  \return 0 if success, positive value otherwise.
1703 */
1704 int IntfWriteProjFile(const string& a_pathToImg, FLTNB** a2p_ImgMatrix, oImageDimensionsAndQuantification* ap_ID, Intf_fields a_Imgfields, int vb)
1705 {
1706  if (vb>=3) Cout("IntfWriteProjFile ..." << endl);
1707 
1708  //Set image names
1709  vector<string> lpath_to_images;
1710  lpath_to_images.push_back(a_pathToImg);
1711 
1712  if(IntfWriteHeaderMainData(a_pathToImg, a_Imgfields, vb) )
1713  {
1714  Cerr("***** IntfWriteProjFile()-> Error : while trying to write the interfile header for'"<< lpath_to_images[0] << "' !" << endl);
1715  return 1;
1716  }
1717 
1718  // Write interfile image
1719 
1720  // Set dimensions
1721  uint32_t dims[2];
1722  dims[0] = a_Imgfields.nb_projections;
1723  dims[1] = a_Imgfields.mtx_size[0]*a_Imgfields.mtx_size[1];
1724  //cout << "dims[0] " << dims[0] << endl;
1725  //cout << "TODO !!! ::: dims[1] " << dims[1] << endl;
1726 
1727  //dims[1] = 128*128;
1728 
1729  if(IntfWriteImage(lpath_to_images, a2p_ImgMatrix, dims, vb) )
1730  {
1731  Cerr("***** IntfWriteFile()-> Error : while trying to write the interfile image '"<< a_pathToImg << "' !" << endl);
1732  return 1;
1733  }
1734 
1735  return 0;
1736 }
1737 
1738 
1739 
1740 
1741 // =====================================================================
1742 // ---------------------------------------------------------------------
1743 // ---------------------------------------------------------------------
1744 // =====================================================================
1745 /*
1746  \fn IntfWriteImgDynCoeffFile
1747  \param a_pathToImg : string containing the path to the image basename
1748  \param a2p_ImgMatrix : 2 dimensional image matrix which contains the image to write.
1749  \param ap_ID : Provide the Image dimensions object containing reconstruction dimensions
1750  \param a_nbParImgs : Number of parametric images
1751  \param vb : verbosity
1752  \param a_mergeDynImgFlag : force merging the dynamic images in a single file if true (default = false)
1753  \brief Function dedicated to Interfile image writing for dynamic coefficients images
1754  \details Call the main functions dedicated to Interfile header and data writing
1755  The total number of basis functions should be provided in parameters
1756  Depending on the output writing mode (stored in sOutputManager),
1757  One image with one file and one will be created for the whole dynamic image
1758  or a metaheader and associated multiple 3D image raw file/headers will be generated
1759  \todo Get metadata from a Intf_fields object ?
1760  (useful to transfer keys from read images to written images)
1761  \return 0 if success, positive value otherwise.
1762 */
1763 int IntfWriteImgDynCoeffFile(const string& a_pathToImg, FLTNB** a2p_ImgMatrix, oImageDimensionsAndQuantification* ap_ID, int a_nbParImgs, int vb, bool a_mergeDynImgFlag)
1764 {
1765  if (vb>=3) Cout("IntfWriteImgDynCoeffFile ..." << endl);
1766 
1767  Intf_fields Img_fields; //TODO keep refs to Intf_fields in case we need it later (transfer keys from read images to written images)
1768  IntfKeySetFieldsOutput(&Img_fields, ap_ID);
1769  Img_fields.nb_time_frames = a_nbParImgs;
1770  Img_fields.nb_resp_gates = Img_fields.nb_card_gates = 1;
1771 
1772  // Get input from user about how dynamic files should be written
1773  // (one global image file, or separate image file for each frame/gates).
1775 
1776  //Set image names
1777  vector<string> lpath_to_images;
1778 
1779  if(p_outputMgr->MergeDynImages()==true || a_mergeDynImgFlag==true) // Case one file
1780  {
1781  lpath_to_images.push_back(a_pathToImg);
1782  lpath_to_images[0].append("_par");
1783 
1784  if(IntfWriteHeaderMainData(lpath_to_images[0], Img_fields, vb) )
1785  {
1786  Cerr("***** IntfWriteImgDynCoeffFile()-> Error : while trying to write the interfile header for'"<< lpath_to_images[0] << "' !" << endl);
1787  return 1;
1788  }
1789  }
1790  else // Case several files
1791  {
1792  int idx_file = 0;
1793 
1794  for(int bf=0 ; bf<a_nbParImgs ; bf++)
1795  {
1796  //lpath_to_images[idx_file] = a_pathToImg;
1797  lpath_to_images.push_back(a_pathToImg);
1798 
1799  // Add a suffix for basis functions
1800  stringstream ss; ss << bf + 1;
1801  lpath_to_images[idx_file].append("_par").append(ss.str());
1802 
1803  // Write header specific to this image
1804  string path_to_hdr = lpath_to_images[idx_file] + ".hdr";
1805  string path_to_img = lpath_to_images[idx_file] + ".img";
1806 
1807  // As the content will be appended, make sure there is no existing file with such name
1808  // remove it otherwise
1809  ifstream fcheck(path_to_hdr.c_str());
1810  if(fcheck.good())
1811  {
1812  fcheck.close();
1813  #ifdef _WIN32
1814  string dos_instruction = "del " + path_to_hdr;
1815  system(dos_instruction.c_str());
1816  #else
1817  remove(path_to_hdr.c_str());
1818  #endif
1819  }
1820 
1821  ofstream ofile(path_to_hdr.c_str(), ios::out | ios::app);
1822  ofile << setprecision(std::numeric_limits<FLTNB>::digits10 +1);
1823  ofile << "!INTERFILE := " << endl;
1824  ofile << "!name of data file := " << GetFileFromPath(path_to_img) << endl;
1825  ofile << "imagedata byte order := " << IntfKeyGetEndianStr(User_Endianness) << endl;
1826  ofile << "!data offset in bytes := " << 0 << endl;
1827  ofile << "patient name:= " << GetFileFromPath(path_to_img) << endl;
1828  ofile << "!type of data := Dynamic" << endl; // /!\ Dynamic by default in order to be readable from ImageJ OpenMed plugin
1829 
1830  if(IntfWriteHeaderImgData(ofile, Img_fields, vb) )
1831  {
1832  Cerr("***** IntfWriteImgDynCoeffFile()-> Error : while trying to write the interfile header for'"<< lpath_to_images[idx_file] << "' !" << endl);
1833  return 1;
1834  }
1835 
1836  ofile << "!END OF INTERFILE := " << endl;
1837 
1838  ofile.close();
1839  idx_file++;
1840  }
1841 
1842  // Write meta header
1843  // First adapt the a_pathToImg name to avoind overwriting the MHD file of the frame images.
1844  string pathToDynCoeffMHD;
1845  pathToDynCoeffMHD=a_pathToImg.c_str();
1846  pathToDynCoeffMHD.append("_par");
1847  if(IntfWriteMHD(pathToDynCoeffMHD, lpath_to_images, Img_fields, ap_ID, vb) )
1848  {
1849  Cerr("***** IntfWriteImgDynCoeffFile()-> Error : while trying to write the interfile header '"<< a_pathToImg << "' !" << endl);
1850  return 1;
1851  }
1852  }
1853 
1854  // Write interfile image
1855 
1856  // Set dimensions
1857  uint32_t dims[2];
1858  dims[0] = a_nbParImgs;
1859  dims[1] = ap_ID->GetNbVoxXYZ();
1860 
1861  if(IntfWriteImage(lpath_to_images, a2p_ImgMatrix, dims, vb) )
1862  {
1863  Cerr("***** IntfWriteImgDynCoeffFile()-> Error : while trying to write the interfile image '"<< a_pathToImg << "' !" << endl);
1864  return 1;
1865  }
1866 
1867  return 0;
1868 }
1869 
1870 
1871 
1872 
1873 // =====================================================================
1874 // ---------------------------------------------------------------------
1875 // ---------------------------------------------------------------------
1876 // =====================================================================
1877 /*
1878  \fn IntfWriteImgFile
1879  \param a_pathToImg : string containing the path to the image basename
1880  \param a4p_ImgMatrix : 4 dimensional image matrix which contains the image to write.
1881  \param ap_ID : Provide the Image dimensions object containing reconstruction dimensions
1882  \param vb : verbosity
1883  \brief Main function dedicated to Interfile 6D (dynamic dims + 3D ) image writing
1884  \details Call the main functions dedicated to Interfile header and data writing
1885  Depending on the output writing mode (stored in sOutputManager),
1886  One image with one file and one will be created for the whole dynamic image
1887  or a metaheader and associated multiple 3D image raw file/headers will be generated
1888  \todo Get metadata from a Intf_fields object ?
1889  (useful to transfer keys from read images to written images)
1890  \return 0 if success, positive value otherwise.
1891 */
1892 int IntfWriteImgFile(const string& a_pathToImg, FLTNB**** a4p_ImgMatrix, oImageDimensionsAndQuantification* ap_ID, int vb)
1893 {
1894  if (vb>=3) Cout("IntfWriteImgFile (static/dynamic image) ..." << endl);
1895 
1896  Intf_fields Img_fields; //TODO keep refs to Intf_fields in case we need it later (transfer keys from read images to written images)
1897  IntfKeySetFieldsOutput(&Img_fields, ap_ID);
1898 //HDR copy hin the line above, the content of the header in ap_ID into one new field of Img_fields (to be created).
1899 
1900  // Get input from user about how dynamic files should be written
1901  // (one global image file, or separate image file for each frame/gates).
1903 
1904  //Set image names
1905  vector<string> lpath_to_images;
1906 
1907  if(Img_fields.nb_dims <=3 || p_outputMgr->MergeDynImages() == true) // One file to write
1908  {
1909  lpath_to_images.push_back(a_pathToImg);
1910 
1911  if(IntfWriteHeaderMainData(a_pathToImg, Img_fields, vb) )
1912  {
1913  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile header for'"<< lpath_to_images[0] << "' !" << endl);
1914  return 1;
1915  }
1916  }
1917  else // Case several files
1918  {
1919 
1920  int idx_file = 0;
1921 
1922  for(int fr=0 ; fr<ap_ID->GetNbTimeFrames() ; fr++)
1923  for(int rg=0 ; rg<ap_ID->GetNbRespGates() ; rg++)
1924  for(int cg=0 ; cg<ap_ID->GetNbCardGates() ; cg++)
1925  {
1926 
1927  //lpath_to_images[idx_file] = a_pathToImg;
1928 
1929  lpath_to_images.push_back(a_pathToImg);
1930 
1931  // Add a suffix for time frames if more than 1
1932  if (ap_ID->GetNbTimeFrames() > 1)
1933  {
1934  stringstream ss; ss << fr + 1;
1935  lpath_to_images[idx_file].append("_fr").append(ss.str());
1936  }
1937  // Add a suffix for respiratory gates if more than 1
1938  if (ap_ID->GetNbRespGates() > 1)
1939  {
1940  stringstream ss; ss << rg + 1;
1941  lpath_to_images[idx_file].append("_rg").append(ss.str());
1942  }
1943  // Add a suffix for cardiac gates if more than 1
1944  if (ap_ID->GetNbCardGates() > 1)
1945  {
1946  stringstream ss; ss << cg + 1;
1947  lpath_to_images[idx_file].append("_cg").append(ss.str());
1948  }
1949 
1950  // Write header specific to this image
1951  string path_to_hdr = lpath_to_images[idx_file];
1952  string path_to_img = lpath_to_images[idx_file];
1953 
1954  // As the content will be appended, make sure there is no existing file with such name
1955  // remove it otherwise
1956  ifstream fcheck(path_to_hdr.append(".hdr").c_str());
1957  if(fcheck.good())
1958  {
1959  fcheck.close();
1960  #ifdef _WIN32
1961  string dos_instruction = "del " + path_to_hdr;
1962  system(dos_instruction.c_str());
1963  #else
1964  remove(path_to_hdr.c_str());
1965  #endif
1966  }
1967 
1968  ofstream ofile(path_to_hdr.c_str(), ios::out | ios::app);
1969  ofile << setprecision(std::numeric_limits<FLTNB>::digits10 +1);
1970  ofile << "!INTERFILE := " << endl;
1971 
1972 
1973  ofile << "!name of data file := " << GetFileFromPath(path_to_img).append(".img") << endl;
1974  ofile << "imagedata byte order := " << IntfKeyGetEndianStr(User_Endianness) << endl;
1975  ofile << "!data offset in bytes := " << 0 << endl;
1976  ofile << "patient name:= " << GetFileFromPath(path_to_img) << endl;
1977  ofile << "!type of data := Dynamic" << endl; // /!\ Dynamic by default in order to be readable from ImageJ OpenMed plugin
1978 
1979  if(IntfWriteHeaderImgData(ofile, Img_fields, vb) )
1980  {
1981  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile header for'"<< lpath_to_images[idx_file] << "' !" << endl);
1982  return 1;
1983  }
1984 
1985  // Write image duration related data (this information will be replicated on each gated image)
1986  ofile << "image duration (sec) := " << (ap_ID->GetFrameTimeStopInSec(0, fr) - ap_ID->GetFrameTimeStartInSec(0, fr) )<< endl;
1987 
1988  // same start time for all gates
1989  ofile << "image start time (sec) := " << ap_ID->GetFrameTimeStartInSec(0, fr) << endl;
1990 
1991  ofile << "!END OF INTERFILE := " << endl;
1992 
1993  // Copy content of the input data header file into the interfile header
1995  {
1996  Cerr("***** IntfWriteImgFile()-> Error: while copying input data header information into output interfile header '" << path_to_hdr << "' !" << endl);
1997  return 1;
1998  }
1999 
2000  ofile.close();
2001 
2002  idx_file++;
2003 
2004  }
2005 
2006  // Write meta header
2007  if(IntfWriteMHD(a_pathToImg, lpath_to_images, Img_fields, ap_ID, vb) )
2008  {
2009  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile header '"<< a_pathToImg << "' !" << endl);
2010  return 1;
2011  }
2012 
2013  }
2014 
2015  // Write interfile image
2016 
2017  // Set dimensions
2018  uint32_t dims[4];
2019  dims[0] = ap_ID->GetNbTimeFrames();
2020  dims[1] = ap_ID->GetNbRespGates();
2021  dims[2] = ap_ID->GetNbCardGates();
2022  dims[3] = ap_ID->GetNbVoxXYZ();
2023 
2024  if(IntfWriteImage(lpath_to_images, a4p_ImgMatrix, dims, vb) )
2025  {
2026  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile image '"<< a_pathToImg << "' !" << endl);
2027  return 1;
2028  }
2029 
2030  return 0;
2031 }
2032 
2033 
2034 
2035 
2036 
2037 // =====================================================================
2038 // ---------------------------------------------------------------------
2039 // ---------------------------------------------------------------------
2040 // =====================================================================
2041 
2042 int IntfWriteWholeDynBasisCoeffImgFile(const string& a_pathToImg, FLTNB**** a4p_ImgMatrix, oImageDimensionsAndQuantification* ap_ID, int vb)
2043 {
2044  if (vb>=3) Cout("IntfWriteDynBasisCoefImgFile (dynamic basis coefficients image) ..." << endl);
2045 
2046  Intf_fields Img_fields; //TODO keep refs to Intf_fields in case we need it later (transfer keys from read images to written images)
2047  IntfKeySetFieldsOutput(&Img_fields, ap_ID);
2048 
2049  // Set image names
2050  vector<string> lpath_to_images;
2051 
2052  // For to write multiple files and a metaheader
2053  int idx_file = 0;
2054  // Loop on basis functions
2055  for (int tbf=0 ; tbf<ap_ID->GetNbTimeBasisFunctions() ; tbf++)
2056  {
2057  for (int rbf=0 ; rbf<ap_ID->GetNbRespBasisFunctions() ; rbf++)
2058  {
2059  for (int cbf=0 ; cbf<ap_ID->GetNbCardBasisFunctions() ; cbf++)
2060  {
2061  lpath_to_images.push_back(a_pathToImg);
2062  // Add a suffix for time basis functions if more than 1
2063  if (ap_ID->GetNbTimeBasisFunctions() > 1)
2064  {
2065  stringstream ss; ss << tbf + 1;
2066  lpath_to_images[idx_file].append("_tbf").append(ss.str());
2067  }
2068  // Add a suffix for respiratory basis functions if more than 1
2069  if (ap_ID->GetNbRespBasisFunctions() > 1)
2070  {
2071  stringstream ss; ss << rbf + 1;
2072  lpath_to_images[idx_file].append("_rbf").append(ss.str());
2073  }
2074  // Add a suffix for cardiac basis functions if more than 1
2075  if (ap_ID->GetNbCardBasisFunctions() > 1)
2076  {
2077  stringstream ss; ss << cbf + 1;
2078  lpath_to_images[idx_file].append("_cbf").append(ss.str());
2079  }
2080  // Path to the image and header files
2081  string path_to_hdr = lpath_to_images[idx_file];
2082  string path_to_img = lpath_to_images[idx_file];
2083  // As the content will be appended, make sure there is no existing file with such name
2084  // remove it otherwise
2085  ifstream fcheck(path_to_hdr.append(".hdr").c_str());
2086  if (fcheck.good())
2087  {
2088  fcheck.close();
2089  #ifdef _WIN32
2090  string dos_instruction = "del " + path_to_hdr;
2091  system(dos_instruction.c_str());
2092  #else
2093  remove(path_to_hdr.c_str());
2094  #endif
2095  }
2096  // Write header specific to this image
2097  ofstream ofile(path_to_hdr.c_str(), ios::out | ios::app);
2098  ofile << setprecision(std::numeric_limits<FLTNB>::digits10 +1);
2099  ofile << "!INTERFILE := " << endl;
2100  ofile << "!name of data file := " << GetFileFromPath(path_to_img).append(".img") << endl;
2101  ofile << "imagedata byte order := " << IntfKeyGetEndianStr(User_Endianness) << endl;
2102  ofile << "!data offset in bytes := " << 0 << endl;
2103  ofile << "patient name:= " << GetFileFromPath(path_to_img) << endl;
2104  ofile << "!type of data := Dynamic" << endl; // /!\ Dynamic by default in order to be readable from ImageJ OpenMed plugin
2105  if (IntfWriteHeaderImgData(ofile, Img_fields, vb) )
2106  {
2107  Cerr("***** IntfWriteWholeDynBasisCoeffImgFile()-> Error : while trying to write the interfile header for'"<< lpath_to_images[idx_file] << "' !" << endl);
2108  return 1;
2109  }
2110  // Write image duration and start related data (this information has no sense here, so put 1 and 0)
2111  ofile << "image duration (sec) := 1" << endl;
2112  ofile << "image start time (sec) := 0" << endl;
2113  ofile << "!END OF INTERFILE := " << endl;
2114  ofile.close();
2115  idx_file++;
2116  }
2117  }
2118  }
2119  // Set dimensions
2120  uint32_t dims[4];
2121  dims[0] = ap_ID->GetNbTimeBasisFunctions();
2122  dims[1] = ap_ID->GetNbRespBasisFunctions();
2123  dims[2] = ap_ID->GetNbCardBasisFunctions();
2124  dims[3] = ap_ID->GetNbVoxXYZ();
2125  // Write interfile image
2126  if (IntfWriteImage(lpath_to_images, a4p_ImgMatrix, dims, vb) )
2127  {
2128  Cerr("***** IntfWriteWholeDynBasisCoeffImgFile()-> Error : while trying to write the interfile image '"<< a_pathToImg << "' !" << endl);
2129  return 1;
2130  }
2131 
2132  return 0;
2133 }
2134 
2135 
2136 // =====================================================================
2137 // ---------------------------------------------------------------------
2138 // ---------------------------------------------------------------------
2139 // =====================================================================
2140 /*
2141  \fn IntfWriteImgFile
2142  \param a_pathToImg : string containing the path to the image basename
2143  \param a4p_ImgMatrix : 4 dimensional image matrix which contains the image to write.
2144  \param ap_ID : Provide the Image dimensions object containing reconstruction dimensions
2145  \param vb : verbosity
2146  \brief Main function dedicated to Interfile 6D (dynamic dims + 3D ) image writing
2147  \details Call the main functions dedicated to Interfile header and data writing
2148  Depending on the output writing mode (stored in sOutputManager),
2149  One image with one file and one will be created for the whole dynamic image
2150  or a metaheader and associated multiple 3D image raw file/headers will be generated
2151  \todo Get metadata from a Intf_fields object ?
2152  (useful to transfer keys from read images to written images)
2153  \return 0 if success, positive value otherwise.
2154 
2155 int IntfWriteSensitivityImg(const string& a_pathToImg, FLTNB**** a4p_ImgMatrix, oImageDimensionsAndQuantification* ap_ID, int vb)
2156 {
2157  if(vb >= 2) Cout("IntfWriteSensitivityImg ..." << endl);
2158 
2159  Intf_fields Img_fields; //TODO keep refs to Intf_fields in case we need it later (transfer keys from read images to written images)
2160  IntfKeySetFieldsOutput(&Img_fields, ap_ID);
2161 
2162  // Get input from user about how dynamic files should be written
2163  // (one global image file, or separate image file for each frame/gates).
2164  sOutputManager* p_outputMgr = sOutputManager::GetInstance();
2165 
2166  //Set image names
2167  vector<string> lpath_to_images;
2168 
2169  // By default, sensitivity image are written in one file One file to write
2170  {
2171  lpath_to_images.push_back(a_pathToImg);
2172 
2173  if(IntfWriteHeaderMainData(a_pathToImg, Img_fields, ap_ID, vb) )
2174  {
2175  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile header for'"<< lpath_to_images[0] << "' !" << endl);
2176  return 1;
2177  }
2178  }
2179 
2180  // Write interfile image
2181 
2182  // Set dimensions
2183  uint32_t dims[4];
2184  dims[0] = ap_ID->GetNbTimeFrames();
2185  dims[1] = ap_ID->GetNbRespGates();
2186  dims[2] = ap_ID->GetNbCardGates();
2187  dims[3] = ap_ID->GetNbVoxXYZ();
2188 
2189  if(IntfWriteImage(lpath_to_images, a4p_ImgMatrix, dims, vb) )
2190  {
2191  Cerr("***** IntfWriteImgFile()-> Error : while trying to write the interfile image '"<< a_pathToImg << "' !" << endl);
2192  return 1;
2193  }
2194 
2195  return 0;
2196 }
2197 */
2198 
2199 
2200 // =====================================================================
2201 // ---------------------------------------------------------------------
2202 // ---------------------------------------------------------------------
2203 // =====================================================================
2204 /*
2205  \fn IntfIsMHD
2206  \param a_pathToFile : String containing path to an Interfile header
2207  \param ap_lPathToImgs : pointer to a list of strings containing the path to the different images
2208  \brief Check if the string in argument contains the path to a Interfile metaheader
2209  \details Check for '!total number of data sets' key, and the associated image file names
2210  If the file is metaheader, the names of image file header will be returned in ap_lPathToImgs list
2211  It not, the file is a single header, that we add to the list as solo file
2212  \return 1 if we have a metaheader, 0 if not, negative value if error.
2213 */
2214 int IntfIsMHD(string a_pathToFile, vector<string> &ap_lPathToImgs)
2215 {
2216  // Create file object and check existence
2217  ifstream hfile(a_pathToFile.c_str(), ios::in);
2218  string line;
2219 
2220  if(hfile)
2221  {
2222  // Read until the end of file and check if we have a '!total number of data sets' key
2223  while(!hfile.eof())
2224  {
2225  getline(hfile, line);
2226 
2227  // Check if empty line.
2228  if(line.empty()) continue;
2229 
2230  // Initiate a Key which will recover the potential interfile data for each line
2231  Intf_key Key;
2232 
2233  // Process the Key at this line. Recover the Key, its counterpart in lower case, and the value in the structure Intf_key
2234  if (IntfRecoverKey(&Key, line) )
2235  {
2236  Cerr("***** IntfIsMHD()-> Error : couldn't correctly read interfile key '"<< line << "' !" << endl);
2237  return -1;
2238  }
2239 
2240  // --- Check if a total number of data set if provided
2241  if(IntfCheckKeyMatch(Key, "total number of data set") ||
2242  IntfCheckKeyMatch(Key, "total number of data sets") ||
2243  IntfCheckKeyMatch(Key, "!total number of data set") ||
2244  IntfCheckKeyMatch(Key, "!total number of data sets"))
2245  {
2246  // File is MHD
2247 
2248  uint16_t nb_datasets = 0;
2249  if(ConvertFromString(Key.kvalue , &nb_datasets) )
2250  {
2251  Cerr("***** IntfIsMHD()-> Error when trying to read data from 'total number of data set' key. Recovered value = " << Key.kvalue << endl);
2252  return -1;
2253  }
2254 
2255  // Get the image file path from the following lines
2256  int file_idx = 0;
2257  stringstream ss;
2258  ss << (file_idx+1);
2259  string file_key = "";
2260  string file_keyp = "%";
2261  string file_key_space = "";
2262  string file_keyp_space = "%";
2263  file_key_space.append("data set [").append(ss.str()).append("]");
2264  file_keyp_space.append("data set [").append(ss.str()).append("]");
2265  file_key.append("data set[").append(ss.str()).append("]");
2266  file_keyp.append("data set[").append(ss.str()).append("]");
2267 
2268  string path_to_mhd_dir = GetPathOfFile(a_pathToFile);
2269 
2270  while(!hfile.eof())
2271  {
2272  // Read the following lines...
2273  getline(hfile, line);
2274 
2275  // Check if empty line.
2276  if(line.empty()) continue;
2277 
2278  // Process the Key at this line. Recover the Key, its counterpart in lower case, and the value in the structure Intf_key
2279  if (IntfRecoverKey(&Key, line) )
2280  {
2281  Cerr("***** IntfIsMHD()-> Error : couldn't correctly read interfile key '"<< line << "' !" << endl);
2282  return -1;
2283  }
2284 
2285  if(IntfCheckKeyMatch(Key, file_key) ||
2286  IntfCheckKeyMatch(Key, file_keyp) ||
2287  IntfCheckKeyMatch(Key, file_key_space) ||
2288  IntfCheckKeyMatch(Key, file_keyp_space))
2289  {
2290  ap_lPathToImgs.push_back(GetPathOfFile(a_pathToFile));
2291  if(IntfKeyIsArray(Key))
2292  {
2293  string elts_str[3];
2294 
2295  // First, check we have the correct number of elts
2296  int nb_elts = IntfKeyGetArrayNbElts(Key);
2297  if(nb_elts<0)
2298  {
2299  Cerr("***** IntfIsMHD()-> Error : when trying to read Interfile array key: '"<< line << "' !" << endl);
2300  return -1;
2301  }
2302  else if(nb_elts != 3)
2303  {
2304  Cerr("***** IntfIsMHD()-> Error : when trying to read Interfile array key: '"<< line << "' !" << endl);
2305  Cerr(" 3 elements are expected following the format {xxx, path_to_the_img_file, xxx} (xxx = ignored data)" << endl);
2306  return -1;
2307  }
2308 
2309  if(IntfKeyGetArrayElts(Key, elts_str) )
2310  {
2311  Cerr("***** IntfIsMHD()-> Error : when trying to read Interfile array key: '"<< line << "' !" << endl);
2312  return -1;
2313  }
2314  ap_lPathToImgs.at(file_idx).append(elts_str[1]);
2315  }
2316  else
2317  ap_lPathToImgs.at(file_idx).append(Key.kvalue);
2318 
2319  // Compute next key to recover
2320  file_idx++;
2321  stringstream ss;
2322  ss << (file_idx+1);
2323  file_key = "";
2324  file_keyp = "%";
2325  file_key.append("data set [").append(ss.str()).append("]");
2326  file_keyp.append("data set [").append(ss.str()).append("]");
2327 
2328  }
2329  }
2330 
2331  // Check we have a correct nb of images
2332  if(nb_datasets != ap_lPathToImgs.size())
2333  {
2334  // error if not found
2335  Cerr("***** IntfIsMHD()-> The number of recovered file in the metaheader ("<<ap_lPathToImgs.size()<<")");
2336  Cerr("does not correspond to the expected number of datasets ("<<nb_datasets<<")!"<< endl);
2337  return -1;
2338  }
2339  else
2340  // Normal end, file is metaheader
2341  return 1;
2342  }
2343  }
2344  }
2345  else
2346  {
2347  Cerr("***** IntfIsMHD()-> Error : couldn't read header file '"<< a_pathToFile << "' !" << endl);
2348  return -1;
2349  }
2350 
2351  // If we reach this, the file is NOT considered as metaheader the 'total number of data set' key was not found
2352  // Add the to the list as unique header file
2353  ap_lPathToImgs.push_back(a_pathToFile);
2354  return 0;
2355 }
2356 
2357 
2358 
2359 
2360 // =====================================================================
2361 // ---------------------------------------------------------------------
2362 // ---------------------------------------------------------------------
2363 // =====================================================================
2364 /*
2365  \fn IntfWriteMHD
2366  \param a_path : path to the Meta interfile header to write
2367  \param ap_lPathToImgs : pointer to a list of strings containing the path to the different images
2368  \param a_IntfF : Structure containing interfile keys of the image to write
2369  \param ap_ID : oImageDimensionsAndQuantification object containing additional infos about reconstruction
2370  \param vb : verbosity
2371  \brief Write an Interfile meta header at the path provided in parameter, using the field stack provided in parameter.
2372  \todo keys for multi bed
2373  \return 0 if success, positive value otherwise.
2374 */
2375 int IntfWriteMHD( const string& a_pathToMhd,
2376  const vector<string>& ap_lPathToImgs,
2377  Intf_fields a_IntfF,
2379  int vb)
2380 {
2381  // Get file and check existence
2382  string path_to_mhd_file = a_pathToMhd;
2383  path_to_mhd_file.append(".mhd");
2384  ofstream ofile(path_to_mhd_file.c_str(), ios::out);
2385  ofile << setprecision(std::numeric_limits<FLTNB>::digits10 +1);
2386 
2387  if(ofile)
2388  {
2390 
2391  // todo fields for multi-beds ?
2392 
2393  ofile << "!INTERFILE := " << endl;
2394  //ofile << "% MetaHeader written by CASToRv1.0 := " << end;
2395  ofile << endl << "!GENERAL DATA := " << endl;
2396  ofile << "data description:=image" << endl;
2397  ofile << "!originating system := " << p_scanMgr->GetScannerName() << endl;
2398 
2399  // Number of bed positions
2400  if (a_IntfF.nb_bed_positions>1)
2401  {
2402  ofile << "number of bed positions := " << a_IntfF.nb_bed_positions << endl;
2403  ofile << "!study duration (sec) := " << a_IntfF.study_duration << endl;
2404  }
2405  // Relative position of each bed
2406  else ofile << "horizontal bed relative position (mm) := " << ap_ID->GetBedPosition(0) << endl;
2407 
2408  ofile << endl << "%DATA MATRIX DESCRIPTION:=" << endl;
2409  ofile << "number of time frames := " << a_IntfF.nb_time_frames << endl;
2410  ofile << "number of time windows := " << a_IntfF.nb_resp_gates *
2411  a_IntfF.nb_card_gates << endl;
2412  ofile << "number of respiratory gates := " << a_IntfF.nb_resp_gates << endl;
2413  ofile << "number of cardiac gates := " << a_IntfF.nb_card_gates << endl;
2414 
2415  ofile << endl << "%DATA SET DESCRIPTION:="<< endl;
2416 
2417  uint16_t nb_imgs = a_IntfF.nb_time_frames * a_IntfF.nb_resp_gates * a_IntfF.nb_card_gates ;
2418  ofile << "!total number of data sets:=" << nb_imgs << endl;
2419 
2420  // Check we have the right number of strings
2421  if(ap_lPathToImgs.size() != nb_imgs)
2422  {
2423  Cerr("***** IntfWriteMHD()-> Error : nb of provided string inconsistent with the expected number of dynamic images: '"<< nb_imgs << "' !" << endl);
2424  ofile.close();
2425  return 1;
2426  }
2427 
2428  for(int ds=0 ; ds<nb_imgs ; ds++)
2429  {
2430  string path_to_header = ap_lPathToImgs.at(ds);
2431  ofile << "%data set ["<<ds+1<<"]:={0," << GetFileFromPath(path_to_header).append(".hdr") << ",UNKNOWN}"<< endl;
2432  }
2433  }
2434  else
2435  {
2436  Cerr("***** IntfWriteMHD()-> Error : couldn't find output Metaheader interfile '"<< path_to_mhd_file << "' !" << endl);
2437  return 1;
2438  }
2439 
2440  ofile.close();
2441 
2442 
2443 
2444  return 0;
2445 }
2446 
2447 
2448 
2449 
2450 // =====================================================================
2451 // ---------------------------------------------------------------------
2452 // ---------------------------------------------------------------------
2453 // =====================================================================
2454 /*
2455  \fn IntfReadHeader
2456  \param const string& a_pathToHeaderFile
2457  \param Intf_fields* ap_IF
2458  \param int vb : Verbosity level
2459  \brief Read an Interfile header
2460  \details Initialize all mandatory fields from the Intf_fields structure passed in parameter with the related interfile key from the image interfile header
2461  \todo Check everything work for Interfile 3.3 version, extended interfile, etc.
2462  \todo Several keys from Intf_fields still needs some management
2463  \todo Deal with sinogram in interfile format (matrix size keys)
2464  \todo orientation related keys
2465  \todo time windows, energy windows keys
2466  \todo check start angle key
2467  \todo Initializations & checks at the end (ok for slice_thickness, check slice_spacing, etc..)
2468  \todo Specification of this function for projection data (currently we consider the interfile is a 3D image)
2469  \return 0 if success, positive value otherwise.
2470 */
2471 int IntfReadHeader(const string& a_pathToHeaderFile, Intf_fields* ap_IF, int vb)
2472 {
2473  if (vb >= 4)
2474  {
2475  Cout("--------------------------------------------------------------- " << endl);
2476  Cout("IntfReadHeader()-> Start reading header interfile " << a_pathToHeaderFile << endl);
2477  Cout("--------------------------------------------------------------- " << endl);
2478  }
2479 
2480 
2481  // Get file and check existence
2482  ifstream input_file(a_pathToHeaderFile.c_str(), ios::in);
2483  string line;
2484 
2485  if(input_file)
2486  {
2487  // Read until the end of file
2488  while(!input_file.eof())
2489  {
2490  getline(input_file, line);
2491 
2492  // Check if empty line.
2493  if(line.empty())
2494  continue;
2495 
2496  // Initiate a Key which will recover the potential interfile data for each line
2497  Intf_key Key;
2498 
2499  // Process the Key at this line. Recover the Key, its counterpart in lower case, and the value in the structure Intf_key
2500  if (IntfRecoverKey(&Key, line) )
2501  {
2502  Cerr("***** ReadIntfHeader()-> Error : couldn't correctly read interfile key '"<< line << "' !" << endl);
2503  return 1;
2504  }
2505 
2506  if (vb >=10) Cout("ReadIntfHeader()-> Key " << Key.kcase << endl);
2507 
2508 
2509  // --- From there, find key Id and process result ---
2510 
2511  // Check version. Warning message if not 3.3 / Castor keys ?
2512  if(IntfCheckKeyMatch(Key, "version of keys"))
2513  {
2514  //if(Key.kvalue != 3.3 ||
2515  // Key.kvalue != CASToRv1.0)
2516  // Cout("***** ReadIntfHeader()-> Warning : Unexpected version of Interfile keys found: " << Key.kvalue << " (supported version = 3.3)" << endl);
2517  }
2518 
2519 
2520  // --- path to image file
2521  // "name of data file" / Intf_fields.path_to_image
2522  else if(IntfCheckKeyMatch(Key, "name of data file"))
2523  {
2524  // Check key isn't empty, throw error otherwise
2525  if ( Key.kvalue.size()>0 )
2526  {
2527  // Look for path separator
2528  if ( Key.kvalue.find(OS_SEP) != string::npos )
2529  {
2530  // Assuming we have an absolute path
2531  ap_IF->path_to_image = Key.kvalue;
2532  }
2533  else
2534  {
2535  // Assuming we just have the image name
2536  // -> use the relative path of the header file, image should be in the same dir
2537  string header_file_directory = GetPathOfFile(a_pathToHeaderFile);
2538  ap_IF->path_to_image = header_file_directory.append(Key.kvalue);
2539  }
2540  }
2541  else
2542  {
2543  Cerr("***** ReadIntfHeader()-> Error : path to interfile image file is empty !" << endl);
2544  return 1;
2545  }
2546  }
2547 
2548 
2549  // --- Little or big endian
2550  // "imagedata byte order" / Intf_fields.endianness
2551  else if(IntfCheckKeyMatch(Key, "imagedata byte order"))
2552  {
2553  string endianness;
2554  IntfToLowerCase(&Key.kvalue); // whole string in low caps
2555 
2556  endianness = Key.kvalue;
2557 
2558  if (endianness == "littleendian")
2559  ap_IF->endianness = 1;
2560  else // Big endian by default
2561  ap_IF->endianness = INTF_BIG_ENDIAN;
2562  }
2563 
2564 
2565  // --- Data offset using "data starting block"
2566  // "data starting block" & "data offset in bytes" / Intf_fields.data_offset
2567  else if(IntfCheckKeyMatch(Key, "data starting block"))
2568  {
2569  // Throw warning indicating that the data offset has already been set
2570  if(ap_IF->data_offset>0 )
2571  {
2572  Cerr("***** ReadIntfHeader()-> Warning : data_offset value was already set to " << ap_IF->data_offset << endl);
2573  Cerr("***** The header may contain both 'data offset in bytes' and 'data starting block' fields " << endl);
2574  }
2575 
2576  if(ConvertFromString(Key.kvalue , &ap_IF->data_offset) )
2577  {
2578  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'data starting block' key. Recovered value = " << Key.kvalue << endl);
2579  return 1;
2580  }
2581 
2582  ap_IF->data_offset*= 2048L;
2583  }
2584 
2585 
2586  // --- Data offset using "data offset in bytes"
2587  // "data starting block" & "data offset in bytes" / Intf_fields.data_offset
2588  else if(IntfCheckKeyMatch(Key, "data offset in bytes"))
2589  {
2590  // Throw warning indicating that the data offset has already been set
2591  if(ap_IF->data_offset>0 )
2592  {
2593  Cerr("***** ReadIntfHeader()-> Warning : data_offset value was already set to " << ap_IF->data_offset << endl);
2594  Cerr("***** The header may contain both 'data offset in bytes' and 'data starting block' fields " << endl);
2595  }
2596 
2597  if(ConvertFromString(Key.kvalue , &ap_IF->data_offset) )
2598  {
2599  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'data offset in bytes' key. Recovered value = " << Key.kvalue << endl);
2600  return 1;
2601  }
2602  }
2603 
2604 
2605  // --- Type of each pixel/voxel data
2606  // "number format" / Intf_fields.nb_format
2607  else if(IntfCheckKeyMatch(Key, "number format"))
2608  {
2609  if(ConvertFromString(Key.kvalue , &ap_IF->nb_format) )
2610  {
2611  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number format' key. Recovered value = " << Key.kvalue << endl);
2612  return 1;
2613  }
2614  }
2615 
2616  // --- Originating system (the scanner)
2617  // "originating system" / Intf_fields.originating_system
2618  else if(IntfCheckKeyMatch(Key, "originating system"))
2619  {
2620  if(ConvertFromString(Key.kvalue , &ap_IF->originating_system) )
2621  {
2622  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'originating system' key. Recovered value = " << Key.kvalue << endl);
2623  return 1;
2624  }
2625  }
2626 
2627  // --- Bed relative position in mm
2628  // "bed relative position (mm)" / Intf_fields.bed_relative_position
2629  else if(IntfCheckKeyMatch(Key, "horizontal bed relative position (mm)"))
2630  {
2631  if(ConvertFromString(Key.kvalue , &ap_IF->bed_relative_position) )
2632  {
2633  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'bed relative position (mm)' key. Recovered value = " << Key.kvalue << endl);
2634  return 1;
2635  }
2636  }
2637 
2638  // --- Width
2639  // "matrix size [1]" / Intf_fields.mtx_size[0]
2640  else if(IntfCheckKeyMatch(Key, "matrix size [1]"))
2641  {
2642  if(ConvertFromString(Key.kvalue , &ap_IF->mtx_size[0]) )
2643  {
2644  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'matrix size [1]' key. Recovered value = " << Key.kvalue << endl);
2645  return 1;
2646  }
2647  }
2648 
2649  // --- Height
2650  // "matrix size [2]" / Intf_fields.mtx_size[1]
2651  else if(IntfCheckKeyMatch(Key, "matrix size [2]"))
2652  {
2653  if(ConvertFromString(Key.kvalue , &ap_IF->mtx_size[1]) )
2654  {
2655  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'matrix size [2]' key. Recovered value = " << Key.kvalue << endl);
2656  return 1;
2657  }
2658  }
2659 
2660  // --- Slices : Tomographic image
2661  // "matrix size [3]" / Intf_fields.mtx_size[2]
2662  else if(IntfCheckKeyMatch(Key, "matrix size [3]"))
2663  {
2664  // Check if we have an array key
2665  if (IntfKeyIsArray(Key))
2666  {
2667  //TODO : perhaps more convenient to use vector to deal with data whose matrix size [3] has more than 1 element (eg: sinograms)
2668  // It will depend how we decide to handle sinogram reading
2669  // For now, throw error by default as there is no implementation yet
2670 
2671  // Throw error by default
2672  Cerr("***** ReadIntfHeader()-> Array reading for 'matrix size [3]' key not implemented yet. Single value expected." << endl);
2673  return 1;
2674  }
2675  else
2676  {
2677  if(ConvertFromString(Key.kvalue , &ap_IF->mtx_size[2]) )
2678  {
2679  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'matrix size [3]' key. Recovered value = " << Key.kvalue << endl);
2680  return 1;
2681  }
2682  }
2683  }
2684 
2685  // --- Read nb frames or other.
2686  // "matrix size [4]" / Intf_fields.mtx_size[3]
2687  // Consistency with "number of time frames" : Priority to number of time frames if this has already been initialized
2688  // TODO : this may cause issue for implementation of sinogram management
2689  else if(IntfCheckKeyMatch(Key, "matrix size [4]"))
2690  {
2691  // Could be sinogram or dynamic related (nb time frames).
2692  // Dynamic reconstructions : consistency with "number of frames"
2693 
2694  if (IntfKeyIsArray(Key))
2695  {
2696  //TODO : perhaps more convenient to use vector to deal with data whose matrix size [4] has more than 1 element (eg: sinograms)
2697  // It will depend how we decide to handle sinogram reading
2698  // For now, throw error by default as there is no implementation yet
2699 
2700  // Throw error by default
2701  Cerr("***** ReadIntfHeader()-> Array reading for 'matrix size [4]' key not implemented yet. Single value expected." << endl);
2702  return 1;
2703  }
2704  else
2705  {
2706  // Check if number time frames has already been initialized
2707  if(ap_IF->nb_time_frames>1)
2708  {
2709  ap_IF->mtx_size[3] = ap_IF->nb_time_frames;
2710  Cerr("***** ReadIntfHeader()-> WARNING : both 'number of time frames' and 'matrix size [4]' keys have been provided");
2711  Cerr(" 'number of time frames' selected by default" << endl);
2712  }
2713  else if( ConvertFromString(Key.kvalue , &ap_IF->mtx_size[3]) )
2714  {
2715  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'matrix size [4]' key. Recovered value = " << Key.kvalue << endl);
2716  return 1;
2717  }
2718  }
2719  }
2720 
2721 
2722  // --- Related to sinogram (not implemented yet)
2723  // "matrix size [5]" / Intf_fields.mtx_size[4]
2724  else if(IntfCheckKeyMatch(Key, "matrix size [5]"))
2725  {
2726  // Could be sinogram or dynamic related
2727  if (IntfKeyIsArray(Key))
2728  {
2729  //TODO : perhaps more convenient to use vector to deal with data whose matrix size [5] has more than 1 element (eg: sinograms)
2730  // It will depend how we decide to handle sinogram reading
2731  // For now, throw error by default as there is no implementation yet
2732 
2733  // Throw error by default
2734  Cerr("***** ReadIntfHeader()-> Array reading for 'matrix size [5]' key not implemented yet. Single value expected." << endl);
2735  return 1;
2736  }
2737  else
2738  {
2739  if(ConvertFromString(Key.kvalue , &ap_IF->mtx_size[4]) )
2740  {
2741  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'matrix size [5]' key. Recovered value = " << Key.kvalue << endl);
2742  return 1;
2743  }
2744  }
2745  }
2746 
2747  // --- Related to sinogram (not implemented yet)
2748  // "matrix size [6]" / Intf_fields.mtx_size[5]
2749  else if(IntfCheckKeyMatch(Key, "matrix size [6]"))
2750  {
2751  // Could be sinogram or dynamic related
2752  if (IntfKeyIsArray(Key))
2753  {
2754  //TODO : perhaps more convenient to use vector to deal with data whose matrix size [6] has more than 1 element (eg: sinograms)
2755  // It will depend how we decide to handle sinogram reading
2756  // For now, throw error by default as there is no implementation yet
2757 
2758  // Throw error by default
2759  Cerr("***** ReadIntfHeader()-> Array reading for 'matrix size [6]' key not implemented yet. Single value expected." << endl);
2760  return 1;
2761  }
2762  else
2763  {
2764  if(ConvertFromString(Key.kvalue , &ap_IF->mtx_size[5]) )
2765  {
2766  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'matrix size [6]' key. Recovered value = " << Key.kvalue << endl);
2767  return 1;
2768  }
2769  }
2770  }
2771 
2772  // --- Related to sinogram (not implemented yet)
2773  // "matrix size [7]" / Intf_fields.mtx_size[6]
2774  else if(IntfCheckKeyMatch(Key, "matrix size [7]"))
2775  {
2776  // Could be sinogram related
2777  if (IntfKeyIsArray(Key))
2778  {
2779  //TODO : perhaps more convenient to use vector to deal with data whose matrix size [7] has more than 1 element (eg: sinograms)
2780  // It will depend how we decide to handle sinogram reading
2781  // For now, throw error by default as there is no implementation yet
2782 
2783  // Throw error by default
2784  Cerr("***** ReadIntfHeader()-> Array reading for 'matrix size [7]' key not implemented yet. Single value expected." << endl);
2785  return 1;
2786  }
2787  else
2788  {
2789  if( ConvertFromString(Key.kvalue , &ap_IF->mtx_size[6]) )
2790  {
2791  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'matrix size [7]' key. Recovered value = " << Key.kvalue << endl);
2792  return 1;
2793  }
2794  }
2795 
2796  }
2797 
2798  // --- Size voxel width
2799  // "scaling factor (mm/pixel) [1]" / Intf_fields.vox_size[0]
2800  else if(IntfCheckKeyMatch(Key, "scaling factor (mm/pixel) [1]")
2801  || IntfCheckKeyMatch(Key, "scale factor (mm/pixel) [1]"))
2802  {
2803  if(ConvertFromString(Key.kvalue , &ap_IF->vox_size[0]) )
2804  {
2805  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'scaling factor (mm/pixel) [1]' key. Recovered value = " << Key.kvalue << endl);
2806  return 1;
2807  }
2808  }
2809 
2810  // --- Size voxel height
2811  // "scaling factor (mm/pixel) [2]" / Intf_fields.vox_size[1]
2812  else if(IntfCheckKeyMatch(Key, "scaling factor (mm/pixel) [2]")
2813  || IntfCheckKeyMatch(Key, "scale factor (mm/pixel) [2]"))
2814  {
2815  if(ConvertFromString(Key.kvalue , &ap_IF->vox_size[1]) )
2816  {
2817  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'scaling factor (mm/pixel) [2]' key. Recovered value = " << Key.kvalue << endl);
2818  return 1;
2819  }
2820  }
2821 
2822  // --- Size voxel axial
2823  // "scaling factor (mm/pixel) [3]" / Intf_fields.vox_size[2]
2824  // Prefered over slice thickness by default
2825  else if(IntfCheckKeyMatch(Key, "scaling factor (mm/pixel) [3]")
2826  || IntfCheckKeyMatch(Key, "scale factor (mm/pixel) [3]"))
2827  {
2828  if(ConvertFromString(Key.kvalue , &ap_IF->vox_size[2]) )
2829  {
2830  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'scaling factor (mm/pixel) [3]' key. Recovered value = " << Key.kvalue << endl);
2831  return 1;
2832  }
2833  }
2834 
2835 
2836  // --- Voxel offset width
2837  // "first pixel offset (mm) [1]" / Intf_fields.vox_offset[0]
2838  else if(IntfCheckKeyMatch(Key, "first pixel offset (mm) [1]")
2839  || IntfCheckKeyMatch(Key, "origin (mm) [1]")
2840  || IntfCheckKeyMatch(Key, "offset [1]"))
2841  {
2842  if(ConvertFromString(Key.kvalue , &ap_IF->vox_offset[0]) )
2843  {
2844  Cerr("***** ReadIntfHeader()-> Error when trying to read data from one of the following key :" << endl);
2845  Cerr("***** 'first pixel offset (mm) [1]'" << Key.kvalue << endl);
2846  Cerr("***** 'origin (mm) [1]'"<< endl);
2847  Cerr("***** 'offset [1]'" << Key.kvalue << endl);
2848  Cerr("***** Recovered value = " << Key.kvalue << endl);
2849  return 1;
2850  }
2851  }
2852 
2853 
2854  // --- Voxel offset height
2855  // "first pixel offset (mm) [2]" / Intf_fields.vox_offset[1]
2856  else if(IntfCheckKeyMatch(Key, "first pixel offset (mm) [2]")
2857  || IntfCheckKeyMatch(Key, "origin (mm) [2]")
2858  || IntfCheckKeyMatch(Key, "offset [2]"))
2859  {
2860  if(ConvertFromString(Key.kvalue , &ap_IF->vox_offset[1]) )
2861  {
2862  Cerr("***** ReadIntfHeader()-> Error when trying to read data from one of the following key :" << endl);
2863  Cerr("***** 'first pixel offset (mm) [2]'" << Key.kvalue << endl);
2864  Cerr("***** 'origin (mm) [2]'"<< endl);
2865  Cerr("***** 'offset [2]'" << Key.kvalue << endl);
2866  Cerr("***** Recovered value = " << Key.kvalue << endl);
2867  return 1;
2868  }
2869  }
2870 
2871 
2872  // --- Voxel offset axial
2873  // "first pixel offset (mm) [3]" / Intf_fields.vox_offset[2]
2874  else if(IntfCheckKeyMatch(Key, "first pixel offset (mm) [3]")
2875  || IntfCheckKeyMatch(Key, "origin (mm) [3]")
2876  || IntfCheckKeyMatch(Key, "offset [3]"))
2877  {
2878  if(ConvertFromString(Key.kvalue , &ap_IF->vox_offset[2]) )
2879  {
2880  Cerr("***** ReadIntfHeader()-> Error when trying to read data from one of the following key :" << endl);
2881  Cerr("***** 'first pixel offset (mm) [3]'" << Key.kvalue << endl);
2882  Cerr("***** 'origin (mm) [3]'"<< endl);
2883  Cerr("***** 'offset [3]'" << Key.kvalue << endl);
2884  Cerr("***** Recovered value = " << Key.kvalue << endl);
2885  return 1;
2886  }
2887  }
2888 
2889 
2890  // --- Size thickness mm
2891  // "slice thickness (pixels)" / Intf_fields.slice_thickness_mm
2892  // We assuming the slice thickness is given in mm regardless of the name
2893  else if(IntfCheckKeyMatch(Key, "slice thickness (pixels)"))
2894  {
2895  if(ConvertFromString(Key.kvalue , &ap_IF->slice_thickness_mm) )
2896  {
2897  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'slice thickness (pixels)' key. Recovered value = " << Key.kvalue << endl);
2898  return 1;
2899  }
2900  }
2901 
2902  // --- centre-centre slice separation (pixels). (used to compute slice spacing)
2903  // "process status" / Intf_fields.ctr_to_ctr_separation
2904  else if(IntfCheckKeyMatch(Key, "centre-centre slice separation (pixels)") ||
2905  IntfCheckKeyMatch(Key, "center-center slice separation (pixels)") )
2906  {
2908  {
2909  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'centre-centre slice separation (pixels)' key.");
2910  Cerr(" Recovered value = " << Key.kvalue << endl);
2911  return 1;
2912  }
2913  Cerr("***** ReadIntfHeader()-> WARNING : 'centre-centre slice separation (pixels)' has no use in the current implementation !"<< endl);
2914  }
2915 
2916  // --- Number of time frames
2917  // "number of time frames" & "number of frame groups" / Intf_fields.nb_time_frames
2918  // Consistency with matrix size 4 : priority to nb_time_frames
2919  else if(IntfCheckKeyMatch(Key, "number of time frames") ||
2920  IntfCheckKeyMatch(Key, "number of frame groups"))
2921  {
2922  if( ConvertFromString(Key.kvalue , &ap_IF->nb_time_frames) )
2923  {
2924  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of time frames' or 'number of frame groups' keys.");
2925  Cerr("Recovered value = " << Key.kvalue << endl);
2926  return 1;
2927  }
2928  }
2929 
2930 
2931  // --- Number of respiratory gates
2932  // "number of respiratory gates" / Intf_fields.nb_resp_gates
2933  else if(IntfCheckKeyMatch(Key, "number of respiratory gates"))
2934  {
2935  if( ConvertFromString(Key.kvalue , &ap_IF->nb_resp_gates) )
2936  {
2937  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of respiratory gates' key. Recovered value = " << Key.kvalue << endl);
2938  return 1;
2939  }
2940  }
2941 
2942 
2943  // --- Number of cardiac gates
2944  // "number of cardiac gates" / Intf_fields.nb_card_gates
2945  else if(IntfCheckKeyMatch(Key, "number of cardiac gates"))
2946  {
2947  if( ConvertFromString(Key.kvalue , &ap_IF->nb_card_gates) )
2948  {
2949  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of cardiac gates' key. Recovered value = " << Key.kvalue << endl);
2950  return 1;
2951  }
2952  }
2953 
2954  // --- Total number of images
2955  // "total number of images" / Intf_fields.nb_total_imgs
2956  else if(IntfCheckKeyMatch(Key, "total number of images") )
2957  {
2958  if(ConvertFromString(Key.kvalue , &ap_IF->nb_total_imgs) )
2959  {
2960  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'total number of images' key. Recovered value = " << Key.kvalue << endl);
2961  return 1;
2962  }
2963  }
2964 
2965  // --- Number of bytes for each pixel/voxel of the image data
2966  // "number of bytes per pixel" / Intf_fields.nb_bytes_pixel
2967  else if(IntfCheckKeyMatch(Key, "number of bytes per pixel"))
2968  {
2969  if(ConvertFromString(Key.kvalue , &ap_IF->nb_bytes_pixel) )
2970  {
2971  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of bytes per pixel' key. Recovered value = " << Key.kvalue << endl);
2972  return 1;
2973  }
2974  }
2975 
2976  // --- Slice orientations
2977  // "slice orientation" / Intf_fields.slice_orientation
2978  // TODO : This information is recovered, but not used for the current implementation
2979  else if(IntfCheckKeyMatch(Key, "slice orientation"))
2980  {
2981  string slice_orientation;
2982  if( ConvertFromString(Key.kvalue , &slice_orientation) )
2983  {
2984  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'slice orientation' key. Recovered value = " << Key.kvalue << endl);
2985  return 1;
2986  }
2987 
2988  if (slice_orientation == "transverse")
2990  else if (slice_orientation == "sagittal")
2992  else if (slice_orientation == "coronal")
2994  }
2995 
2996  // --- Patient rotation
2997  // "patient rotation" / Intf_fields.pat_rotation
2998  // TODO : This information is recovered, but not used for the current implementation
2999  else if(IntfCheckKeyMatch(Key, "patient rotation"))
3000  {
3001  string pat_rotation;
3002  if( ConvertFromString(Key.kvalue , &pat_rotation) )
3003  {
3004  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'patient rotation' key. Recovered value = " << Key.kvalue << endl);
3005  return 1;
3006  }
3007 
3008  if (pat_rotation == "supine")
3009  ap_IF->pat_rotation = INTF_SUPINE;
3010  else if (pat_rotation == "prone")
3011  ap_IF->pat_rotation = INTF_PRONE;
3012  }
3013 
3014 
3015  // --- Patient orientation
3016  // "patient orientation" / Intf_fields.pat_orientation
3017  // TODO : This information is recovered, but not used for the current implementation
3018  else if(IntfCheckKeyMatch(Key, "patient orientation"))
3019  {
3020  string pat_orientation;
3021  if( ConvertFromString(Key.kvalue , &pat_orientation) )
3022  {
3023  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'patient orientation' key. Recovered value = " << Key.kvalue << endl);
3024  return 1;
3025  }
3026 
3027  if (pat_orientation == "head_in")
3028  ap_IF->pat_orientation = INTF_HEADIN;
3029  else if (pat_orientation == "feet_in")
3030  ap_IF->pat_orientation = INTF_FEETIN;
3031  }
3032 
3033 
3034  // --- Multiplicative calibration value
3035  // "rescale slope" / Intf_fields.rescale_slope
3036  else if(IntfCheckKeyMatch(Key, "rescale slope")||
3037  IntfCheckKeyMatch(Key, "data rescale slope"))
3038  {
3039  double rs= 1.;
3040  if( ConvertFromString(Key.kvalue , &rs) )
3041  {
3042  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'rescale slope' or 'data rescale slope' key. Recovered value = " << Key.kvalue << endl);
3043  return 1;
3044  }
3045  ap_IF->rescale_slope = rs; // factorization as this variable can also be modified by quantification units
3046 
3047  if (ap_IF->rescale_slope == 0.) // Only checking initialization, no need for FLTNBisEqual()
3048  {
3049  Cerr("***** ReadIntfHeader()-> Error : field 'resclale slope' units should be >0!" << endl);
3050  return 1;
3051  }
3052  }
3053 
3054  // Several global scale factors.
3055  // --- Multiplicative calibration value
3056  // "quantification units" / Intf_fields.rescale_slope
3057  // Note : throw warning if bad conversion, as this key is sometimes
3058  // used with a string gathering the data unit (eg : kbq/cc)
3059  else if(IntfCheckKeyMatch(Key, "quantification units"))
3060  {
3061  double qu= 1.;
3062  if(ConvertFromString(Key.kvalue , &qu) )
3063  {
3064  Cerr("***** ReadIntfHeader()-> WARNING : Error when trying to read numeric value from 'quantification units' key. Actual value = " << Key.kvalue << endl);
3065  Cerr("***** This key will be ignored" << endl);
3066  qu= 1.;
3067  }
3068  ap_IF->quant_units = qu; // factorization as this variable can also be modified by rescale slope
3069 
3070  if (ap_IF->quant_units == 0.) // Only checking initialization, no need for FLTNBisEqual()
3071  {
3072  Cerr("***** ReadIntfHeader()-> Error : field 'quantification units' should be >0!" << endl);
3073  return 1;
3074  }
3075  }
3076 
3077  // --- Additive calibration value
3078  // "rescale intercept" / Intf_fields.rescale_intercept
3079  else if(IntfCheckKeyMatch(Key, "rescale intercept") ||
3080  IntfCheckKeyMatch(Key, "data rescale intercept"))
3081  {
3082  if( ConvertFromString(Key.kvalue , &ap_IF->rescale_intercept) )
3083  {
3084  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'rescale intercept' or 'data rescale intercept' key. Recovered value = " << Key.kvalue << endl);
3085  return 1;
3086  }
3087 
3088  if (ap_IF->rescale_intercept == 0.) // Only checking initialization, no need for FLTNBisEqual()
3089  {
3090  Cerr("***** ReadIntfHeader()-> Error : field 'resclale intercept' units should be >0!" << endl);
3091  return 1;
3092  }
3093  }
3094 
3095 
3096  // --- Type of projeted/reconstructed dataset (Static|Dynamic|Gated|Tomographic|Curve|ROI|GSPECT|Other)
3097  // Curve|ROI|Other are considered as Static
3098  // "type of data" / Intf_fields.data_type
3099  else if(IntfCheckKeyMatch(Key, "type of data"))
3100  {
3101  string data_str;
3102  if(ConvertFromString(Key.kvalue ,&data_str) )
3103  {
3104  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'type of data' key. Recovered value = " << Key.kvalue << endl);
3105  return 1;
3106  }
3107 
3108  ap_IF->data_type = IntfKeyGetInputImgDataType(data_str);
3109  }
3110 
3111 
3112  // --- Acquisition duration
3113  // "study duration (sec)" / Intf_fields.study_duration
3114  else if(IntfCheckKeyMatch(Key, "study duration (sec)"))
3115  {
3116  if(ConvertFromString(Key.kvalue ,&ap_IF->study_duration) )
3117  {
3118  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'study duration (sec)' key. Recovered value = " << Key.kvalue << endl);
3119  return 1;
3120  }
3121  }
3122 
3123 
3124  // --- Image duration
3125  // "image_duration (sec)" / Intf_fields.image_duration
3126  // Note : check after reading all headers that the number of image durations matches the actual number of frames
3127  else if(IntfCheckKeyMatch(Key, "image duration (sec)"))
3128  {
3129  FLTNB duration;
3130  if(ConvertFromString(Key.kvalue ,&duration) )
3131  {
3132  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'study duration (sec)' key. Recovered value = " << Key.kvalue << endl);
3133  return 1;
3134  }
3135  ap_IF->image_duration.push_back(duration);
3136  }
3137 
3138 
3139 
3140  // --- Image start time
3141  // "image start time (sec)" / Intf_fields.image_start_time
3142  // Note : check after reading all headers that the number of image start time matches the actual number of frames
3143  else if(IntfCheckKeyMatch(Key, "image start time (sec)"))
3144  {
3145  FLTNB stime;
3146  if(ConvertFromString(Key.kvalue ,&stime) )
3147  {
3148  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'study duration (sec)' key. Recovered value = " << Key.kvalue << endl);
3149  return 1;
3150  }
3151  ap_IF->image_start_time.push_back(stime);
3152  }
3153 
3154 
3155 
3156  // --- Pause between frame groups
3157  // "pause between frame groups (sec)" / Intf_fields.pause_duration
3158  // Note : check after reading all headers that the number of pauses matches the actual number of frames
3159  else if(IntfCheckKeyMatch(Key, "pause between frame groups (sec)"))
3160  {
3161  FLTNB pause_duration;
3162  if(ConvertFromString(Key.kvalue ,&pause_duration) )
3163  {
3164  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'study duration (sec)' key. Recovered value = " << Key.kvalue << endl);
3165  return 1;
3166  }
3167  ap_IF->frame_group_pause.push_back(pause_duration);
3168  }
3169 
3170 
3171  // TODO : this key is not used in the current implementation
3172  // --- number of time windows
3173  // "number of time windows " / Intf_fields.nb_time_windows
3174  else if(IntfCheckKeyMatch(Key, "number of time windows "))
3175  {
3176  if(ConvertFromString(Key.kvalue ,&ap_IF->nb_time_windows) )
3177  {
3178  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of time windows ' key. Recovered value = " << Key.kvalue << endl);
3179  return 1;
3180  }
3181  //Cerr("***** ReadIntfHeader()-> WARNING : 'number of time windows' has no use in the current implementation !"<< endl);
3182  }
3183 
3184 
3185  // --- Process status : acquired or reconstructed
3186  // "process status" / Intf_fields.process_status
3187  else if(IntfCheckKeyMatch(Key, "process status"))
3188  {
3189  if(ConvertFromString(Key.kvalue ,&ap_IF->process_status) )
3190  {
3191  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'process status' key. Recovered value = " << Key.kvalue << endl);
3192  return 1;
3193  }
3194  }
3195 
3196 
3197  // --- Number of energy windows
3198  // "number of energy windows" / Intf_fields.nb_energy_windows
3199  // TODO : not implemented yet.
3200  else if(IntfCheckKeyMatch(Key, "number of energy windows"))
3201  {
3202  if(ConvertFromString(Key.kvalue , &ap_IF->nb_energy_windows) )
3203  {
3204  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of energy windows' key. Recovered value = " << Key.kvalue << endl);
3205  return 1;
3206  }
3207  //Cerr("***** ReadIntfHeader()-> WARNING : 'number of energy windows' has no use in the current implementation !"<< endl);
3208  }
3209 
3210 
3211  // --- Number of detector heads (SPECT systems)
3212  // "number of detector heads" / Intf_fields.nb_detector_heads
3213  else if(IntfCheckKeyMatch(Key, "number of detector heads"))
3214  {
3215  if(ConvertFromString(Key.kvalue , &ap_IF->nb_detector_heads) )
3216  {
3217  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of detector heads' key. Recovered value = " << Key.kvalue << endl);
3218  return 1;
3219  }
3220  }
3221 
3222 
3223  // --- Number of projections (for one head in SPECT)
3224  // "number of projections" / Intf_fields.nb_energy_windows
3225  else if(IntfCheckKeyMatch(Key, "number of projections"))
3226  {
3227  if(ConvertFromString(Key.kvalue , &ap_IF->nb_projections) )
3228  {
3229  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of projections' key. Recovered value = " << Key.kvalue << endl);
3230  return 1;
3231  }
3232  }
3233 
3234 
3235  // --- Angular span of the projections ex: 180, 360
3236  // "extent of rotation " / Intf_fields.extent_rotation
3237  else if(IntfCheckKeyMatch(Key, "extent of rotation"))
3238  {
3239  if(ConvertFromString(Key.kvalue , &ap_IF->extent_rotation) )
3240  {
3241  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'extent of rotation' key. Recovered value = " << Key.kvalue << endl);
3242  return 1;
3243  }
3244  }
3245 
3246  // --- Direction of rotation : CCW (counter-clockwise), CW (clockwise)
3247  // "direction of rotation" / Intf_fields.direction_rotation
3248  else if(IntfCheckKeyMatch(Key, "direction of rotation"))
3249  {
3250  if(ConvertFromString(Key.kvalue , &ap_IF->direction_rotation) )
3251  {
3252  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'direction_rotation' key. Recovered value = " << Key.kvalue << endl);
3253  return 1;
3254  }
3255  }
3256 
3257  // --- Angle of the first view
3258  // "start angle" / Intf_fields.first_angle
3259  // TODO : This may be wrong. First angle may be sum of values of :
3260  // "start angle" + "first projection angle in data set"
3261  else if(IntfCheckKeyMatch(Key, "start angle"))
3262  {
3263  if(ConvertFromString(Key.kvalue , &ap_IF->first_angle) )
3264  {
3265  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'start angle' key. Recovered value = " << Key.kvalue << endl);
3266  return 1;
3267  }
3268  }
3269 
3270 
3271  // --- All projection angles as an array key
3272  // "projection_angles" / Intf_fields.projection_angles
3273  else if(IntfCheckKeyMatch(Key, "projection_angles"))
3274  {
3275  if(ConvertFromString(Key.kvalue , &ap_IF->projection_angles) )
3276  {
3277  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'projection_angles' key. Recovered value = " << Key.kvalue << endl);
3278  return 1;
3279  }
3280  }
3281 
3282 
3283  // --- All distance between center of rotation and detectors, as unique value similar for each angles
3284  // "Center of rotation to detector distance" / Intf_fields.radius
3285  else if(IntfCheckKeyMatch(Key, "Center of rotation to detector distance"))
3286  {
3287  if(ConvertFromString(Key.kvalue , &ap_IF->radius) )
3288  {
3289  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'Center of rotation to detector distance' key.");
3290  Cerr(" Recovered value = " << Key.kvalue << endl);
3291  return 1;
3292  }
3293  }
3294 
3295 
3296  // --- All distance between center of rotation and detectors, as an array key
3297  // "Radius" / Intf_fields.radius
3298  else if(IntfCheckKeyMatch(Key, "Radius"))
3299  {
3300  if(ConvertFromString(Key.kvalue , &ap_IF->radius) )
3301  {
3302  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'Radius' key. Recovered value = " << Key.kvalue << endl);
3303  return 1;
3304  }
3305  }
3306 
3307  // Return error if data compression
3308  else if(IntfCheckKeyMatch(Key, "data compression"))
3309  {
3310  if(! (Key.kvalue == "none" || Key.kvalue.empty() ) )
3311  {
3312  Cerr("***** ReadIntfHeader()-> Error : compressed interfile images not handled by the current implementation !" << endl);
3313  return 1;
3314  }
3315  }
3316 
3317  // Return error if data encoded
3318  else if(IntfCheckKeyMatch(Key, "data encode"))
3319  {
3320  if(! (Key.kvalue == "none" || Key.kvalue.empty() ) )
3321  {
3322  Cerr("***** ReadIntfHeader()-> Error : encoded interfile images not handled by the current implementation !" << endl);
3323  return 1;
3324  }
3325  }
3326 
3327 
3328  if(IntfCheckKeyMatch(Key, "number of slices") )
3329  {
3330  if(!(ap_IF->mtx_size[2] >0)) // Check if not been initialized elsewhere using other field (matrix size [3])
3331  {
3332  if(ConvertFromString(Key.kvalue , &ap_IF->mtx_size[2]) )
3333  {
3334  Cerr("***** ReadIntfHeader()-> Error when trying to read data from 'number of slices' key. Recovered value = " << Key.kvalue << endl);
3335  return 1;
3336  }
3337  ap_IF->nb_dims++;
3338  }
3339  continue;
3340  }
3341 
3342  }
3343  }
3344  else
3345  {
3346  Cerr("***** ReadIntfHeader()-> Error : couldn't find or read header interfile '"<< a_pathToHeaderFile << "' !" << endl);
3347  return 1;
3348  }
3349 
3350  // Recover slice number in ap_IF->mtx_size[2] if provided by "total number of images" or "number of images" tags, and not "matrix size [3]" tag.
3351  if(ap_IF->mtx_size[2]==0 && ap_IF->nb_total_imgs>0)
3352  ap_IF->mtx_size[2] = ap_IF->nb_total_imgs;
3353 
3354 
3355  // Compute slice thickness if not initialized
3356  ap_IF->vox_size[2] = (ap_IF->vox_size[2] < 0) ?
3357  ((ap_IF->vox_size[0]+ap_IF->vox_size[1])/2.) * ap_IF->slice_thickness_mm:
3358  ap_IF->vox_size[2];
3359 
3360  // Compute nb dimensions for the image
3361  ap_IF->nb_dims = 3;
3362 
3363  if(ap_IF->mtx_size[3] >1 ||
3364  ap_IF->nb_time_frames>1)
3365  ap_IF->nb_dims++;
3366  if( ap_IF->nb_resp_gates >1)
3367  ap_IF->nb_dims++;
3368  if( ap_IF->nb_card_gates >1)
3369  ap_IF->nb_dims++;
3370 
3371  // If there are any frames, check nb frame durations matches nb (frames) group pauses
3372  if(ap_IF->image_duration.size() > 0 &&
3373  ap_IF->frame_group_pause.size() > 0 &&
3374  ap_IF->image_duration.size() != ap_IF->frame_group_pause.size())
3375  {
3376  Cerr("***** ReadIntfHeader()-> Error : nb of recovered frame duration ('"<< ap_IF->image_duration.size()
3377  << ") does not match the nb of recovered pauses between frames ('"<< ap_IF->frame_group_pause.size() << ") !" << endl);
3378  return 1;
3379  }
3380 
3381  return 0;
3382 }
3383 
3384 
3385 
3386 
3387 // =====================================================================
3388 // ---------------------------------------------------------------------
3389 // ---------------------------------------------------------------------
3390 // =====================================================================
3391 /*
3392  \fn IntfWriteHeaderMainData
3393  \param a_path : path to the interfile header to write
3394  \param ap_IntfF : Structure containing interfile keys of the image to write
3395  \param vb : verbosity
3396  \brief Write main infos of an Interfile header at the path provided in parameter,
3397  using the interfile keys provided by the field structure parameter.
3398  \todo Recover in 'patient name' the name of the datafile used to reconstruct the images [ bed ]?
3399  \return 0 if success, positive value otherwise.
3400 */
3401 int IntfWriteHeaderMainData(const string& a_path, const Intf_fields& ap_IntfF, int vb)
3402 {
3403  if (vb >= 4)
3404  {
3405  Cout("--------------------------------------------------------------- " << endl);
3406  Cout("IntfWriteHeaderMainData()-> Start writing header interfile " << a_path << endl);
3407  Cout("--------------------------------------------------------------- " << endl);
3408  }
3409 
3410  string path_to_header, path_to_image;
3411  path_to_header = a_path;
3412 
3413  path_to_header.append(".hdr");
3414  path_to_image = GetFileFromPath(a_path).append(".img");
3415 
3416  // Get file and check existence
3417  ofstream ofile(path_to_header.c_str(), ios::out);
3418  ofile << setprecision(std::numeric_limits<FLTNB>::digits10 +1);
3419 
3420  if(ofile)
3421  {
3423 
3424  ofile << "!INTERFILE := " << endl;
3425 
3426  // PET/SPECT/CT according to the scanner type defined in scanner manager
3427  string imaging_modality;
3428  if (p_scanMgr->GetScannerType() == SCANNER_PET)
3429  imaging_modality = "PET";
3430  else if (p_scanMgr->GetScannerType() == SCANNER_SPECT_CONVERGENT || p_scanMgr->GetScannerType() == SCANNER_SPECT_PINHOLE)
3431  imaging_modality = "SPECT";
3432  else if(p_scanMgr->GetScannerType() == SCANNER_CT)
3433  imaging_modality = "CT";
3434  else
3435  imaging_modality = "UNKOWN";
3436 
3437  ofile << "!imaging modality := " << imaging_modality << endl;
3438 
3439  // 3D SPECT use 3.3
3440  if(ap_IntfF.data_type == INTF_IMG_SPECT)
3441  ofile << "!version of keys := " << "3.3" << endl;
3442  else
3443  ofile << "!version of keys := " << "CASToRv" << CASTOR_VERSION << endl;
3444  // CASToR version
3445  ofile << "CASToR version := " << CASTOR_VERSION << endl;
3446 
3447  ofile << endl << "!GENERAL DATA := " << endl;
3448  ofile << "!originating system := " << p_scanMgr->GetScannerName() << endl;
3449 
3450  // If more than one bed, then say it
3451  if (ap_IntfF.nb_bed_positions>1) ofile << "number of bed positions := " << ap_IntfF.nb_bed_positions << endl;
3452  // Otherwise, write the relative position
3453  else if (ap_IntfF.bed_position_provided) ofile << "horizontal bed relative position (mm) := " << ap_IntfF.bed_relative_position << endl;
3454 
3455  ofile << "!data offset in bytes := " << 0 << endl;
3456  ofile << "!name of data file := " << path_to_image << endl;
3457 
3458  //string patient_name = IntfKeyGetPatientNameTag();
3459  ofile << "patient name := " << GetFileFromPath(a_path) << endl;
3460 
3461 
3462  ofile << endl << "!GENERAL IMAGE DATA " << endl;
3463 
3464  string data_type_str = (ap_IntfF.data_type == INTF_IMG_SPECT) ?
3465  "Tomographic" :
3466  "Static";
3467  //ofile << "!type of data := " << data_type_str << endl; // such as Static|Dynamic|Gated|Tomographic|Curve|ROI|GSPECT|Other
3468  ofile << "!type of data := Dynamic" << endl; // /!\ Dynamic by default in order to be readable from ImageJ OpenMed plugin
3469 
3470  uint32_t nb_imgs = (ap_IntfF.process_status == "acquired") ?
3471  ap_IntfF.nb_projections : // projeted data
3472  ap_IntfF.nb_total_imgs ; // reconstructed data
3473  ofile << "!total number of images := " << nb_imgs << endl;
3474 
3475  ofile << "imagedata byte order := " << IntfKeyGetEndianStr(ap_IntfF.endianness) << endl;
3476 
3477  // Depending on the mode of output writing, we should write a header for each dynamic image, or append everything in one single header
3478  // output dynamic files as one single file instead of 3D images with a meta header
3479  if( ap_IntfF.nb_time_frames>1 )
3480  {
3481  ofile << "number of time frames := " << ap_IntfF.nb_time_frames << endl;
3482  ofile << "!number of frame groups := " << ap_IntfF.nb_time_frames << endl;
3483  }
3484  else
3485  ofile << "!number of frame groups :=1 " << endl;
3486 
3487 
3488  if( ap_IntfF.nb_energy_windows>1 )
3489  ofile << "number of time windows := " << ap_IntfF.nb_resp_gates *
3490  ap_IntfF.nb_card_gates << endl;
3491  if(ap_IntfF.nb_resp_gates > 1)
3492  ofile << "number of respiratory gates := " << ap_IntfF.nb_resp_gates << endl;
3493  if(ap_IntfF.nb_card_gates > 1)
3494  ofile << "number of cardiac gates := " << ap_IntfF.nb_card_gates << endl;
3495 
3496  if(ap_IntfF.process_status == "acquired")
3497  {
3498  ofile << "!number of projections := " << ap_IntfF.nb_projections << endl;
3499  ofile << "!extent of rotation := " << ap_IntfF.extent_rotation << endl;
3500  }
3501  ofile << "process status := " << ap_IntfF.process_status << endl;
3502 
3503  if (ap_IntfF.nb_bed_positions>1)
3504  {
3505  ofile << "number of bed positions := " << ap_IntfF.nb_bed_positions << endl;
3506  ofile << "!study duration (sec) := " << ap_IntfF.study_duration << endl;
3507  }
3508 
3509  if (ap_IntfF.data_type == INTF_IMG_DYNAMIC) // Dynamic study
3510  ofile << endl << "!DYNAMIC STUDY (General) :=" << endl;
3511  else if(p_scanMgr->GetScannerType() == 2) // SPECT study
3512  {
3513  if( ap_IntfF.data_type == INTF_IMG_GATED)
3514  ofile << endl << "!GSPECT STUDY (General) :=" << endl;
3515  else
3516  {
3517  ofile << endl << "!SPECT STUDY (General) :=" << endl;
3518  ofile << endl << "!SPECT STUDY ("<< ap_IntfF.process_status <<" data) :="<< endl;
3519  }
3520  }
3521  else if (ap_IntfF.data_type == INTF_IMG_GATED )
3522  ofile << endl << "!GATED STUDY (General) :=" << endl;
3523  else // Standard study
3524  ofile << endl << "!STATIC STUDY (General) :=" << endl;
3525 
3526 
3527  // Patient position
3528  // (not implemented as these informations are most of the time missing, and could be misleading)
3529  // output_header << "patient orientation := " << ap_IntfF.pat_orientation << endl;
3530  // output_header << "patient rotation := " << ap_IntfF.pat_rotation << endl;
3531  // output_header << "slice orientation := " << ap_IntfF.slice_orientation << endl;
3532 
3533 
3534  // Loop on dynamic images (if any) and write data
3535 
3536  if (ap_IntfF.nb_resp_gates>1)
3537  ofile << "!number of frame groups :=" << ap_IntfF.nb_time_frames << endl;
3538 
3539  // loop on frames
3540  for(int fr=0 ; fr<ap_IntfF.nb_time_frames ; fr++)
3541  {
3542  if( ap_IntfF.nb_time_frames>1 )
3543  {
3544  ofile << "!Dynamic Study (each frame group) :=" << endl;
3545  ofile << "!frame group number := " << fr+1 << endl;
3546  }
3547 
3548  // loop on respiratory gates
3549  for(int rg=0 ; rg<ap_IntfF.nb_resp_gates ; rg++)
3550  {
3551  if( ap_IntfF.nb_resp_gates>1 )
3552  {
3553  ofile << "!Respiratory Gated Study (each time window) :=" << endl;
3554  ofile << "!time window number := " << rg+1 << endl;
3555  }
3556 
3557  // loop on cardiac gates
3558  for(int cg=0 ; cg<ap_IntfF.nb_card_gates ; cg++)
3559  {
3560  if( ap_IntfF.nb_card_gates>1 )
3561  {
3562  ofile << "!Cardiac Gated Study (each time window) :=" << endl;
3563  ofile << "!time window number := " << cg+1 << endl;
3564  }
3565 
3566  // Write image specific data
3567  if(IntfWriteHeaderImgData(ofile, ap_IntfF, vb) )
3568  {
3569  Cerr("***** IntfWriteHeaderMainData()-> Error : while trying to write the interfile header '"<< path_to_header << "' !" << endl);
3570  return 1;
3571  }
3572 
3573  if( ap_IntfF.nb_card_gates>1 )
3574  ofile << "!number of images in this time window :="
3575  << ap_IntfF.mtx_size[2] << endl;
3576 
3577  }
3578 
3579  if( ap_IntfF.nb_resp_gates>1 )
3580  ofile << "!number of images in this time window :="
3581  << ap_IntfF.nb_card_gates *
3582  ap_IntfF.mtx_size[2] << endl;
3583 
3584  }
3585  // Write frame information even for static acquisition as we need it
3586  if (ap_IntfF.nb_time_frames>=1)
3587  {
3588  ofile << "!number of images in this frame group := "
3589  << ap_IntfF.nb_resp_gates *
3590  ap_IntfF.nb_card_gates *
3591  ap_IntfF.mtx_size[2] << endl;
3592 
3593  ofile << "!image duration (sec) := " << ap_IntfF.image_duration[fr] << endl;
3594  ofile << "!image start time (sec) := " << ap_IntfF.image_start_time[fr] << endl;
3595 
3596  if(fr+1 == ap_IntfF.nb_time_frames) // Check if we reached the last frame
3597  ofile << "pause between frame groups (sec) := " << 0.0 << endl;
3598  else
3599  ofile << "pause between frame groups (sec) := " << ap_IntfF.frame_group_pause[fr] << endl;
3600 
3601  }
3602  }
3603 
3604  ofile << "!END OF INTERFILE := " << endl;
3605 
3606  // Copy content of the input data header file into the interfile header
3608  {
3609  Cerr("***** IntfWriteHeaderMainData()-> Error: while copying input data header information into output interfile header '" << path_to_header << "' !" << endl);
3610  return 1;
3611  }
3612  }
3613  else
3614  {
3615  Cerr("***** IntfWriteHeaderMainData()-> Error : couldn't find output header interfile '"<< a_path << "' !" << endl);
3616  return 1;
3617  }
3618 
3619  return 0;
3620 }
3621 
3622 // =====================================================================
3623 // ---------------------------------------------------------------------
3624 // ---------------------------------------------------------------------
3625 // =====================================================================
3626 
3628 {
3629  // This will be the return value
3630  int return_value = 0;
3631  // Get the vector of input data header file names
3632  vector<string> dfName = sOutputManager::GetInstance()->GetDataFileName();
3633  for (size_t df=0; df<dfName.size(); df++)
3634  {
3635  // Open header file
3636  ifstream header(dfName[df].c_str());
3637  if (!header)
3638  {
3639  Cerr("***** IntfWriteContentOfInputDataHeaderIntoInterfileHeader() -> Input data header file '" << dfName[df] << "' is missing or corrupted !" << endl);
3640  return_value = 1;
3641  continue;
3642  }
3643  // Use a specific field to specify the beginning of header data
3644  ap_ofile << endl << "!COPY OF INPUT HEADER " << df+1 << endl;
3645  // Copy line by line
3646  string line = "";
3647  getline(header,line);
3648  while (!header.eof())
3649  {
3650  ap_ofile << line << endl;
3651  getline(header,line);
3652  }
3653  // Close input header file
3654  header.close();
3655  // End of header data
3656  ap_ofile << "!END OF COPY OF INPUT HEADER " << df+1 << endl;
3657  }
3658  // End
3659  return return_value;
3660 }
3661 
3662 
3663 
3664 // =====================================================================
3665 // ---------------------------------------------------------------------
3666 // ---------------------------------------------------------------------
3667 // =====================================================================
3668 /*
3669  \fn IntfWriteHeaderImgData
3670  \param ap_ofile : pointer to the ofstream linked to the image header file
3671  \param ap_IntfF : reference to a structure containing interfile keys of the image to write
3672  \param verbosity : verbosity
3673  \brief Write basic image data info in the file provided in parameter
3674  \todo Data about positionning/offsets ?
3675  \todo : include dectector heads (a set of fields for each head)
3676  \return 0 if success, positive value otherwise.
3677 */
3678 int IntfWriteHeaderImgData(ofstream &ap_ofile, const Intf_fields& ap_IntfF, int vb)
3679 {
3680  if(ap_ofile)
3681  {
3682  if(ap_IntfF.process_status == "acquired") // projection data (SPECT)
3683  {
3684  // Todo : include dectector heads (a set of fields for each head)
3685  // That means a lot of modifications as angles, radius, ... should be provided for each head
3686  ap_ofile << "!direction of rotation := " << ap_IntfF.direction_rotation << endl;
3687  ap_ofile << "start angle := " << ap_IntfF.first_angle << endl;
3688  ap_ofile << "projection angles := " << ap_IntfF.projection_angles << endl;
3689 
3690  // If one common radius for each projection (no "{}"), write it in the 'Radius' key
3691  if( ap_IntfF.radius.find("{}") != string::npos )
3692  ap_ofile << "Center of rotation to detector distance := " << ap_IntfF.radius << endl;
3693  else
3694  ap_ofile << "Radius := " << ap_IntfF.radius << endl;
3695 
3696  ap_ofile << "!matrix size [1] := " << ap_IntfF.mtx_size[0] << endl;
3697  ap_ofile << "!matrix size [2] := " << ap_IntfF.mtx_size[1] << endl;
3698  ap_ofile << "!number format := " << ap_IntfF.nb_format << endl;
3699  ap_ofile << "!number of bytes per pixel := " << sizeof(FLTNB) << endl;
3700  ap_ofile << "scaling factor (mm/pixel) [1] := " << ap_IntfF.vox_size[0] << endl;
3701  ap_ofile << "scaling factor (mm/pixel) [2] := " << ap_IntfF.vox_size[1] << endl;
3702  ap_ofile << "!data offset in bytes := " << 0 << endl;
3703  }
3704  else
3705  {
3706  ap_ofile << "number of dimensions := " << 3 << endl;
3707  ap_ofile << "!matrix size [1] := " << ap_IntfF.mtx_size[0] << endl;
3708  ap_ofile << "!matrix size [2] := " << ap_IntfF.mtx_size[1] << endl;
3709  ap_ofile << "!matrix size [3] := " << ap_IntfF.mtx_size[2] << endl;
3710  ap_ofile << "!number format := " << ap_IntfF.nb_format << endl;
3711  ap_ofile << "!number of bytes per pixel := " << sizeof(FLTNB) << endl;
3712  ap_ofile << "scaling factor (mm/pixel) [1] := " << ap_IntfF.vox_size[0] << endl;
3713  ap_ofile << "scaling factor (mm/pixel) [2] := " << ap_IntfF.vox_size[1] << endl;
3714  ap_ofile << "scaling factor (mm/pixel) [3] := " << ap_IntfF.vox_size[2] << endl;
3715  ap_ofile << "first pixel offset (mm) [1] := " << ap_IntfF.vox_offset[0] << endl;
3716  ap_ofile << "first pixel offset (mm) [2] := " << ap_IntfF.vox_offset[1] << endl;
3717  ap_ofile << "first pixel offset (mm) [3] := " << ap_IntfF.vox_offset[2] << endl;
3718 
3719  if(ap_IntfF.rescale_intercept != 1. ||
3720  ap_IntfF.rescale_slope != 0. ||
3721  ap_IntfF.quant_units != 1. )
3722  {
3723  ap_ofile << "data rescale offset := " << ap_IntfF.rescale_intercept << endl;
3724  ap_ofile << "data rescale slope := " << ap_IntfF.rescale_slope << endl;
3725  ap_ofile << "quantification units := " << ap_IntfF.quant_units << endl;
3726  }
3727  // Todo, we may need keys for image positionning in space
3728  //ap_ofile << "origin (mm) [1] := " << ap_ID->Get??? << endl;
3729  //ap_ofile << "origin (mm) [2] := " << ap_ID->Get??? << endl;
3730  //ap_ofile << "origin (mm) [3] := " << ap_ID->Get??? << endl;
3731 
3732 
3733  // The following keys were used in interfiles format 3.3
3734  // Seem to have been replaced by matrix size [3]/scaling factor (mm/pixel) [3]
3735  // but we may need them for some compatibilities issues
3736  // ap_ofile << "!number of slices := " << ap_IntfF.mtx_size[2] << endl;
3737  // ap_ofile << "slice thickness (pixels) := " <<
3738  // ap_IntfF.vox_size[2] /( (ap_IntfF.vox_size[0]+ap_IntfF.vox_size[1])/2.) << endl;
3739  // ap_ofile << "centre-centre slice separation (pixels) := " << endl;
3740  }
3741 
3742  }
3743  else
3744  {
3745  Cerr("***** IntfWriteHeaderImgData()-> Error : couldn't open provided interfile header file !" << endl);
3746  return 1;
3747  }
3748 
3749  return 0;
3750 }
3751 
3752 
3753 
3754 
3755 // =====================================================================
3756 // ---------------------------------------------------------------------
3757 // ---------------------------------------------------------------------
3758 // =====================================================================
3759 /*
3760  \fn IntfWriteImage()
3761  \param a_pathToImg : th to the directory where the image will be written
3762  \param ap_outImgMtx : Matrix containing the image to write
3763  \param a_dim : Number of voxels in the 3D image
3764  \param vb : Verbosity level
3765  \brief Write Interfile raw data whose path is provided in parameter, using image matrix provided in parameter.
3766  \brief For 1 dimensional image matrices
3767  \return 0 if success, positive value otherwise.
3768 */
3769 int IntfWriteImage(const string& a_pathToImg, FLTNB* ap_outImgMtx, uint32_t a_dim, int vb)
3770 {
3771  if(vb >= 5) Cout("IntfWriteImage()*" << endl);
3772 
3773  ofstream img_file(a_pathToImg.c_str(), ios::binary | ios::out);
3774 
3775  if(img_file)
3776  {
3777  // Call to writing function.
3778  if(IntfWriteData(&img_file, ap_outImgMtx, a_dim, vb) )
3779  {
3780  Cerr("***** IntfWriteImage()-> Error occurred when writing the image file '"<< a_pathToImg << "' !" << endl);
3781  return 1;
3782  }
3783  }
3784  else
3785  {
3786  Cerr("***** IntfWriteImage()-> Error occurred while trying to write the image file '"<< a_pathToImg << "' !" << endl);
3787  return 1;
3788  }
3789 
3790  return 0;
3791 }
3792 
3793 
3794 
3795 
3796 // =====================================================================
3797 // ---------------------------------------------------------------------
3798 // ---------------------------------------------------------------------
3799 // =====================================================================
3800 /*
3801  \fn IntfWriteImage
3802  \param ap_pathToImgs: List of string containing the paths to the image to write
3803  \param a2p_outImgMtx : 2 dimensional image matrix (1D temporal + 3D)
3804  \param ap_imgDim[2] : Dimensions of ap_outImgMtx
3805  \param vb : Verbosity level
3806  \brief Write Interfile raw data whose path is provided in parameter, using the image matrix provided in parameter.
3807  \brief For 2 dimensional image matrices
3808  \return 0 if success, positive value otherwise.
3809 */
3810 int IntfWriteImage(vector<string> ap_pathToImgs, FLTNB** a2p_outImgMtx, uint32_t ap_dim[2], int vb)
3811 {
3812  if(vb >= 5) Cout("IntfWriteImage()**" << endl);
3813 
3814  if(ap_pathToImgs.size() == 1) // We have one single data file containing all the data
3815  {
3816  // As the content will be appended, make sure there is no existing file with such name
3817  // remove it otherwise
3818  ifstream fcheck(ap_pathToImgs.at(0).append(".img").c_str());
3819  if(fcheck.good())
3820  {
3821  fcheck.close();
3822  #ifdef _WIN32
3823  string dos_instruction = "del " + ap_pathToImgs.at(0);
3824  system(dos_instruction.c_str());
3825  #else
3826  remove(ap_pathToImgs.at(0).c_str());
3827  #endif
3828  }
3829 
3830  // Add app to the options as we will write the image in several step.
3831  ofstream img_file(ap_pathToImgs.at(0).c_str(), ios::binary | ios::out | ios::app);
3832 
3833  if(img_file)
3834  {
3835  // Call to writing function for each image matrix
3836  for(uint32_t d1=0 ; d1<ap_dim[0] ; d1++)
3837  if( IntfWriteData(&img_file, a2p_outImgMtx[d1], ap_dim[1], vb) )
3838  {
3839  Cerr("***** IntfWriteImage()-> Error occurred when writing the image file '"<< ap_pathToImgs.at(d1) << "' !" << endl);
3840  return 1;
3841  }
3842  }
3843  else
3844  {
3845  Cerr("***** IntfWriteImage()-> Error occurred while trying to write the image file at the path: '"<< ap_pathToImgs.at(0) << "' !" << endl);
3846  return 1;
3847  }
3848  }
3849  else // We have a number of data file equal to the number of image to load
3850  {
3851  if(ap_pathToImgs.size()!=ap_dim[0]) // Check we have a number of file corresponding to the number of images to load
3852  {
3853  Cerr("***** IntfWriteImage()-> Error : number of interfile images ("<< ap_pathToImgs.size() << ") not consistent with the number of images to load (" << ap_dim[0] << ") !" << endl);
3854  return 1;
3855  }
3856 
3857  for(uint32_t d1=0 ; d1<ap_dim[0] ; d1++)
3858  {
3859  ofstream img_file(ap_pathToImgs.at(d1).append(".img").c_str(), ios::binary | ios::out); // Should be one different path by file
3860 
3861  if(img_file)
3862  {
3863  // Call to writing function.
3864  if(IntfWriteData(&img_file, a2p_outImgMtx[d1], ap_dim[1], vb) )
3865  {
3866  Cerr("***** IntfWriteImage()-> Error occurred when writing the image file '"<< ap_pathToImgs.at(d1) << "' !" << endl);
3867  return 1;
3868  }
3869  }
3870  else
3871  {
3872  Cerr("***** IntfWriteImage()-> Error occurred while trying to write the image file at the path: '"<< ap_pathToImgs.at(d1) << "' !" << endl);
3873  return 1;
3874  }
3875  }
3876  }
3877 
3878  return 0;
3879 }
3880 
3881 
3882 
3883 
3884 // =====================================================================
3885 // ---------------------------------------------------------------------
3886 // ---------------------------------------------------------------------
3887 // =====================================================================
3888 /*
3889  \fn IntfWriteImage()
3890  \param vector<string> ap_pathToImgs: List of string containing the paths to the image to write
3891  \param FLTNB**** a4p_outImgMtx : 4 dimensional image matrix (3D temporal + 3D)
3892  \param int ap_imgDim[4] : Dimensions of ap_outImgMtx
3893  \param int vb : Verbosity level
3894  \brief Write Interfile raw data whose path is provided in parameter, using the image matrix provided in parameter.
3895  \brief For 4 dimensional image matrices
3896  \return 0 if success, positive value otherwise.
3897 */
3898 int IntfWriteImage(vector<string> ap_pathToImgs, FLTNB**** a4p_outImgMtx, uint32_t ap_dim[4], int vb)
3899 {
3900  if(vb >= 5) Cout("IntfWriteImage()" << endl);
3901 
3902  if(ap_pathToImgs.size() == 1) // We have one single data file containing all the data
3903  {
3904  // As the content will be appended, make sure there is no existing file with such name
3905  // remove it otherwise
3906  ifstream fcheck(ap_pathToImgs.at(0).append(".img").c_str());
3907  if(fcheck.good())
3908  {
3909  fcheck.close();
3910  #ifdef _WIN32
3911  string dos_instruction = "del " + ap_pathToImgs.at(0);
3912  system(dos_instruction.c_str());
3913  #else
3914  remove(ap_pathToImgs.at(0).c_str());
3915  #endif
3916  }
3917 
3918  // Add app to the options as we will write the image in several step.
3919  ofstream img_file(ap_pathToImgs.at(0).c_str(), ios::binary | ios::out | ios::app);
3920 
3921  if(img_file)
3922  {
3923  // Call to writing function for each image matrix
3924  for(uint32_t d1=0 ; d1<ap_dim[0] ; d1++)
3925  for(uint32_t d2=0 ; d2<ap_dim[1] ; d2++)
3926  for(uint32_t d3=0 ; d3<ap_dim[2] ; d3++)
3927  if(IntfWriteData(&img_file, a4p_outImgMtx[d1][d2][d3], ap_dim[3], vb) )
3928  {
3929  int idx_img = d1*ap_dim[1]*ap_dim[2] + d2*ap_dim[2] + d3;
3930  Cerr("***** IntfWriteImage()-> Error occurred when writing the image file '"<< ap_pathToImgs.at(idx_img) << "' !" << endl);
3931  return 1;
3932  }
3933  }
3934  else
3935  {
3936  Cerr("***** IntfWriteImage()-> Error occurred while trying to write the image file '"<< ap_pathToImgs.at(0) << "' !" << endl);
3937  return 1;
3938  }
3939 
3940  }
3941  else // We have a number of data file equal to the number of image to load
3942  {
3943  // Check we have a number of file corresponding to the number of images to load
3944  if(ap_pathToImgs.size() != ap_dim[0]*ap_dim[1]*ap_dim[2])
3945  {
3946  Cerr("***** IntfWriteImage()-> Error : number of interfile images (="<< ap_pathToImgs.size()
3947  << ") not consistent with the number of images to load (="
3948  << ap_dim[0]*ap_dim[1]*ap_dim[2] << ") !" << endl);
3949  return 1;
3950  }
3951 
3952  for(uint32_t d1=0 ; d1<ap_dim[0] ; d1++)
3953  for(uint32_t d2=0 ; d2<ap_dim[1] ; d2++)
3954  for(uint32_t d3=0 ; d3<ap_dim[2] ; d3++)
3955  {
3956  int idx_img = d1*ap_dim[1]*ap_dim[2] + d2*ap_dim[2] + d3;
3957  ofstream img_file(ap_pathToImgs.at(idx_img).append(".img").c_str(), ios::binary | ios::out); // Should be one different path by file
3958 
3959  if(img_file)
3960  {
3961  // Call to writing function.
3962  if(IntfWriteData(&img_file, a4p_outImgMtx[d1][d2][d3], ap_dim[3], vb) )
3963  {
3964  Cerr("***** IntfWriteImage()-> Error occurred when writing the image file '"<< ap_pathToImgs.at(idx_img) << "' !" << endl);
3965  return 1;
3966  }
3967  }
3968  else
3969  {
3970  Cerr("***** IntfWriteImage()-> Error occurred while trying to write the image file at the path: '"<< ap_pathToImgs.at(idx_img) << "' !" << endl);
3971  return 1;
3972  }
3973  }
3974  }
3975 
3976  return 0;
3977 }
3978 
3979 
3980 
3981 
3982 // =====================================================================
3983 // ---------------------------------------------------------------------
3984 // ---------------------------------------------------------------------
3985 // =====================================================================
3986 /*
3987  \fn IntfWriteData
3988  \param ap_oFile : Ofstream pointing to an image file
3989  \param ap_outImgMtx : 3D image matrix with reconstruction dimensions/voxel size containing the image data
3990  \param a_nbVox : A number of voxels in the 3D image matrix with reconstruction dimensions/voxel size
3991  \param vb : Verbosity level
3992  \brief Write the content of the image matrix in the file pointed by ofstream
3993  \todo keep original orientations ? Would require a loop on voxels and a reindexing of voxel indexes
3994  \todo check writing error
3995  \return 0 if success, positive value otherwise.
3996 */
3997 int IntfWriteData(ofstream* ap_oFile, FLTNB* ap_outImgMatrix, int a_nbVox, int vb)
3998 {
3999  if(vb >= 5) Cout("IntfWriteData() " << endl);
4000 
4001  // TODO : Keep original orientations ? Would require a loop on voxels and a reindexing of voxel indexes
4002  // TODO : Perhaps check here if nothing wrong happened during the writing (deletion of objects or writing to full disk). Not sure about how this is done for ofstream
4003  if (ap_oFile->write(reinterpret_cast<char*>(ap_outImgMatrix), a_nbVox*sizeof(FLTNB)) ) // implement error check here
4004  {
4005  //Cerr("***** IntfWriteData()-> Error occurred when trying to write the image file !" << endl);
4006  //return 1;
4007  }
4008 
4009  return 0;
4010 }
4011 
4012 
4013 
4014 
4015 // =====================================================================
4016 // ---------------------------------------------------------------------
4017 // ---------------------------------------------------------------------
4018 // =====================================================================
4019 /*
4020  \fn IntfKeyInitFields
4021  \param ap_IF : Structure containing Interfile keys
4022  \brief Init the keys of an the Interfile structure passed in parameter to default values
4023 */
4025 {
4026  ap_IF->nb_bed_positions = 0;
4027  // The default bed relative position is intentionaly set to FLT_MIN to guess if the corresponding field has been supplied when reading an interfile image.
4028  // In any unexpected case, at least the FLT_MIN value is very close to 0. (e-38), so as it is interpreted in mm, it should not deviate the results in a significative extent.
4029  ap_IF->bed_relative_position = FLT_MIN;
4030  ap_IF->bed_position_provided = false;
4031  ap_IF->originating_system = "";
4032  ap_IF->path_to_image = "";
4033  ap_IF->endianness = User_Endianness;
4034  ap_IF->data_offset = 0;
4035  ap_IF->nb_format = IntfKeyGetPixTypeStr();
4036  ap_IF->nb_dims = 0;
4037  for(int d=0 ; d<7 ; d++)
4038  ap_IF->mtx_size[d] = 0;
4039  for(int d=0 ; d<3 ; d++)
4040  ap_IF->vox_offset[d] = 0.;
4041  for(int d=0 ; d<3 ; d++)
4042  ap_IF->vox_size[d] = -1.;
4043 
4044  ap_IF->slice_thickness_mm = -1.;
4045  ap_IF->ctr_to_ctr_separation = 0;
4046  ap_IF->nb_time_frames = 1;
4047  ap_IF->nb_resp_gates = 1;
4048  ap_IF->nb_card_gates = 1;
4049  ap_IF->nb_total_imgs = 1;
4050  ap_IF->nb_bytes_pixel = 0;
4051  ap_IF->slice_orientation = 0;
4052  ap_IF->pat_rotation = 0;
4053  ap_IF->pat_orientation = 0;
4054  ap_IF->rescale_slope = 1.;
4055  ap_IF->rescale_intercept = 0.;
4056  ap_IF->quant_units = 1.;
4057 
4058  for(int d=0 ; d<3 ; d++)
4059  {
4060  ap_IF->cmtx_size[d] = 0;
4061  ap_IF->cvox_size[d] = -1.;
4062  ap_IF->cvox_offset[d] = 0.;
4063  }
4064  ap_IF->is_mtx_size_different = false;
4065  ap_IF->data_type = -1;
4066  ap_IF->study_duration = 1.;
4067  // ap_IF->image_duration = vector<float>;
4068  // ap_IF->image_start_time = vector<float>;
4069  // ap_IF->frame_group_pause = vector<float>;
4070  ap_IF->nb_time_windows = 0;
4071  ap_IF->process_status = "reconstructed";
4072 
4073  ap_IF->nb_detector_heads = 0;
4074  ap_IF->nb_energy_windows = 0;
4075  ap_IF->nb_projections = 1;
4076  ap_IF->extent_rotation = -1.;
4077  ap_IF->direction_rotation = "CW";
4078  ap_IF->first_angle = -1.;
4079  ap_IF->projection_angles = "";
4080  ap_IF->radius = "";
4081 };
4082 
4083 
4084 
4085 
4086 // =====================================================================
4087 // ---------------------------------------------------------------------
4088 // ---------------------------------------------------------------------
4089 // =====================================================================
4090 /*
4091  \fn IntfKeySetFieldsOutput
4092  \param ap_IF : Structure containing Interfile keys
4093  \param ap_ID : oImageDimensionsAndQuantification object containing additional infos about reconstruction
4094  \brief Init the keys of the Interfile header of an image to be written on disk
4095  \details Init the keys of the Interfile structure passed in parameter for output writing \n
4096  using the ImageDimensions object containing information about the reconstruction
4097 */
4099 {
4100  // Set header metadata using Image Dimensions object
4101  ap_IF->endianness = User_Endianness;
4102  ap_IF->nb_format = IntfKeyGetPixTypeStr();
4103  ap_IF->nb_dims = 3;
4104  if(ap_ID->GetNbTimeFrames()>1) ap_IF->nb_dims++;
4105  if(ap_ID->GetNbRespGates()>1) ap_IF->nb_dims++;
4106  if(ap_ID->GetNbCardGates()>1) ap_IF->nb_dims++;
4107  ap_IF->mtx_size[0] = ap_ID->GetNbVoxX();
4108  ap_IF->mtx_size[1] = ap_ID->GetNbVoxY();
4109  ap_IF->mtx_size[2] = ap_ID->GetNbVoxZ();
4110  ap_IF->vox_size[0] = ap_ID->GetVoxSizeX();
4111  ap_IF->vox_size[1] = ap_ID->GetVoxSizeY();
4112  ap_IF->vox_size[2] = ap_ID->GetVoxSizeZ();
4113  ap_IF->vox_offset[0] = ap_ID->GetOffsetX();
4114  ap_IF->vox_offset[1] = ap_ID->GetOffsetY();
4115  ap_IF->vox_offset[2] = ap_ID->GetOffsetZ();
4116 
4117  ap_IF->slice_thickness_mm = ap_ID->GetVoxSizeZ();
4118  ap_IF->ctr_to_ctr_separation = 0;
4119  ap_IF->nb_time_frames = ap_ID->GetNbTimeFrames();
4120  ap_IF->nb_resp_gates = ap_ID->GetNbRespGates();
4121  ap_IF->nb_card_gates = ap_ID->GetNbCardGates();
4122  ap_IF->nb_total_imgs = ap_IF->nb_time_frames *
4123  ap_IF->nb_resp_gates *
4124  ap_IF->nb_card_gates*
4125  ap_IF->mtx_size[2];
4126 
4127  ap_IF->nb_bytes_pixel = sizeof(FLTNB);
4128  //ap_IF->slice_orientation = 0; // slice orientation : (=0, default)
4129  //ap_IF->pat_rotation = 0; // patient rotation : supine (=0, default)
4130  //ap_IF->pat_orientation = 0; // slice orientation : head-in (=0, default)
4131  ap_IF->rescale_slope=1.; // multiplicative calibration values.
4132  ap_IF->rescale_intercept=0.; // additive calibration values.
4133  ap_IF->quant_units=1.; // multiplicative calibration values.
4134 
4135  // Just required for reading, not for writing
4136  //ap_IF->cmtx_size[0] = ap_ID->GetNbVoxX();
4137  //ap_IF->cmtx_size[1] = ap_ID->GetNbVoxY();
4138  //ap_IF->cmtx_size[2] = ap_ID->GetNbVoxZ();
4139  //ap_IF->cvox_size[0] = ap_ID->GetVoxSizeX();
4140  //ap_IF->cvox_size[1] = ap_ID->GetVoxSizeY();
4141  //ap_IF->cvox_size[2] = ap_ID->GetVoxSizeZ();
4142  //ap_IF->cvox_offset[0] = ap_ID->GetOffsetX();
4143  //ap_IF->cvox_offset[1] = ap_ID->GetOffsetY();
4144  //ap_IF->cvox_offset[2] = ap_ID->GetOffsetZ();
4145 
4146 
4147  //ap_IF->is_mtx_size_different = false;
4148  ap_IF->data_type = IntfKeyGetOutputImgDataType(ap_ID);
4149  ap_IF->study_duration = ap_ID->GetFinalTimeStopInSec(ap_ID->GetNbBeds()-1) -
4150  ap_ID->GetFrameTimeStartInSec(0,0);
4151  for(int fr=0 ; fr<ap_ID->GetNbTimeFrames() ; fr++)
4152  {
4153  ap_IF->image_duration.push_back(ap_ID->GetFrameDurationInSec(0, fr));
4154  ap_IF->image_start_time.push_back(ap_ID->GetFrameTimeStartInSec(0, fr));
4155  ap_IF->frame_group_pause.push_back((fr == 0) ? 0
4156  : ap_ID->GetFrameTimeStartInSec(0,fr) - ap_ID->GetFrameTimeStopInSec(0,fr-1));
4157  }
4158  ap_IF->nb_time_windows = ap_ID->GetNbRespGates() *
4159  ap_ID->GetNbCardGates();
4160  //ap_IF->process_status;
4161 
4162  //ap_IF->detector_heads = 0;
4163  ap_IF->nb_energy_windows = ap_IF->nb_resp_gates *
4164  ap_IF->nb_card_gates;
4165  //ap_IF->nb_projections
4166  //ap_IF->extent_rotation;
4167  //ap_IF->extent_rotation;
4168  //ap_IF->first_angle;
4169  //ap_IF->projection_angles;
4170  //ap_IF->radius;
4171  // Set the number of bed positions
4172  ap_IF->nb_bed_positions = ap_ID->GetNbBeds();
4173  // Set the bed position to 0 if multiple bed positions
4174  ap_IF->bed_relative_position = 0.;
4175  // Set it to the actual value if only one bed
4176  if (ap_ID->GetNbBeds()==1) ap_IF->bed_relative_position = ap_ID->GetBedPosition(0);
4177  // Set the flag that say if the bed relative position was provided from the datafile or not; in order
4178  // to know if this information should be written in the image header or not
4180 }
4181 
4182 
4183 
4184 
4185 // =====================================================================
4186 // ---------------------------------------------------------------------
4187 // ---------------------------------------------------------------------
4188 // =====================================================================
4189 /*
4190  \fn IntfKeyPrintFields
4191  \param ap_IF
4192  \brief Print all the keys of the Intf_fields structure passed in parameter, as well as their values for debugging purposes
4193 */
4195 {
4196  Cout("// ------ IntfKeyPrintFields ------ // " << endl << endl);
4197  Cout("path_to_image : " << a_IF.path_to_image << endl);
4198  Cout("endianness : " << IntfKeyGetEndianStr(a_IF.endianness) << endl);
4199  Cout("data_offset : " << a_IF.data_offset << endl);
4200  Cout("nb_format : " << a_IF.nb_format << endl);
4201  Cout("nb_dims : " << unsigned(a_IF.nb_dims) << endl); // uint_8t doesn't output with cout, hence the (us) cast
4202  for(int i=0 ; i<7 ; i++)
4203  Cout("mtx_size["<<i<<"] : " << a_IF.mtx_size[i] << endl);
4204  for(int i=0 ; i<3 ; i++)
4205  Cout("vox_offset["<<i<<"] : " << a_IF.vox_offset[i] << endl);
4206  for(int i=0 ; i<3 ; i++)
4207  Cout("vox_size["<<i<<"] : " << a_IF.vox_size[i] << endl);
4208 
4209  Cout("slice_thickness_mm : " << a_IF.slice_thickness_mm << endl);
4210  Cout("ctr_to_ctr_separation : " << a_IF.ctr_to_ctr_separation << endl);
4211  Cout("nb_time_frames : " << a_IF.nb_time_frames << endl);
4212  Cout("nb_resp_gates : " << a_IF.nb_resp_gates << endl);
4213  Cout("nb_card_gates : " << a_IF.nb_card_gates << endl);
4214  Cout("nb_total_imgs : " << a_IF.nb_total_imgs << endl);
4215  Cout("nb_bytes_pixel : " << unsigned(a_IF.nb_bytes_pixel) << endl); // uint_8t doesn't output with cout, hence the (us) cast
4216  Cout("slice_orientation : " << a_IF.slice_orientation << endl);
4217  Cout("pat_rotation : " << a_IF.pat_rotation << endl);
4218  Cout("pat_orientation : " << a_IF.pat_orientation << endl);
4219  Cout("rescale_slope : " << a_IF.rescale_slope << endl);
4220  Cout("rescale_intercept : " << a_IF.rescale_intercept << endl);
4221  Cout("quantification_units : " << a_IF.quant_units << endl);
4222  for(int i=0 ; i<3 ; i++)
4223  Cout("cmtx_size["<<i<<"] : " << a_IF.cmtx_size[i] << endl);
4224  for(int i=0 ; i<3 ; i++)
4225  Cout("cvox_size["<<i<<"] : " << a_IF.cvox_size[i] << endl);
4226  for(int i=0 ; i<3 ; i++)
4227  Cout("cvox_offset["<<i<<"] : " << a_IF.cvox_offset[i] << endl);
4228 
4229  Cout("is_mtx_size_different : " << a_IF.is_mtx_size_different << endl);
4230  Cout("data_type : " << a_IF.data_type << endl);
4231  Cout("study_duration : " << a_IF.study_duration << endl);
4232  Cout("image_duration(fr) : " << endl);
4233  for(uint32_t fr=0 ; fr<a_IF.image_duration.size() ; fr++)
4234  Cout("image_duration("<<fr+1<<") : " << a_IF.image_duration.at(fr) << endl);
4235  Cout("image_start_time(fr) : " << endl);
4236  for(uint32_t fr=0 ; fr<a_IF.image_start_time.size() ; fr++)
4237  Cout("image_start_time("<<fr+1<<") : " << a_IF.image_start_time.at(fr) << endl);
4238  Cout("pause_duration(fr) : " << endl);
4239  for(uint32_t fr=0 ; fr<a_IF.frame_group_pause.size() ; fr++)
4240  Cout("pause_duration("<<fr+1<<") : " << a_IF.frame_group_pause.at(fr) << endl);
4241 
4242  //ap_IF->image_duration = vector<float>;
4243  Cout("nb_time_windows : " << a_IF.nb_time_windows << endl);
4244  Cout("process_status : " << a_IF.process_status << endl);
4245 
4246  // SPECT and projection related data
4247  Cout("nb_detector_heads : " << a_IF.nb_detector_heads << endl);
4248  Cout("nb_energy_windows : " << a_IF.nb_energy_windows << endl);
4249  Cout("nb_projections : " << a_IF.nb_projections << endl);
4250  Cout("extent_rotation : " << a_IF.extent_rotation << endl);
4251  Cout("direction_rotation : " << a_IF.direction_rotation << endl);
4252  Cout("first_angle : " << a_IF.first_angle << endl);
4253  Cout("projection_angles : " << a_IF.projection_angles << endl);
4254  Cout("radius : " << a_IF.radius << endl);
4255  Cout("// ------ ------------------ ------ // " << endl << endl);
4256 }
4257 
4258 
4259 
4260 
4261 // =====================================================================
4262 // ---------------------------------------------------------------------
4263 // ---------------------------------------------------------------------
4264 // =====================================================================
4265 /*
4266  \fn IntfRecoverKey
4267  \param ap_Key : Structure to recover the parsed key components (key, value,..)
4268  \param a_line : String to process
4269  \brief Process the line passed in parameter and write the key information in the ap_Key Intf_key member structure
4270  \details .korig : Get original line without comments
4271  .kcase : Get key without spaces and without comments
4272  .klcase: Same as kcase, in lower case
4273  .kvalue: Value of the key, without spaces
4274  \todo Check that IntfToLowerCase() doesn't cause issue with some characters or some ASCII file format (unicode, etc..)
4275  \return 0 if success, positive value otherwise.
4276 */
4277 int IntfRecoverKey(Intf_key* ap_Key, const string& a_line)
4278 {
4279  string intf_sep = ":=";
4280 
4281  // Remove any comment from the original key line
4282  int pos = a_line.find_first_of(';',0);
4283  ap_Key->korig = a_line.substr(0, pos);
4284 
4285  // check for interfile separator.
4286  pos = ap_Key->korig.find_first_of(intf_sep);
4287 
4288  // If interfile key is not found (not an interfile key or wrong), just add it at the end of the key and proceed
4289  if (ap_Key->korig.find(intf_sep) == string::npos)
4290  ap_Key->korig.append(":=");
4291 
4292  // Get key title
4293  ap_Key->kcase = ap_Key->korig.substr(0,pos);
4294  // Get key value
4295  ap_Key->kvalue = ap_Key->korig.substr(pos+2);
4296 
4297  // Get case insensitive key, and without space
4298  ap_Key->klcase = ap_Key->kcase;
4299  IntfToLowerCase(&ap_Key->klcase); // TODO Safe ?
4300 
4301  // Get key value
4302  ap_Key->kvalue = ap_Key->korig.substr(pos+2);
4303 
4304  // Clear the space before the first and after the last element in the key and value;
4305  IntfEraseSpaces(&ap_Key->klcase);
4306  IntfEraseSpaces(&ap_Key->kvalue);
4307 
4308  return 0;
4309 }
4310 
4311 
4312 
4313 
4314 // =====================================================================
4315 // ---------------------------------------------------------------------
4316 // ---------------------------------------------------------------------
4317 // =====================================================================
4318 /*
4319  \fn IntfCheckKeyMatch(Intf_key ap_Key, const string& a_field)
4320  \param ap_Key : Structure containing the parsed key components (key, value,..)
4321  \param a_line : String containing an interfile key
4322  \brief Check if the key matches the string passed in parameter
4323  \todo Be sure it is appropriate to use Intf_key.klcase for each key comparison
4324  \return 1 if success, 0 otherwise (not found).
4325 */
4326 int IntfCheckKeyMatch(Intf_key ap_Key, const string& a_field)
4327 {
4328 /*
4329  // TODO Check if appropriate to use .klcase in any situation for key comparison
4330  // SS: but the klcase has already been applied by the IntfRecoverKey called before calling this IntfCheckKeyMatch function ?
4331  if(ap_Key.klcase == a_field)
4332  return 1;
4333  else
4334  return 0;
4335 */
4336  // ----------------
4337  // SS: we now remove all blanks, tabulations, carriage returns, end of line, and compare the strings without any of these characters
4338  // ----------------
4339  string a_copy_of_the_key = ap_Key.klcase;
4340  string a_copy_of_the_field = a_field;
4341  size_t found_char_at_pos = string::npos;
4342  // Process the key
4343  while ( (found_char_at_pos=a_copy_of_the_key.find_first_of(" \t\r\n")) != string::npos) a_copy_of_the_key.replace(found_char_at_pos,1,"");
4344  // Process the field
4345  while ( (found_char_at_pos=a_copy_of_the_field.find_first_of(" \t\r\n")) != string::npos) a_copy_of_the_field.replace(found_char_at_pos,1,"");
4346  // Now compare them
4347 //cout << "Copy of the key: " << a_copy_of_the_key << endl;
4348 //cout << "Copy of the field: " << a_copy_of_the_field << endl;
4349  if (a_copy_of_the_key==a_copy_of_the_field) return 1;
4350  else return 0;
4351 }
4352 
4353 
4354 
4355 
4356 
4357 // =====================================================================
4358 // ---------------------------------------------------------------------
4359 // ---------------------------------------------------------------------
4360 // =====================================================================
4361 /*
4362  \fn IntfKeyIsArray()
4363  \param ap_Key
4364  \brief Check if the key passed in parameter is an array (contains brackets '{' and '}' )
4365  \return 1 if success, 0 otherwise (not array).
4366 */
4368 {
4369  if(ap_Key.kvalue.find("{") != string::npos &&
4370  ap_Key.kvalue.find("}") != string::npos)
4371  return 1;
4372 
4373  return 0;
4374 }
4375 
4376 
4377 
4378 
4379 // =====================================================================
4380 // ---------------------------------------------------------------------
4381 // ---------------------------------------------------------------------
4382 // =====================================================================
4383 /*
4384  \fn IntfKeyGetArrayNbElts
4385  \param a_Key
4386  \brief Return the number of elts in an Interfile array Key
4387  \return the number of elements in the array key, or negative value if error
4388 */
4390 {
4391  int nb_elts = 0;
4392  string val_str = a_Key.kvalue;
4393 
4394  size_t pos = val_str.find_first_of('{')+1;
4395 
4396  while (pos < val_str.length())
4397  {
4398  size_t pos_c = val_str.find_first_of(",", pos);
4399 
4400  // no comma found, looking for closing bracket
4401  if(pos_c == string::npos)
4402  {
4403  pos_c = val_str.find_first_of("}", pos);
4404  if(! (IntfKeyIsArray(a_Key)) )
4405  {
4406  Cerr("***** IntfKeyGetArrayNbElts-> Error : closing bracket not found in interfile array key : "<< a_Key.korig << " !" << endl);
4407  return -1;
4408  }
4409  else
4410  return nb_elts+1; // add last elt before the end bracket
4411  }
4412  pos = pos_c+1;
4413  nb_elts++;
4414  }
4415 
4416  // return error if we end of key if reached without closing bracket
4417  Cerr("***** IntfKeyGetArrayNbElts-> Error : closing bracket not found in interfile array key : "<< a_Key.korig << " !" << endl);
4418  return -1;
4419 }
4420 
4421 
4422 
4423 
4424 // =====================================================================
4425 // ---------------------------------------------------------------------
4426 // ---------------------------------------------------------------------
4427 // =====================================================================
4428 /*
4429  \fn IntfKeyGetMaxArrayKey
4430  \param ap_Key
4431  \brief Return the maximum value in an array (key value contains brackets '{,,}' )
4432  \return the max value in the array key.
4433 */
4435 {
4436  int value, max=0;
4437  string val_str = ap_Key.kvalue;
4438  if (val_str == "") return(max);
4439 
4440  size_t pos = val_str.find_first_of('{')+1;
4441 
4442  while (pos < val_str.length())
4443  {
4444  size_t pos_c = val_str.find_first_of(",", pos);
4445 
4446  // no comma found, looking for closing bracket
4447  if(pos_c == string::npos) pos_c = val_str.find_first_of("}", pos);
4448 
4449  if(ConvertFromString(val_str.substr(pos,pos_c-pos), &value) )
4450  {
4451  Cerr("***** IntfKeyGetMaxArrayKey()-> An error occurred when trying to recover the following value from the array key : "<< val_str.substr(pos,pos_c-pos) << " !" << endl);
4452  return 1;
4453  }
4454 
4455  if (value > max) max = value;
4456 
4457  pos = pos_c+1;
4458  }
4459 
4460  return max;
4461 }
4462 
4463 
4464 
4465 
4466 // =====================================================================
4467 // ---------------------------------------------------------------------
4468 // ---------------------------------------------------------------------
4469 // =====================================================================
4470 /*
4471  \fn int IntfKeyGetArrayElts()
4472  \param ap_Key
4473  \param T* ap_return : Templated parameter in which the elts will be returned
4474  \brief Get all the elements in an array key in a templated array passed in parameter.
4475  It assumes the return variable has been instanciated with a correct number of elements.
4476  \return 0 if success, positive value otherwise
4477 */
4478 template<class T>
4479 int IntfKeyGetArrayElts(Intf_key a_Key, T* ap_return)
4480 {
4481  string val_str = a_Key.kvalue;
4482 
4483  // Check if interfile key
4484  if(! (IntfKeyIsArray(a_Key)) )
4485  {
4486  Cerr("***** IntfKeyGetArrayElts-> Error : Problem reading the following interfile array key : "<< a_Key.korig << " !" << endl);
4487  return 1;
4488  }
4489 
4490  if (val_str == "")
4491  {
4492  Cerr("***** IntfKeyGetArrayElts-> Error : no elements in the array key : "<< a_Key.korig << " !" << endl);
4493  return 1;
4494  }
4495 
4496  size_t pos = val_str.find_first_of('{')+1;
4497 
4498  int elt = 0;
4499 
4500  while (pos < val_str.length())
4501  {
4502  size_t pos_c = val_str.find_first_of(",", pos);
4503 
4504  // no comma found, looking for closing bracket
4505  if(pos_c == string::npos) pos_c = val_str.find_first_of("}", pos);
4506 
4507  if(ConvertFromString(val_str.substr(pos,pos_c-pos), &ap_return[elt]) )
4508  {
4509  Cerr("***** IntfKeyGetMaxArrayKey()-> An error occurred when trying to recover the following value from the array key : "<< val_str.substr(pos,pos_c-pos) << " !" << endl);
4510  return 1;
4511  }
4512 
4513  pos = pos_c+1;
4514  elt++;
4515  }
4516 
4517  return 0;
4518 }
4519 
4520 
4521 
4522 
4523 // =====================================================================
4524 // ---------------------------------------------------------------------
4525 // ---------------------------------------------------------------------
4526 // =====================================================================
4527 /*
4528  \fn IntfGetVoxIdxSHTOrientation(Intf_fields a_IF, int a_voxId)
4529  \param ap_IF
4530  \param a_voxId : index of the voxel in a 1-D image vector
4531  \brief Compute a voxel index corresponding to the default orientation (Sup/Hin/Trans) \n
4532  from the orientation informations contained in the Intf_fields passed in parameter
4533  \todo NOT CURRENTLY IMPLEMENTED
4534  \return new voxel index
4535 */
4537 {
4538  int dimX=a_IF.vox_size[0], dimY=a_IF.vox_size[1], dimZ=a_IF.vox_size[2];
4539  int dimXY=dimX*dimY;
4540 
4541  // Get x,y,z original image indexes
4542  int z = a_voxId/dimXY;
4543  int y = (a_voxId - z*dimXY) / dimX;
4544  int x = a_voxId - z*dimXY - y*dimX;
4545 
4546  // new image indexes
4547  int X = x;
4548  int Y = y;
4549  int Z = z;
4550 
4551  // Convert to default orientations (TRANSVERSE / SUPINE / HEADIN)
4552  if(a_IF.slice_orientation == INTF_TRANSVERSE) // X=x, Y=y, Z=z
4553  {
4554  if(a_IF.pat_orientation == INTF_HEADIN)
4555  {
4556  if(a_IF.pat_rotation == INTF_SUPINE)
4557  {
4558  // nothing to change
4559  return a_voxId;
4560  }
4561  else // a_IF.pat_rotation == INTF_PRONE
4562  {
4563  X = dimX-x;
4564  Y = dimY-y;
4565  }
4566  }
4567  else // a_IF.pat_orientation == INTF_FEETIN
4568  {
4569  if(a_IF.pat_rotation == INTF_SUPINE)
4570  {
4571  X = dimX-x;
4572  Z = dimZ-z;
4573  }
4574  else // a_IF.pat_rotation == INTF_PRONE
4575  {
4576  Y = dimY-y;
4577  Z = dimZ-z;
4578  }
4579  }
4580  }
4581  else if(a_IF.slice_orientation == INTF_CORONAL) // X=x, Y=z, Z=y
4582  {
4583  if(a_IF.pat_orientation == INTF_HEADIN)
4584  {
4585  if(a_IF.pat_rotation == INTF_SUPINE)
4586  {
4587  Y = z;
4588  Z = y;
4589  }
4590  else // a_IF.pat_rotation == INTF_PRONE
4591  {
4592  X = dimX-x;
4593  Y = dimZ-z;
4594  Z = y;
4595  }
4596  }
4597  else // a_IF.pat_orientation == INTF_FEETIN
4598  {
4599  if(a_IF.pat_rotation == INTF_SUPINE)
4600  {
4601  X = dimX-x;
4602  Y = z;
4603  Z = dimY-y;
4604  }
4605  else // a_IF.pat_rotation == INTF_PRONE
4606  {
4607  X = dimX-x;
4608  Y = dimZ-z;
4609  Z = dimY-y;
4610  }
4611  }
4612  }
4613  else // a_IF.slice_orientation == INTF_SAGITTAL // X=z, Y=x, Z=y
4614  {
4615  if(a_IF.pat_orientation == INTF_HEADIN)
4616  {
4617  if(a_IF.pat_rotation == INTF_SUPINE)
4618  {
4619  X = dimZ-z;
4620  Y = dimX-x;
4621  Z = y;
4622  }
4623  else // a_IF.pat_rotation == INTF_PRONE
4624  {
4625  X = z;
4626  Y = x;
4627  Z = y;
4628  }
4629  }
4630  else // a_IF.pat_orientation == INTF_FEETIN
4631  {
4632  if(a_IF.pat_rotation == INTF_SUPINE)
4633  {
4634  X = z;
4635  Y = dimX-x;
4636  Z = dimY-y;
4637  }
4638  else // a_IF.pat_rotation == INTF_PRONE
4639  {
4640  X = dimZ-z;
4641  Y = x;
4642  Z = dimY-y;
4643  }
4644  }
4645  }
4646 
4647  a_voxId = X + Y*dimX + Z*dimX*dimY;
4648  return a_voxId;
4649 }
4650 
4651 
4652 
4653 
4654 // =====================================================================
4655 // ---------------------------------------------------------------------
4656 // ---------------------------------------------------------------------
4657 // =====================================================================
4658 /*
4659  \fn IntfKeyGetEndianStr()
4660  \param a_val :
4661  \brief return the endian string corresponding to the value passed in parameter (see module INTF_ENDIANNESS).
4662  \return "BIG_ENDIAN" if 0, \n
4663  "LITTLE_ENDIAN" if 1, \n
4664  "UNKNOWN" otherwise
4665 */
4666 string IntfKeyGetEndianStr(int a_val)
4667 {
4668  if(a_val == 0) return "BIGENDIAN";
4669  if(a_val == 1) return "LITTLEENDIAN";
4670  return "UNKNOWN";
4671 }
4672 
4673 
4674 /*
4675  \fn IntfKeyGetModalityStr
4676  \param a_modalityIdx
4677  \brief Convert the integer provided in parameter to the string related
4678  to the corresponding modality as defined by the scanner objects
4679  \todo Add other modalities as we implement them
4680  \return string corresponding to the modality
4681 */
4682 string IntfKeyGetModalityStr(int a_modalityIdx)
4683 {
4684  if(a_modalityIdx == 0)
4685  return "PET";
4686  else if(a_modalityIdx == 1)
4687  return "SPECT";
4688  /*
4689  else if(a_modalityIdx == 2)
4690  return "CT";
4691  else if(a_modalityIdx == 3) // Pinhole
4692  return "SPECT";
4693  */
4694  else
4695  return "UNKNOWN";
4696 }
4697 
4698 
4699 
4700 
4701 // =====================================================================
4702 // ---------------------------------------------------------------------
4703 // ---------------------------------------------------------------------
4704 // =====================================================================
4705 /*
4706  \fn int IntfKeyGetInputImgDataType()
4707  \param a_str : original string
4708  \brief Get the image data type corresponding to the the input string
4709  \return int value corresponding to the image data type (see module INTF_IMG_TYPE).
4710 */
4711 int IntfKeyGetInputImgDataType(const string& a_str)
4712 {
4713  if (a_str == "static")
4714  return INTF_IMG_STATIC;
4715  if (a_str == "dynamic")
4716  return INTF_IMG_DYNAMIC;
4717  if (a_str == "gated")
4718  return INTF_IMG_GATED;
4719  if (a_str == "tomographic")
4720  return INTF_IMG_SPECT;
4721  if (a_str == "gspect")
4722  return INTF_IMG_GSPECT;
4723  if (a_str == "pet")
4724  return INTF_IMG_PET;
4725 
4726  return INTF_IMG_UNKNOWN;
4727 }
4728 
4729 
4730 
4731 
4732 // =====================================================================
4733 // ---------------------------------------------------------------------
4734 // ---------------------------------------------------------------------
4735 // =====================================================================
4736 /*
4737  \fn IntfKeyGetOutImgDataType
4738  \param ap_ID
4739  \brief Get the image data type corresponding to the image metadata passed in parameter
4740  \return int value corresponding to the image data type (see module INTF_IMG_TYPE).
4741 */
4743 {
4745 
4746  int data_type = 0;
4747 
4748  // Update data type key
4749  if(ap_ID->GetNbTimeFrames() > 1)
4750  data_type = INTF_IMG_DYNAMIC;
4751  else if(p_scanMgr->GetScannerType() == 2 &&
4752  (ap_ID->GetNbRespGates() > 1 ||
4753  ap_ID->GetNbCardGates() > 1 ) )
4754  data_type = INTF_IMG_GSPECT;
4755  else if(ap_ID->GetNbRespGates() > 1 ||
4756  ap_ID->GetNbCardGates() > 1 )
4757  data_type = INTF_IMG_GATED;
4758  else if(p_scanMgr->GetScannerType() == 2)
4759  data_type = INTF_IMG_SPECT;
4760  else
4761  data_type = INTF_IMG_STATIC;
4762 
4763  return data_type;
4764 }
4765 
4766 
4767 
4768 
4769 // =====================================================================
4770 // ---------------------------------------------------------------------
4771 // ---------------------------------------------------------------------
4772 // =====================================================================
4773 /*
4774  \fn IntfKeyGetPixTypeStr()
4775  \brief Return the string corresponding to the nb of bytes in the type FLTNB
4776  \return string corresponding to the pixel data type
4777 */
4779 {
4780  // Return either "long long float" (long double type), "long float" (doubletype ) or "short float" (float type)
4781  if (sizeof(FLTNB) == 4) return "short float";
4782  else if (sizeof(FLTNB) == 8) return "long float";
4783  else if (sizeof(FLTNB) == 16) return "long long float";
4784  else
4785  {
4786  Cerr("***** oInterfileIO::IntfKeyGetPixTypeStr() -> Size of current float type (" << sizeof(FLTNB) << ") does not correspond to a known type !" << endl);
4787  Exit(EXIT_FAILURE);
4788  }
4789 }
4790 
4791 
4792 
4793 
4794 // =====================================================================
4795 // ---------------------------------------------------------------------
4796 // ---------------------------------------------------------------------
4797 // =====================================================================
4798 /*
4799  \fn IntfKeyGetPatOrientation()
4800  \param ap_IF
4801  \brief Get the complete patient orientation from an Intf_fields structure according to
4802  the values of keys 'slice orientation', 'patient rotation', and 'patient orientation'
4803  \return int value corresponding to the patient orientation (see module PATIENT_ORIENTATION).
4804 */
4806 {
4807  switch (ap_IF.pat_rotation)
4808  {
4809  case INTF_SUPINE:
4810  switch (ap_IF.pat_orientation)
4811  {
4812  case INTF_HEADIN:
4813  switch (ap_IF.slice_orientation)
4814  {
4815  case INTF_TRANSVERSE:
4816  return(INTF_SUPINE_HEADIN_TRANSAXIAL); break;
4817  case INTF_SAGITTAL :
4818  return(INTF_SUPINE_HEADIN_SAGITTAL); break;
4819  case INTF_CORONAL :
4820  return(INTF_SUPINE_HEADIN_CORONAL); break;
4821  }
4822  break;
4823 
4824  case INTF_FEETIN:
4825  switch(ap_IF.slice_orientation)
4826  {
4827  case INTF_TRANSVERSE:
4828  return(INTF_SUPINE_FEETIN_TRANSAXIAL); break;
4829  case INTF_SAGITTAL :
4830  return(INTF_SUPINE_FEETIN_SAGITTAL); break;
4831  case INTF_CORONAL :
4832  return(INTF_SUPINE_FEETIN_CORONAL); break;
4833  }
4834  break;
4835  }
4836  break;
4837 
4838  case INTF_PRONE :
4839  switch (ap_IF.pat_orientation)
4840  {
4841  case INTF_HEADIN:
4842  switch (ap_IF.slice_orientation)
4843  {
4844  case INTF_TRANSVERSE:
4845  return(INTF_PRONE_HEADIN_TRANSAXIAL); break;
4846  case INTF_SAGITTAL :
4847  return(INTF_PRONE_HEADIN_SAGITTAL); break;
4848  case INTF_CORONAL :
4849  return(INTF_PRONE_HEADIN_CORONAL); break;
4850  }
4851  break;
4852  case INTF_FEETIN:
4853  switch (ap_IF.slice_orientation)
4854  {
4855  case INTF_TRANSVERSE :
4856  return(INTF_PRONE_FEETIN_TRANSAXIAL); break;
4857  case INTF_SAGITTAL :
4858  return(INTF_PRONE_FEETIN_SAGITTAL); break;
4859  case INTF_CORONAL :
4860  return(INTF_PRONE_FEETIN_CORONAL); break;
4861  }
4862  break;
4863  }
4864  break;
4865  }
4866 
4867  return(INTF_SUPINE_HEADIN_TRANSAXIAL); // default
4868 }
4869 
4870 
4871 
4872 
4873 // =====================================================================
4874 // ---------------------------------------------------------------------
4875 // ---------------------------------------------------------------------
4876 // =====================================================================
4877 /*
4878  \fn GetUserEndianness()
4879  \brief Check user/host computer endianness and write it to the global variable User_Endianness
4880  \details This function should be called once during the initialization step of the algorithm (currently, the singleton initialization)
4881  \todo Maybe better compute it in a preprocessor macro
4882 */
4884 {
4885  int n = 1;
4886  User_Endianness = (*(char *)&n) == 1 ? 1 : 0 ;
4887 }
4888 
4889 
4890 
4891 
4892 // =====================================================================
4893 // ---------------------------------------------------------------------
4894 // ---------------------------------------------------------------------
4895 // =====================================================================
4896 /*
4897  \fn IntfEraseSpaces()
4898  \param string* input_str
4899  \brief Erase space, blank characters (\t\r\n), and '!' before and after the characters in the string passed in parameter
4900 */
4901 void IntfEraseSpaces(string* input_str)
4902 {
4903  input_str->erase(0, input_str->find_first_not_of(" !\t\r\n")); // Erase all blank stuff before first character
4904  input_str->erase(input_str->find_last_not_of(" \t\r\n")+1 , input_str->length()); // Erase all blank stuff after last character
4905 }
4906 
4907 
4908 
4909 
4910 // =====================================================================
4911 // ---------------------------------------------------------------------
4912 // ---------------------------------------------------------------------
4913 // =====================================================================
4914 /*
4915  \fn IntfToLowerCase()
4916  \param string* ap_str : original string
4917  \brief Set all characters of the string passed in parameter to lower case
4918  \todo May have issue with non ASCII characters, file decoding, etc..
4919 */
4920 void IntfToLowerCase(string* ap_str)
4921 {
4922  std::transform(ap_str->begin(), ap_str->end(), ap_str->begin(), ::tolower);
4923 }
4924 
4925 
4926 
4927 
4928 // =====================================================================
4929 // ---------------------------------------------------------------------
4930 // ---------------------------------------------------------------------
4931 // =====================================================================
4932 /*
4933  \fn void IntfKeyGetPatientNameTag()
4934  \brief Recover datafile name(s) stored in sOutputManager in one string
4935 */
4937 {
4938  // Recover here the name of the datafile used to reconstruct the images [ bed ]
4939  string patient_tag_str = "";
4941 
4942  vector<string> dfName = p_outputMgr->GetDataFileName();
4943 
4944  if(dfName.size() > 1)
4945  {
4946  patient_tag_str += "{ " + dfName[0];
4947  for(size_t n=1 ; n<dfName.size() ; n++ )
4948  patient_tag_str += ", " + dfName[n];
4949  patient_tag_str += "} ";
4950  }
4951  else if (dfName.size() == 1)
4952  patient_tag_str += dfName[0];
4953  else
4954  patient_tag_str += "unknown data";
4955 
4956  return patient_tag_str;
4957 }
4958 
4959 
4960 
4961 // =====================================================================
4962 // ---------------------------------------------------------------------
4963 // ---------------------------------------------------------------------
4964 // =====================================================================
4965 /*
4966  \fn SwapBytes()
4967  \param T *ap_type : Variable of type T
4968  \brief Use std::reverse to swap the bits of a variable of any type
4969  \details Used for Little/Big Endian conversion
4970 */
4971 template <class T>
4972 void SwapBytes(T *ap_type)
4973 {
4974  char *buffer = reinterpret_cast<char*>(ap_type);
4975  std::reverse(buffer, buffer + sizeof(T));
4976 
4977  //int i, j;
4978  //for (i=0,j=bytes-1;i < (bytes/2); i++, j--)
4979  //{
4980  // ptr[i]^=ptr[j]; ptr[j]^=ptr[i]; ptr[i]^=ptr[j];
4981  //}
4982 
4983  //for (int i = 0; i < size; ++i)
4984  // buffer[i] = ((buffer[i]>> 8) | (buffer[i] << 8));
4985 }
template int IntfKeyGetValueFromFile< uint16_t >(const string &a_pathToHeader, const string &a_key, uint16_t *ap_return, int a_nbElts, int a_mandatoryFlag)
#define INTF_SUPINE_FEETIN_CORONAL
void SwapBytes(T *ap_type)
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
void GetUserEndianness()
Check user/host computer endianness and write it to the global variable User_Endianness.
int IntfWriteImage(const string &a_pathToImg, FLTNB *ap_outImgMtx, uint32_t a_dim, int vb)
Write Interfile raw data whose path is provided in parameter, using image matrix provided in paramete...
int IntfWriteWholeDynBasisCoeffImgFile(const string &a_pathToImg, FLTNB ****a4p_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int vb)
Main function dedicated to Interfile 6D (dynamic dims + 3D ) image writing of basis function coeffici...
void IntfKeySetFieldsOutput(Intf_fields *ap_IF, oImageDimensionsAndQuantification *ap_ID)
Init the keys of the Interfile header of an image to be written on disk.
int IntfIsMHD(string a_pathToFile, vector< string > &ap_lPathToImgs)
Check if the string in argument contains the path to a Interfile metaheader.
int IntfCheckDimensionsConsistency(Intf_fields ImgFields1, Intf_fields ImgFields2)
string GetPathOfFile(const string &a_pathToFile)
Simply return the path to the directory of a file path string passed in parameter.
template int IntfKeyGetRecurringValueFromFile< uint32_t >(const string &a_pathToHeader, const string &a_key, uint32_t *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
int GetNbCardBasisFunctions()
Get the number of cardiac basis functions.
int IntfReadData(Intf_fields a_IF, ifstream *ap_iFile, FLTNB *ap_outImgMatrix, FLTNB *ap_inImgMatrix, uint32_t *a_offset, int a_nbVox, int a_verbose, T *bytes)
Templated function which read an image voxel by voxel and store it in the ap_outImgMtx image matrix...
template int IntfKeyGetValueFromFile< uint32_t >(const string &a_pathToHeader, const string &a_key, uint32_t *ap_return, int a_nbElts, int a_mandatoryFlag)
#define INTF_PRONE_HEADIN_CORONAL
FLTNB GetVoxSizeX()
Get the voxel&#39;s size along the X axis, in mm.
#define Cerr(MESSAGE)
int IntfWriteContentOfInputDataHeaderIntoInterfileHeader(ofstream &ap_ofile, int vb)
int IntfReadImgDynCoeffFile(const string &a_pathToHeaderFile, FLTNB **a2p_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int a_nbFbasis, int a_verbose, bool a_lerpFlag)
Function dedicated to Interfile image reading for dynamic coefficients images.
int IntfGetPixelTypeAndReadData(Intf_fields a_IF, ifstream *ap_iFile, FLTNB *ap_outImgMatrix, FLTNB *ap_inImgMatrix, uint32_t *ap_offset, int a_nbVox, int a_verbose)
The purpose of this function is to call the templated ReadData() function with the data type correspo...
string IntfKeyGetPatientNameTag()
Recover datafile name(s) stored in sOutputManager in one string.
template int IntfKeyGetValueFromFile< double >(const string &a_pathToHeader, const string &a_key, double *ap_return, int a_nbElts, int a_mandatoryFlag)
FLTNB GetVoxSizeZ()
Get the voxel&#39;s size along the Z axis, in mm.
int IntfWriteImageFromIntfFields(const string &a_pathToImg, FLTNB *ap_ImgMatrix, Intf_fields Img_fields, int vb)
#define INTF_PRONE_FEETIN_TRANSAXIAL
void IntfKeyInitFields(Intf_fields *ap_IF)
Init the file of an Interfile fields structure passed in parameter to their default values...
#define INTF_PRONE_FEETIN_CORONAL
int IntfWriteImgFile(const string &a_pathToImg, FLTNB *ap_ImgMatrix, const Intf_fields &ap_IntfF, int vb)
Main function dedicated to Interfile 3D image writing. Recover image information from a provided In...
string IntfKeyGetPixTypeStr()
Return the string corresponding to the nb of bytes in the type FLTNB.
#define INTF_IMG_SPECT
#define INTF_SUPINE_FEETIN_SAGITTAL
int IntfKeyGetInputImgDataType(const string &a_str)
Get the image data type corresponding to the image metadata passed in parameter.
template int IntfKeyGetRecurringValueFromFile< string >(const string &a_pathToHeader, const string &a_key, string *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
template int IntfKeyGetRecurringValueFromFile< uint16_t >(const string &a_pathToHeader, const string &a_key, uint16_t *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
#define INTF_IMG_STATIC
int IntfRecoverKey(Intf_key *ap_Key, const string &a_line)
Process the line passed in parameter and write the key information in the ap_Key Intf_key member stru...
#define INTF_PRONE_FEETIN_SAGITTAL
int GetNbTimeBasisFunctions()
Get the number of time basis functions.
int IntfWriteData(ofstream *ap_oFile, FLTNB *ap_outImgMatrix, int a_nbVox, int vb)
Write the content of the image matrix in the file pointed by ofstream.
void Exit(int code)
int IntfReadHeader(const string &a_pathToHeaderFile, Intf_fields *ap_IF, int vb)
Read an Interfile header.
bool FLTNBIsEqual(FLTNB a, FLTNB b, FLTNB a_eps)
Comparison of FLTNB numbers.
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
template int IntfKeyGetValueFromFile< int64_t >(const string &a_pathToHeader, const string &a_key, int64_t *ap_return, int a_nbElts, int a_mandatoryFlag)
template int IntfKeyGetValueFromFile< long double >(const string &a_pathToHeader, const string &a_key, long double *ap_return, int a_nbElts, int a_mandatoryFlag)
#define KEYWORD_MANDATORY_NOT_FOUND
template int IntfKeyGetRecurringValueFromFile< double >(const string &a_pathToHeader, const string &a_key, double *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
bool GetProvidedBedPositionFlag()
Say if the bed relative position was provided from the datafile or not.
int IntfWriteImgDynCoeffFile(const string &a_pathToImg, FLTNB **a2p_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int a_nbParImgs, int vb, bool a_mergeDynImgFlag)
int IntfWriteProjFile(const string &a_pathToImg, FLTNB **a2p_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, Intf_fields a_Imgfields, int vb)
Function dedicated to Interfile image writing for projected data.
template int IntfKeyGetRecurringValueFromFile< uint8_t >(const string &a_pathToHeader, const string &a_key, uint8_t *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
int IntfKeyGetPatOrientation(Intf_fields ap_IF)
Get the complete patient orientation from an Intf_fields structure according to the values of keys &#39;s...
int IntfReadImage(const string &a_pathToHeaderFile, FLTNB *ap_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int a_verbose, bool a_lerpFlag)
Main function dedicated to Interfile 3D image loading.
#define INTF_SUPINE_HEADIN_SAGITTAL
FLTNB GetVoxSizeY()
Get the voxel&#39;s size along the Y axis, in mm.
#define INTF_IMG_GSPECT
#define SCANNER_SPECT_CONVERGENT
int IntfGetVoxIdxSHTOrientation(Intf_fields a_IF, int a_voxId)
Compute a voxel index corresponding to the default orientation (Sup/Hin/Trans) from the orientation...
template int IntfKeyGetValueFromFile< float >(const string &a_pathToHeader, const string &a_key, float *ap_return, int a_nbElts, int a_mandatoryFlag)
int IntfKeyGetOutputImgDataType(oImageDimensionsAndQuantification *ap_ID)
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
template int IntfKeyGetValueFromFile< string >(const string &a_pathToHeader, const string &a_key, string *ap_return, int a_nbElts, int a_mandatoryFlag)
void IntfAllocInterpImg(FLTNB **a2p_img, Intf_fields a_IF)
Allocate memory for an image matrix to recover an image to interpolate.
Singleton class that Instantiate and initialize the scanner object.
FLTNB * IntfLoadImageFromScratch(const string &a_pathToHeaderFile, Intf_fields *ap_ImgFields, int vb)
void IntfToLowerCase(string *ap_str)
Set all characters of the string passed in parameter to lower case.
int IntfKeyGetRecurringValueFromFile(const string &a_pathToHeader, const string &a_key, T *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
int IntfReadProjectionImage(const string &a_pathToHeaderFile, FLTNB *ap_ImgMatrix, Intf_fields *ap_IF, int vb, bool a_lerpFlag)
Main function which reads a projection Interfile 3D projection image and store its content in the pro...
int ImageInterpolation(FLTNB *ap_iImg, FLTNB *ap_oImg, const uint32_t ap_iDimVox[3], const uint32_t ap_oDimVox[3], const FLTNB ap_iSizeVox[3], const FLTNB ap_oSizeVox[3], const FLTNB ap_iOffVox[3], const FLTNB ap_oOffVox[3])
#define INTF_SUPINE_HEADIN_TRANSAXIAL
int IntfKeyGetArrayElts(Intf_key a_Key, T *ap_return)
Get all the elements in an array key in a templated array passed in parameter. It assumes the retur...
int IntfCheckKeyMatch(Intf_key ap_Key, const string &a_field)
Check if the key matches the string passed in parameter.
#define INTF_SUPINE_HEADIN_CORONAL
int IntfCheckConsistency(Intf_fields *ap_IF, oImageDimensionsAndQuantification *ap_ID, int vb, int a_lerpFlag)
Check if the mandatory fields have been initialize in the ap_IF structure, and check consistencies wi...
template int IntfKeyGetRecurringValueFromFile< int64_t >(const string &a_pathToHeader, const string &a_key, int64_t *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
template int IntfKeyGetValueFromFile< int >(const string &a_pathToHeader, const string &a_key, int *ap_return, int a_nbElts, int a_mandatoryFlag)
int IntfKeyIsArray(Intf_key ap_Key)
Check if the key passed in parameter is an array (contains brackets &#39;{&#39; and &#39;}&#39; ) ...
template int IntfKeyGetRecurringValueFromFile< int >(const string &a_pathToHeader, const string &a_key, int *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
FLTNB GetOffsetY()
Get the image offset along the Y axis, in mm.
Interfile key elements. This structure is used to recover and process the elements of an Interfile ...
int IntfKeyGetValueFromFile(const string &a_pathToHeader, const string &a_key, T *ap_return, int a_nbElts, int a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_pathToHeader" interfile header matching the "a_keyword" key passed...
int IntfWriteHeaderImgData(ofstream &ap_ofile, const Intf_fields &ap_IntfF, int vb)
void IntfKeyPrintFields(Intf_fields a_IF)
Print all the keys of the Intf_fields structure passed in parameter, as well as their values for debu...
#define KEYWORD_OPTIONAL_NOT_FOUND
bool MergeDynImages()
Indicate if a dynamic serie of 3D images should be merged in one file (true) or written on disk as on...
string IntfKeyGetEndianStr(int a_val)
return the endian string corresponding to the value passed in parameter (see module INTF_ENDIANNESS)...
Interfile fields. This structure contains all the Interfile keys currently managed by CASToR Decl...
FLTNB GetOffsetZ()
Get the image offset along the Z axis, in mm.
template int IntfKeyGetValueFromFile< uint8_t >(const string &a_pathToHeader, const string &a_key, uint8_t *ap_return, int a_nbElts, int a_mandatoryFlag)
int ConvertFromString(const string &a_str, string *a_result)
Copy the &#39;a_str&#39; string in the position pointed by &#39;a_result&#39;.
#define INTF_SUPINE_FEETIN_TRANSAXIAL
template int IntfKeyGetRecurringValueFromFile< long double >(const string &a_pathToHeader, const string &a_key, long double *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
template int IntfKeyGetRecurringValueFromFile< float >(const string &a_pathToHeader, const string &a_key, float *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
#define INTF_IMG_GATED
#define INTF_PRONE_HEADIN_SAGITTAL
This class is designed to manage all dimensions and quantification related stuff. ...
#define INTF_IMG_UNKNOWN
string IntfKeyGetModalityStr(int a_modalityIdx)
Convert the integer provided in parameter to the string related to the corresponding modality as de...
FLTNB GetOffsetX()
Get the image offset along the X axis, in mm.
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
int IntfKeyGetArrayNbElts(Intf_key a_Key)
Return the number of elts in an Interfile array Key.
string GetFileFromPath(const string &a_pathToFile)
Simply return the file from a path string passed in parameter.
template int IntfKeyGetRecurringValueFromFile< bool >(const string &a_pathToHeader, const string &a_key, bool *ap_return, int a_nbElts, int a_mandatoryFlag, uint16_t a_nbOccurrences)
#define INTF_IMG_DYNAMIC
int IntfWriteHeaderMainData(const string &a_path, const Intf_fields &ap_IntfF, int vb)
int IntfKeyGetMaxArrayKey(Intf_key ap_Key)
Return the maximum value from an array key (key value contains brackets &#39;{,,}&#39; )
#define INTF_PRONE_HEADIN_TRANSAXIAL
#define Cout(MESSAGE)
int GetNbRespBasisFunctions()
Get the number of respiratory basis functions.
void IntfEraseSpaces(string *input_str)
Erase space, blank characters ((t,r,n)), and &#39;!&#39; before and after the characters in the string passed...
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.
int IntfWriteMHD(const string &a_pathToMhd, const vector< string > &ap_lPathToImgs, Intf_fields a_IntfF, oImageDimensionsAndQuantification *ap_ID, int vb)
Write an Interfile meta header at the path provided in parameter, using the field stack provided in p...
template int IntfKeyGetValueFromFile< bool >(const string &a_pathToHeader, const string &a_key, bool *ap_return, int a_nbElts, int a_mandatoryFlag)
#define INTF_BIG_ENDIAN