CASToR  2.0
Tomographic Reconstruction (PET/SPECT/CT)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
oSensitivityGenerator.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2018 all CASToR contributors listed below:
18 
19  --> current contributors: Thibaut MERLIN, Simon STUTE, Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Mael MILLARDET
20  --> past contributors: Valentin VIELZEUF
21 
22 This is CASToR version 2.0.
23 */
24 
31 #include "gVariables.hh"
32 #include "oSensitivityGenerator.hh"
33 #include "iDataFilePET.hh"
34 
35 // =====================================================================
36 // ---------------------------------------------------------------------
37 // ---------------------------------------------------------------------
38 // =====================================================================
39 
41 {
42  // Simply default all members to "empty" values
45  mp_ImageSpace = NULL;
46  mp_Scanner = NULL;
47  mp_ProjectorManager = NULL;
49  mp_DeformationManager = NULL;
50  m2p_DataFile = NULL;
58  m_pathToMaskImg = "";
59  m_mumapAttenuationFlag = false;
63  mp_lineCounter = NULL;
64  m_flagGPU = false;
65  m_verbose = -1;
66  m_checked = false;
67  m_initialized = false;
68 }
69 
70 // =====================================================================
71 // ---------------------------------------------------------------------
72 // ---------------------------------------------------------------------
73 // =====================================================================
74 
76 {
77  // Delete all normalization data files if any
79  {
80  for (int bed=0; bed<mp_ImageDimensionsAndQuantification->GetNbBeds(); bed++) if (m3p_NormalizationDataFile[bed])
81  {
82  for (int fr=0; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++)
83  if (m3p_NormalizationDataFile[bed][fr]) delete m3p_NormalizationDataFile[bed][fr];
84  free(m3p_NormalizationDataFile[bed]);
85  }
87  }
88  if (mp_lineCounter) free(mp_lineCounter);
89 }
90 
91 // =====================================================================
92 // ---------------------------------------------------------------------
93 // ---------------------------------------------------------------------
94 // =====================================================================
95 
97 {
98  // Check all mandatory parameters
100  {
101  Cerr("***** oSensitivityGenerator::CheckParameters() -> Image dimensions and quantification object is null !" << endl);
102  return 1;
103  }
104  if (mp_ImageSpace==NULL)
105  {
106  Cerr("***** oSensitivityGenerator::CheckParameters() -> Image space object is null !" << endl);
107  return 1;
108  }
109  if (mp_Scanner==NULL)
110  {
111  Cerr("***** oSensitivityGenerator::CheckParameters() -> Scanner object is null !" << endl);
112  return 1;
113  }
114  if (mp_ProjectorManager==NULL)
115  {
116  Cerr("***** oSensitivityGenerator::CheckParameters() -> Projector manager object is null !" << endl);
117  return 1;
118  }
119  if (mp_ImageConvolverManager==NULL)
120  {
121  Cerr("***** oSensitivityGenerator::CheckParameters() -> Convolver manager object is null !" << endl);
122  return 1;
123  }
124  if (mp_DeformationManager==NULL)
125  {
126  Cerr("***** oSensitivityGenerator::CheckParameters() -> Deformation manager object is null !" << endl);
127  return 1;
128  }
129  if (m2p_DataFile==NULL)
130  {
131  Cerr("***** oSensitivityGenerator::CheckParameters() -> Data file array is null !" << endl);
132  return 1;
133  }
134  for (int b=0; b<mp_ImageDimensionsAndQuantification->GetNbBeds(); b++)
135  {
136  if (m2p_DataFile[b]==NULL)
137  {
138  Cerr("***** oSensitivityGenerator::CheckParameters() -> Data file object for bed " << b+1 << " is null !" << endl);
139  return 1;
140  }
141  }
142  if (m_verbose<0)
143  {
144  Cerr("***** oSensitivityGenerator::CheckParameters() -> Verbose level is negative !" << endl);
145  return 1;
146  }
147  // If compute the sensitivity from a histogram datafile, then check if the datafile is actually an histogram
148  if (m_computeFromHistogramFlag && m2p_DataFile[0]->GetDataMode()!=MODE_HISTOGRAM)
149  {
150  Cerr("***** oSensitivityGenerator::CheckParameters() -> It was asked to compute global sensitivity from the provided datafile whereas it is not a histogram !" << endl);
151  return 1;
152  }
153  // Sensitivity computation from histogram do not work with dynamism for the moment
157  {
158  Cerr("***** oSensitivityGenerator::CheckParameters() -> Global sensitivity computation from histogram does not work wth dynamic data yet !" << endl);
159  return 1;
160  }
161  // Now it is checked
162  m_checked = true;
163  // End
164  return 0;
165 }
166 
167 // =====================================================================
168 // ---------------------------------------------------------------------
169 // ---------------------------------------------------------------------
170 // =====================================================================
171 
173 {
174  // First check that the parameters has been checked !
175  if (!m_checked)
176  {
177  Cerr("***** oSensitivityGenerator::Initialize() -> Must call the CheckParameters() function before initialization !" << endl);
178  return 1;
179  }
180  // Verbose
181  if (m_verbose>=2) Cout("oSensitivityGenerator::Initialize() -> Start initialization" << endl);
182  // For the moment, SPECT and CT sensitivity are not implemented
183  if (m2p_DataFile[0]->GetDataType()==TYPE_SPECT || m2p_DataFile[0]->GetDataType()==TYPE_CT)
184  {
185  Cerr("***** oSensitivityGenerator::Initialize() -> Sensitivity for SPECT and CT not yet implemented !" << endl);
186  return 1;
187  }
188 
189  // Allocate the forward image
191 
192  // Initialization of normalization files:
193  // --> must be done before attenuation files, because attenuation can be inside the normalization data file
194  // --> is not done if sensitivity generated from the histogram datafile
196  {
197  Cerr("***** oSensitivityGenerator::Initialize() -> A problem occured while initializing the normalization data files !" << endl);
198  return 1;
199  }
200  // Initialization of attenuation related stuff: not done if sensitivity generated from the histogram datafile
202  {
203  Cerr("***** oSensitivityGenerator::Initialize() -> A problem occured while initializing the attenuation files !" << endl);
204  return 1;
205  }
206  // We want to know if we will need to forward project into the attenuation image, so here the full condition
207  if (
208  m_mumapAttenuationFlag && // 1. We must have an attenuation image that is loaded
209  m2p_DataFile[0]->GetDataType()==TYPE_PET && // 2. This is only for PET
210  m2p_DataFile[0]->GetDataMode()==MODE_LIST && // 3. This is only for sensitivity generation for list-mode datafile
211  ( m3p_NormalizationDataFile==NULL || // 4.1 There is no normalization data file (potentially including ACF)
212  !(dynamic_cast<iDataFilePET*>(m3p_NormalizationDataFile[0][0]))->GetAtnCorrectionFlag() ) // 4.2 OR the normalization data file does not contain ACF
213  )
214  {
215  // We use a specific boolean that says we need to forward project into the attenuation image
217  }
218  else
219  {
220  // Otherwise, we don't
222  }
223 
224  // Allocate the backward image for multi-thread
226  // Allocate the mask image if provided
228  {
229  Cerr("***** oSensitivityGenerator::Initialize() -> Error during mask image initialization !" << endl);
230  return 1;
231  }
232  // Create sensitivity image file name
233  sOutputManager* p_outputManager = sOutputManager::GetInstance();
234  m_pathToSensitivityImage = p_outputManager->GetPathName() + p_outputManager->GetBaseName() + "_sensitivity";
235  // Allocate line counters
236  mp_lineCounter = (uint64_t*)calloc(mp_ImageDimensionsAndQuantification->GetNbThreadsForProjection(),sizeof(uint64_t));
237  // End
238  return 0;
239 }
240 
241 // =====================================================================
242 // ---------------------------------------------------------------------
243 // ---------------------------------------------------------------------
244 // =====================================================================
245 
247 {
248  // TODO: check the consistency between all normalization files (atnflag, normfactor, etc)
249 
250  // If no normalization file name provided, then we will exit, but we do some checks before
251  if (mp_pathToNormalizationFileName.size()==0)
252  {
253  // If the ignore-normalization-correction-flag is on, then we can exit the function
255  // In PET, throw an error if normalization correction is in the datafile and is taken into account (only if the datafile is a list-mode)
256  if (m2p_DataFile[0]->GetDataType()==TYPE_PET && m2p_DataFile[0]->GetDataMode()==MODE_LIST)
257  {
258  // Cast the vDataFile into iDataFilePET
259  iDataFilePET* p_pet_file = (dynamic_cast<iDataFilePET*>(m2p_DataFile[0]));
260  // Check if the normalization data are in the file and we do not ignore it
261  if (p_pet_file->GetNormCorrectionFlag())
262  {
263  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Normalization correction is included in the data file while it is not in the sensitivity computation !" << endl);
264  return 1;
265  }
266  }
267  // We can exit now
268  return 0;
269  }
270 
271  // Verbose
272  if (m_verbose>=2) Cout("oSensitivityGenerator::InitializeNormalizationFiles() -> Initialize normalization files" << endl);
273 
274  // Check that the number of normalization files options is the same as the number of beds
276  {
277  // If only one is provided, then we assume that it is the same for all bed positions
279  // Otherwise, throw an error
280  else
281  {
282  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Number of normalization files options should be one or equal to the number of data files !" << endl);
283  return 1;
284  }
285  }
286 
287  // Allocate the normalization data files array for the bed positions
289 
290  // Get the scanner manager
291  sScannerManager* p_ScannerManager = sScannerManager::GetInstance();
292 
293  // Loop on the number of beds
294  for (int bed=0; bed<mp_ImageDimensionsAndQuantification->GetNbBeds(); bed++)
295  {
296  // --------------------------------------------------------------------------
297  // Step 0: When only one normalization file names is provided for all beds
298  // --------------------------------------------------------------------------
300  {
301  // If not the first bed
302  if (bed!=0)
303  {
304  // We copy the pointer of the first bed into this one
306  // And we continue the loop
307  continue;
308  }
309  }
310  // --------------------------------------------------------------------------
311  // Step 1: Get filenames and check consistency with number of beds and frames
312  // --------------------------------------------------------------------------
313  // We will get the normalization file names for this bed
314  vector<string> normalization_files;
315  // Compute the bed index associated to the file name; in case of reverse order, we change the index only for datafile management here
316  int bed_data_file_name_index = bed;
317  if (m_inverseDataFileOrderFlag) bed_data_file_name_index = mp_ImageDimensionsAndQuantification->GetNbBeds() - 1 - bed;
318  // Then we search for commas separating the file name associated to each frame
319  size_t comma = mp_pathToNormalizationFileName[bed_data_file_name_index].find_first_of(",");
320  // Loop to get all file names
321  while (comma!=string::npos)
322  {
323  // Get the file name before the comma
324  normalization_files.push_back(mp_pathToNormalizationFileName[bed_data_file_name_index].substr(0,comma));
325  // Remove this part from the collection of file names
326  mp_pathToNormalizationFileName[bed_data_file_name_index] = mp_pathToNormalizationFileName[bed_data_file_name_index].substr(comma+1);
327  // Search for the next comma
328  comma = mp_pathToNormalizationFileName[bed_data_file_name_index].find_first_of(",");
329  }
330  // Get the last file name
331  normalization_files.push_back(mp_pathToNormalizationFileName[bed_data_file_name_index]);
332  // If we have only one frame, then set the m_oneNormalizationFileForAllFrames to true right now
334  // Check that the number of file names found is equal to the number of frames
335  if (normalization_files.size()!=((size_t)(mp_ImageDimensionsAndQuantification->GetNbTimeFrames())))
336  {
337  // If only one is provided, then we assume that it is the same for all frames
338  if (normalization_files.size()==1) m_oneNormalizationFileForAllFrames = true;
339  // Otherwise, throw an error
340  else
341  {
342  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Number of normalization files for bed " << bed+1 << " should be one or equal to the number of frames !" << endl);
343  return 1;
344  }
345  }
346  // If this is the case, then check for previous beds that it was so
347  else if (bed!=0 && !m_oneNormalizationFileForAllFrames)
348  {
349  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> If only one normalization file is provided for all frames, then it must be so for all beds !" << endl);
350  return 1;
351  }
352  // --------------------------------------------------------------------------
353  // Step 2: Create the normalization data file objects
354  // --------------------------------------------------------------------------
355  // Allocate the normalization data file array for the frames
357  // Loop on the number of frames
358  for (int fr=0; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++)
359  {
360  // If one normalization file for all frames
362  {
363  // Then, if not the first frame
364  if (fr!=0)
365  {
366  // We copy the pointer of the first frame into this one
368  // And we continue the loop
369  continue;
370  }
371  }
372  // Switch on the scanner type and create the specific data files
373  if (p_ScannerManager->GetScannerType() == SCANNER_PET) m3p_NormalizationDataFile[bed][fr] = new iDataFilePET();
374  else
375  {
376  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Unknown scanner type (" << p_ScannerManager->GetScannerType() << ") or not yet implemented !" << endl);
377  return 1;
378  }
379  m3p_NormalizationDataFile[bed][fr]->SetHeaderDataFileName(normalization_files[fr]);
380  m3p_NormalizationDataFile[bed][fr]->SetBedIndex(bed);
381  m3p_NormalizationDataFile[bed][fr]->SetVerbose(m2p_DataFile[0]->GetVerbose());
383  bool affect_quantification_flag = false;
384  if (m3p_NormalizationDataFile[bed][fr]->ReadInfoInHeader(affect_quantification_flag))
385  {
386  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred during normalization datafile header reading !" << endl);
387  return 1;
388  }
390  {
391  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred while checking normalization datafile parameters !" << endl);
392  return 1;
393  }
394  if (m3p_NormalizationDataFile[bed][fr]->ComputeSizeEvent())
395  {
396  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred in normalization datafile event size computation !" << endl);
397  return 1;
398  }
399  if (m3p_NormalizationDataFile[bed][fr]->InitializeMappedFile())
400  {
401  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred in normalization datafile initialization !" << endl);
402  return 1;
403  }
404  if (m3p_NormalizationDataFile[bed][fr]->PrepareDataFile())
405  {
406  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occured in normalization datafile preparation !" << endl);
407  return 1;
408  }
409  // Check that the normalization data mode is histogram or normalization
410  if (m3p_NormalizationDataFile[bed][fr]->GetDataMode()!=MODE_HISTOGRAM && m3p_NormalizationDataFile[bed][fr]->GetDataMode()!=MODE_NORMALIZATION)
411  {
412  Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Normalization data file used for sensitivity computation must be either histogram or normalization mode !" << endl);
413  return 1;
414  }
415  }
416  }
417 
418  // End
419  return 0;
420 }
421 
422 // =====================================================================
423 // ---------------------------------------------------------------------
424 // ---------------------------------------------------------------------
425 // =====================================================================
426 
428 {
429  // If it is asked to ignore attenuation correction, then nothing to do
431 
432  // If attenuation image file name is empty, then we do some checks before
433  if (m_pathToAttenuationImage=="")
434  {
435  // Case for PET: if no normalization data file OR no ACF in the normalization data file
436  if (m2p_DataFile[0]->GetDataType()==TYPE_PET && (m3p_NormalizationDataFile==NULL || !(dynamic_cast<iDataFilePET*>(m3p_NormalizationDataFile[0][0]))->GetAtnCorrectionFlag()))
437  {
438  // Cast the vDataFile into iDataFilePET
439  iDataFilePET* p_pet_file = (dynamic_cast<iDataFilePET*>(m2p_DataFile[0]));
440  // Throw an error if attenuation correction is in the datafile (at this step we know we do not ignore it)
441  if (p_pet_file->GetAtnCorrectionFlag())
442  {
443  Cerr("***** oSensitivityGenerator::InitializeAttenuationFiles() -> Attenuation correction is included in the data file while it is not in the sensitivity computation !" << endl);
444  return 1;
445  }
446  }
447  // We can exit now
448  return 0;
449  }
450 
451  // In PET, if the ACF is already present in the normalization data file, then say it and ignore mumap
452  if (m2p_DataFile[0]->GetDataType()==TYPE_PET && m3p_NormalizationDataFile!=NULL && (dynamic_cast<iDataFilePET*>(m3p_NormalizationDataFile[0][0]))->GetAtnCorrectionFlag())
453  {
454  Cout("oSensitivityGenerator::InitializeAttenuationFiles() -> Ignore provided mumap and consider ACF provided in the normalization data file" << endl);
455  return 0;
456  }
457 
458  // Verbose
459  if (m_verbose>=2) Cout("oSensitivityGenerator::InitializeAttenuationFiles() -> Allocate and read attenuation images (assumed to be in cm-1)" << endl);
460 
461  // Allocate and read the attenuation image (this puts the image into the m4p_attenuation buffer of oImageSpace)
463  {
464  Cerr("***** oSensitivityGenerator::Initialize() -> A problem occured while initializing the attenuation image into the image space !" << endl);
465  return 1;
466  }
467 
468  // Allocate the foward image, where the attenuation will be copied and deformed if needed
469  //mp_ImageSpace->LMS_InstantiateForwardImage();
470 
471  // Copy attenuation image into forward-image buffer
474 
475  // We do not need the m4p_attenuation buffer of oImageSpace anymore, so free it now
477 
478  // TODO: Think about adding a field in the attenuation header to specify if the attenuation images have the same spatial resolution as the
479  // scanner in use, in order to apply a smoothing on it. For the moment, we assume it has been done beforehand.
480 
481  // TODO: for the moment, we assume that the attenuation image is in cm-1, maybe use a header field to detect that and convert it
482  // automatically.
483 
484  // Put attenuation flag on
485  m_mumapAttenuationFlag = true;
486 
487  // End
488  return 0;
489 }
490 
491 // =====================================================================
492 // ---------------------------------------------------------------------
493 // ---------------------------------------------------------------------
494 // =====================================================================
495 
497 {
498  // Verbose
499  if (m_verbose>=1) Cout("oSensitivityGenerator::Launch() -> Start the sensitivity computation" << endl);
500  // Simply switches between the CPU and GPU versions
501  #ifdef CASTOR_GPU
502  if (m_flagGPU) return LaunchGPU();
503  else return LaunchCPU();
504  #else
505  return LaunchCPU();
506  #endif
507 }
508 
509 // =====================================================================
510 // ---------------------------------------------------------------------
511 // ---------------------------------------------------------------------
512 // =====================================================================
513 
515 {
516  // Loop on the bed positions
517  for (int bed=0; bed<mp_ImageDimensionsAndQuantification->GetNbBeds(); bed++)
518  {
519  // Reset line counters
521  // Apply the bed offset for this bed position
523  // Case where the computation is performed directly from the histogram datafile
525  {
527  {
528  Cerr("***** oSensitivityGenerator::LaunchCPU() -> A problem occured while computing the sensitivity using the histogram data file for bed " << bed+1 << " !" << endl);
529  return 1;
530  }
531  }
532  // If a normalization file is provided, then use them to iterate on the elements to be taken into account
533  else if (m3p_NormalizationDataFile!=NULL)
534  {
536  {
537  Cerr("***** oSensitivityGenerator::LaunchCPU() -> A problem occured while computing the sensitivity using the normalization file for bed " << bed+1 << " !" << endl);
538  return 1;
539  }
540  }
541  // Otherwise, use a generic method from the scanner elements
542  else
543  {
545  {
546  Cerr("***** oSensitivityGenerator::LaunchCPU() -> A problem occured while computing the sensitivity from scanner elements for bed " << bed+1 << " !" << endl);
547  return 1;
548  }
549  // Synchronize MPI processes here as the sensitivity computation from scanner elements has not MPI implementation yet
550  #ifdef CASTOR_MPI
551  MPI_Barrier(MPI_COMM_WORLD);
552  #endif
553  }
554 
555  // Sum up the line counter into the first thread
557  // Verbose
558  if (m_verbose>=2) Cout(" --> Number of effectively projected lines: " << mp_lineCounter[0] << endl);
559  }
560 
561 
562  // Set the number of threads for image computations
563  #ifdef CASTOR_OMP
565  #endif
566 
567  // Reduce all results (merging parallel computation)
568  for (int fr=0 ; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames() ; fr++)
569  {
570  //for (int rg=mp_ImageDimensionsAndQuantification->GetPMotionFirstIndexForLMS(fr) ; rg<mp_ImageDimensionsAndQuantification->GetNb1stMotImgsForLMS(fr) ; rg++)
572  for (int rg=0 ; rg<frm_nb_1st_mot_imgs ; rg++)
573  for (int cg=0 ; cg<mp_ImageDimensionsAndQuantification->GetNb2ndMotImgsForLMS() ; cg++)
574  {
575  // Merge parallel results into the backward image
576  int img_0 = 0;
577  mp_ImageSpace->ReduceBackwardImage(img_0, fr, rg, cg);
578  // Apply mask if provided
579  mp_ImageSpace->ApplyMaskToBackwardImage(img_0, fr, rg, cg);
580  // Perform any deformations on the backward image
581 
582  /*
583  mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace,
584  BACKWARD_DEFORMATION,
585  fr,
586  rg + mp_ImageDimensionsAndQuantification->GetPMotionFirstIndexForLMS(fr),
587  cg);*/
588 
592  fr,
593  rg,
594  cg);
595 
596  //mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, BACKWARD_DEFORMATION, 1, fr, rg, cg);
597  //mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, BACKWARD_DEFORMATION, 2, fr, rg, cg);
598  }
599  }
600 
601  // Allocate sensitivity image
603  // Copy backward image to sensitivity image
605  // Deallocate backward image
607 
608  // Deallocate the forward image only if attenuation was used
609  //if (m_mumapAttenuationFlag) mp_ImageSpace->LMS_DeallocateForwardImage();
610  // Deallocate forward image
612  // Deallocate mask image
614 
615  // Apply PSF and save only for first instance
617  {
618  // Apply PSF if needed
620  // Save sensitivity image
622  }
623  // Deallocate sensitivity image
625  // End
626  return 0;
627 }
628 
629 // =====================================================================
630 // ---------------------------------------------------------------------
631 // ---------------------------------------------------------------------
632 // =====================================================================
633 
635 {
636  // For the moment, we restrict the validity of this function to non-gated data
638  {
639  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromHistogramDataFile() -> Cannot compute sensitivity from histogram file with gating !" << endl);
640  return 1;
641  }
642 
643  // Verbose
644  if (m_verbose>=2)
645  {
646  if (mp_ImageDimensionsAndQuantification->GetNbBeds()>1) Cout("oSensitivityGenerator::ComputeSensitivityFromHistogramDataFile() -> Start computation for bed " << a_bed+1 << endl);
647  else Cout("oSensitivityGenerator::ComputeSensitivityFromHistogramDataFile() -> Start computation" << endl);
648  }
649 
650  // Initial clock
651  clock_t clock_start = clock();
652  time_t time_start = time(NULL);
653 
654  // In case a problem occurs in the parallel loop
655  bool problem = false;
656  // Set the number of threads for projections
657  #ifdef CASTOR_OMP
659  #endif
660 
661  // Get index start and stop
662  int64_t index_start = 0;
663  int64_t index_stop = 0;
664  m2p_DataFile[a_bed]->GetEventIndexStartAndStop(&index_start, &index_stop);
665 
666  // Reinitialize 4D gate indexes
668 
669  // Launch the loop on all events
670  int64_t index = 0, printing_index = 0;
671  #pragma omp parallel for private(index) schedule(static, 1)
672  for ( index = index_start ; index < index_stop ; index++)
673  {
674  // Get the thread index
675  int th = 0;
676  #ifdef CASTOR_OMP
677  th = omp_get_thread_num();
678  #endif
679  // Verbose
681  {
682  if (printing_index%1000==0)
683  {
684  int percent = ((int)( ((FLTNB)(index-index_start)) * 100. / ((FLTNB)(index_stop-index_start)) ));
685  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b "
686  << percent << " % " << flush;
687  }
688  printing_index++;
689  }
690  // Get the current event for that thread index
691  vEvent* event = m2p_DataFile[a_bed]->GetEvent(index, th);
692  if (event==NULL)
693  {
694  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromHistogramDataFile() -> An error occured while getting the event from index "
695  << index << " (thread " << th << ") !" << endl);
696  // A problem was encountered
697  problem = true;
698  // We must continue here because we are inside an OpenMP loop
699  continue;
700  }
701 /*
702  // Call the dynamic switch function that updates the current frame and gate numbers, and also detects involuntary patient motion
703  int dynamic_switch_value = mp_ImageDimensionsAndQuantification->DynamicSwitch(index, event->GetTimeInMs(), a_bed, th);
704  // If the DYNAMIC_SWITCH_CONTINUE is returned, then it means that we are not yet at the first frame
705  if (dynamic_switch_value==DYNAMIC_SWITCH_CONTINUE)
706  {
707  // Then we just skip this event
708  continue;
709  }
710 */
711  // Compute the projection line
713  if (line==NULL)
714  {
715  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromHistogramDataFile() -> A problem occured while computing the projection line !" << endl);
716  // Specify that there was a problem
717  problem = true;
718  // We must continue here because we are inside an OpenMP loop
719  continue;
720  }
721 
722  // Process this line
723  int no_frame = 0;
724  int no_resp_gate = 0;
725  int no_card_gate = 0;
726 // if (line->NotEmptyLine() && ProcessThisLine(line, event, a_bed, mp_ImageDimensionsAndQuantification->GetCurrentTimeFrame(th), no_resp_gate, no_card_gate, th))
727  if (line->NotEmptyLine() && ProcessThisLine(line, event, a_bed, no_frame, no_resp_gate, no_card_gate, th))
728  {
729  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromHistogramDataFile() -> A problem occured while processing a line !" << endl);
730  }
731  }
732 
733  // Synchronize MPI processes
734  #ifdef CASTOR_MPI
735  MPI_Barrier(MPI_COMM_WORLD);
736  #endif
737 
738  // End of progression printing (do not log out with Cout here)
740  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
741  << " --> 100 % " << endl;
742  // If a problem was encountered, then report it here
743  if (problem)
744  {
745  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromHistogramDataFile() -> A problem occured inside the parallel loop over events !" << endl);
746  return 1;
747  }
748 
749  // Clock total
750  clock_t clock_stop = clock();
751  time_t time_stop = time(NULL);
753  Cout(" --> Time spent for sensitivity generation for bed " << a_bed+1 << " | User: " << time_stop-time_start
754  << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
755  else
756  Cout(" --> Time spent for sensitivity generation | User: " << time_stop-time_start
757  << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
758 
759  // End
760  return 0;
761 }
762 
763 // =====================================================================
764 // ---------------------------------------------------------------------
765 // ---------------------------------------------------------------------
766 // =====================================================================
767 
769 {
770  // Verbose
771  if (m_verbose>=2)
772  {
773  if (mp_ImageDimensionsAndQuantification->GetNbBeds()>1) Cout("oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> Start computation for bed " << a_bed+1 << endl);
774  else Cout("oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> Start computation" << endl);
775  }
776 
777  // Initial clock
778  clock_t clock_start = clock();
779  time_t time_start = time(NULL);
780 
781  // Loop on frames
782  for (int fr=0 ; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames() ; fr++)
783  {
784  // Verbose
786  cout << " --> Processing frame " << fr+1 << endl;
787  // Loop on respiratory gates
788  for (int rg=0 ; rg<mp_ImageDimensionsAndQuantification->GetNb1stMotImgsForLMS(fr) ; rg++)
789  {
790  // Verbose
792  cout << " --> Processing respiratory gate " << rg+1 << endl;
793  // Loop on cardiac gates
794  for (int cg=0 ; cg<mp_ImageDimensionsAndQuantification->GetNb2ndMotImgsForLMS() ; cg++)
795  {
796  // Verbose
798  cout << " --> Processing cardiac gate " << cg+1 << endl;
799 
800  // Perform any forward deformations on the forward image
801  //mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, FORWARD_DEFORMATION, fr, rg, cg);
805  fr,
806  rg,
807  cg);
808 
809  // In case a problem occurs in the parallel loop
810  bool problem = false;
811  // Set the number of threads for projections
812  #ifdef CASTOR_OMP
814  #endif
815 
816  // Get index start and stop
817  int64_t index_start = 0;
818  int64_t index_stop = 0;
819  m3p_NormalizationDataFile[a_bed][fr]->GetEventIndexStartAndStop(&index_start, &index_stop);
820 
821  // Index for progression printing
822  uint64_t progression_index_total = (index_stop-index_start)
826 
827  // Launch the loop on all events
828  int64_t index = 0, printing_index = 0;
829  #pragma omp parallel for private(index) schedule(static, 1)
830  for ( index = index_start ; index < index_stop ; index++)
831  {
832  // Get the thread index
833  int th = 0;
834  #ifdef CASTOR_OMP
835  th = omp_get_thread_num();
836  #endif
837 
838  // Verbose
839  /*
840  if (th==0 && m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetMPIRank()==0)
841  {
842  if (printing_index%1000==0)
843  {
844  int percent = ((int)( ((FLTNB)(index-index_start)) * 100. / ((FLTNB)(index_stop-index_start)) ));
845  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b "
846  << percent << " % " << flush;
847  }
848  printing_index++;
849  }
850  */
851 
853  {
854  if (printing_index%1000==0)
855  {
856  int64_t progression_chunk = (int64_t)((FLTNB)(index_stop-index_start));
857  int64_t progression_index_current = fr * mp_ImageDimensionsAndQuantification->GetNb1stMotImgsForLMS(fr) * mp_ImageDimensionsAndQuantification->GetNb2ndMotImgsForLMS() * progression_chunk
858  + rg * mp_ImageDimensionsAndQuantification->GetNb2ndMotImgsForLMS() * progression_chunk
859  + cg * progression_chunk
860  + index;
861 
862  int64_t percent = (int64_t) (( ((FLTNB)progression_index_current)/((FLTNB)progression_index_total) ) * ((FLTNB)100));
863  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b "
864  << percent << " % " << flush;
865  }
866  printing_index++;
867  }
868 
869  // Get the current event for that thread index
870  vEvent* event = m3p_NormalizationDataFile[a_bed][fr]->GetEvent(index, th);
871 
872  if (event==NULL)
873  {
874  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> An error occured while getting the event from index "
875  << index << " (thread " << th << ") !" << endl);
876  // A problem was encountered
877  problem = true;
878  // We must continue here because we are inside an OpenMP loop
879  continue;
880  }
881  // Compute the projection line
883  if (line==NULL)
884  {
885  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> A problem occured while computing the projection line !" << endl);
886  // Specify that there was a problem
887  problem = true;
888  // We must continue here because we are inside an OpenMP loop
889  continue;
890  }
891  // Process this line
892  if (line->NotEmptyLine() && ProcessThisLine(line, event, a_bed, fr, rg, cg, th))
893  {
894  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> A problem occured while processing a line !" << endl);
895  }
896  }
897  // End of progression printing (do not log out with Cout here)
899  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
900  << " --> 100 % " << endl;
901  // If a problem was encountered, then report it here
902  if (problem)
903  {
904  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> A problem occured inside the parallel loop over events !" << endl);
905  return 1;
906  }
907  }
908  }
909  }
910 
911  // Clock total
912  clock_t clock_stop = clock();
913  time_t time_stop = time(NULL);
915  Cout(" --> Time spent for sensitivity generation for bed " << a_bed+1 << " | User: " << time_stop-time_start
916  << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
917  else
918  Cout(" --> Time spent for sensitivity generation | User: " << time_stop-time_start
919  << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
920 
921  // End
922  return 0;
923 }
924 
925 // =====================================================================
926 // ---------------------------------------------------------------------
927 // ---------------------------------------------------------------------
928 // =====================================================================
929 
931 {
932  // No MPI implementation for this function yet, so we exit if not the first instance
934 
935  // Verbose
936  if (m_verbose>=2)
937  {
938  if (mp_ImageDimensionsAndQuantification->GetNbBeds()>1) Cout("oSensitivityGenerator::ComputeSensitivityFromScanner() -> Start computation for bed " << a_bed+1 << endl);
939  else Cout("oSensitivityGenerator::ComputeSensitivityFromScanner() -> Start computation" << endl);
940  }
941 
942  // Get the scanner manager
943  sScannerManager* p_scannerManager = sScannerManager::GetInstance();
944 
945  // Total number of elements
946  int nb_total_elts = mp_Scanner->GetSystemNbElts();
947 
948  // Initial clock
949  clock_t clock_start = clock();
950  time_t time_start = time(NULL);
951 
952  // Initialize main loop start and stop values
953  int64_t main_loop_start_index = 0 ;
954  int64_t main_loop_stop_index = 0;
955 
956  // Check beforehand any issue with the loop start/stop values (not possible in the inner multithreaded loop)
957  if (p_scannerManager->PROJ_GetModalityStopValueMainLoop()>0) main_loop_stop_index = p_scannerManager->PROJ_GetModalityStopValueMainLoop();
958  else
959  {
960  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> An error occured when trying to initialize main loop stop index !" << endl);
961  return 1;
962  }
963  if (p_scannerManager->PROJ_GetModalityStartValueInnerLoop(0)<=0)
964  {
965  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> An error occured when trying to initialize inner loop start index !" << endl);
966  return 1;
967  }
968 
969  // Prepare pre-computed sums of events to avoid the exponential evolution of the percentage (for PET)
970  // TODO Perhaps replace this with a call to scannerManager for error management
971  int64_t* progression_elts_array = new int64_t[nb_total_elts*sizeof(int64_t)];
972  progression_elts_array[0] = 0;
973  for (int idx_elt=1 ; idx_elt<mp_Scanner->GetSystemNbElts() ; idx_elt++)
974  progression_elts_array[idx_elt] = progression_elts_array[idx_elt-1] + (mp_Scanner->GetSystemNbElts()-idx_elt);
975 
976  // Index for progression printing
977  uint64_t printing_index = 0;
978  uint64_t progression_index_total = p_scannerManager->PROJ_GetProgressionFinalValue()
981 
982  for (int fr=1 ; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames() ; fr++)
983  progression_index_total += p_scannerManager->PROJ_GetProgressionFinalValue()
986 
987  //mp_ImageSpace->LMS_InstantiateForwardImage();
988  // Loop on frames
989  for (int fr=0 ; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames() ; fr++)
990  {
991  // Verbose
993  cout << " --> Processing frame " << fr+1 << endl;
994  // Loop on respiratory gates
995  //for (int rg=mp_ImageDimensionsAndQuantification->GetPMotionFirstIndexForLMS(fr) ; rg<mp_ImageDimensionsAndQuantification->GetNb1stMotImgsForLMS(fr) ; rg++)
997  for (int rg=0 ; rg<frm_nb_1st_mot_imgs ; rg++)
998  {
999  // Verbose
1001  cout << " --> Processing respiratory gate " << rg+1 << endl;
1002  // Loop on cardiac gates
1003  for (int cg=0 ; cg<mp_ImageDimensionsAndQuantification->GetNb2ndMotImgsForLMS() ; cg++)
1004  {
1005  // Verbose
1007  cout << " --> Processing cardiac gate " << cg+1 << endl;
1008 
1009  // Loop for involuntary patient motion
1010  //for (int im=0 ; im<mp_ImageDimensionsAndQuantification->GetNbIPatMotionSubsets() ; im++)
1011  //{
1012  // Verbose
1013  // if (m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetNbIPatMotionSubsets()>1)
1014  // cout << " --> Processing patient motion data subset " << im+1 << endl;
1015 
1016  // Perform any forward deformations on the forward image (only if attenuation is used)
1017  //if (m_mumapAttenuationFlag)
1018 
1019  /*
1020  mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace,
1021  FORWARD_DEFORMATION,
1022  fr,
1023  rg + mp_ImageDimensionsAndQuantification->GetPMotionFirstIndexForLMS(fr),
1024  cg);*/
1025 
1029  fr,
1030  rg,
1031  cg);
1032 
1033  // In case a problem occurs in the parallel loop
1034  bool problem = false;
1035  // Set the number of threads for projections
1036  #ifdef CASTOR_OMP
1038  #endif
1039 
1040  // Start the loop
1041  int64_t idx_elt1;
1042 
1043  #pragma omp parallel for private(idx_elt1) schedule(static, 1)
1044  for(idx_elt1=main_loop_start_index ; idx_elt1<main_loop_stop_index ; idx_elt1++)
1045  {
1046  // Get the thread index
1047  int th = 0;
1048  #ifdef CASTOR_OMP
1049  th = omp_get_thread_num();
1050  #endif
1051 
1052  // Initialize inner loop start and stop values
1053  int64_t inner_loop_start_index = p_scannerManager->PROJ_GetModalityStartValueInnerLoop(idx_elt1) ;
1054  int64_t inner_loop_stop_index = mp_Scanner->GetSystemNbElts();
1055 
1056  // Inner loop on scanner elements (idx_elt2) which are used to form LOR using the first scanner element (idx_elt1)
1057  for (int64_t idx_elt2=inner_loop_start_index ; idx_elt2<inner_loop_stop_index ; idx_elt2++)
1058  {
1059  // Print progression (do not log out with Cout here)
1060  if (th==0)
1061  {
1062  if (m_verbose>=2 && printing_index%10000==0)
1063  {
1064  int64_t progression_index_current = p_scannerManager->PROJ_GetCurrentProgression(idx_elt1, idx_elt2, progression_elts_array,
1067  fr, rg, cg);
1068  int64_t percent = (int64_t) (( ((FLTNB)progression_index_current)/((FLTNB)progression_index_total) ) * ((FLTNB)100));
1069  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b "
1070  << percent << " % ";
1071  }
1072  printing_index++;
1073  }
1074 
1075  // Allocate an event using the iDataFile
1076  vEvent* event = m2p_DataFile[a_bed]->PROJ_GenerateEvent(idx_elt1, idx_elt2, th);
1077 
1078  // Check from the scanner requirements that this LOR is allowed
1079  if (!mp_Scanner->IsAvailableLOR(event->GetID1(0), event->GetID2(0))) continue;
1080 
1081  // Generate the projection event and compute the projection line
1083  if (line==NULL)
1084  {
1085  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> A problem occured while computing the projection line !" << endl);
1086  // Specify that there was a problem
1087  problem = true;
1088  // We must continue here because we are inside an OpenMP loop
1089  continue;
1090  }
1091 
1092  // Process this line
1093  if (line->NotEmptyLine() && ProcessThisLine(line, event, a_bed, fr, rg, cg, th))
1094  {
1095  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> A problem occured while processing a line !" << endl);
1096  }
1097  }
1098  }
1099  // End of progression printing (do not log out with Cout here)
1101  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
1102  << " --> 100 % " << endl;
1103  // If a problem was encountered, then report it here
1104  if (problem)
1105  {
1106  Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> A problem occured inside the parallel loop over events !" << endl);
1107  return 1;
1108  }
1109  //}
1110  }
1111  }
1112  }
1113 
1114  delete[] progression_elts_array;
1115 
1116  // Clock total
1117  clock_t clock_stop = clock();
1118  time_t time_stop = time(NULL);
1120  Cout(" --> Time spent for sensitivity generation for bed " << a_bed+1 << " | User: " << time_stop-time_start
1121  << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
1122  else
1123  Cout(" --> Time spent for sensitivity generation | User: " << time_stop-time_start
1124  << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
1125 
1126  // End
1127  return 0;
1128 }
1129 
1130 // =====================================================================
1131 // ---------------------------------------------------------------------
1132 // ---------------------------------------------------------------------
1133 // =====================================================================
1134 
1135 int oSensitivityGenerator::ProcessThisLine(oProjectionLine* ap_Line, vEvent* ap_Event, int a_bed, int a_frame, int a_respGate, int a_cardGate, int a_thread)
1136 {
1137  // ----------------------------------------------------------------------------------------------
1138  // Part 0: get the multiplicative corrections from the vEvent and the quantification factor
1139  // ----------------------------------------------------------------------------------------------
1140 
1141  // The multiplicative corrections from the vEvent can be:
1142  // 1: generated from vDataFile::GenerateEvent(), this will be a default value, so this will be 1.
1143  // 2: taken from the normalization data file, so this will be the normalization correction factor times the attenuation correction factor
1144  FLTNB multiplicative_factor = ap_Event->GetMultiplicativeCorrections();
1145  // If null, then skip this event
1146  if (multiplicative_factor<=0.) return 0;
1147  // Get the quantification factor
1148  FLTNB quantification_factor = mp_ImageDimensionsAndQuantification->GetQuantificationFactor(a_bed, a_frame, a_respGate, a_cardGate);
1149  // If null, then skip this event
1150  if (quantification_factor<=0.) return 0;
1151 
1152  // ----------------------------------------------------------------------------------------------
1153  // Part 1: deal with the attenuation forward projection for PET if any
1154  // ----------------------------------------------------------------------------------------------
1155 
1156  // The ACF
1157  FLTNB attenuation_correction_factor = 1.;
1158 
1159  // Do a forward projection only if the attenuation image was provided and the ACF was not included in the normalization data file
1161  {
1162  // Cumulative mu (in cm-1)
1163  FLTNB cumulative_mu = 0.;
1164  // Loop on TOF bins
1165  for (int b=0; b<ap_Line->GetNbTOFBins(); b++)
1166  {
1167  // Projection operation (the attenuation image in the m4p_forwardImage buffer of the oImageSpace)
1168  for (int vl=0 ; vl<ap_Line->GetCurrentNbVoxels(FORWARD, b) ; vl++)
1169  cumulative_mu += ap_Line->GetVoxelWeights(FORWARD, b, vl) * mp_ImageSpace->m4p_forwardImage[a_frame][a_respGate][a_cardGate][ap_Line->GetVoxelIndex(FORWARD, b,vl)];
1170  }
1171  // Update the ACF (converting the cumulative mu in mm-1)
1172  attenuation_correction_factor = max(((FLTNB)1.),exp(cumulative_mu*((FLTNB)0.1)));
1173  }
1174 
1175  // ----------------------------------------------------------------------------------------------
1176  // Part 2: back-projection of the LOR sensitivity
1177  // ----------------------------------------------------------------------------------------------
1178 
1179  // Compute the global sensitivity (calibration / (ACF * norm))
1180  FLTNB lor_sensitivity = 1. / (quantification_factor * attenuation_correction_factor * multiplicative_factor);
1181 
1182  // Loop on TOF bins
1183  for (int b=0; b<ap_Line->GetNbTOFBins(); b++)
1184  {
1185  // Backprojection operation into the backward image
1186  for (int vl=0 ; vl<ap_Line->GetCurrentNbVoxels(BACKWARD, b) ; vl++)
1187  mp_ImageSpace->m6p_backwardImage[0][a_thread][a_frame][a_respGate][a_cardGate][ap_Line->GetVoxelIndex(BACKWARD, b, vl)] += ap_Line->GetVoxelWeights(BACKWARD, b, vl)
1188  * lor_sensitivity;
1189  }
1190 
1191  // Increment line counters
1192  mp_lineCounter[a_thread]++;
1193 
1194  // End
1195  return 0;
1196 }
1197 
1198 // =====================================================================
1199 // ---------------------------------------------------------------------
1200 // ---------------------------------------------------------------------
1201 // =====================================================================
1202 
1204 {
1205  // Get the flag that says if we merge the dynamic files or not
1206  bool merge_dynamic_files = sOutputManager::GetInstance()->MergeDynImages();
1207  // If we merge dynamic file, or we are in a static case (no dynamic), we stay with the hdr extension
1211  merge_dynamic_files )
1212  return m_pathToSensitivityImage + ".hdr";
1213  else
1214  return m_pathToSensitivityImage + ".mhd";
1215 }
int ApplyMaskToBackwardImage(int a_imageIndex, int a_timeIndex, int a_respIndex, int a_cardIndex)
Apply the mask to the backward image matrix of the first thread for the specific image / time / respi...
oImageConvolverManager * mp_ImageConvolverManager
FLTNB **** m4p_forwardImage
Definition: oImageSpace.hh:88
This class is designed to be a mother virtual class for DataFile.
Definition: vDataFile.hh:103
This header file is mainly used to declare some macro definitions and all includes needed from the st...
bool GetNormCorrectionFlag()
Simply return m_normCorrectionFlag.
oProjectorManager * mp_ProjectorManager
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
void GetEventIndexStartAndStop(int64_t *ap_indexStart, int64_t *ap_indexStop, int a_subsetNum=0, int a_NbSubsets=1)
Compute the index start and stop of the events loop with respect to the current subset and MPI size a...
Definition: vDataFile.cc:671
#define MODE_HISTOGRAM
Definition: vDataFile.hh:59
#define TYPE_PET
Definition: vDataFile.hh:74
int ProcessThisLine(oProjectionLine *ap_Line, vEvent *ap_Event, int a_bed, int a_frame, int a_respGate, int a_cardGate, int a_thread)
This function manages the computation of the sensitivity contribution of the projection line provided...
#define MODE_LIST
Definition: vDataFile.hh:57
void LMS_CopyAtnToForwardImage(bool a_use1stMotion, bool a_use2ndMotion)
Copy the attenuation image contained in the 'm2p_attenuation' matrix inside the m4p_forwardImage matr...
int64_t PROJ_GetModalityStartValueInnerLoop(int64_t a_elt1)
Get the start value for the inner loop of analytic projection depending on the modality.
#define FLTNB
Definition: gVariables.hh:81
#define MODE_NORMALIZATION
Definition: vDataFile.hh:61
int InitMaskImage(const string &a_pathToImage)
Memory allocation and initialization for the mask image.
Definition: oImageSpace.cc:675
oSensitivityGenerator()
The constructor of oSensitivityGenerator.
int Initialize()
A public function used to initialize the sensitivity generator.
bool UseDeformationInv()
Indicate if the involuntary patient motion deformation is enabled.
int LMS_SaveSensitivityImage(const string &a_pathToSensitivityImage, oDeformationManager *ap_DeformationManager)
Call the interfile function to write the sensitivity image on disk.
vDataFile *** m3p_NormalizationDataFile
int GetNb1stMotImgsForLMS(int a_fr)
call the eponym function from the oDynamicDataManager object
oDeformationManager * mp_DeformationManager
string GetPathToSensitivityImage()
This function return the path to the sensitivity image.
int64_t PROJ_GetProgressionFinalValue()
Get numerator value according to the modality to compute percent progression during the projection pr...
int ApplyDeformationForSensitivityGeneration(oImageSpace *ap_Image, int a_defDirection, int idx, int fr, int rg, int cg)
Apply deformations during the list-mode sensitivity image generation.
void SetVerbose(int a_verboseLevel)
set verbosity
Definition: vDataFile.hh:439
bool GetIgnoreAttnCorrectionFlag()
Get the boolean that says if the attenuation correction is ignored or not.
~oSensitivityGenerator()
The destructor of oSensitivityGenerator.
int Launch()
A public function used to launch the sensitivity generator (compute the sensitivity image) ...
virtual FLTNB GetMultiplicativeCorrections()=0
This is a pure virtual function implemented in the child classes.
void LMS_InstantiateForwardImage()
Allocate memory for the forward image matrices (for list-mode sensitivity generation) ...
bool UseDeformationCard()
Indicate if the cardiac motion deformation is enabled.
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define TYPE_SPECT
Definition: vDataFile.hh:76
#define SCANNER_PET
int InitAttenuationImage(const string &a_pathToAtnImage)
Memory allocation and initialisation for the attenuation image using either :
#define TYPE_CT
Definition: vDataFile.hh:78
Declaration of class iDataFilePET.
void DeallocateMaskImage()
Free memory for the mask image.
Definition: oImageSpace.cc:716
bool GetIgnoreNormCorrectionFlag()
Get the boolean that says if the normalization correction is ignored or not.
vEvent * GetEvent(int64_t a_eventIndex, int a_th=0)
Definition: vDataFile.cc:598
INTNB GetVoxelIndex(int a_direction, int a_TOFBin, INTNB a_voxelInLine)
This function is used to get the contributing voxel index of the provided direction, TOF bin and voxel rank.
#define Cerr(MESSAGE)
const string & GetPathName()
FLTNB ****** m6p_backwardImage
Definition: oImageSpace.hh:95
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
int ComputeSensitivityFromNormalizationFile(int a_bed)
Launch the computation of the sensitivity image for this bed, based on normalization data files...
void LMS_DeallocateSensitivityImage()
Free memory for the sensitivity image matrices (for list-mode sensitivity generation) ...
int GetNbBeds()
Get the number of bed positions.
Singleton class that Instantiate and initialize the scanner object.
#define BACKWARD_DEFORMATION
Definition: vDeformation.hh:38
void ResetCurrentDynamicIndices()
Call the eponym function from the oDynamicDataManager class using the member object.
INTNB GetCurrentNbVoxels(int a_direction, int a_TOFBin)
This function is used to get the current number of contributing voxels to the line.
oProjectionLine * ComputeProjectionLine(vEvent *ap_Event, int a_th)
This function is used to compute system matrix elements from the associated projector or pre-computed...
FLTNB GetQuantificationFactor(int a_bed, int a_frame, int a_respGate, int a_cardGate)
Get the quantification factor corresponding to the provided bed, frame, respiratory and cardiac gates...
int InitializeAttenuationFiles()
Initialize the attenuation images provided for sensitivity computation.
void SetHeaderDataFileName(const string &a_headerFileName)
set the data header file name
Definition: vDataFile.hh:476
void ApplyBedOffset(int a_bed)
Compute the bed offset from the provided bed index and apply it to all projection lines...
vEvent * PROJ_GenerateEvent(int idx_elt1, int idx_elt2, int a_th)
Generate a standard event and set up its ID Used by the projection, list-mode sensitivity generatio...
Definition: vDataFile.cc:824
bool NotEmptyLine()
This function is used to know if the line contains any voxel contribution.
int LaunchCPU()
Launch the computation of the sensitivity image (CPU version)
virtual int GetSystemNbElts()=0
This is a pure virtual method that must be implemented by children.
void ReduceBackwardImage(int a_imageIndex, int a_timeIndex, int a_respIndex, int a_cardIndex)
Merge parallel results into the backward image matrix of the first thread for the specific image / ti...
This class is designed to manage and store system matrix elements associated to a vEvent...
const string & GetBaseName()
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
void InstantiateBackwardImageFromDynamicBins()
Allocate memory for the backward image matrices and initialize them.
Definition: oImageSpace.cc:309
Declaration of class oSensitivityGenerator.
void LMS_DeallocateAttenuationImage()
Free memory for the Attenuation image matrices (for analytical projection or list-mode sensitivity ge...
int GetNbCardGates()
Get the number of cardiac gates.
bool MergeDynImages()
Indicate if a dynamic serie of 3D images should be merged in one file (true) or written on disk as on...
void SetBedIndex(int a_bedIndex)
set the bed index corresponding to this data file
Definition: vDataFile.hh:420
bool UseDeformationResp()
Indicate if the respiratory motion deformation is enabled.
Mother class for the Event objects.
Definition: vEvent.hh:43
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
int GetDataMode()
Definition: vDataFile.hh:300
int GetNbTimeFrames()
Get the number of time frames.
int GetNb2ndMotImgsForLMS()
call the eponym function from the oDynamicDataManager object
int InitializeNormalizationFiles()
Initialize the normalization datafiles provided for sensitivity computation.
int CheckParameters()
A public function used to check the parameters settings.
int64_t PROJ_GetCurrentProgression(int64_t a_elt1, int64_t a_elt2, int64_t *ap_nbEltsArray, int a_nbRGates, int a_nbCGates, int a_fr, int a_rg, int a_cg)
Get numerator value according to the modality to compute percent progression during the analytical pr...
int GetNbTOFBins()
This function is used to get the number of TOF bins in use.
int GetNbThreadsForProjection()
Get the number of threads used for projections.
FLTNB GetVoxelWeights(int a_direction, int a_TOFBin, INTNB a_voxelInLine)
This function is used to get the contributing voxel weight of the provided direction, TOF bin and voxel rank.
vector< string > mp_pathToNormalizationFileName
int ComputeSensitivityFromHistogramDataFile(int a_bed)
Launch the computation of the sensitivity image for this bed, based on the input histogram data files...
int GetNbRespGates()
Get the number of respiratory gates.
#define FORWARD
#define Cout(MESSAGE)
#define FORWARD_DEFORMATION
Definition: vDeformation.hh:37
int64_t PROJ_GetModalityStopValueMainLoop()
Get the stop value for the main loop of analytic projection depending on the modality.
int ConvolveSensitivity(oImageSpace *ap_ImageSpace)
A function used to apply convolvers onto the sensitivity image of the oImageSpace.
virtual int IsAvailableLOR(int a_elt1, int a_elt2)
This function is implemented in child classes. Check if the LOR is available according to the scann...
Definition: vScanner.cc:181
int ComputeSensitivityFromScanner(int a_bed)
Launch the computation of the sensitivity image for this bed, based on a loop over all scanner elemen...
bool GetAtnCorrectionFlag()
Simply return m_atnCorrectionFlag.
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
set the pointer to the oImageDimensionsAndQuantification object
Definition: vDataFile.hh:453
void DeallocateBackwardImageFromDynamicBins()
Free memory of the backward image matrices.
Definition: oImageSpace.cc:345
Inherit from vDataFile. Class that manages the reading of a PET input file (header + data)...
Definition: iDataFilePET.hh:44
void LMS_InstantiateSensitivityImage()
Allocate memory for the sensitivity image matrices (for list-mode sensitivity generation) ...
int GetMPIRank()
Get the MPI instance number (the rank)
void LMS_CopyBackwardToSensitivity()
#define BACKWARD
void LMS_DeallocateForwardImage()
Free memory for the forward image matrices (for list-mode sensitivity generation) ...