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