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