CASToR  1.1
Tomographic Reconstruction (PET/SPECT)
 All Classes Files Functions Variables Typedefs Macros Groups Pages
oImageDimensionsAndQuantification.cc
Go to the documentation of this file.
1 
2 /*
3  Implementation of class oImageDimensionsAndQuantification
4 
5  - separators: X
6  - doxygen:
7  - default initialization: X
8  - CASTOR_DEBUG:
9  - CASTOR_VERBOSE:
10 */
11 
19 #include "vDataFile.hh"
20 
21 // =====================================================================
22 // ---------------------------------------------------------------------
23 // ---------------------------------------------------------------------
24 // =====================================================================
25 
27 {
28  // Set all members to default values
29  m_nbVoxX = -1;
30  m_nbVoxY = -1;
31  m_nbVoxZ = -1;
32  m_nbVoxXY = -1;
33  m_nbVoxXYZ = -1;
34  m_voxSizeX = -1.;
35  m_voxSizeY = -1.;
36  m_voxSizeZ = -1.;
37  m_fovSizeX = -1.;
38  m_fovSizeY = -1.;
39  m_fovSizeZ = -1.;
40  m_fovOutPercent = 0.;
41  m_flipOutX = false;
42  m_flipOutY = false;
43  m_flipOutZ = false;
44  m_offsetX = 0.;
45  m_offsetY = 0.;
46  m_offsetZ = 0.;
47  m_frameList = "";
48  m_nbTimeFrames = 1;
53  m2p_frameTimeStopInMs = NULL;
55  m_timeStaticFlag = false;
66  m_nbRespGates = 1;
70  m_respStaticFlag = false;
71  m_nbCardGates = 1;
75  m_cardStaticFlag = false;
78  m_mpiRank = 0;
79  m_mpiSize = 1;
80  m_nbBeds = -1;
81  m_verbose = 0;
82  m_checked = false;
83  m_initialized = false;
84  // Allocate Dynamic data manager object
86 }
87 
88 // =====================================================================
89 // ---------------------------------------------------------------------
90 // ---------------------------------------------------------------------
91 // =====================================================================
92 
94 {
95  // Free frame duration
97  {
98  for (int bed=0; bed<m_nbBeds; bed++) if (m2p_frameDurationsInMs[bed]) delete m2p_frameDurationsInMs[bed];
100  }
101  // Free frame time start
103  {
104  for (int bed=0; bed<m_nbBeds; bed++) if (m2p_frameTimeStartInMs[bed]) delete m2p_frameTimeStartInMs[bed];
105  delete m2p_frameTimeStartInMs;
106  }
107  // Free frame time stop
109  {
110  for (int bed=0; bed<m_nbBeds; bed++) if (m2p_frameTimeStopInMs[bed]) delete m2p_frameTimeStopInMs[bed];
111  delete m2p_frameTimeStopInMs;
112  }
113  // Free quantification factors
115  {
116  for (int bed=0; bed<m_nbBeds; bed++)
117  {
118  if (m3p_quantificationFactors[bed])
119  {
120  for (int fr=0; fr<m_nbTimeFrames; fr++)
121  if (m3p_quantificationFactors[bed][fr]) delete m3p_quantificationFactors[bed][fr];
122  }
123  delete m3p_quantificationFactors[bed];
124  }
126  }
127  // Delete the dynamic data manager
128  delete mp_DynamicDataManager;
129 }
130 
131 // =====================================================================
132 // ---------------------------------------------------------------------
133 // ---------------------------------------------------------------------
134 // =====================================================================
135 
137 {
138  // If empty, then put all flags to false and return
139  if (a_flipOut=="")
140  {
141  m_flipOutX = false;
142  m_flipOutY = false;
143  m_flipOutZ = false;
144  return 0;
145  }
146  // Test all possible settings !
147  if (a_flipOut=="x" || a_flipOut=="X")
148  {
149  m_flipOutX = true;
150  m_flipOutY = false;
151  m_flipOutZ = false;
152  }
153  else if (a_flipOut=="y" || a_flipOut=="Y")
154  {
155  m_flipOutX = false;
156  m_flipOutY = true;
157  m_flipOutZ = false;
158  }
159  else if (a_flipOut=="z" || a_flipOut=="Z")
160  {
161  m_flipOutX = false;
162  m_flipOutY = false;
163  m_flipOutZ = true;
164  }
165  else if (a_flipOut=="xy" || a_flipOut=="yx" || a_flipOut=="XY" || a_flipOut=="YX")
166  {
167  m_flipOutX = true;
168  m_flipOutY = true;
169  m_flipOutZ = false;
170  }
171  else if (a_flipOut=="zy" || a_flipOut=="yz" || a_flipOut=="ZY" || a_flipOut=="YZ")
172  {
173  m_flipOutX = false;
174  m_flipOutY = true;
175  m_flipOutZ = true;
176  }
177  else if (a_flipOut=="xz" || a_flipOut=="zx" || a_flipOut=="XZ" || a_flipOut=="ZX")
178  {
179  m_flipOutX = true;
180  m_flipOutY = false;
181  m_flipOutZ = true;
182  }
183  else if ( a_flipOut=="xyz" || a_flipOut=="xzy" || a_flipOut=="yxz" || a_flipOut=="yzx" || a_flipOut=="zxy" || a_flipOut=="zyx" ||
184  a_flipOut=="XYZ" || a_flipOut=="XZY" || a_flipOut=="YXZ" || a_flipOut=="YZX" || a_flipOut=="ZXY" || a_flipOut=="ZYX" )
185  {
186  m_flipOutX = true;
187  m_flipOutY = true;
188  m_flipOutZ = true;
189  }
190  // Otherwise, throw an error
191  else
192  {
193  Cerr("***** oImageDimensionsAndQuantification::SetFlipOut() -> Output flip settings is incorrect !" << endl);
194  return 1;
195  }
196  // Normal end
197  return 0;
198 }
199 
200 // =====================================================================
201 // ---------------------------------------------------------------------
202 // ---------------------------------------------------------------------
203 // =====================================================================
204 
206 {
207  // The number of threads can be given as two separated numbers to distinguish between the number of threads for projection computation
208  // and the number of threads for image computation. Be careful that the thread-safe images are allocated with respect to the number
209  // of projection threads; the number of threads used for image computation are strictly used for computation over images (i.e. when
210  // using a loop over voxels.
211 
212  // Look for a comma and check that there is only one comma without missing parameters
213  size_t first_comma = a_nbThreads.find_first_of(",");
214  size_t last_comma = a_nbThreads.find_last_of(",");
215  if (first_comma!=last_comma || first_comma==0 || first_comma==a_nbThreads.length()-1)
216  {
217  Cerr("***** oImageDimensionsAndQuantification::SetNbThreads() -> Wrong syntax in the thread parameters ! See help." << endl);
218  return 1;
219  }
220 
221  // Case for a single parameter
222  if (first_comma==string::npos)
223  {
224  m_nbThreadsForProjection = atoi( a_nbThreads.c_str() );
226  }
227  // Case for two parameters
228  else
229  {
230  m_nbThreadsForProjection = atoi( (a_nbThreads.substr(0,first_comma)).c_str() );
231  m_nbThreadsForImageComputation = atoi( (a_nbThreads.substr(first_comma+1)).c_str() );
232  }
233 
234  // Checks for negative threads numbers
236  {
237  Cerr("***** oImageDimensionsAndQuantification::SetNbThreads() -> Negative number of threads provided for projection computation !" << endl);
238  return 1;
239  }
241  {
242  Cerr("***** oImageDimensionsAndQuantification::SetNbThreads() -> Negative number of threads provided for image computation !" << endl);
243  return 1;
244  }
245 
246  #ifdef CASTOR_OMP
247  // If number of threads is 0, we automatically determine the maximum number of threads using OMP functions
248  if (m_nbThreadsForProjection==0) m_nbThreadsForProjection = omp_get_max_threads();
249  if (m_nbThreadsForImageComputation==0) m_nbThreadsForImageComputation = omp_get_max_threads();
250  // At this step, set by default the number of threads for image operations
251  omp_set_num_threads(m_nbThreadsForImageComputation);
252  #endif
253 
254  // End
255  return 0;
256 }
257 
258 // =====================================================================
259 // ---------------------------------------------------------------------
260 // ---------------------------------------------------------------------
261 // =====================================================================
262 
264 {
265  // Number of threads
267  {
268  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Should provide a strictly positive number of threads !" << endl);
269  return 1;
270  }
271  // Number of voxels
272  if (m_nbVoxX<=0 || m_nbVoxY<=0 || m_nbVoxZ<=0)
273  {
274  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Should provide strictly positive number of voxels !" << endl);
275  return 1;
276  }
277  // When FOV and voxel sizes are both not provided
278  if ((m_voxSizeX<=0. || m_voxSizeY<=0. || m_voxSizeZ<=0.) && (m_fovSizeX<=0. || m_fovSizeY<=0. || m_fovSizeZ<=0.))
279  {
280  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Should provide strictly positive voxel or FOV dimensions !" << endl);
281  return 1;
282  }
283  // When both FOV and voxel sizes are provided
284  if (m_voxSizeX>0. && m_voxSizeY>0. && m_voxSizeZ>0. && m_fovSizeX>0. && m_fovSizeY>0. && m_fovSizeZ>0.)
285  {
286  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Both FOV and voxels dimensions provided, should not provide both !" << endl);
287  return 1;
288  }
289  // Check output transaxial FOV which is in percent (0 means we do not apply any FOV masking)
290  if (m_fovOutPercent<0. || m_fovOutPercent>100.)
291  {
292  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Output transaxial FOV percentage must be in ]0:100] !" << endl);
293  return 1;
294  }
295  // Check that the number of axial slices to be masked is between 0 and dimZ/2
296  if (m_nbSliceOutMask<0 || m_nbSliceOutMask>m_nbVoxZ/2)
297  {
298  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Number of output axial slices to be masked is incorrectly set !" << endl);
299  return 1;
300  }
301  // Check whether the DynamicDataManager object has been instanciated or not
303  {
304  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Error : DynamicDataManager object not initialized !" << endl);
305  return 1;
306  }
307  // Unlock
308  m_checked = true;
309  // End
310  return 0;
311 }
312 
313 // =====================================================================
314 // ---------------------------------------------------------------------
315 // ---------------------------------------------------------------------
316 // =====================================================================
317 
319 {
320  // Mandatory check
321  if (!m_checked)
322  {
323  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> Cannot initialize before a call to CheckParameters() !" << endl);
324  return 1;
325  }
326 
327  // Verbose
328  if (m_verbose>=2) Cout("oImageDimensionsAndQuantification::Initialize() -> Initialize image dimensions, basis functions and quantification" << endl);
329 
330  // Precompute number of voxels in a slice (XY) and total number of voxels (XYZ)
333 
334  // If FOV dimensions are provided, then compute the voxel dimensions
335  if (m_fovSizeX>0. && m_fovSizeY>0. && m_fovSizeZ>0.)
336  {
338  m_voxSizeY = m_fovSizeY / ((FLTNB)m_nbVoxY);
339  m_voxSizeZ = m_fovSizeZ / ((FLTNB)m_nbVoxZ);
340  }
341  // If voxel dimensions are provided, then compute the voxel dimensions
342  else if (m_voxSizeX>0. && m_voxSizeY>0. && m_voxSizeZ>0.)
343  {
345  m_fovSizeY = m_voxSizeY * ((FLTNB)m_nbVoxY);
346  m_fovSizeZ = m_voxSizeZ * ((FLTNB)m_nbVoxZ);
347  }
348 
349  // Deal with frame list and quantification factors
351  {
352  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occured while initializing framing and quantification tabs !" << endl);
353  return 1;
354  }
355 
356  // Deal with time basis functions
358  {
359  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occured while initializing time basis functions !" << endl);
360  return 1;
361  }
362 
363  // Deal with respiratory basis functions
365  {
366  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occured while initializing respiratory basis functions !" << endl);
367  return 1;
368  }
369 
370  // Deal with cardiac basis functions
372  {
373  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occured while initializing cardiac basis functions !" << endl);
374  return 1;
375  }
376 
377  // Deal with ignored corrections
379  {
380  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occured while initializing ignored corrections !" << endl);
381  return 1;
382  }
383 
384  // Verbose
385  if (m_verbose>=2)
386  {
387  Cout(" --> Image dimensions: [" << m_nbVoxX << ";" << m_nbVoxY << ";" << m_nbVoxZ << "] voxels of [" << m_voxSizeX << ";" << m_voxSizeY << ";" << m_voxSizeZ << "] mm3" << endl);
388  Cout(" --> FOV size: [" << m_fovSizeX << ";" << m_fovSizeY << ";" << m_fovSizeZ << "] mm3" << endl);
389  if (m_nbTimeFrames>1)
390  {
391  if (m_timeStaticFlag) Cout(" --> Time frames: " << m_nbTimeFrames << endl);
392  else Cout(" --> Time frames: " << m_nbTimeFrames << " | Time basis functions: " << m_nbTimeBasisFunctions << endl);
393  }
394  if (m_respBasisFunctionsFile!="")
395  {
396  if (m_respStaticFlag) Cout(" --> Respiratory gates: " << m_nbRespGates << endl);
397  else Cout(" --> Respiratory gates: " << m_nbRespGates << " | Respiratory basis functions: " << m_nbRespBasisFunctions << endl);
398  }
399  if (m_cardBasisFunctionsFile!="")
400  {
401  if (m_cardStaticFlag) Cout(" --> Cardiac gates: " << m_nbCardGates << endl);
402  else Cout(" --> Cardiac gates: " << m_nbCardGates << " | Cardiac basis functions: " << m_nbCardBasisFunctions << endl);
403  }
405  {
406  if (m_nbThreadsForImageComputation==m_nbThreadsForProjection) Cout(" --> Number of parallel threads: " << m_nbThreadsForProjection << endl);
407  else Cout(" --> Number of parallel threads for projection / image computation: [" << m_nbThreadsForProjection << "/" << m_nbThreadsForImageComputation << "]" << endl);
408  }
409  }
410 
411  // Initialized
412  m_initialized = true;
413 
414  // End
415  return 0;
416 }
417 
418 // =====================================================================
419 // ---------------------------------------------------------------------
420 // ---------------------------------------------------------------------
421 // =====================================================================
422 
424 {
425  // Loop on all datafiles
426  for (int bed=0; bed<m_nbBeds; bed++)
427  {
428  // If the number of events in this datafile is below the number of projection threads, then reduce the number of projection threads
429  // to this number
430  if (a2p_Datafile[bed]->GetSize()<m_nbThreadsForProjection)
431  {
432  m_nbThreadsForProjection = a2p_Datafile[bed]->GetSize();
433  Cerr("!!!!! oImageDimensionsAndQuantification::CheckNumberOfProjectionThreadsConsistencyWithDatafileSize() !!!!!" << endl);
434  Cerr(" --> The number of projection threads was reduced to the provided datafile's number of events: " << m_nbThreadsForProjection << endl);
435  }
436  }
437 }
438 
439 // =====================================================================
440 // ---------------------------------------------------------------------
441 // ---------------------------------------------------------------------
442 // =====================================================================
443 
445 {
446 // TODO: for whole body 4D dynamic acquisitions, will need to read a set of frames for each bed where the number of frames and frame durations
447 // should be the same for all beds, except which frame is dead and time start and stop also... Have to think about and manage this.
448  // This function requires that the number of beds be set
449  if (m_nbBeds<=0)
450  {
451  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Number of beds must be set before setting the framing of the acquisition !" << endl);
452  return 1;
453  }
454 
455  // --------------------------------------------------
456  // Allocate time start, stop and duration, to 1 frame
457  m2p_frameDurationsInMs = (uint32_t**)malloc(m_nbBeds*sizeof(uint32_t*));
458  m2p_frameTimeStartInMs = (uint32_t**)malloc(m_nbBeds*sizeof(uint32_t*));
459  m2p_frameTimeStopInMs = (uint32_t**)malloc(m_nbBeds*sizeof(uint32_t*));
460  m3p_quantificationFactors = (FLTNB***)malloc(m_nbBeds*sizeof(FLTNB**));
461  for (int bed=0; bed<m_nbBeds; bed++)
462  {
463  m2p_frameDurationsInMs[bed] = (uint32_t*)malloc(1*sizeof(uint32_t));
464  m2p_frameTimeStartInMs[bed] = (uint32_t*)malloc(1*sizeof(uint32_t));
465  m2p_frameTimeStopInMs[bed] = (uint32_t*)malloc(1*sizeof(uint32_t));
466  }
467 
468  // ---------------------------------------
469  // Particular case where the list is empty
470  if (m_frameList=="")
471  {
472  // Set number of frames to 1
473  m_nbTimeFrames = 1;
474  // Init them to 0 (the vDataFile will set them to the appropriate value later through the oImageDimensionsAndQuantification::SetAcquisitionTime() function)
475  for (int bed=0; bed<m_nbBeds; bed++)
476  {
477  m2p_frameDurationsInMs[bed][0] = 0;
478  m2p_frameTimeStartInMs[bed][0] = 0;
479  m2p_frameTimeStopInMs[bed][0] = 0;
480  // Allocate quantification factors and set them to 1.
481  m3p_quantificationFactors[bed] = (FLTNB**)malloc(1*sizeof(FLTNB*));
483  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++) m3p_quantificationFactors[bed][0][g] = 1.;
484  }
485  // Exit the function
486  return 0;
487  }
488 
489  // --------------------------------------------------------------------
490  // Otherwise, get the parameter as a non-constant string and process it
491  string frame_list = m_frameList;
492  // Current time start
493  uint32_t time_start = 0;
494  // Init number of frames
495  m_nbTimeFrames = 0;
496  // Loop on all commas found in the list
497  size_t comma_pos = 0;
498  while ((comma_pos=frame_list.find_first_of(","))!=string::npos)
499  {
500  // Extract the parameter before the comma
501  string param = frame_list.substr(0,comma_pos);
502  // Search for a dead-frame sign ";0"
503  size_t semi_column_pos = param.find(";0");
504  // If does not find then this is an actual frame
505  if (semi_column_pos==string::npos)
506  {
507  // Increment number of frames
508  m_nbTimeFrames++;
509  // Realloc the time start, stop and duration
510  for (int bed=0; bed<m_nbBeds; bed++)
511  {
512  m2p_frameDurationsInMs[bed] = (uint32_t*)realloc( m2p_frameDurationsInMs[bed], m_nbTimeFrames*sizeof(uint32_t) );
513  m2p_frameTimeStartInMs[bed] = (uint32_t*)realloc( m2p_frameTimeStartInMs[bed], m_nbTimeFrames*sizeof(uint32_t) );
514  m2p_frameTimeStopInMs[bed] = (uint32_t*)realloc( m2p_frameTimeStopInMs[bed] , m_nbTimeFrames*sizeof(uint32_t) );
515  }
516  // Extract frame duration
517  FLTNB duration = atof(param.c_str());
518  if (duration<=0.)
519  {
520  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Found a duration which is negative or null !" << endl);
521  return 1;
522  }
523  // Affect time start, stop and duration
524  for (int bed=0; bed<m_nbBeds; bed++)
525  {
526  m2p_frameDurationsInMs[bed][m_nbTimeFrames-1] = ((uint32_t)(duration*1000.));
527  m2p_frameTimeStartInMs[bed][m_nbTimeFrames-1] = time_start;
528  }
529  time_start += m2p_frameDurationsInMs[0][m_nbTimeFrames-1];
530  for (int bed=0; bed<m_nbBeds; bed++)
531  m2p_frameTimeStopInMs[bed][m_nbTimeFrames-1] = time_start;
532  }
533  // Otherwise, this is a dead frame
534  else
535  {
536  // Check that the keyword ";0" if at the end of the parameter
537  if (semi_column_pos!=param.size()-2)
538  {
539  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Invalid dead frame format '" << param << "' !" << endl);
540  return 1;
541  }
542  // Extract the dead frame duration
543  FLTNB duration = atof( (param.substr(0,semi_column_pos)).c_str() );
544  if (duration<=0.)
545  {
546  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Found a duration which is negative or null !" << endl);
547  return 1;
548  }
549  // Increment time start
550  time_start += ((uint32_t)(duration*1000.));
551  }
552  // Remove first parameter
553  frame_list = frame_list.substr(comma_pos+1);
554  }
555  // Last parameter to extract; search for a dead-frame sign ";0"
556  size_t semi_column_pos = frame_list.find(";0");
557  // If does not find then this is an actual frame
558  if (semi_column_pos==string::npos)
559  {
560  // Increment number of frames
561  m_nbTimeFrames++;
562  // Realloc the time start, stop and duration
563  for (int bed=0; bed<m_nbBeds; bed++)
564  {
565  m2p_frameDurationsInMs[bed] = (uint32_t*)realloc(m2p_frameDurationsInMs[bed],m_nbTimeFrames*sizeof(uint32_t));
566  m2p_frameTimeStartInMs[bed] = (uint32_t*)realloc(m2p_frameTimeStartInMs[bed],m_nbTimeFrames*sizeof(uint32_t));
567  m2p_frameTimeStopInMs[bed] = (uint32_t*)realloc(m2p_frameTimeStopInMs[bed] ,m_nbTimeFrames*sizeof(uint32_t));
568  }
569  // Extract frame duration
570  FLTNB duration = atof(frame_list.c_str());
571  if (duration<=0.)
572  {
573  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Found a duration which is negative or null !" << endl);
574  return 1;
575  }
576  // Affect time start, stop and duration
577  for (int bed=0; bed<m_nbBeds; bed++)
578  {
579  m2p_frameDurationsInMs[bed][m_nbTimeFrames-1] = ((uint32_t)(duration*1000.));
580  m2p_frameTimeStartInMs[bed][m_nbTimeFrames-1] = time_start;
581  }
582  time_start += m2p_frameDurationsInMs[0][m_nbTimeFrames-1];
583  for (int bed=0; bed<m_nbBeds; bed++)
584  m2p_frameTimeStopInMs[bed][m_nbTimeFrames-1] = time_start;
585  }
586  // Otherwise, this is a dead frame, so we just ignore it as it is in last position
587  else
588  {
589  ;
590  }
591 
592  // ----------------------------------------------
593  // Allocate and affect the quantification factors
594  for (int bed=0; bed<m_nbBeds; bed++)
595  {
596  m3p_quantificationFactors[bed] = (FLTNB**)malloc(m_nbTimeFrames*sizeof(FLTNB*));
597  for (int fr=0; fr<m_nbTimeFrames; fr++)
598  {
599  m3p_quantificationFactors[bed][fr] = (FLTNB*)malloc(m_nbRespGates*m_nbCardGates*sizeof(FLTNB));
600  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++) m3p_quantificationFactors[bed][fr][g] = 1.;
601  }
602  }
603 
604  // ---
605  // End
606  return 0;
607 }
608 
609 // =====================================================================
610 // ---------------------------------------------------------------------
611 // ---------------------------------------------------------------------
612 // =====================================================================
613 
615 {
616  // Case 1: no time basis functions file provided
617  if (m_timeBasisFunctionsFile=="")
618  {
619  // Set the number of time basis functions to the number of frames
621  // Allocate the conversion matrix from basis functions to frames
623  for (int tbf=0; tbf<m_nbTimeBasisFunctions; tbf++)
624  {
625  m2p_timeBasisFunctions[tbf] = (FLTNB*)malloc(m_nbTimeFrames*sizeof(FLTNB));
626  for (int fr=0; fr<m_nbTimeFrames; fr++)
627  {
628  // Set diagonal to 1, the rest to 0
629  if (tbf==fr) m2p_timeBasisFunctions[tbf][fr] = 1.;
630  else m2p_timeBasisFunctions[tbf][fr] = 0.;
631  }
632  }
633  // Set the static flag to true
634  m_timeStaticFlag = true;
635  }
636  // Case 2: there is a file
637  else
638  {
639  // Allocate the conversion matrix from basis functions to frames
641  for (int tbf=0; tbf<m_nbTimeBasisFunctions; tbf++) m2p_timeBasisFunctions[tbf] = (FLTNB*)malloc(m_nbTimeFrames*sizeof(FLTNB));
642  // Open file
643  ifstream fin(m_timeBasisFunctionsFile.c_str());
644  if (!fin)
645  {
646  Cerr("***** oImageDimensionsAndQuantification::InitializeTimeBasisFunctions() -> Input time basis functions file '" << m_timeBasisFunctionsFile << "' is missing or corrupted !" << endl);
647  return 1;
648  }
649  // Read values
650  for (int tbf=0; tbf<m_nbTimeBasisFunctions; tbf++) for (int fr=0; fr<m_nbTimeFrames; fr++) fin >> m2p_timeBasisFunctions[tbf][fr];
651  // Close file
652  fin.close();
653  }
654  // End
655  return 0;
656 }
657 
658 // =====================================================================
659 // ---------------------------------------------------------------------
660 // ---------------------------------------------------------------------
661 // =====================================================================
662 
664 {
665  // Case 1: no respiratory basis functions file provided
666  if (m_respBasisFunctionsFile=="")
667  {
668  // Set the number of respiratory basis functions to the number of respiratory gates
670 
671  // Allocate the conversion matrix from basis functions to respiratory gates
673  for (int rbf=0; rbf<m_nbRespBasisFunctions; rbf++)
674  {
675  m2p_respBasisFunctions[rbf] = (FLTNB*)malloc(m_nbRespGates*sizeof(FLTNB));
676  for (int rg=0; rg<m_nbRespGates; rg++)
677  {
678  // Set diagonal to 1, the rest to 0
679  if (rbf==rg) m2p_respBasisFunctions[rbf][rg] = 1.;
680  else m2p_respBasisFunctions[rbf][rg] = 0.;
681  }
682  }
683  // Set the static flag to true
684  m_respStaticFlag = true;
685  }
686  // Case 2: there is a file
687  else
688  {
689  // Allocate the conversion matrix from basis functions to respiratory gates
691  for (int rbf=0; rbf<m_nbRespBasisFunctions; rbf++) m2p_respBasisFunctions[rbf] = (FLTNB*)malloc(m_nbRespGates*sizeof(FLTNB));
692  // Open file
693  ifstream fin(m_respBasisFunctionsFile.c_str());
694  if (!fin)
695  {
696  Cerr("***** oImageDimensionsAndQuantification::InitializeRespBasisFunctions() -> Input respiratory basis functions file '" << m_respBasisFunctionsFile << "' is missing or corrupted !" << endl);
697  return 1;
698  }
699  // Read values
700  for (int rbf=0; rbf<m_nbRespBasisFunctions; rbf++) for (int rg=0; rg<m_nbRespGates; rg++) fin >> m2p_respBasisFunctions[rbf][rg];
701  // Close file
702  fin.close();
703  }
704  // End
705  return 0;
706 }
707 
708 // =====================================================================
709 // ---------------------------------------------------------------------
710 // ---------------------------------------------------------------------
711 // =====================================================================
712 
714 {
715  // Case 1: no cardiac basis functions file provided
716  if (m_cardBasisFunctionsFile=="")
717  {
718  // Set the number of cardiac basis functions to the number of cardiac gates
720  // Allocate the conversion matrix from cardiac basis functions to cardiac gates
722  for (int cbf=0; cbf<m_nbCardBasisFunctions; cbf++)
723  {
724  m2p_cardBasisFunctions[cbf] = (FLTNB*)malloc(m_nbCardGates*sizeof(FLTNB));
725  for (int cg=0; cg<m_nbCardGates; cg++)
726  {
727  // Set diagonal to 1, the rest to 0
728  if (cbf==cg) m2p_cardBasisFunctions[cbf][cg] = 1.;
729  else m2p_cardBasisFunctions[cbf][cg] = 0.;
730  }
731  }
732  // Set the static flag to true
733  m_cardStaticFlag = true;
734  }
735  // Case 2: there is a file
736  else
737  {
738  // Allocate the conversion matrix from basis functions to cardiac gates
740  for (int cbf=0; cbf<m_nbCardBasisFunctions; cbf++) m2p_cardBasisFunctions[cbf] = (FLTNB*)malloc(m_nbCardGates*sizeof(FLTNB));
741  // Open file
742  ifstream fin(m_cardBasisFunctionsFile.c_str());
743  if (!fin)
744  {
745  Cerr("***** oImageDimensionsAndQuantification::InitializeCardBasisFunctions() -> Input cardiac basis functions file '" << m_cardBasisFunctionsFile << "' is missing or corrupted !" << endl);
746  return 1;
747  }
748  // Read values
749  for (int cbf=0; cbf<m_nbCardBasisFunctions; cbf++) for (int cg=0; cg<m_nbCardGates; cg++) fin >> m2p_cardBasisFunctions[cbf][cg];
750  // Close file
751  fin.close();
752  }
753  // End
754  return 0;
755 }
756 
757 // =====================================================================
758 // ---------------------------------------------------------------------
759 // ---------------------------------------------------------------------
760 // =====================================================================
761 
763 {
764  // If the m_ignoredCorrectionsList is empty, force all booleans to false and leave this function
765  if (m_ignoredCorrectionsList=="")
766  {
775  return 0;
776  }
777 
778  // Then, count the number of commas in the m_ignoredCorrectionsList to know the number of keywords
779  size_t nb_keywords = count(m_ignoredCorrectionsList.begin(), m_ignoredCorrectionsList.end(), ',') + 1;
780 
781  // Read the keywords
782  string *p_keywords = new string[nb_keywords];
783 
784  if (ReadStringOption(m_ignoredCorrectionsList, p_keywords, nb_keywords, ",", "Ignored corrections list"))
785  {
786  Cerr("***** oImageDimensionsAndQuantification::InitializeIgnoredCorrections() -> An error occured while reading the list of ignored corrections !" << endl);
787  return 1;
788  }
789 
790  // Process them
791  for (size_t k=0; k<nb_keywords; k++)
792  {
793  // Test the different keywords
794  if (p_keywords[k]=="attn") m_ignoreAttnCorrectionFlag = true;
795  else if (p_keywords[k]=="norm") m_ignoreNormCorrectionFlag = true;
796  else if (p_keywords[k]=="scat") m_ignoreScatCorrectionFlag = true;
797  else if (p_keywords[k]=="rand") m_ignoreRandCorrectionFlag = true;
798  else if (p_keywords[k]=="deca") m_ignoreDecaCorrectionFlag = true;
799  else if (p_keywords[k]=="brat") m_ignoreBratCorrectionFlag = true;
800  else if (p_keywords[k]=="fdur") m_ignoreFdurCorrectionFlag = true;
801  else if (p_keywords[k]=="cali") m_ignoreCaliCorrectionFlag = true;
802  else
803  {
804  Cerr("***** oImageDimensionsAndQuantification::InitializeIgnoredCorrections() -> Unknown keyword '" << p_keywords[k] << "' in the provided ignored corrections list !" << endl);
805  return 1;
806  }
807  }
808 
809  delete[] p_keywords;
810  // Normal end
811  return 0;
812 }
813 
814 // =====================================================================
815 // ---------------------------------------------------------------------
816 // ---------------------------------------------------------------------
817 // =====================================================================
818 
819 int oImageDimensionsAndQuantification::SetAcquisitionTime(int a_bed, FLTNB a_timeStartInSec, FLTNB a_durationInSec)
820 {
821  // Check if initialized
822  if (!m_initialized)
823  {
824  Cerr("***** oImageDimensionsAndQuantification::SetAcquisitionTime() -> Object not initialized !" << endl);
825  return 1;
826  }
827  // If the number of frames is more than one, then it was already specified, so we just update the quantification factors and exit
828  if (m_nbTimeFrames>1)
829  {
831  {
832  for (int fr=0; fr<m_nbTimeFrames; fr++) for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
833  m3p_quantificationFactors[a_bed][fr][g] /= (((FLTNB)(m2p_frameDurationsInMs[a_bed][fr]))/1000.);
834  }
835  }
836  // If timeStart, timeStop and duration are 0, then it means that it was not specified yet
837  else if (m2p_frameDurationsInMs[a_bed][0]==0 && m2p_frameTimeStartInMs[a_bed][0]==0 && m2p_frameTimeStopInMs[a_bed][0]==0)
838  {
839  m2p_frameDurationsInMs[a_bed][0] = ((uint32_t)(a_durationInSec*1000.));
840  m2p_frameTimeStartInMs[a_bed][0] = ((uint32_t)(a_timeStartInSec*1000.));
841  m2p_frameTimeStopInMs[a_bed][0] = m2p_frameTimeStartInMs[a_bed][0] + m2p_frameDurationsInMs[a_bed][0];
842  // Apply frame's duration onto quantification factors
844  {
845  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
846  m3p_quantificationFactors[a_bed][0][g] /= a_durationInSec;
847  }
848  }
849  // Verbose
850  if (m_verbose>=2 && (a_bed==m_nbBeds-1))
851  {
852  // Case 1: a static single bed
853  if (m_nbTimeFrames==1 && m_nbBeds==1)
854  {
855  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Static single bed acquisition with duration [ " << GetFrameTimeStartInSec(0,0) << " : "
856  << GetFrameTimeStopInSec(0,0) << " ] seconds" << endl);
857  }
858  // Case 2: a static multi beds
859  else if (m_nbTimeFrames==1 && m_nbBeds>1)
860  {
861  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Static " << m_nbBeds << " beds acquisition with following bed durations:" << endl);
862  for (int bed=0; bed<m_nbBeds; bed++)
863  Cout(" --> Bed " << bed+1 << " with duration [ " << GetFrameTimeStartInSec(bed,0) << " : " << GetFrameTimeStopInSec(bed,0) << " ] seconds" << endl);
864  }
865  // Case 3: a dynamic single bed
866  else if (m_nbTimeFrames>1 && m_nbBeds==1)
867  {
868  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Dynamic single bed acquisition with following " << m_nbTimeFrames << " frame durations:" << endl);
869  for (int fr=0; fr<m_nbTimeFrames; fr++)
870  Cout(" --> Frame " << fr+1 << " with duration [ " << GetFrameTimeStartInSec(0,fr) << " : " << GetFrameTimeStopInSec(0,fr) << " ] seconds" << endl);
871  }
872  // Case 4: a dynamic multi beds
873  else
874  {
875  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Dynamic " << m_nbBeds << " beds acquistion with following " << m_nbTimeFrames << " frame durations:" << endl);
876  for (int bed=0; bed<m_nbBeds; bed++)
877  {
878  Cout(" --> Bed " << bed+1 << " as following framing:" << endl);
879  for (int fr=0; fr<m_nbTimeFrames; fr++)
880  Cout(" | Frame " << fr+1 << " with duration [ " << GetFrameTimeStartInSec(bed,fr) << " : " << GetFrameTimeStopInSec(bed,fr) << " ] seconds" << endl);
881  }
882  }
883  // Correct for frame duration or not
884  if (m_ignoreFdurCorrectionFlag) Cout(" --> Ignore frame duration correction" << endl);
885  else Cout(" --> Correct for frame duration" << endl);
886  }
887  // End
888  return 0;
889 }
890 
891 // =====================================================================
892 // ---------------------------------------------------------------------
893 // ---------------------------------------------------------------------
894 // =====================================================================
895 
897 {
898  // Check if initialized
899  if (!m_initialized)
900  {
901  Cerr("***** oImageDimensionsAndQuantification::SetCalibrationFactor() -> Object not initialized !" << endl);
902  return 1;
903  }
904  // Ignore calibration factor if asked for
906  {
907  // Verbose
908  if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout("oImageDimensionsAndQuantification::SetCalibrationFactor() -> Ignore calibration factor correction" << endl);
909  // Exit the function
910  return 0;
911  }
912  // Check if calibration factor is strictly positive
913  if (a_calibrationFactor<=0.)
914  {
915  Cerr("***** oImageDimensionsAndQuantification::SetCalibrationFactor() -> Provided calibration factor (" << a_calibrationFactor << ") is negative or null !" << endl);
916  return 1;
917  }
918  // Affect quantification factor for all frames for this bed (even though the calibration factor should be the same
919  // for all beds, we do not check it, it should be self consistent in the input files)
921  {
922  for (int fr=0; fr<m_nbTimeFrames; fr++) for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
923  m3p_quantificationFactors[a_bed][fr][g] *= a_calibrationFactor;
924  }
925  // Verbose
926  if (m_verbose>=2 && a_bed==m_nbBeds-1)
927  Cout("oImageDimensionsAndQuantification::SetCalibrationFactor() -> Correct for following calibration factor: " << a_calibrationFactor << endl);
928  // End
929  return 0;
930 }
931 
932 // =====================================================================
933 // ---------------------------------------------------------------------
934 // ---------------------------------------------------------------------
935 // =====================================================================
936 
938 {
939  // Check if initialized
940  if (!m_initialized)
941  {
942  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Object not initialized !" << endl);
943  return 1;
944  }
945 
946  // Provide the DynamicDataManager with a 2D array containing quantitative factors to update during initialization
948 
949  // Exit the function if no file provided
950  if (a_quantificationFile=="") return 0;
951 
952  // Currently, it is not possible to reconstruction a multi-bed gated acquisition, so we throw an error in this function
953  if (m_nbBeds>1)
954  {
955  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Multi-bed gated acquisitions cannot be reconstructed !" << endl);
956  return 1;
957  }
958 
959  // Verbose
960  if (m_verbose>=2) Cout("oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors()-> Processing quantification file '" << a_quantificationFile << "'" << endl);
961 
962  // GET QUANTIFICATION CORRECTION FACTORS WITH RESPECT TO FRAMES or GATES
963 
964  // READ USER-DEFINED QUANTIFICATION FACTORS
965 
966  // TODO : For now we assume the configuration file holds :
967  // TODO : - frame quantification factors : specific to each bed & frame
968  // TODO : - gate quantification factors : specific to each frame and gates
969  // TODO : Might require a new ReadDataASCIIFile function to recover this
970  //
971  // TODO : Additionally, we should be careful in documentation and explained which gate quantitfication factors are natively taken into account or not
972 
973 
974  // Quantification factors for each bed/frame/gate, intialized to 1.
975  FLTNB*** pp_dynamic_quantification_factors = new FLTNB**[m_nbBeds];
976  for (int bed=0 ; bed<m_nbBeds ; bed++)
977  {
978  pp_dynamic_quantification_factors[bed] = new FLTNB*[m_nbTimeFrames];
979  for (int fr=0; fr<m_nbTimeFrames; fr++)
980  {
981  pp_dynamic_quantification_factors[bed][fr] = new FLTNB[m_nbRespGates*m_nbCardGates];
982  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++) pp_dynamic_quantification_factors[bed][fr][g] = 1.;
983  }
984  }
985 
986  // Build bed-related key words
987  string *bed_name = new string[m_nbBeds + 1];
988  for(int bed=0 ; bed<m_nbBeds ; bed++)
989  {
990  ostringstream oss( ostringstream::out );
991  oss << "bed" << bed+1;
992  bed_name[bed] = oss.str();
993  }
994  bed_name[m_nbBeds] = "eof";
995 
996  for (int bed=0 ; bed<m_nbBeds ; bed++)
997  {
998  int return_value = ReadDataASCIIFile(a_quantificationFile, "QUANTIFICATION_FACTORS", pp_dynamic_quantification_factors[bed], m_nbRespGates*m_nbCardGates, m_nbTimeFrames, KEYWORD_MANDATORY, bed_name[bed], bed_name[bed+1]);
999  if (return_value<0) // string not found error
1000  {
1001  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Didn't found quantitative factors in file " << a_quantificationFile << " !" << endl);
1002  return 1;
1003  }
1004  else if(return_value == 1) // reading error
1005  {
1006  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> An error occured while trying to recover specific quantitative factors for frame !" << endl);
1007  return 1;
1008  }
1009  else if(return_value == 0) // correct reading
1010  {
1011  for(int bed=0 ; bed<m_nbBeds ; bed++)
1012  for (int fr=0; fr<m_nbTimeFrames; fr++)
1013  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++)
1014  if (pp_dynamic_quantification_factors[bed][fr][g] <= 0)
1015  {
1016  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Provided quantification factor (" << pp_dynamic_quantification_factors[bed][fr][g] << ") is negative or null !" << endl);
1017  return 1;
1018  }
1019  else
1020  {
1021  m3p_quantificationFactors[bed][fr][g] *= pp_dynamic_quantification_factors[bed][fr][g];
1022  }
1023  }
1024  }
1025 
1026  // Delete temporary tabs
1027  for (int bed=0; bed<m_nbBeds; bed++)
1028  {
1029  for (int fr=0; fr<m_nbTimeFrames; fr++) delete pp_dynamic_quantification_factors[bed][fr];
1030  delete[] pp_dynamic_quantification_factors[bed];
1031  }
1032  delete[] pp_dynamic_quantification_factors;
1033  delete[] bed_name;
1034 
1035  // End
1036  return 0;
1037 }
1038 
1039 // =====================================================================
1040 // ---------------------------------------------------------------------
1041 // ---------------------------------------------------------------------
1042 // =====================================================================
1043 
1044 FLTNB oImageDimensionsAndQuantification::GetQuantificationFactor(int a_bed, int a_frame, int a_respGate, int a_cardGate)
1045 {
1046  // Make sure that if respiratory or cardiac motion is enabled, we will recover the factor corresponding to the first image
1047  // TODO TM : perhaps more logical to initialize the oImageDimensions with the actual number of gates even when motion is enabled
1048  // TODO TM : (i.e : m_nbRespGates or m_nbCardGates will be equal to 1, but the sensitivity image (list-mode) will require quantification factor for each gate
1049  // TODO TM : and add GetNbRespImages() functions anywhere in the code we need to know the number of resp/card images to be reconstructed
1050  // SS: If the motion correction is enabled, then all gates are pulled together in the optimization process, so there is no need for any quantification factor specific to each gate...
1051  if (m_nbRespGates == 1) a_respGate = 0;
1052  if (m_nbCardGates == 1) a_cardGate = 0;
1053  return m3p_quantificationFactors[a_bed][a_frame][a_respGate*m_nbCardGates+a_cardGate];
1054 }
1055 
1056 // =====================================================================
1057 // ---------------------------------------------------------------------
1058 // ---------------------------------------------------------------------
1059 // =====================================================================
1060 
1061 int oImageDimensionsAndQuantification::SetSPECTIsotope(int a_bed, const string& a_isotope)
1062 {
1063  // Check if initialized
1064  if (!m_initialized)
1065  {
1066  Cerr("***** oImageDimensionsAndQuantification::SetSPECTIsotope() -> Object not initialized !" << endl);
1067  return 1;
1068  }
1069 
1070  // Not yet implemented
1071 
1072  // Normal end
1073  return 0;
1074 }
1075 
1076 // =====================================================================
1077 // ---------------------------------------------------------------------
1078 // ---------------------------------------------------------------------
1079 // =====================================================================
1080 
1081 int oImageDimensionsAndQuantification::SetPETIsotope(int a_bed, const string& a_isotope)
1082 {
1083  // Check if initialized
1084  if (!m_initialized)
1085  {
1086  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Object not initialized !" << endl);
1087  return 1;
1088  }
1089 
1090  // ------------------------------------------------------------------
1091  // Preliminary step is checking if we ignore isotope corrections
1092  // ------------------------------------------------------------------
1093 
1095  {
1096  // Verbose
1097  if (m_verbose>=2 && a_bed==m_nbBeds-1)
1098  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Ignore isotope dependent corrections" << endl);
1099  // Exit the function
1100  return 0;
1101  }
1102 
1103  // ------------------------------------------------------------------
1104  // Preliminary step is checking for unknown keyword
1105  // ------------------------------------------------------------------
1106 
1107  // Check if the isotope is named as unknown
1108  if (a_isotope=="UNKNOWN" || a_isotope=="Unknown" || a_isotope=="unknown")
1109  {
1110  // In this case we simply assume perfect branching ratio and infinite half-life
1111  if (m_verbose>=2 && a_bed==m_nbBeds-1)
1112  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Un-specified isotope; no decay nor branching ratio correction" << endl);
1113  // Exit the function
1114  return 0;
1115  }
1116 
1117  // ------------------------------------------------------------------
1118  // First step is open the isotopes file and find the provided isotope
1119  // ------------------------------------------------------------------
1120 
1121  // Get oOutputManager instance and config directory
1122  sOutputManager* p_output_manager = sOutputManager::GetInstance();
1123  string config_dir = p_output_manager->GetPathToConfigDir();
1124 
1125  // Open the isotope file based on config directory
1126  string file_name = config_dir + "/misc/isotopes_pet.txt";
1127  ifstream fin(file_name.c_str());
1128 
1129  if (!fin)
1130  {
1131  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Failed to open PET isotopes data file '" << file_name << "' !" << endl);
1132  return 1;
1133  }
1134 
1135  // Loop on lines to find the isotope
1136  int line_max_size = 10240;
1137  char *line = new char[line_max_size];
1138  FLTNB half_life = -1.;
1139  FLTNB branching_ratio = -1.;
1140  bool found_it = false;
1141  fin.getline(line,line_max_size);
1142  while (!fin.eof())
1143  {
1144  // For the word position
1145  size_t found_position;
1146  // Cast the line into a string for convenience
1147  string test = (string)line;
1148  // Jump to next line if we find a # character as the first character
1149  if ((found_position=test.find("#"))==0)
1150  {
1151  // Read a new line before continuing
1152  fin.getline(line,line_max_size);
1153  continue;
1154  }
1155  // Check if we see the isotope name
1156  found_position = test.find(a_isotope);
1157  if (found_position!=string::npos)
1158  {
1159  // Each line is organised as follows: (some spaces/tabs) isotope_name (some spaces/tabs) half_life (some spaces/tabs) branching_ratio
1160  // We first remove the isotope name from the line
1161  test = test.substr(found_position+1+a_isotope.length());
1162  // Then we convert it into a string stream to ease the reading of both numbers
1163  istringstream fstr(test);
1164  fstr >> half_life >> branching_ratio;
1165  // We found it
1166  found_it = true;
1167  break;
1168  }
1169  // Read a new line
1170  fin.getline(line,line_max_size);
1171  }
1172 
1173  delete[] line;
1174  // Close file
1175  fin.close();
1176 
1177  // Check if we found it or not
1178  if (found_it)
1179  {
1180  // Check rationality of values
1181  if (branching_ratio<=0. || branching_ratio>1.)
1182  {
1183  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Branching ratio (" << branching_ratio << ") is not in the ]0:1] range !" << endl);
1184  return 1;
1185  }
1186  // Verbose
1187  if (m_verbose>=2 && a_bed==m_nbBeds-1)
1188  {
1189  // If negative half-life, then we consider it is infinite (no half-life)
1190  if (half_life<=0.)
1191  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Isotope " << a_isotope << " has infinite half life and " << branching_ratio << " branching ratio" << endl);
1192  else
1193  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Isotope " << a_isotope << " has " << half_life << " seconds half life and " << branching_ratio << " branching ratio" << endl);
1194  }
1195  }
1196  else
1197  {
1198  // Throw error message
1199  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Did not find " << a_isotope << " isotope in the PET isotope data file, please add it !" << endl);
1200  return 1;
1201  }
1202 
1203  // ------------------------------------------------------------------------
1204  // Second step is applying branching ratio and decay wrt to frame durations
1205  // ------------------------------------------------------------------------
1206 
1207  // Branching ratio
1209  {
1210  // Apply correction
1211  for (int fr=0; fr<m_nbTimeFrames; fr++)
1212  {
1213  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
1214  m3p_quantificationFactors[a_bed][fr][g] /= branching_ratio;
1215  }
1216  // Verbose
1217  if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Correct for branching ratio" << endl);
1218  }
1219  // Verbose
1220  else if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Ignore branching ratio correction" << endl);
1221 
1222  // Apply decay factors if half life is not negative (meaning no half-life)
1223  if (half_life>0.)
1224  {
1225  // We correct for it
1227  {
1228  // Apply correction
1229  for (int fr=0; fr<m_nbTimeFrames; fr++)
1230  {
1231  long double lambda = log(2.0)/half_life;
1232  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
1233  {
1234  long double dstart = lambda*GetFrameTimeStartInSec(a_bed,fr);
1235  long double dacq = lambda*GetFrameDurationInSec(a_bed,fr);
1236  // Time start decay correction
1237  m3p_quantificationFactors[a_bed][fr][g] *= exp(dstart);
1238  // Frame duration decay correction
1239  m3p_quantificationFactors[a_bed][fr][g] *= dacq/(1.0-exp(-dacq));
1240 
1241  /* Time start decay correction
1242  m3p_quantificationFactors[a_bed][fr][g] *= exp(lambda*GetFrameTimeStartInSec(a_bed,fr));
1243  // Frame duration decay correction
1244  m3p_quantificationFactors[a_bed][fr][g] *= lambda*GetFrameDurationInSec(a_bed,fr)/(1.0-exp(-lambda*(GetFrameDurationInSec(a_bed,fr))));*/
1245  }
1246  }
1247  // Verbose
1248  if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Correct for half-life" << endl);
1249  }
1250  // Verbose
1251  else if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Ignore half-life correction" << endl);
1252  }
1253 
1254  // End
1255  return 0;
1256 }
1257 
1258 // =====================================================================
1259 // ---------------------------------------------------------------------
1260 // ---------------------------------------------------------------------
1261 // =====================================================================
1262 
1263 int oImageDimensionsAndQuantification::InitDynamicData( string a_pathTo4DDataSplittingFile,
1264  int a_respMotionCorrectionFlag,
1265  int a_cardMotionCorrectionFlag,
1266  int a_doubleMotionCorrectionFlag,
1267  int a_invMotionCorrectionFlag,
1268  int a_nbRespGates, int a_nbCardGates )
1269 {
1270  if (m_verbose>=5) Cout("oImageDimensionsAndQuantification::InitDynamicData()" << endl);
1273  if (mp_DynamicDataManager->InitDynamicData( a_nbRespGates, a_nbCardGates, a_pathTo4DDataSplittingFile,
1274  a_respMotionCorrectionFlag, a_cardMotionCorrectionFlag, a_doubleMotionCorrectionFlag, a_invMotionCorrectionFlag ))
1275  {
1276  Cerr("***** oImageDimensionsAndQuantification::InitDynamicData() -> A problem occured while initializing the dynamic data from dynamic data manager !" << endl);
1277  return 1;
1278  }
1279  return 0;
1280 }
1281 
1282 // =====================================================================
1283 // ---------------------------------------------------------------------
1284 // ---------------------------------------------------------------------
1285 // =====================================================================
1286 
1288 {
1289  if (m_verbose>=5) Cout("oImageDimensionsAndQuantification::CheckDynamicParameters()" << endl);
1290  return mp_DynamicDataManager->CheckParameters(a_nbEvents);
1291 }
1292 
1293 // =====================================================================
1294 // ---------------------------------------------------------------------
1295 // ---------------------------------------------------------------------
1296 // =====================================================================
int InitializeFramingAndQuantification()
A function used to initialize the framing and quantification tables.
int CheckParameters(int64_t a_nbEvents)
Check all mandatory parameters.
This class is designed to be a mother virtual class for Datafile.
Definition: vDataFile.hh:67
int SetSPECTIsotope(int a_bed, const string &a_isotope)
Set the SPECT isotope for the provided bed.
int SetPETIsotope(int a_bed, const string &a_isotope)
Set the PET isotope for the provided bed.
Declaration of class oImageDimensionsAndQuantification.
int InitDynamicData(string a_pathTo4DDataSplittingFile, int a_respMotionCorrectionFlag, int a_cardMotionCorrectionFlag, int a_doubleMotionCorrectionFlag, int a_invMotionCorrectionFlag, int a_nbRespGates, int a_nbCardGates)
Call the eponym function from the oDynamicDataManager object in order to initialize its data...
void SetVerbose(int a_verboseLevel)
set verbosity
oImageDimensionsAndQuantification()
The constructor of oImageDimensionsAndQuantification.
FLTNB GetFrameTimeStartInSec(int a_bed, int a_frame)
Get the frame time start for the given bed, in seconds as a FLTNB.
#define FLTNB
Definition: gVariables.hh:55
This class gathers the information about the dynamic splitting of the data.
int SetCalibrationFactor(int a_bed, FLTNB a_calibrationFactor)
Set the calibration factor for the provided bed.
int64_t GetSize()
Definition: vDataFile.hh:292
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
int SetNbThreads(const string &a_nbThreads)
Set the number of threads.
~oImageDimensionsAndQuantification()
The destructor of oImageDimensionsAndQuantification.
int CheckParameters()
A function used to check the parameters settings.
#define Cerr(MESSAGE)
int Initialize()
A function used to initialize all that is needed.
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
const string & GetPathToConfigDir()
Return the path to the CASTOR config directory.
Declaration of class vDataFile.
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
Definition: gOptions.cc:111
int InitializeTimeBasisFunctions()
A function used to initialize the time basis functions.
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
set the pointer to the oImageDimensionsAndQuantification object
FLTNB GetQuantificationFactor(int a_bed, int a_frame, int a_respGate, int a_cardGate)
Get the quantification factor corresponding to the provided bed, frame, respiratory and cardiac gates...
int SetDynamicSpecificQuantificationFactors(FLTNB **a2p_quantificationFactors)
Compute gate-specific quantificative factors using the number of events within each gate...
#define KEYWORD_MANDATORY
Definition: gOptions.hh:25
int CheckDynamicParameters(int64_t a_nbEvents)
Call the eponym function from the oDynamicDataManager object in order to check its parameters...
int InitializeRespBasisFunctions()
A function used to initialize the respiratory basis functions.
FLTNB GetFrameTimeStopInSec(int a_bed, int a_frame)
Get the frame time stop for the given bed, in seconds as a FLTNB.
int SetAcquisitionTime(int a_bed, FLTNB a_timeStartInSec, FLTNB a_durationInSec)
Set the acquisition time if not already set by the SetTimeFrames()
int InitDynamicData(int a_nbRespGates, int a_nbCardGates, const string &a_pathTo4DDataSplittingFile, int a_rmMCorrFlag, int a_cMmCorrFlag, int a_dmCorrFlag, int a_pMotionCorrFlag)
Main function for instanciation and initialization of the member variables and arrays. Call the specific initialization function depending of the type of dataset.
#define Cout(MESSAGE)
int InitializeCardBasisFunctions()
A function used to initialize the cardiac basis functions.
FLTNB GetFrameDurationInSec(int a_bed, int a_frame)
Get the frame duration for the given bed, in seconds as a FLTNB.
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the 'a_input' string corresponding to the 'a_option' into 'a_nbElts' elements, using the 'sep' separator. The results are returned in the templated 'ap_return' dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
Definition: gOptions.cc:50
int InitializeIgnoredCorrections()
A function used to initialize the ignored corrections.
void CheckNumberOfProjectionThreadsConsistencyWithDatafileSize(vDataFile **a2p_Datafile)
int SetFlipOut(const string &a_flipOut)
Set the output flip options, the parameter being a string potentially containing the letters x...
int SetDynamicSpecificQuantificationFactors(const string &a_quantificationFile)
Apply specific quantification factors manually provided as an option.