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