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