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