CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/image/oImageDimensionsAndQuantification.cc
Go to the documentation of this file.
1 
8 #include "oImageDimensionsAndQuantification.hh"
9 #include "vDataFile.hh"
10 
11 // =====================================================================
12 // ---------------------------------------------------------------------
13 // ---------------------------------------------------------------------
14 // =====================================================================
15 
17 {
18  // Set all members to default values
19  m_nbVoxX = -1;
20  m_nbVoxY = -1;
21  m_nbVoxZ = -1;
22  m_nbVoxXY = -1;
23  m_nbVoxXYZ = -1;
24  m_voxSizeX = -1.;
25  m_voxSizeY = -1.;
26  m_voxSizeZ = -1.;
27  m_fovSizeX = -1.;
28  m_fovSizeY = -1.;
29  m_fovSizeZ = -1.;
30  m_fovOutPercent = 0.;
31  m_nbSliceOutMask = 0;
32  m_flipOutX = false;
33  m_flipOutY = false;
34  m_flipOutZ = false;
35  m_offsetX = 0.;
36  m_offsetY = 0.;
37  m_offsetZ = 0.;
38  m_frameList = "";
39  m_nbFramesToSkip = 0;
40  m_nbTimeFrames = 1;
45  m2p_frameTimeStopInMs = NULL;
46  m_timeStaticFlag = false;
57  m_nbRespGates = 1;
61  m_respStaticFlag = false;
62  m_nbCardGates = 1;
66  m_cardStaticFlag = false;
69  m_mpiRank = 0;
70  m_mpiSize = 1;
71  m_nbBeds = -1;
72  mp_bedPositions = NULL;
73  m_providedBedPosition = false;
74  m_verbose = 0;
75  m_checked = false;
76  m_initialized = false;
78  // Allocate Dynamic data manager object
81  m_lambda = -1.;
82  m_hasMask = false;
83 }
84 
85 // =====================================================================
86 // ---------------------------------------------------------------------
87 // ---------------------------------------------------------------------
88 // =====================================================================
89 
91 {
92  // Free frame duration
94  {
95  for (int bed=0; bed<m_nbBeds; bed++) if (m2p_frameDurationsInMs[bed]) delete m2p_frameDurationsInMs[bed];
97  }
98  // Free frame time start
100  {
101  for (int bed=0; bed<m_nbBeds; bed++) if (m2p_frameTimeStartInMs[bed]) delete m2p_frameTimeStartInMs[bed];
102  delete m2p_frameTimeStartInMs;
103  }
104  // Free frame time stop
106  {
107  for (int bed=0; bed<m_nbBeds; bed++) if (m2p_frameTimeStopInMs[bed]) delete m2p_frameTimeStopInMs[bed];
108  delete m2p_frameTimeStopInMs;
109  }
110  // Free quantification factors
112  {
113  for (int bed=0; bed<m_nbBeds; bed++)
114  {
115  if (m3p_quantificationFactors[bed])
116  {
117  for (int fr=0; fr<m_nbTimeFrames; fr++)
118  if (m3p_quantificationFactors[bed][fr]) delete m3p_quantificationFactors[bed][fr];
119  }
120  delete m3p_quantificationFactors[bed];
121  }
123  }
124  // Free the bed positions
125  if (mp_bedPositions) free(mp_bedPositions);
126  // Delete the dynamic data manager
127  delete mp_DynamicDataManager;
128 }
129 
130 // =====================================================================
131 // ---------------------------------------------------------------------
132 // ---------------------------------------------------------------------
133 // =====================================================================
134 
135 int oImageDimensionsAndQuantification::SetFlipOut(const string& a_flipOut)
136 {
137  // If empty, then put all flags to false and return
138  if (a_flipOut=="")
139  {
140  m_flipOutX = false;
141  m_flipOutY = false;
142  m_flipOutZ = false;
143  return 0;
144  }
145  // Test all possible settings !
146  if (a_flipOut=="x" || a_flipOut=="X")
147  {
148  m_flipOutX = true;
149  m_flipOutY = false;
150  m_flipOutZ = false;
151  }
152  else if (a_flipOut=="y" || a_flipOut=="Y")
153  {
154  m_flipOutX = false;
155  m_flipOutY = true;
156  m_flipOutZ = false;
157  }
158  else if (a_flipOut=="z" || a_flipOut=="Z")
159  {
160  m_flipOutX = false;
161  m_flipOutY = false;
162  m_flipOutZ = true;
163  }
164  else if (a_flipOut=="xy" || a_flipOut=="yx" || a_flipOut=="XY" || a_flipOut=="YX")
165  {
166  m_flipOutX = true;
167  m_flipOutY = true;
168  m_flipOutZ = false;
169  }
170  else if (a_flipOut=="zy" || a_flipOut=="yz" || a_flipOut=="ZY" || a_flipOut=="YZ")
171  {
172  m_flipOutX = false;
173  m_flipOutY = true;
174  m_flipOutZ = true;
175  }
176  else if (a_flipOut=="xz" || a_flipOut=="zx" || a_flipOut=="XZ" || a_flipOut=="ZX")
177  {
178  m_flipOutX = true;
179  m_flipOutY = false;
180  m_flipOutZ = true;
181  }
182  else if ( a_flipOut=="xyz" || a_flipOut=="xzy" || a_flipOut=="yxz" || a_flipOut=="yzx" || a_flipOut=="zxy" || a_flipOut=="zyx" ||
183  a_flipOut=="XYZ" || a_flipOut=="XZY" || a_flipOut=="YXZ" || a_flipOut=="YZX" || a_flipOut=="ZXY" || a_flipOut=="ZYX" )
184  {
185  m_flipOutX = true;
186  m_flipOutY = true;
187  m_flipOutZ = true;
188  }
189  // Otherwise, throw an error
190  else
191  {
192  Cerr("***** oImageDimensionsAndQuantification::SetFlipOut() -> Output flip settings is incorrect !" << endl);
193  return 1;
194  }
195  // Normal end
196  return 0;
197 }
198 
199 // =====================================================================
200 // ---------------------------------------------------------------------
201 // ---------------------------------------------------------------------
202 // =====================================================================
203 
204 int oImageDimensionsAndQuantification::SetNbThreads(const string& a_nbThreads)
205 {
206  // The number of threads can be given as two separated numbers to distinguish between the number of threads for projection computation
207  // and the number of threads for image computation. Be careful that the thread-safe images are allocated with respect to the number
208  // of projection threads; the number of threads used for image computation are strictly used for computation over images (i.e. when
209  // using a loop over voxels.
210 
211  // Look for a comma and check that there is only one comma without missing parameters
212  size_t first_comma = a_nbThreads.find_first_of(",");
213  size_t last_comma = a_nbThreads.find_last_of(",");
214  if (first_comma!=last_comma || first_comma==0 || first_comma==a_nbThreads.length()-1)
215  {
216  Cerr("***** oImageDimensionsAndQuantification::SetNbThreads() -> Wrong syntax in the thread parameters ! See help." << endl);
217  return 1;
218  }
219 
220  // Case for a single parameter
221  if (first_comma==string::npos)
222  {
223  m_nbThreadsForProjection = atoi( a_nbThreads.c_str() );
225  }
226  // Case for two parameters
227  else
228  {
229  m_nbThreadsForProjection = atoi( (a_nbThreads.substr(0,first_comma)).c_str() );
230  m_nbThreadsForImageComputation = atoi( (a_nbThreads.substr(first_comma+1)).c_str() );
231  }
232 
233  // Checks for negative threads numbers
235  {
236  Cerr("***** oImageDimensionsAndQuantification::SetNbThreads() -> Negative number of threads provided for projection computation !" << endl);
237  return 1;
238  }
239  if (m_nbThreadsForImageComputation<0)
240  {
241  Cerr("***** oImageDimensionsAndQuantification::SetNbThreads() -> Negative number of threads provided for image computation !" << endl);
242  return 1;
243  }
244 
245  #ifdef CASTOR_OMP
246  // If number of threads is 0, we automatically determine the maximum number of threads using OMP functions
247  if (m_nbThreadsForProjection==0) m_nbThreadsForProjection = omp_get_max_threads();
248  if (m_nbThreadsForImageComputation==0) m_nbThreadsForImageComputation = omp_get_max_threads();
249  // At this step, set by default the number of threads for image operations
250  omp_set_num_threads(m_nbThreadsForImageComputation);
251  #endif
252 
253  // End
254  return 0;
255 }
256 
257 // =====================================================================
258 // ---------------------------------------------------------------------
259 // ---------------------------------------------------------------------
260 // =====================================================================
261 
263 {
264  // Number of threads
266  m_nbThreadsForImageComputation = 1;
267  // MPI stuff
268  m_mpiSize = 1;
269  m_mpiRank = 0;
270  // Unlock
271  m_checked = true;
272 }
273 
274 
275 // =====================================================================
276 // ---------------------------------------------------------------------
277 // ---------------------------------------------------------------------
278 // =====================================================================
279 
281 {
282  // Number of threads
284  {
285  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Should provide a strictly positive number of threads !" << endl);
286  return 1;
287  }
288  // TODO: authorize -fov and -vox without -dim, checking that the number of voxels obtained is an integer
289  // Number of voxels
290  if (m_nbVoxX<=0 || m_nbVoxY<=0 || m_nbVoxZ<=0)
291  {
292  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Should provide strictly positive number of voxels !" << endl);
293  return 1;
294  }
295  // When FOV and voxel sizes are both not provided
296  if ((m_voxSizeX<=0. || m_voxSizeY<=0. || m_voxSizeZ<=0.) && (m_fovSizeX<=0. || m_fovSizeY<=0. || m_fovSizeZ<=0.))
297  {
298  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Should provide strictly positive voxel or FOV dimensions !" << endl);
299  return 1;
300  }
301  // When both FOV and voxel sizes are provided
302  if (m_voxSizeX>0. && m_voxSizeY>0. && m_voxSizeZ>0. && m_fovSizeX>0. && m_fovSizeY>0. && m_fovSizeZ>0.)
303  {
304  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Both FOV and voxels dimensions provided, should not provide both !" << endl);
305  return 1;
306  }
307  // Check output transaxial FOV which is in percent (0 means we do not apply any FOV masking)
308  if (m_fovOutPercent<0.)
309  {
310  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Output transaxial FOV percentage must be strictly positive !" << endl);
311  return 1;
312  }
313  // Check that the number of axial slices to be masked is between 0 and dimZ/2
314  if (m_nbSliceOutMask<0 || m_nbSliceOutMask>m_nbVoxZ/2)
315  {
316  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Number of output axial slices to be masked is incorrectly set !" << endl);
317  return 1;
318  }
319  // Check whether the DynamicDataManager object has been instanciated or not
321  {
322  Cerr("***** oImageDimensionsAndQuantification::CheckParameters() -> Error : DynamicDataManager object not initialized !" << endl);
323  return 1;
324  }
325  // Unlock
326  m_checked = true;
327  // End
328  return 0;
329 }
330 
331 // =====================================================================
332 // ---------------------------------------------------------------------
333 // ---------------------------------------------------------------------
334 // =====================================================================
335 
337 {
338  // Mandatory check
339  if (!m_checked)
340  {
341  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> Cannot initialize before a call to CheckParameters() !" << endl);
342  return 1;
343  }
344 
345  // Verbose
346  if (m_verbose>=2) Cout("oImageDimensionsAndQuantification::Initialize() -> Initialize image dimensions, basis functions and quantification" << endl);
347 
348  // Precompute number of voxels in a slice (XY) and total number of voxels (XYZ)
351 
352  // If FOV dimensions are provided, then compute the voxel dimensions
353  if (m_fovSizeX>0. && m_fovSizeY>0. && m_fovSizeZ>0.)
354  {
356  m_voxSizeY = m_fovSizeY / ((FLTNB)m_nbVoxY);
357  m_voxSizeZ = m_fovSizeZ / ((FLTNB)m_nbVoxZ);
358  }
359  // If voxel dimensions are provided, then compute the voxel dimensions
360  else if (m_voxSizeX>0. && m_voxSizeY>0. && m_voxSizeZ>0.)
361  {
363  m_fovSizeY = m_voxSizeY * ((FLTNB)m_nbVoxY);
364  m_fovSizeZ = m_voxSizeZ * ((FLTNB)m_nbVoxZ);
365  }
366 
367  // Deal with frame list and quantification factors
369  {
370  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occurred while initializing framing and quantification tabs !" << endl);
371  return 1;
372  }
373 
374  // Deal with time basis functions
375  /*
376  if (InitializeTimeBasisFunctions())
377  {
378  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occurred while initializing time basis functions !" << endl);
379  return 1;
380  }
381 
382  // Deal with respiratory basis functions
383  if (InitializeRespBasisFunctions())
384  {
385  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occurred while initializing respiratory basis functions !" << endl);
386  return 1;
387  }
388 
389  // Deal with cardiac basis functions
390  if (InitializeCardBasisFunctions())
391  {
392  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occurred while initializing cardiac basis functions !" << endl);
393  return 1;
394  }
395 //*/
396  // Deal with ignored corrections
398  {
399  Cerr("***** oImageDimensionsAndQuantification::Initialize() -> A problem occurred while initializing ignored corrections !" << endl);
400  return 1;
401  }
402 
403  // Verbose
404  if (m_verbose>=2)
405  {
406  Cout(" --> Image dimensions: [" << m_nbVoxX << ";" << m_nbVoxY << ";" << m_nbVoxZ << "] voxels of [" << m_voxSizeX << ";" << m_voxSizeY << ";" << m_voxSizeZ << "] mm3" << endl);
407  Cout(" --> FOV size: [" << m_fovSizeX << ";" << m_fovSizeY << ";" << m_fovSizeZ << "] mm3" << endl);
408  if (m_nbTimeFrames>1)
409  {
410  if (m_timeStaticFlag) Cout(" --> Time frames: " << m_nbTimeFrames << endl);
411  else Cout(" --> Time frames: " << m_nbTimeFrames << " | Time basis functions: " << m_nbTimeBasisFunctions << endl);
412  }
413  if (m_respBasisFunctionsFile!="")
414  {
415  if (m_respStaticFlag) Cout(" --> Respiratory gates: " << m_nbRespGates << endl);
416  else Cout(" --> Respiratory gates: " << m_nbRespGates << " | Respiratory basis functions: " << m_nbRespBasisFunctions << endl);
417  }
418  if (m_cardBasisFunctionsFile!="")
419  {
420  if (m_cardStaticFlag) Cout(" --> Cardiac gates: " << m_nbCardGates << endl);
421  else Cout(" --> Cardiac gates: " << m_nbCardGates << " | Cardiac basis functions: " << m_nbCardBasisFunctions << endl);
422  }
423  if (m_nbThreadsForProjection>1 || m_nbThreadsForImageComputation>1)
424  {
425  if (m_nbThreadsForImageComputation==m_nbThreadsForProjection) Cout(" --> Number of parallel threads: " << m_nbThreadsForProjection << endl);
426  else Cout(" --> Number of parallel threads for projection / image computation: [" << m_nbThreadsForProjection << "/" << m_nbThreadsForImageComputation << "]" << endl);
427  }
428  }
429 
430  // Initialized
431  m_initialized = true;
432 
433  // End
434  return 0;
435 }
436 
437 // =====================================================================
438 // ---------------------------------------------------------------------
439 // ---------------------------------------------------------------------
440 // =====================================================================
441 
443 {
444  // Loop on all datafiles
445  for (int bed=0; bed<m_nbBeds; bed++)
446  {
447  // If the number of events in this datafile is below the number of projection threads, then reduce the number of projection threads
448  // to this number
449  if (a2p_DataFile[bed]->GetSize()<m_nbThreadsForProjection)
450  {
451  m_nbThreadsForProjection = a2p_DataFile[bed]->GetSize();
452  Cerr("!!!!! oImageDimensionsAndQuantification::CheckNumberOfProjectionThreadsConsistencyWithDataFileSize() !!!!!" << endl);
453  Cerr(" --> The number of projection threads was reduced to the provided datafile's number of events: " << m_nbThreadsForProjection << endl);
454  }
455  }
456 }
457 
458 // =====================================================================
459 // ---------------------------------------------------------------------
460 // ---------------------------------------------------------------------
461 // =====================================================================
462 
464 {
465  // Note that the consistency between bed positions should have already been tested when this function is called.
466 
467  // First particular case for a single bed
468  if (m_nbBeds==1)
469  {
470  // Here we just copy the relative bed position if provided by the datafile header. It will be ignored in the projection when
471  // there is only one bed position, so no worries.
472  mp_bedPositions[0] = a2p_DataFile[0]->GetRelativeBedPosition();
473  // Flag to say that the bed positions were provided from the datafile
474  m_providedBedPosition = a2p_DataFile[0]->GetBedPositionFlag();
475  // We directly exit here, because below in this function, if the relative bed position has not been provided in the datafile header,
476  // then the scanner default bed displacement will be used, however, this value is not mandatory as some scanners may not be designed
477  // for wholebody.
478  return 0;
479  }
480 
481  // Case where the bed relative positions were provided for all the datafiles
482  if (a2p_DataFile[0]->GetBedPositionFlag())
483  {
484  // In this case we have to compute the center of mass of the provided bed positions, and move it to the 0-center of the Z axis
485  FLTNB center = 0.;
486  // Compute the center of mass
487  for (int bed=0; bed<m_nbBeds; bed++) center += a2p_DataFile[bed]->GetRelativeBedPosition();
488  center /= ((FLTNB)m_nbBeds);
489  // Compute the new bed positions from their relative values to recenter the mass at 0
490  for (int bed=0; bed<m_nbBeds; bed++) mp_bedPositions[bed] = a2p_DataFile[bed]->GetRelativeBedPosition() - center;
491  // Flag to say that the bed positions were provided from the datafile
492  m_providedBedPosition = true;
493  }
494 
495  // Case where the scanner default bed displacement is used
496  else
497  {
498  // Get the scanner object in use
500  // Check that the default bed displacement is more than 0. in the scanner
501  if (ap_Scanner->GetDefaultBedDisplacementInMm()<=0.)
502  {
503  Cerr("***** oImageDimensionsAndQuantification::DealWithBedPositions() -> Bed displacement between two successive bed positions must be strictly positive !" << endl);
504  return 1;
505  }
506  // Loop on the bed positions
507  for (int bed=0; bed<m_nbBeds; bed++)
508  {
509  // Compute bed offset (remember here that the 0. on the Z axis is at the center of the whole image)
510  FLTNB bed_offset = 0.;
511  // For odd numbers of bed positions
512  if (m_nbBeds%2==1) bed_offset = ((FLTNB)( bed-m_nbBeds/2 )) * ap_Scanner->GetDefaultBedDisplacementInMm();
513  // For even numbers of bed positions
514  else bed_offset = (((FLTNB)( bed-m_nbBeds/2 )) + 0.5) * ap_Scanner->GetDefaultBedDisplacementInMm();
515  // Record the value
516  mp_bedPositions[bed] = bed_offset;
517  }
518  // Flag to say that the bed positions were not provided from the datafile
519  m_providedBedPosition = false;
520  }
521 
522  // Verbose
524  {
525  Cout("oImageDimensionsAndQuantification::DealWithBedPositions() -> Use following relative bed positions:" << endl);
526  for (int bed=0; bed<m_nbBeds; bed++) Cout(" --> Bed " << bed << " | Relative axial position: " << mp_bedPositions[bed] << " mm" << endl);
527  }
528 
529  // End
530  return 0;
531 }
532 
533 // =====================================================================
534 // ---------------------------------------------------------------------
535 // ---------------------------------------------------------------------
536 // =====================================================================
537 
539 {
540 // 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
541 // 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.
542  // This function requires that the number of beds be set
543  if (m_nbBeds<=0)
544  {
545  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Number of beds must be set before setting the framing of the acquisition !" << endl);
546  return 1;
547  }
548 
549  // --------------------------------------------------
550  // Allocate time start, stop and duration, to 1 frame
551  m2p_frameDurationsInMs = (uint32_t**)malloc(m_nbBeds*sizeof(uint32_t*));
552  m2p_frameTimeStartInMs = (uint32_t**)malloc(m_nbBeds*sizeof(uint32_t*));
553  m2p_frameTimeStopInMs = (uint32_t**)malloc(m_nbBeds*sizeof(uint32_t*));
554  m3p_quantificationFactors = (FLTNB***)malloc(m_nbBeds*sizeof(FLTNB**));
555  for (int bed=0; bed<m_nbBeds; bed++)
556  {
557  m2p_frameDurationsInMs[bed] = (uint32_t*)malloc(1*sizeof(uint32_t));
558  m2p_frameTimeStartInMs[bed] = (uint32_t*)malloc(1*sizeof(uint32_t));
559  m2p_frameTimeStopInMs[bed] = (uint32_t*)malloc(1*sizeof(uint32_t));
560  }
561 
562  // ---------------------------------------
563  // Particular case where the list is empty
564  if (m_frameList=="")
565  {
566  // Set number of frames to 1
567  m_nbTimeFrames = 1;
568  // Init them to 0 (the vDataFile will set them to the appropriate value later through the oImageDimensionsAndQuantification::SetAcquisitionTime() function)
569  for (int bed=0; bed<m_nbBeds; bed++)
570  {
571  m2p_frameDurationsInMs[bed][0] = 0;
572  m2p_frameTimeStartInMs[bed][0] = 0;
573  m2p_frameTimeStopInMs[bed][0] = 0;
574  // Allocate quantification factors and set them to 1.
575  m3p_quantificationFactors[bed] = (FLTNB**)malloc(1*sizeof(FLTNB*));
577  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++) m3p_quantificationFactors[bed][0][g] = 1.;
578  }
579  // Exit the function
580  return 0;
581  }
582 
583  // --------------------------------------------------------------------
584  // Otherwise, get the parameter as a non-constant string and process it
585  string frame_list = m_frameList;
586 
587  // Declare frame start and duration
588  HPFLTNB frame_start ;
589  HPFLTNB frame_duration ;
590  // Units are defaulted to seconds, unless minutes are specified within the frame decleration.
591  bool frame_start_inMinutes = false;
592  bool frame_duration_inMinutes = false;
593  // Init number of frames
594  m_nbTimeFrames = 0;
595  // Loop on all commas and colons found in the list
596  size_t comma_pos = 0;
597  size_t colon_pos = 0;
598  size_t unit_pos = 0;
599  while ((comma_pos=frame_list.find_first_of(","))!=string::npos)
600  {
601  // Set default values to 0
602  frame_start = (HPFLTNB) 0.;
603  frame_duration = (HPFLTNB) 0.;
604  // By default set/reset units to seconds
605  frame_start_inMinutes = false;
606  frame_duration_inMinutes = false;
607  // Increment number of frames by 1
608  m_nbTimeFrames++;
609  // Realloc the time start, stop and duration arrays for all beds
610  for (int bed=0; bed<m_nbBeds; bed++)
611  {
612  m2p_frameDurationsInMs[bed] = (uint32_t*)realloc( m2p_frameDurationsInMs[bed], m_nbTimeFrames*sizeof(uint32_t) );
613  m2p_frameTimeStartInMs[bed] = (uint32_t*)realloc( m2p_frameTimeStartInMs[bed], m_nbTimeFrames*sizeof(uint32_t) );
614  m2p_frameTimeStopInMs[bed] = (uint32_t*)realloc( m2p_frameTimeStopInMs[bed] , m_nbTimeFrames*sizeof(uint32_t) );
615  }
616  // Extract the parameter ("phrase") before the first comma
617  string param = frame_list.substr(0,comma_pos);
618  // Check if the parameter is empty.
619  if (param.empty())
620  {
621  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Null framing definition detected !" << endl);
622  return 1;
623  }
624  // Search for a colon within the param (this means that time 'start:duration' are both provided)
625  if ((colon_pos = param.find_first_of(":")) != string::npos)
626  {
627  // Getting frame start declaration
628  string param_start = param.substr(0, colon_pos);
629  // Search for definition of units within the frame start declaration
630  if ((unit_pos = param_start.find("s"))!= string::npos)
631  {
632  // If seconds unit found - remove from param_start
633  param_start.erase(unit_pos);
634  }
635  else if ((unit_pos = param_start.find("m"))!= string::npos)
636  {
637  // If minutes unit found - remove from param_start
638  param_start.erase(unit_pos);
639  frame_start_inMinutes = true;
640  }
641  // Checking that frame start declaration is not empty
642  if (param_start.empty())
643  {
644  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Null framing definition detected !" << endl);
645  return 1;
646  }
647  // Getting frame duration declaration
648  string param_duration = param.substr(colon_pos + 1, comma_pos);
649  // Search for definition of units
650  if ((unit_pos = param_duration.find("s"))!= string::npos)
651  {
652  param_duration.erase(unit_pos);
653  }
654  else if ((unit_pos = param_duration.find("m"))!= string::npos)
655  {
656  param_duration.erase(unit_pos);
657  frame_duration_inMinutes = true;
658  }
659  // Checking that frame duration declaration is not empty
660  if (param_duration.empty())
661  {
662  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Null framing duration detected !" << endl);
663  return 1;
664  }
665  // Assign numerical values to frame start and duration
666  frame_start = (HPFLTNB) atof(param_start.c_str());
667  frame_duration = (HPFLTNB) atof(param_duration.c_str());
668  // If units declared in minutes - convert to seconds
669  if (frame_start_inMinutes) frame_start *= (HPFLTNB)60.;
670  if (frame_duration_inMinutes) frame_duration *= (HPFLTNB)60.;
671  // Check for negative or null frame duration
672  if (frame_duration<=0.)
673  {
674  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Negative or null frame duration detected !" << endl);
675  return 1;
676  }
677  // Check for negative or null frame start time
678  if (frame_start<0.)
679  {
680  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Negative or null frame start detected !" << endl);
681  return 1;
682  }
683  // Affect time start and duration; Conversion to milliseconds and cast to unit32_t.
684  for (int bed=0; bed<m_nbBeds; bed++)
685  {
686  m2p_frameDurationsInMs[bed][m_nbTimeFrames-1] = ((uint32_t)(frame_duration*1000.));
687  m2p_frameTimeStartInMs[bed][m_nbTimeFrames-1] = ((uint32_t)(frame_start*1000.));
688  }
689  }
690  // Case where frame duration has not been declared (no colon found)
691  else
692  {
693  // Search for definition of units within the frame start declaration
694  if ((unit_pos = param.find("s"))!= string::npos)
695  {
696  // If seconds unit found - remove from param_start
697  param.erase(unit_pos);
698  }
699  else if ((unit_pos = param.find("m"))!= string::npos)
700  {
701  // If minutes unit found - remove from param_start
702  param.erase(unit_pos);
703  frame_start_inMinutes=true;
704  }
705  // Check that frame start is not empty
706  if (param.empty())
707  {
708  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Null framing definition detected !" << endl);
709  return 1;
710  }
711  // Assign value
712  frame_start = (HPFLTNB) atof(param.c_str());
713  // If units declared in minutes - convert to seconds
714  if (frame_start_inMinutes) frame_start *= (HPFLTNB)60.;
715  // Check for negative or null frame start time
716  if (frame_start<0.)
717  {
718  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Negative or null frame start detected !" << endl);
719  return 1;
720  }
721  // Affect time start
722  for (int bed=0; bed<m_nbBeds; bed++)
723  {
724  // Frame duration not provided, we set 0 temporarily; it will be computed after reading all frames, and set to fit until the next frame start
726  m2p_frameTimeStartInMs[bed][m_nbTimeFrames-1] = ((uint32_t)(frame_start*1000.));
727  }
728  }
729  // Remove the evaluated parameter "phrase"
730  frame_list = frame_list.substr(comma_pos+1);
731  }
732 
733  // --- Last parameter to evaluate and extract ---
734  // Last frame must always have a declaration of frame duration
735 
736  // Set default values to 0
737  frame_start = (HPFLTNB) 0.;
738  frame_duration = (HPFLTNB) 0.;
739  // By default units set to seconds
740  frame_start_inMinutes = false;
741  frame_duration_inMinutes = false;
742  // Case with the duration (here it is mandatory for the last parameter)
743  string param = frame_list;
744  if ((colon_pos = param.find_first_of(":")) != string::npos)
745  {
746  // Increment number of frames
747  m_nbTimeFrames++;
748  // Realloc the time start, stop and duration
749  for (int bed=0; bed<m_nbBeds; bed++)
750  {
751  m2p_frameDurationsInMs[bed] = (uint32_t*)realloc(m2p_frameDurationsInMs[bed],m_nbTimeFrames*sizeof(uint32_t));
752  m2p_frameTimeStartInMs[bed] = (uint32_t*)realloc(m2p_frameTimeStartInMs[bed],m_nbTimeFrames*sizeof(uint32_t));
753  m2p_frameTimeStopInMs[bed] = (uint32_t*)realloc(m2p_frameTimeStopInMs[bed] ,m_nbTimeFrames*sizeof(uint32_t));
754  }
755  // Getting frame start declaration
756  string param_start = param.substr(0, colon_pos);
757  // Search for definition of units within the frame start declaration
758  if ((unit_pos = param_start.find("s"))!= string::npos)
759  {
760  // if minutes unit found - remove from param_start
761  param_start.erase(unit_pos);
762  }
763  else if ((unit_pos = param_start.find("m"))!= string::npos)
764  {
765  // if seconds unit found - remove from param_start
766  param_start.erase(unit_pos);
767  frame_start_inMinutes = true;
768  }
769  // Check that start frame is not empty
770  if (param_start.empty())
771  {
772  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Null framing definition detected !" << endl);
773  return 1;
774  }
775  // Getting frame duration declaration
776  string param_duration = param.substr(colon_pos + 1, param.size());
777  // Search for definition of units
778  if ((unit_pos = param_duration.find("s"))!= string::npos)
779  {
780  param_duration.erase(unit_pos);
781  }
782  else if ((unit_pos = param_duration.find("m"))!= string::npos)
783  {
784  param_duration.erase(unit_pos);
785  frame_duration_inMinutes=true;
786  }
787  // Checking that frame duration is not empty
788  if (param_duration.empty())
789  {
790  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Null framing duration detected !" << endl);
791  return 1;
792  }
793  // Assign numerical values to frame start and duration
794  frame_start = (HPFLTNB) atof(param_start.c_str());
795  frame_duration = (HPFLTNB) atof(param_duration.c_str());
796  // If units declared in minutes - convert to seconds
797  if (frame_start_inMinutes) frame_start *= (HPFLTNB)60.;
798  if (frame_duration_inMinutes) frame_duration *= (HPFLTNB)60.;
799  // Check for negative or null duration
800  if (frame_duration<=0.)
801  {
802  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Found a duration which is negative or null !" << endl);
803  return 1;
804  }
805  // Check for negative frame start
806  if (frame_start<0.)
807  {
808  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Negative or null frame start detected !" << endl);
809  return 1;
810  }
811  // Affect time start, stop and duration
812  for (int bed=0; bed<m_nbBeds; bed++)
813  {
814  m2p_frameDurationsInMs[bed][m_nbTimeFrames-1] = ((uint32_t)(frame_duration*1000.));
815  m2p_frameTimeStartInMs[bed][m_nbTimeFrames-1] = ((uint32_t)(frame_start*1000.));
816  }
817 
818  }
819  // Otherwise, if no colon was found in last frame, give an error as the last frame must always have declaration of duration
820  else
821  {
822  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Last frame duration has not been provided !" << endl);
823  return 1;
824  }
825 
826  // -----------------------------------------------
827  // Compute the frame durations for the frames it has not been declared, skipping the last frame as its duration is always declared
828  for (int frm=0; frm<m_nbTimeFrames-1; frm++)
829  {
830  // Duration was set to 0 when not provided
831  if (m2p_frameDurationsInMs[0][frm] == 0)
832  {
833  for (int bed=0; bed<m_nbBeds; bed++)
834  {
835  m2p_frameDurationsInMs[bed][frm] = m2p_frameTimeStartInMs[bed][frm+1] - m2p_frameTimeStartInMs[bed][frm];
836  }
837  }
838  }
839 
840  // -----------------------------------------------
841  // Allocate the frame end times for all frames
842  for (int frm=0; frm<m_nbTimeFrames; frm++)
843  {
844  for (int bed=0; bed<m_nbBeds; bed++)
845  {
846  m2p_frameTimeStopInMs[bed][frm] = m2p_frameTimeStartInMs[bed][frm] + m2p_frameDurationsInMs[bed][frm];
847  }
848  }
849 
850  // -------------------------------------------------
851  // Checking for Overlap of frames
852  // Checking that for every single frame, the start time and end time do not fall within any other frame
853  // TODO: checking a single bed at the moment - as definition of times for all beds is the same
854 
855  for (int frmch=0; frmch<m_nbTimeFrames; frmch++)
856  {
857  for (int frm=frmch+1; frm<m_nbTimeFrames; frm++)
858  {
859  if (m2p_frameTimeStartInMs[0][frm] < m2p_frameTimeStopInMs[0][frmch])
860  {
861  Cerr("***** oImageDimensionsAndQuantification::InitializeFramingAndQuantification() -> Illegal frame overlap detected between frames: " << frmch+1 << " and "<< frm+1 << endl);
862  return 1;
863  }
864  }
865  }
866 
867  // ----------------------------------------------
868  // Allocate and affect the quantification factors
869  for (int bed=0; bed<m_nbBeds; bed++)
870  {
871  m3p_quantificationFactors[bed] = (FLTNB**)malloc(m_nbTimeFrames*sizeof(FLTNB*));
872  for (int fr=0; fr<m_nbTimeFrames; fr++)
873  {
874  m3p_quantificationFactors[bed][fr] = (FLTNB*)malloc(m_nbRespGates*m_nbCardGates*sizeof(FLTNB));
875  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++) m3p_quantificationFactors[bed][fr][g] = 1.;
876  }
877  }
878 
879  // ---
880  // End
881  return 0;
882 }
883 
884 
885 
886 
887 // =====================================================================
888 // ---------------------------------------------------------------------
889 // ---------------------------------------------------------------------
890 // =====================================================================
891 
893 {
894  // If the m_ignoredCorrectionsList is empty, force all booleans to false and leave this function
895  if (m_ignoredCorrectionsList=="")
896  {
905  return 0;
906  }
907 
908  // Then, count the number of commas in the m_ignoredCorrectionsList to know the number of keywords
909  size_t nb_keywords = count(m_ignoredCorrectionsList.begin(), m_ignoredCorrectionsList.end(), ',') + 1;
910 
911  // Read the keywords
912  string *p_keywords = new string[nb_keywords];
913 
914  if (ReadStringOption(m_ignoredCorrectionsList, p_keywords, nb_keywords, ",", "Ignored corrections list"))
915  {
916  Cerr("***** oImageDimensionsAndQuantification::InitializeIgnoredCorrections() -> An error occurred while reading the list of ignored corrections !" << endl);
917  return 1;
918  }
919 
920  // Process them
921  for (size_t k=0; k<nb_keywords; k++)
922  {
923  // Test the different keywords
924  if (p_keywords[k]=="attn") m_ignoreAttnCorrectionFlag = true;
925  else if (p_keywords[k]=="norm") m_ignoreNormCorrectionFlag = true;
926  else if (p_keywords[k]=="scat") m_ignoreScatCorrectionFlag = true;
927  else if (p_keywords[k]=="rand") m_ignoreRandCorrectionFlag = true;
928  else if (p_keywords[k]=="deca") m_ignoreDecaCorrectionFlag = true;
929  else if (p_keywords[k]=="brat") m_ignoreBratCorrectionFlag = true;
930  else if (p_keywords[k]=="fdur") m_ignoreFdurCorrectionFlag = true;
931  else if (p_keywords[k]=="cali") m_ignoreCaliCorrectionFlag = true;
932  else
933  {
934  Cerr("***** oImageDimensionsAndQuantification::InitializeIgnoredCorrections() -> Unknown keyword '" << p_keywords[k] << "' in the provided ignored corrections list !" << endl);
935  return 1;
936  }
937  }
938 
939  delete[] p_keywords;
940  // Normal end
941  return 0;
942 }
943 
944 // =====================================================================
945 // ---------------------------------------------------------------------
946 // ---------------------------------------------------------------------
947 // =====================================================================
948 
949 int oImageDimensionsAndQuantification::SetAcquisitionTime(int a_bed, FLTNB a_timeStartInSec, FLTNB a_durationInSec, string a_gateListDurationInSec)
950 {
951  // Check if initialized
952  if (!m_initialized)
953  {
954  Cerr("***** oImageDimensionsAndQuantification::SetAcquisitionTime() -> Object not initialized !" << endl);
955  return 1;
956  }
957 
958  // TODO TM : Once the dynamic format will be updated (one file by frame/gate)
959  // The gate duration will have to be read from the datafile header, and the quantification factors must be updated here
960  // (Yet to see the most practical way to manage information with several datafiles
961  // Hete, the quantification factors for all frames and gates are simultaneously updated)
962  //
963  // For now, just one factor by gate is considered, for data without dynamic frame (must be frame dependent /!!!!!!\)
964  // Parse the list containing the gate durations
965  // Local array to recover gate parameters
966  FLTNB* pgate_duration_sec = new FLTNB[ m_nbRespGates*m_nbCardGates ];
967 
968  // First check that gate duration have not been provided in BOTH the datafile header and the gating configuration file, the later being the current mandatory way (make your mind!)
969  if(a_gateListDurationInSec != "" && mp_DynamicDataManager->GateDurationProvided())
970  {
971  Cerr("***** oImageDimensionsAndQuantification::SetAcquisitionTime() -> Gate durations have been initialized in both the datafile header and the gating configuration file!" << endl);
972  Cerr("***** Only one must be used (preferably the gating configuration file) " << endl);
973  return 1;
974  }
975 
976  if(a_gateListDurationInSec != "")
977  {
978  if (ReadStringOption(a_gateListDurationInSec,
979  pgate_duration_sec,
981  ",",
982  "Gate duration (s)"))
983  {
984  Cerr("***** oImageDimensionsAndQuantification::SetAcquisitionTime() -> Failed to correctly read the following list of gate durations (datafile header): !" << endl);
985  Cerr("***** "<<a_gateListDurationInSec << endl);
986  Cerr("***** "<<m_nbRespGates*m_nbCardGates<<" parameters were expected (1 for each gate)" << endl);
987  return 1;
988  }
989  }
990  // No gate, initialize with the acquisition duration
991  else
992  {
993  // Initialize with acquisition duration, unless gate duration have been provided and quantification factors already integrate time quantification correction
994  // TODO TM: This has to be deleted when new dynamic datafile format will be implemented (gate duration directly in datafile header)
995  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
996  pgate_duration_sec[ g ]= mp_DynamicDataManager->GateDurationProvided() ? 1 : a_durationInSec;
997  }
998 
999  // If we have only one frame and that, timeStart, timeStop and duration are 0, then it means that nothing has been specified yet.
1000  // (this was done in the InitializeFramingAndQuantification() function)
1001  if (m_nbTimeFrames==1 && m2p_frameDurationsInMs[a_bed][0]==0 && m2p_frameTimeStartInMs[a_bed][0]==0 && m2p_frameTimeStopInMs[a_bed][0]==0)
1002  {
1003  m2p_frameDurationsInMs[a_bed][0] = ((uint32_t)(a_durationInSec*1000.));
1004  m2p_frameTimeStartInMs[a_bed][0] = ((uint32_t)(a_timeStartInSec*1000.));
1005  m2p_frameTimeStopInMs[a_bed][0] = m2p_frameTimeStartInMs[a_bed][0] + m2p_frameDurationsInMs[a_bed][0];
1006  // Apply frame's duration onto quantification factors
1008  {
1009  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
1010  m3p_quantificationFactors[a_bed][0][g] /= pgate_duration_sec[ g ];
1011  }
1012  }
1013  // Otherwise it was already specified, so we just update the quantification factors and exit
1014  else
1015  {
1017  && !mp_DynamicDataManager->GateDurationProvided() ) // Durations provided in gate configuration file. No need to update quantification factors.
1018  {
1019  for (int fr=0; fr<m_nbTimeFrames; fr++) for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
1020  m3p_quantificationFactors[a_bed][fr][g] /= (((FLTNB)(m2p_frameDurationsInMs[a_bed][fr]))/1000.);
1021  }
1022  }
1023 
1024  // Verbose
1025  if (m_verbose>=2 && (a_bed==m_nbBeds-1))
1026  {
1027  // Case 1: a static single bed
1028  if (m_nbTimeFrames==1 && m_nbBeds==1)
1029  {
1030  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Static single bed acquisition with duration [ " << GetFrameTimeStartInSec(0,0) << " : "
1031  << GetFrameTimeStopInSec(0,0) << " ] seconds" << endl);
1032  }
1033  // Case 2: a static multi beds
1034  else if (m_nbTimeFrames==1 && m_nbBeds>1)
1035  {
1036  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Static " << m_nbBeds << " beds acquisition with following bed durations:" << endl);
1037  for (int bed=0; bed<m_nbBeds; bed++)
1038  Cout(" --> Bed " << bed+1 << " with duration [ " << GetFrameTimeStartInSec(bed,0) << " : " << GetFrameTimeStopInSec(bed,0) << " ] seconds" << endl);
1039  }
1040  // Case 3: a dynamic single bed
1041  else if (m_nbTimeFrames>1 && m_nbBeds==1)
1042  {
1043  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Dynamic single bed acquisition with following " << m_nbTimeFrames << " frame durations:" << endl);
1044  for (int fr=0; fr<m_nbTimeFrames; fr++)
1045  Cout(" --> Frame " << fr+1 << " with duration [ " << GetFrameTimeStartInSec(0,fr) << " : " << GetFrameTimeStopInSec(0,fr) << " ] seconds" << endl);
1046  }
1047  // Case 4: a dynamic multi beds
1048  else
1049  {
1050  Cout("oImageDimensionsAndQuantification::SetAcquisitionTime() -> Dynamic " << m_nbBeds << " beds acquistion with following " << m_nbTimeFrames << " frame durations:" << endl);
1051  for (int bed=0; bed<m_nbBeds; bed++)
1052  {
1053  Cout(" --> Bed " << bed+1 << " as following framing:" << endl);
1054  for (int fr=0; fr<m_nbTimeFrames; fr++)
1055  Cout(" | Frame " << fr+1 << " with duration [ " << GetFrameTimeStartInSec(bed,fr) << " : " << GetFrameTimeStopInSec(bed,fr) << " ] seconds" << endl);
1056  }
1057  }
1058  // Correct for frame duration or not
1059  if (m_ignoreFdurCorrectionFlag) Cout(" --> Ignore frame duration correction" << endl);
1060  else Cout(" --> Correct for frame duration" << endl);
1061  }
1062  // End
1063  return 0;
1064 }
1065 
1066 // =====================================================================
1067 // ---------------------------------------------------------------------
1068 // ---------------------------------------------------------------------
1069 // =====================================================================
1070 
1071 int oImageDimensionsAndQuantification::SetCalibrationFactor(int a_bed, FLTNB a_calibrationFactor)
1072 {
1073  // Check if initialized
1074  if (!m_initialized)
1075  {
1076  Cerr("***** oImageDimensionsAndQuantification::SetCalibrationFactor() -> Object not initialized !" << endl);
1077  return 1;
1078  }
1079  // Ignore calibration factor if asked for
1081  {
1082  // Verbose
1083  if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout("oImageDimensionsAndQuantification::SetCalibrationFactor() -> Ignore calibration factor correction" << endl);
1084  // Exit the function
1085  return 0;
1086  }
1087  // Check if calibration factor is strictly positive
1088  if (a_calibrationFactor<=0.)
1089  {
1090  Cerr("***** oImageDimensionsAndQuantification::SetCalibrationFactor() -> Provided calibration factor (" << a_calibrationFactor << ") is negative or null !" << endl);
1091  return 1;
1092  }
1093  // Affect quantification factor for all frames for this bed (even though the calibration factor should be the same
1094  // for all beds, we do not check it, it should be self consistent in the input files)
1096  {
1097  for (int fr=0; fr<m_nbTimeFrames; fr++) for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
1098  m3p_quantificationFactors[a_bed][fr][g] *= a_calibrationFactor;
1099  }
1100  // Verbose
1101  if (m_verbose>=2 && a_bed==m_nbBeds-1)
1102  Cout("oImageDimensionsAndQuantification::SetCalibrationFactor() -> Correct for following calibration factor: " << a_calibrationFactor << endl);
1103  // End
1104  return 0;
1105 }
1106 
1107 // =====================================================================
1108 // ---------------------------------------------------------------------
1109 // ---------------------------------------------------------------------
1110 // =====================================================================
1111 
1113 {
1114  // Check if allocated
1116  {
1117  // Loop on beds
1118  for (int bed=0; bed<m_nbBeds; bed++)
1119  {
1120  // Check if allocated
1121  if (m3p_quantificationFactors[bed])
1122  {
1123  // Loop on frame
1124  for (int fr=0; fr<m_nbTimeFrames; fr++)
1125  {
1126  // Check if allocated
1127  if (m3p_quantificationFactors[bed][fr])
1128  {
1129  // Loop on gates and finally reset value to 1
1130  for (int gate=0; gate<m_nbRespGates*m_nbCardGates; gate++) m3p_quantificationFactors[bed][fr][gate] = 1.;
1131  }
1132  else
1133  {
1134  Cerr("***** oImageDimensionsAndQuantification::ResetQuantificationFactors() -> Quantification factor table not fully allocated !" << endl);
1135  return 1;
1136  }
1137  }
1138  }
1139  else
1140  {
1141  Cerr("***** oImageDimensionsAndQuantification::ResetQuantificationFactors() -> Quantification factor table not fully allocated !" << endl);
1142  return 1;
1143  }
1144  }
1145  }
1146  else
1147  {
1148  Cerr("***** oImageDimensionsAndQuantification::ResetQuantificationFactors() -> Quantification factor table not allocated !" << endl);
1149  return 1;
1150  }
1151  // End
1152  return 0;
1153 }
1154 
1155 // =====================================================================
1156 // ---------------------------------------------------------------------
1157 // ---------------------------------------------------------------------
1158 // =====================================================================
1159 
1161 {
1162  // Check if initialized
1163  if (!m_initialized)
1164  {
1165  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Object not initialized !" << endl);
1166  return 1;
1167  }
1168 
1169  // Provide the DynamicDataManager with a 2D array containing quantitative factors to update during initialization
1171 
1172  // Exit the function if no file provided
1173  if (a_quantificationFile=="") return 0;
1174 
1175  // Currently, it is not possible to reconstruction a multi-bed gated acquisition, so we throw an error in this function
1176  if (m_nbBeds>1)
1177  {
1178  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Multi-bed gated acquisitions cannot be reconstructed !" << endl);
1179  return 1;
1180  }
1181 
1182  // Verbose
1183  if (m_verbose>=2) Cout("oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors()-> Processing quantification file '" << a_quantificationFile << "'" << endl);
1184 
1185  // GET QUANTIFICATION CORRECTION FACTORS WITH RESPECT TO FRAMES or GATES
1186 
1187  // READ USER-DEFINED QUANTIFICATION FACTORS
1188 
1189  // TODO : For now we assume the configuration file holds :
1190  // TODO : - frame quantification factors : specific to each bed & frame
1191  // TODO : - gate quantification factors : specific to each frame and gates
1192  // TODO : Might require a new ReadDataASCIIFile function to recover this
1193  //
1194  // TODO : Additionally, we should be careful in documentation and explained which gate quantitfication factors are natively taken into account or not
1195 
1196 
1197  // Quantification factors for each bed/frame/gate, intialized to 1.
1198  FLTNB*** pp_dynamic_quantification_factors = new FLTNB**[m_nbBeds];
1199  for (int bed=0 ; bed<m_nbBeds ; bed++)
1200  {
1201  pp_dynamic_quantification_factors[bed] = new FLTNB*[m_nbTimeFrames];
1202  for (int fr=0; fr<m_nbTimeFrames; fr++)
1203  {
1204  pp_dynamic_quantification_factors[bed][fr] = new FLTNB[m_nbRespGates*m_nbCardGates];
1205  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++) pp_dynamic_quantification_factors[bed][fr][g] = 1.;
1206  }
1207  }
1208 
1209  // Build bed-related key words
1210  string *bed_name = new string[m_nbBeds + 1];
1211 
1212  for(int bed=0 ; bed<m_nbBeds ; bed++)
1213  {
1214  ostringstream oss( ostringstream::out );
1215  oss << "bed" << bed+1;
1216  bed_name[bed] = oss.str();
1217  }
1218 
1219  bed_name[m_nbBeds] = "eof";
1220 
1221  for (int bed=0 ; bed<m_nbBeds ; bed++)
1222  {
1223  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]);
1224  if (return_value<0) // string not found error
1225  {
1226  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Didn't found quantitative factors in file " << a_quantificationFile << " !" << endl);
1227  return 1;
1228  }
1229  else if(return_value == 1) // reading error
1230  {
1231  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> An error occurred while trying to recover specific quantitative factors for frame !" << endl);
1232  return 1;
1233  }
1234  else if(return_value == 0) // correct reading
1235  {
1236  for(int bed=0 ; bed<m_nbBeds ; bed++)
1237  for (int fr=0; fr<m_nbTimeFrames; fr++)
1238  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++)
1239  if (pp_dynamic_quantification_factors[bed][fr][g] <= 0)
1240  {
1241  Cerr("***** oImageDimensionsAndQuantification::SetDynamicSpecificQuantificationFactors() -> Provided quantification factor (" << pp_dynamic_quantification_factors[bed][fr][g] << ") is negative or null !" << endl);
1242  return 1;
1243  }
1244  else
1245  {
1246  m3p_quantificationFactors[bed][fr][g] *= pp_dynamic_quantification_factors[bed][fr][g];
1247  }
1248  }
1249  }
1250 
1251  // Delete temporary tabs
1252  for (int bed=0; bed<m_nbBeds; bed++)
1253  {
1254  for (int fr=0; fr<m_nbTimeFrames; fr++) delete pp_dynamic_quantification_factors[bed][fr];
1255  delete[] pp_dynamic_quantification_factors[bed];
1256  }
1257  delete[] pp_dynamic_quantification_factors;
1258  delete[] bed_name;
1259 
1260  // End
1261  return 0;
1262 }
1263 
1264 // =====================================================================
1265 // ---------------------------------------------------------------------
1266 // ---------------------------------------------------------------------
1267 // =====================================================================
1268 
1269 FLTNB oImageDimensionsAndQuantification::GetQuantificationFactor(int a_bed, int a_frame, int a_respGate, int a_cardGate)
1270 {
1271  // Make sure that if respiratory or cardiac motion is enabled, we will recover the factor corresponding to the first image
1272  // TODO TM : perhaps more logical to initialize the oImageDimensions with the actual number of gates even when motion is enabled
1273  // 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
1274  // TODO TM : and add GetNbRespImages() functions anywhere in the code we need to know the number of resp/card images to be reconstructed
1275  // 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...
1276  if (m_nbRespGates == 1) a_respGate = 0;
1277  if (m_nbCardGates == 1) a_cardGate = 0;
1278  return m3p_quantificationFactors[a_bed][a_frame][a_respGate*m_nbCardGates+a_cardGate];
1279 }
1280 
1281 // =====================================================================
1282 // ---------------------------------------------------------------------
1283 // ---------------------------------------------------------------------
1284 // =====================================================================
1285 
1286 int oImageDimensionsAndQuantification::SetSPECTIsotope(int a_bed, const string& a_isotope)
1287 {
1288  // Check if initialized
1289  if (!m_initialized)
1290  {
1291  Cerr("***** oImageDimensionsAndQuantification::SetSPECTIsotope() -> Object not initialized !" << endl);
1292  return 1;
1293  }
1294 
1295  // Not yet implemented
1296 
1297  // Normal end
1298  return 0;
1299 }
1300 
1301 // =====================================================================
1302 // ---------------------------------------------------------------------
1303 // ---------------------------------------------------------------------
1304 // =====================================================================
1305 
1306 int oImageDimensionsAndQuantification::SetPETIsotope(int a_bed, const string& a_isotope)
1307 {
1308  // Check if initialized
1309  if (!m_initialized)
1310  {
1311  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Object not initialized !" << endl);
1312  return 1;
1313  }
1314 
1315  // ------------------------------------------------------------------
1316  // Preliminary step is checking if we ignore isotope corrections
1317  // ------------------------------------------------------------------
1318 
1320  {
1321  // Verbose
1322  if (m_verbose>=2 && a_bed==m_nbBeds-1)
1323  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Ignore isotope dependent corrections" << endl);
1324  // Exit the function
1325  return 0;
1326  }
1327 
1328  // ------------------------------------------------------------------
1329  // Preliminary step is checking for unknown keyword
1330  // ------------------------------------------------------------------
1331 
1332  // Check if the isotope is named as unknown
1333  if (a_isotope=="UNKNOWN" || a_isotope=="Unknown" || a_isotope=="unknown")
1334  {
1335  // In this case we simply assume perfect branching ratio and infinite half-life
1336  if (m_verbose>=2 && a_bed==m_nbBeds-1)
1337  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Un-specified isotope; no decay nor branching ratio correction" << endl);
1338  // Exit the function
1339  return 0;
1340  }
1341 
1342  // ------------------------------------------------------------------
1343  // First step is open the isotopes file and find the provided isotope
1344  // ------------------------------------------------------------------
1345 
1346  // Get oOutputManager instance and config directory
1347  sOutputManager* p_output_manager = sOutputManager::GetInstance();
1348  string config_dir = p_output_manager->GetPathToConfigDir();
1349 
1350  // Open the isotope file based on config directory
1351  string file_name = config_dir + "/misc/isotopes_pet.txt";
1352  ifstream fin(file_name.c_str());
1353 
1354  if (!fin)
1355  {
1356  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Failed to open PET isotopes data file '" << file_name << "' !" << endl);
1357  return 1;
1358  }
1359 
1360  // Loop on lines to find the isotope
1361  int line_max_size = 10240;
1362  char *line = new char[line_max_size];
1363  FLTNB half_life = -1.;
1364  FLTNB branching_ratio = -1.;
1365  bool found_it = false;
1366  fin.getline(line,line_max_size);
1367  while (!fin.eof())
1368  {
1369  // For the word position
1370  size_t found_position;
1371  // Cast the line into a string for convenience
1372  string test = (string)line;
1373  // Jump to next line if we find a # character as the first character
1374  if ((found_position=test.find("#"))==0)
1375  {
1376  // Read a new line before continuing
1377  fin.getline(line,line_max_size);
1378  continue;
1379  }
1380  // Check if we see the isotope name
1381  found_position = test.find(a_isotope);
1382  if (found_position!=string::npos)
1383  {
1384  // Each line is organised as follows: (some spaces/tabs) isotope_name (some spaces/tabs) half_life (some spaces/tabs) branching_ratio
1385  // We first remove the isotope name from the line
1386  test = test.substr(found_position+1+a_isotope.length());
1387  // Then we convert it into a string stream to ease the reading of both numbers
1388  istringstream fstr(test);
1389  fstr >> half_life >> branching_ratio;
1390  // We found it
1391  found_it = true;
1392  break;
1393  }
1394  // Read a new line
1395  fin.getline(line,line_max_size);
1396  }
1397 
1398  delete[] line;
1399  // Close file
1400  fin.close();
1401 
1402  // Check if we found it or not
1403  if (found_it)
1404  {
1405  // Check rationality of values
1406  if (branching_ratio<=0. || branching_ratio>1.)
1407  {
1408  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Branching ratio (" << branching_ratio << ") is not in the ]0:1] range !" << endl);
1409  return 1;
1410  }
1411  // Verbose
1412  if (m_verbose>=2 && a_bed==m_nbBeds-1)
1413  {
1414  // If negative half-life, then we consider it is infinite (no half-life)
1415  if (half_life<=0.)
1416  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Isotope " << a_isotope << " has infinite half life and " << branching_ratio << " branching ratio" << endl);
1417  else
1418  Cout("oImageDimensionsAndQuantification::SetPETIsotope() -> Isotope " << a_isotope << " has " << half_life << " seconds half life and " << branching_ratio << " branching ratio" << endl);
1419  }
1420  }
1421  else
1422  {
1423  // Throw error message
1424  Cerr("***** oImageDimensionsAndQuantification::SetPETIsotope() -> Did not find " << a_isotope << " isotope in the PET isotope data file, please add it !" << endl);
1425  return 1;
1426  }
1427 
1428  // ------------------------------------------------------------------------
1429  // Second step is applying branching ratio and decay wrt to frame durations
1430  // ------------------------------------------------------------------------
1431 
1432  // Branching ratio
1434  {
1435  // Apply correction
1436  for (int fr=0; fr<m_nbTimeFrames; fr++)
1437  {
1438  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
1439  m3p_quantificationFactors[a_bed][fr][g] /= branching_ratio;
1440  }
1441  // Verbose
1442  if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Correct for branching ratio" << endl);
1443  }
1444  // Verbose
1445  else if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Ignore branching ratio correction" << endl);
1446 
1447  // Apply decay factors if half life is not negative (meaning no half-life)
1448  if (half_life>0.)
1449  {
1450  // We correct for it
1452  {
1453  // Apply correction
1454  for (int fr=0; fr<m_nbTimeFrames; fr++)
1455  {
1456  m_lambda = log(2.0)/half_life;
1457  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
1458  {
1459  long double dstart = m_lambda*GetFrameTimeStartInSec(a_bed,fr);
1460  long double dacq = m_lambda*GetFrameDurationInSec(a_bed,fr);
1461  // Time start decay correction
1462  m3p_quantificationFactors[a_bed][fr][g] *= exp(dstart);
1463  // Frame duration decay correction
1464  m3p_quantificationFactors[a_bed][fr][g] *= dacq/(1.0-exp(-dacq));
1465 
1466  /* Time start decay correction
1467  m3p_quantificationFactors[a_bed][fr][g] *= exp(lambda*GetFrameTimeStartInSec(a_bed,fr));
1468  // Frame duration decay correction
1469  m3p_quantificationFactors[a_bed][fr][g] *= lambda*GetFrameDurationInSec(a_bed,fr)/(1.0-exp(-lambda*(GetFrameDurationInSec(a_bed,fr))));*/
1470  }
1471  }
1472  // Verbose
1473  if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Correct for half-life" << endl);
1474  }
1475  // Verbose
1476  else if (m_verbose>=2 && a_bed==m_nbBeds-1) Cout(" --> Ignore half-life correction" << endl);
1477  }
1478 
1479  // End
1480  return 0;
1481 }
1482 
1483 // =====================================================================
1484 // ---------------------------------------------------------------------
1485 // ---------------------------------------------------------------------
1486 // =====================================================================
1487 
1488 int oImageDimensionsAndQuantification::InitDynamicData( string a_pathTo4DDataSplittingFile,
1489  int a_respMotionCorrectionFlag,
1490  int a_cardMotionCorrectionFlag,
1491  int a_invMotionCorrectionFlag,
1492  int a_nbRespGates, int a_nbCardGates )
1493 {
1494  if (m_verbose>=5) Cout("oImageDimensionsAndQuantification::InitDynamicData()" << endl);
1497  if (mp_DynamicDataManager->InitDynamicData( a_nbRespGates, a_nbCardGates, a_pathTo4DDataSplittingFile,
1498  a_respMotionCorrectionFlag, a_cardMotionCorrectionFlag, a_invMotionCorrectionFlag ))
1499  {
1500  Cerr("***** oImageDimensionsAndQuantification::InitDynamicData() -> A problem occurred while initializing the dynamic data from dynamic data manager !" << endl);
1501  return 1;
1502  }
1503 
1504  // Set type of dynamic reconstruction
1505  if (a_respMotionCorrectionFlag || a_cardMotionCorrectionFlag)
1507  else if (a_nbRespGates>1 || a_nbCardGates>1)
1509  else if (a_invMotionCorrectionFlag)
1511  else if (m_nbTimeFrames>1)
1513 
1514 
1515  return 0;
1516 }
1517 
1518 // =====================================================================
1519 // ---------------------------------------------------------------------
1520 // ---------------------------------------------------------------------
1521 // =====================================================================
1522 
1524 {
1525  if (m_verbose>=5) Cout("oImageDimensionsAndQuantification::CheckDynamicParameters()" << endl);
1526  return mp_DynamicDataManager->CheckParameters(a_nbEvents);
1527 }
1528 
1529 // =====================================================================
1530 // ---------------------------------------------------------------------
1531 // ---------------------------------------------------------------------
1532 // =====================================================================
1534 {
1535  // Check that the mask has not already been set
1536  if (m_hasMask)
1537  {
1538  Cerr("***** oImageDimensionsAndQuantification::ProcessAndSetMask() -> Mask already initialized !" << endl);
1539  return 1;
1540  }
1541  // Allocate
1542  mp_mask = new bool[m_nbVoxXYZ];
1543  // Fill the mask
1544  for (INTNB v=0; v<m_nbVoxXYZ;v++)
1545  {
1546  // Currently, values not greater than 0 are regarded as background
1547  // As the mask has a real number type, to avoid issues with zeros potentially written
1548  // as extremely tiny floating point values, the values are first rounded to an integer type
1549  // and then checked for greater than zero, improve this in the future (implicit behaviour)
1550  mp_mask[v] = ((INTNB)std::round(ap_maskImage[v]))>0;
1551  }
1552  // Mask on
1553  m_hasMask = true;
1554  // End
1555  return 0;
1556 }
1557 
1558 // =====================================================================
1559 // ---------------------------------------------------------------------
1560 // ---------------------------------------------------------------------
1561 // =====================================================================
1562 
1564 {
1565  return (m_hasMask && !mp_mask[a_voxIndex]);
1566 }
1567 
1568 // =====================================================================
1569 // ---------------------------------------------------------------------
1570 // ---------------------------------------------------------------------
1571 // =====================================================================
int InitializeFramingAndQuantification()
A function used to initialize the framing and quantification tables.
This class is designed to be a mother virtual class for DataFile.
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
oImageDimensionsAndQuantification()
The constructor of oImageDimensionsAndQuantification.
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the &#39;a_input&#39; string corresponding to the &#39;a_option&#39; into &#39;a_nbElts&#39; elements, using the &#39;sep&#39; separator. The results are returned in the templated &#39;ap_return&#39; dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
#define Cerr(MESSAGE)
This class gathers the information about the dynamic splitting of the data.
int SetCalibrationFactor(int a_bed, FLTNB a_calibrationFactor)
void CheckNumberOfProjectionThreadsConsistencyWithDataFileSize(vDataFile **a2p_DataFile)
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
~oImageDimensionsAndQuantification()
The destructor of oImageDimensionsAndQuantification.
int InitDynamicData(int a_nbRespGates, int a_nbCardGates, const string &a_pathTo4DDataSplittingFile, int a_rmMCorrFlag, int a_cMmCorrFlag, int a_pMotionCorrFlag)
int ResetQuantificationFactors()
If already initialized, set the quantification factors to 1.
int CheckParameters()
A function used to check the parameters settings.
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.
FLTNB GetQuantificationFactor(int a_bed, int a_frame, int a_respGate, int a_cardGate)
int SetDynamicSpecificQuantificationFactors(FLTNB **a2p_quantificationFactors)
int InitDynamicData(string a_pathTo4DDataSplittingFile, int a_respMotionCorrectionFlag, int a_cardMotionCorrectionFlag, int a_invMotionCorrectionFlag, int a_nbRespGates, int a_nbCardGates)
#define KEYWORD_MANDATORY
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
void SetDefault()
A function used to set number of threads and MPI instances to 1 and bypass the CheckParameters() func...
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...
int SetAcquisitionTime(int a_bed, FLTNB a_timeStartInSec, FLTNB a_durationInSec, string a_GateListDurationsInSec)
int InitializeIgnoredCorrections()
A function used to initialize the ignored corrections.
#define Cout(MESSAGE)
Generic class for scanner objects.
int SetDynamicSpecificQuantificationFactors(const string &a_quantificationFile)