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