CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/algorithm/iIterativeAlgorithm.cc
Go to the documentation of this file.
1 
8 #include "gVariables.hh"
9 #include "iIterativeAlgorithm.hh"
10 #include "iEventHistoCT.hh"
11 
12 // =====================================================================
13 // ---------------------------------------------------------------------
14 // ---------------------------------------------------------------------
15 // =====================================================================
16 
18 {
19 }
20 
21 // =====================================================================
22 // ---------------------------------------------------------------------
23 // ---------------------------------------------------------------------
24 // =====================================================================
25 
27 {
28 }
29 
30 // =====================================================================
31 // ---------------------------------------------------------------------
32 // ---------------------------------------------------------------------
33 // =====================================================================
34 
36 {
38  {
39  Cerr("***** iIterativeAlgorithm::StepBeforeIterationLoop() -> A problem occurred while calling StepBeforeIterationLoop() function !" << endl);
40  return 1;
41  }
42  if (m_verbose>=2) Cout("iIterativeAlgorithm::StepBeforeIterationLoop ... " << endl);
43 
46 
47  // Main image initialization
49  {
50  Cerr("***** iIterativeAlgorithm::StepBeforeIterationLoop() -> An error occurred while reading the initialization image !" << endl);
51  return 1;
52  }
53 
54  // Set numbers of iterations and subsets to the optimizer
56 
57  // Set the output iterations to the optimizer
59 
60  // If the optimizer needs a pre-processing step, do it here
62  {
63  // Set pre-process flag of the optimizer
65  // Call the independent step function
67  {
68  Cerr("***** iIterativeAlgorithm::StepBeforeIterationLoop() -> A problem occured while calling PreOrPostIterationIndependentStep() function !" << endl);
69  return 1;
70  }
71  // Unset pre-process flag of the optimizer
73  }
74 
75  // End
76  return 0;
77 }
78 
79 // =====================================================================
80 // ---------------------------------------------------------------------
81 // ---------------------------------------------------------------------
82 // =====================================================================
83 
85 {
86  if (m_verbose>=3) Cout("iIterativeAlgorithm::StepBeforeSubsetLoop ... " << endl);
87 
88  // Set the current iteration to the optimizer
90 
91  // End
92  return 0;
93 }
94 
95 // =====================================================================
96 // ---------------------------------------------------------------------
97 // ---------------------------------------------------------------------
98 // =====================================================================
99 
100 int iIterativeAlgorithm::StepPreProcessInsideSubsetLoop(int a_iteration, int a_subset)
101 {
102  if (m_verbose>=3) Cout("iIterativeAlgorithm::StepPreProcessInsideSubsetLoop ... " << endl);
103 
104  // Set the current subset to the optimizer
106 
107  // Get the chrono manager singleton pointer
108  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
109 
110  // Initialize the correction backward image(s)
112 
113  // Copy current image in forward-image buffer
115 
116  // Apply image processing to forward image
118  {
119  Cerr("***** iIterativeAlgorithm::StepPreProcessInsideSubsetLoop() -> A problem occurred while applyin image processing to forward image !" << endl);
120  return 1;
121  }
122 
123  // Apply convolver to forward image
124  p_ChronoManager->StartConvolution();
126  {
127  Cerr("***** iIterativeAlgorithm::StepPreProcessInsideSubsetLoop() -> A problem occurred while applying image convolver to forward image !" << endl);
128  return 1;
129  }
130  p_ChronoManager->StopConvolution();
131 
132  // Call the pre-event loop function from the optimizer manager
135 
136  // Initialisation of the backup images for deformation
137  // (bu_fward image initialized with current forward image)
138  // (bu_bward and bu_sens images set to 0)
139 
141 
142  // End
143  return 0;
144 }
145 
146 // =====================================================================
147 // ---------------------------------------------------------------------
148 // ---------------------------------------------------------------------
149 // =====================================================================
150 
151 int iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop(int a_iteration, int a_subset, int a_bed)
152 {
153  // Verbose
155  {
156  if (m_nbBeds>1) Cout("iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> Start loop over events for bed " << a_bed+1 << endl << flush);
157  else Cout("iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> Start loop over events" << endl << flush);
158  }
159 
160  // Reinitialize 4D gate indexes
162 
163  // Apply the bed offset for this bed position
165 
166  // Progression (increments of 2%)
168  {
169  cout << "0 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%" << endl;
170  cout << "|----|----|----|----|----|----|----|----|----|----|" << endl;
171  cout << "|" << flush;
172  }
173  int progression_percentage_old = 0;
174  int progression_nb_bars = 0;
175  uint64_t progression_printing_index = 0;
176 
177  // Compute start and stop indices taking MPI into account (the vDataFile does that)
178  int64_t index_start = 0;
179  int64_t index_stop = 0;
180  m2p_DataFile[a_bed]->GetEventIndexStartAndStop(&index_start, &index_stop, a_subset, mp_nbSubsets[a_iteration]);
181 
182  // Synchronize MPI processes
183  #ifdef CASTOR_MPI
184  MPI_Barrier(MPI_COMM_WORLD);
185  #endif
186 
187  // Set the number of threads for projections (right after this loop, we set back the number of threads for image computations)
188  #ifdef CASTOR_OMP
189  omp_set_num_threads(mp_ID->GetNbThreadsForProjection());
190  #endif
191 
192  // This boolean is used to report any problem inside the parallel loop
193  bool problem = false;
194 
195  // Get the chrono manager singleton pointer
196  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
197 
198  // These flags (one per thread) are used to signal when events are beyond the last frame time stop
199  bool* p_end_loop_flags = (bool*)malloc(mp_ID->GetNbThreadsForProjection()*sizeof(bool));
200  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++) p_end_loop_flags[th] = false;
201 
202  // Launch the loop with precomputed start and stop and using a step equal to the number of subsets
203  int64_t index;
204  // Keep the static scheduling with a chunk size at 1, it is important
205  #pragma omp parallel for private(index) schedule(static, 1)
206  for ( index = index_start ; index < index_stop ; index += mp_nbSubsets[a_iteration] )
207  {
208  // Get the thread index
209  int th = 0;
210  #ifdef CASTOR_OMP
211  th = omp_get_thread_num();
212  #endif
213  // Print progression (do not log out with Cout here)
214  if (m_verbose>=2 && th==0 && mp_ID->GetMPIRank()==0)
215  {
216  if (progression_printing_index%1000==0)
217  {
218  int progression_percentage_new = ((int)( (((float)(index-index_start+1))/((float)(index_stop-index_start)) ) * 100.));
219  if (progression_percentage_new>=progression_percentage_old+2) // Increments of 2%
220  {
221  int nb_steps = (progression_percentage_new-progression_percentage_old)/2;
222  for (int i=0; i<nb_steps; i++)
223  {
224  cout << "-" << flush;
225  progression_nb_bars++;
226  }
227  progression_percentage_old += nb_steps*2;
228  }
229  }
230  progression_printing_index++;
231  }
232  // Skip to the end
233  if (p_end_loop_flags[th]) continue;
234 
235  // Step 1: Get the current event for that thread index
236  p_ChronoManager->StartIterativeDataUpdateStep1(th);
237  #ifdef CASTOR_DEBUG
238  if (m_verbose>=4) Cout("iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> Step1: Get current event for that thread index " << endl);
239  #endif
240  vEvent* event = m2p_DataFile[a_bed]->GetEvent(index, th);
241  if (event==NULL)
242  {
243  Cerr("***** iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> An error occurred while getting the event from index "
244  << index << " (thread " << th << ") !" << endl);
245  // Specify that there was a problem
246  problem = true;
247  // We must continue here because we are inside an OpenMP loop
248  continue;
249  }
250  p_ChronoManager->StopIterativeDataUpdateStep1(th);
251 
252  // Step 2: Call the dynamic switch function that updates the current frame and gate numbers, and also detects involuntary patient motion
253  p_ChronoManager->StartIterativeDataUpdateStep2(th);
254  #ifdef CASTOR_DEBUG
255  if (m_verbose>=4)
256  {
257  Cout("iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> Step2: Check for Dynamic event (frame/gate switch, image-based deformation " << endl);
258  }
259  #endif
260  int dynamic_switch_value = mp_ID->DynamicSwitch(index, event->GetTimeInMs(), a_bed, th);
261  // If the DYNAMIC_SWITCH_CONTINUE is returned, then it means that we are not yet at the first frame
262  if ( dynamic_switch_value == DYNAMIC_SWITCH_CONTINUE )
263  {
264  // Then we just skip this event
265  continue;
266  }
267  // Else, if the DYNAMIC_SWITCH_DEFORMATION is returned, then it means that a change of gate or involuntary motion has occurred
268  // and is associated to a deformation
269  else if ( dynamic_switch_value == DYNAMIC_SWITCH_DEFORMATION )
270  {
271  #ifdef CASTOR_OMP
272  // set deformation requirement for this thread
273  #pragma omp critical (deformation_requirements_first)
274  {
275  mp_DeformationManager->SetDeformationRequirement(th);
276  }
277  // block this thread until the deformation is done
278  bool wait_for_deformation = true;
279  while (wait_for_deformation)
280  {
281  // the first thread performs the deformation on the forward image
282  // as soon as all the threads are ready for deformation
283  if (th==0)
284  {
285  bool all_threads_ready = false;
286  //check whether all the threads require deformation
287  #pragma omp critical (deformation_requirements_first)
288  {
289  all_threads_ready = mp_DeformationManager->AllThreadsRequireDeformation();
290  }
291  if (all_threads_ready)
292  {
293  // Perform the deformation
295  // Free all the threads so that they can continue
296  #pragma omp critical (deformation_requirements_second)
297  {
298  mp_DeformationManager->UnsetDeformationRequirements();
299  }
300  }
301  }
302  // check whether the deformation is done and the thread can continue
303  #pragma omp critical (deformation_requirements_second)
304  {
305  wait_for_deformation = mp_DeformationManager->GetDeformationRequirement(th);
306  }
307  }
308  #else
309  // Perform here any resp related deformations on the forward image
311  #endif
312 
313  // Restore progression printing
314  if (th==0 && m_verbose>=3 && mp_ID->GetMPIRank()==0)
315  {
316  cout << "0 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%" << endl;
317  cout << "|----|----|----|----|----|----|----|----|----|----|" << endl;
318  int progression_percentage_new = ((int)( (((float)(index-index_start+1))/((float)(index_stop-index_start)) ) * 100.));
319  cout << "|";
320 
321  for (int i=0; i< progression_percentage_new / 2 ; i++) cout << "-";
322  }
323 
324  }
325  // Else, if the DYNAMIC_SWITCH_END_LOOP is returned, set the local end_loop_flag boolean to true (which allows to quickly end the OMP parallel loop)
326  else if ( dynamic_switch_value == DYNAMIC_SWITCH_END_LOOP )
327  {
328  p_end_loop_flags[th] = true;
329  continue;
330  }
331  p_ChronoManager->StopIterativeDataUpdateStep2(th);
332 
333  // Step 3: Compute the projection line
334  p_ChronoManager->StartIterativeDataUpdateStep3(th);
335  #ifdef CASTOR_DEBUG
336  if (m_verbose>=4) Cout("iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> Step3: Compute the projection line " << endl);
337  #endif
339  if (line==NULL)
340  {
341  Cerr("***** iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> A problem occurred while computing the projection line !" << endl);
342  // Specify that there was a problem
343  problem = true;
344  // We must continue here because we are inside an OpenMP loop
345  continue;
346  }
347  p_ChronoManager->StopIterativeDataUpdateStep3(th);
348 
349  // Step 4: Optimize in the data space (forward-proj, update, backward-proj)
350  p_ChronoManager->StartIterativeDataUpdateStep4(th);
351  #ifdef CASTOR_DEBUG
352  if (m_verbose>=4) Cout("iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> Step4: Optimize in the data space " << endl);
353  #endif
354  if (line->NotEmptyLine()) mp_OptimizerManager->DataUpdateStep( line,
355  event,
356  a_bed,
360  th);
361  p_ChronoManager->StopIterativeDataUpdateStep4(th);
362  } // End of indices loop (OpenMP stops here)
363 
364  // Synchronize MPI processes
365  #ifdef CASTOR_MPI
366  MPI_Barrier(MPI_COMM_WORLD);
367  #endif
368 
369  // Set back the number of threads for image computation
370  #ifdef CASTOR_OMP
371  omp_set_num_threads(mp_ID->GetNbThreadsForImageComputation());
372  #endif
373 
374  // End of progression printing (do not log out with Cout here)
375  if (m_verbose>=2 && mp_ID->GetMPIRank()==0)
376  {
377  int progression_total_bars = 49;
378  for (int i=0; i<progression_total_bars-progression_nb_bars; i++) cout << "-";
379  cout << "|" << endl;
380  }
381 
382  // If a problem was encountered, then report it here
383  if (problem)
384  {
385  Cerr("***** iIterativeAlgorithm::StepInnerLoopInsideSubsetLoop() -> A problem occurred inside the parallel loop over events !" << endl);
386  return 1;
387  }
388 
389  // End
390  return 0;
391 }
392 
393 // =====================================================================
394 // ---------------------------------------------------------------------
395 // ---------------------------------------------------------------------
396 // =====================================================================
397 
398 int iIterativeAlgorithm::StepPostProcessInsideSubsetLoop(int a_iteration, int a_subset)
399 {
400  if (m_verbose>=3) Cout("iIterativeAlgorithm::StepPostProcessInsideSubsetLoop ... " << endl);
401 
402  // Get the chrono manager singleton pointer
403  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
404 
405  // Merge parallel results
407 
408  // Perform here any image-based deformations related to motion on the backward image.
410 
411  // Apply convolver to backward images and sensitivity-if-needed
412  p_ChronoManager->StartConvolution();
414  {
415  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying convolver to backward images !" << endl);
416  return 1;
417  }
418  p_ChronoManager->StopConvolution();
419 
420  // Call the pre image update step function from the optimizer manager
422  {
423  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while performing the pre-image-update step !" << endl);
424  return 1;
425  }
426 
427  // Optimize in the image space (apply corrections, MAP and sensitivity); pass the number of subsets for list-mode sensitivity scaling
429  {
430  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while performing the image update step !" << endl);
431  return 1;
432  }
433 
434  // Apply convolver to current estimed images
435  p_ChronoManager->StartConvolution();
437  {
438  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying convolver to current estimate images !" << endl);
439  return 1;
440  }
441  p_ChronoManager->StopConvolution();
442 
443  // Post-update Dynamic Modeling step (if enabled). Manage either linear/non linear dynamic model (physiological or not)
444  if (mp_DynamicModelManager->ApplyDynamicModel(mp_ImageSpace, a_iteration, a_subset))
445  {
446  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying dynamic model to current estimate images !" << endl);
447  return 1;
448  }
449 
450  // Apply image processing to current estime images
452  {
453  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying image processing to current estimate images !" << endl);
454  return 1;
455  }
456 
457  // Save the sensitivity image in histogram mode, if asked for
459  {
460  // Get output manager to build the file name
461  sOutputManager* p_output_manager = sOutputManager::GetInstance();
462  // Build the file name
463  string temp_sens = p_output_manager->GetPathName() + p_output_manager->GetBaseName();
464  stringstream temp_it; temp_it << a_iteration/mp_OptimizerManager->GetNbSubIterationsInOneIteration() + 1;
465  stringstream temp_ss; temp_ss << a_subset + 1;
466  temp_sens.append("_it").append(temp_it.str()).append("_ss").append(temp_ss.str()).append("_sensitivity");
467  // Save sensitivity
469  }
470 
471  // Save the current image estimate if asked for
472  if (mp_ID->GetMPIRank()==0 && mp_outputIterations[a_iteration] && m_saveImageAfterSubsets)
473  {
474  // Number of displayed iteration is current iteration divided by number of sub steps
476  a_iteration /= nb_sub_iter;
477  // Verbose
478  if (m_verbose>=1) Cout("iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> Save image at iteration " << a_iteration+1 << " for subset " << a_subset+1 << endl);
479  // Save image
480  if (StepImageOutput(a_iteration,a_subset))
481  {
482  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while saving images at iteration " << a_iteration+1 << " !" << endl);
483  return 1;
484  }
485  }
486 
487  // End
488  return 0;
489 }
490 
491 // =====================================================================
492 // ---------------------------------------------------------------------
493 // ---------------------------------------------------------------------
494 // =====================================================================
495 
496 int iIterativeAlgorithm::StepAfterSubsetLoop(int a_iteration)
497 {
498  if (m_verbose>=3) Cout("iIterativeAlgorithm::StepAfterSubsetLoop() -> Clean never visited voxels and save images if needed" << endl);
499  // Clean never visited voxels
501  // Save the main image if not already done for each subset
502  if (mp_ID->GetMPIRank()==0 && mp_outputIterations[a_iteration] && !m_saveImageAfterSubsets)
503  {
504  // Number of displayed iteration is current iteration divided by number of sub steps
506  a_iteration /= nb_sub_iter;
507  // Verbose
508  if (m_verbose>=1) Cout("iIterativeAlgorithm::StepAfterSubsetLoop() -> Save image at iteration " << a_iteration+1 << endl);
509  // Save image
510  if (StepImageOutput(a_iteration))
511  {
512  Cerr("***** iIterativeAlgorithm::StepAfterSubsetLoop() -> A problem occurred while saving images at iteration " << a_iteration+1 << " !" << endl);
513  return 1;
514  }
515  }
516  // End
517  return 0;
518 }
519 
520 // =====================================================================
521 // ---------------------------------------------------------------------
522 // ---------------------------------------------------------------------
523 // =====================================================================
524 
526 {
527  // If the optimizer needs a post-processing step, do it here
529  {
530  // Set post-process flag of the optimizer
532  // Call the independent step function
534  {
535  Cerr("***** iIterativeAlgorithm::StepAfterIterationLoop() -> A problem occured while calling PreOrPostIterationIndependentStep() function !" << endl);
536  return 1;
537  }
538  // Unset post-process flag of the optimizer
540  }
541 
543  {
544  Cerr("***** iIterativeAlgorithm::StepAfterIterationLoop() -> A problem occurred while calling StepAfterIterationLoop() function !" << endl);
545  return 1;
546  }
547  if (m_verbose>=2) Cout("iIterativeAlgorithm::StepAfterIterationLoop ... " << endl);
548 
549  // Deallocate everything
552 
553  // Normal end
554  return 0;
555 }
556 
557 // =====================================================================
558 // ---------------------------------------------------------------------
559 // ---------------------------------------------------------------------
560 // =====================================================================
561 
562 int iIterativeAlgorithm::StepImageOutput(int a_iteration, int a_subset)
563 {
564  // Get the chrono manager singleton pointer
565  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
566  // =================================================================================================
567  // Save dynamic coefficient images from the vDynamicModel
568  // =================================================================================================
569  if (mp_DynamicModelManager->SaveParametricImages(a_iteration, a_subset))
570  {
571  Cerr("***** iIterativeAlgorithm::StepImageOutput() -> A problem occurred while saving parametric images related to the dynamic model !" << endl);
572  return 1;
573  }
574  // =================================================================================================
575  // Apply pre-processing steps
576  // =================================================================================================
577  // Copy the current image into the forward image
579  // Apply post-convolution if needed
580  p_ChronoManager->StartConvolution();
582  {
583  Cerr("***** iIterativeAlgorithm::StepImageOutput() -> A problem occurred while convolving the output image !" << endl);
584  return 1;
585  }
586  p_ChronoManager->StopConvolution();
587  // Apply post-processing if needed
589  {
590  Cerr("***** iIterativeAlgorithm::StepImageOutput() -> A problem occurred while applying image processing the output image !" << endl);
591  return 1;
592  }
593  // Apply output transaxial FOV masking
595  {
596  Cerr("***** iIterativeAlgorithm::StepImageOutput() -> A problem occurred while applying output FOV masking !" << endl);
597  return 1;
598  }
599  // Apply output flip
601  {
602  Cerr("***** iIterativeAlgorithm::StepImageOutput() -> A problem occurred while applying output flip !" << endl);
603  return 1;
604  }
605  // =================================================================================================
606  // If some basis functions are really in use
607  // =================================================================================================
609  {
610  // Save the basis coefficients images if asked for
612  {
613  if (mp_ImageSpace->SaveOutputBasisCoefficientImage(a_iteration, a_subset))
614  {
615  Cerr("***** iIterativeAlgorithm::StepImageOutput() -> A problem occurred while saving output image !" << endl);
616  return 1;
617  }
618  }
619  // Compute the output image from the forward image basis functions
621  }
622  // =================================================================================================
623  // Save frames/gates
624  // =================================================================================================
625 
626  // Save output image (note that if no basis functions at all are in use, the the output image already points to the forward image)
627  if (mp_ImageSpace->SaveOutputImage(a_iteration, a_subset))
628  {
629  Cerr("***** iIterativeAlgorithm::StepImageOutput() -> A problem occurred while saving output image !" << endl);
630  return 1;
631  }
632  // Normal end
633  return 0;
634 }
635 
636 // =====================================================================
637 // ---------------------------------------------------------------------
638 // ---------------------------------------------------------------------
639 // =====================================================================
640 
642 {
643  // Verbose
644  if (m_verbose>=VERBOSE_NORMAL) Cout("iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> Additional data processing" << endl);
645 
646  // Get the chrono manager singleton pointer
647  sChronoManager* p_ChronoManager = sChronoManager::GetInstance();
648 
649  // Clock start for subset execution time
650  clock_t clock_start_step = clock();
651  time_t time_start_step = time(NULL);
652 
653  // Initialize the correction backward image(s)
655 
656  // Copy current image in forward-image buffer
658 
659  // Apply image processing to forward image
661  {
662  Cerr("***** iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> A problem occurred while applying image processing to forward image !" << endl);
663  return 1;
664  }
665 
666  // Apply convolver to forward image
667  p_ChronoManager->StartConvolution();
669  {
670  Cerr("***** iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> A problem occurred while applying image convolver to forward image !" << endl);
671  return 1;
672  }
673  p_ChronoManager->StopConvolution();
674 
675  // Call the pre-event loop function from the optimizer manager
678 
679  // Initialisation of the backup images for deformation
680  // (bu_fward image initialized with current forward image)
681  // (bu_bward and bu_sens images set to 0)
683 
684  // Synchronize MPI processes
685  #ifdef CASTOR_MPI
686  MPI_Barrier(MPI_COMM_WORLD);
687  #endif
688 
689  // Loop on bed positions
690  for (int bed=0 ; bed<m_nbBeds ; bed++)
691  {
692  // Reinitialize 4D gate indexes
694  // Apply the bed offset for this bed position
696  // Progression (increments of 2%)
698  {
699  cout << "0 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%" << endl;
700  cout << "|----|----|----|----|----|----|----|----|----|----|" << endl;
701  cout << "|" << flush;
702  }
703  int progression_percentage_old = 0;
704  int progression_nb_bars = 0;
705  uint64_t progression_printing_index = 0;
706  // Compute start and stop indices taking MPI into account (the vDataFile does that)
707  int64_t index_start = 0;
708  int64_t index_stop = 0;
709  int first_subset = 0;
710  int one_subset = 1;
711  m2p_DataFile[bed]->GetEventIndexStartAndStop(&index_start, &index_stop, first_subset, one_subset);
712  // Synchronize MPI processes
713  #ifdef CASTOR_MPI
714  MPI_Barrier(MPI_COMM_WORLD);
715  #endif
716  // Set the number of threads for projections (right after this loop, we set back the number of threads for image computations)
717  #ifdef CASTOR_OMP
718  omp_set_num_threads(mp_ID->GetNbThreadsForProjection());
719  #endif
720  // This boolean is used to report any problem inside the parallel loop
721  bool problem = false;
722  // These flags (one per thread) are used to signal when events are beyond the last frame time stop
723  bool* p_end_loop_flags = (bool*)malloc(mp_ID->GetNbThreadsForProjection()*sizeof(bool));
724  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++) p_end_loop_flags[th] = false;
725  // Launch the loop with precomputed start and stop and using a step equal to the number of subsets
726  int64_t index;
727  // Keep the static scheduling with a chunk size at 1, it is important
728  #pragma omp parallel for private(index) schedule(static, 1)
729  for ( index = index_start ; index < index_stop ; index += one_subset )
730  {
731  // Get the thread index
732  int th = 0;
733  #ifdef CASTOR_OMP
734  th = omp_get_thread_num();
735  #endif
736  // Print progression (do not log out with Cout here)
737  if (m_verbose>=2 && th==0 && mp_ID->GetMPIRank()==0)
738  {
739  if (progression_printing_index%1000==0)
740  {
741  int progression_percentage_new = ((int)( (((float)(index-index_start+1))/((float)(index_stop-index_start)) ) * 100.));
742  if (progression_percentage_new>=progression_percentage_old+2) // Increments of 2%
743  {
744  int nb_steps = (progression_percentage_new-progression_percentage_old)/2;
745  for (int i=0; i<nb_steps; i++)
746  {
747  cout << "-" << flush;
748  progression_nb_bars++;
749  }
750  progression_percentage_old += nb_steps*2;
751  }
752  }
753  progression_printing_index++;
754  }
755  // Skip to the end
756  if (p_end_loop_flags[th]) continue;
757  // Step 1: Get the current event for that thread index
758  p_ChronoManager->StartIterativeDataUpdateStep1(th);
759  #ifdef CASTOR_DEBUG
760  if (m_verbose>=4) Cout("iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> Step1: Get current event for that thread index " << endl);
761  #endif
762 
763  vEvent* event = m2p_DataFile[bed]->GetEvent(index, th);
764  if (event==NULL)
765  {
766  Cerr("***** iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> An error occurred while getting the event from index "
767  << index << " (thread " << th << ") !" << endl);
768  // Specify that there was a problem
769  problem = true;
770  // We must continue here because we are inside an OpenMP loop
771  continue;
772  }
773  p_ChronoManager->StopIterativeDataUpdateStep1(th);
774  // Step 2: Call the dynamic switch function that updates the current frame and gate numbers, and also detects involuntary patient motion
775  p_ChronoManager->StartIterativeDataUpdateStep2(th);
776  #ifdef CASTOR_DEBUG
777  if (m_verbose>=4)
778  {
779  Cout("iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> Step2: Check for Dynamic event (frame/gate switch, image-based deformation " << endl);
780  }
781  #endif
782  int dynamic_switch_value = mp_ID->DynamicSwitch(index, event->GetTimeInMs(), bed, th);
783  // If the DYNAMIC_SWITCH_CONTINUE is returned, then it means that we are not yet at the first frame
784  if ( dynamic_switch_value == DYNAMIC_SWITCH_CONTINUE )
785  {
786  // Then we just skip this event
787  continue;
788  }
789  // Else, if the DYNAMIC_SWITCH_DEFORMATION is returned, then it means that a change of gate or involuntary motion has occurred
790  // and is associated to a deformation
791  else if ( dynamic_switch_value == DYNAMIC_SWITCH_DEFORMATION )
792  {
793  #ifdef CASTOR_OMP
794  // set deformation requirement for this thread
795  #pragma omp critical (deformation_requirements_first)
796  {
797  mp_DeformationManager->SetDeformationRequirement(th);
798  }
799  // block this thread until the deformation is done
800  bool wait_for_deformation = true;
801  while (wait_for_deformation)
802  {
803  // the first thread performs the deformation on the forward image
804  // as soon as all the threads are ready for deformation
805  if (th==0)
806  {
807  bool all_threads_ready = false;
808  //check whether all the threads require deformation
809  #pragma omp critical (deformation_requirements_first)
810  {
811  all_threads_ready = mp_DeformationManager->AllThreadsRequireDeformation();
812  }
813  if (all_threads_ready)
814  {
815  // Perform the deformation
817  // Free all the threads so that they can continue
818  #pragma omp critical (deformation_requirements_second)
819  {
820  mp_DeformationManager->UnsetDeformationRequirements();
821  }
822  }
823  }
824  // check whether the deformation is done and the thread can continue
825  #pragma omp critical (deformation_requirements_second)
826  {
827  wait_for_deformation = mp_DeformationManager->GetDeformationRequirement(th);
828  }
829  }
830  #else
831  // Perform here any resp related deformations on the forward image
833  #endif
834  // Restore progression printing
835  if (th==0 && m_verbose>=3 && mp_ID->GetMPIRank()==0)
836  {
837  cout << "0 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%" << endl;
838  cout << "|----|----|----|----|----|----|----|----|----|----|" << endl;
839  int progression_percentage_new = ((int)( (((float)(index-index_start+1))/((float)(index_stop-index_start)) ) * 100.));
840  cout << "|";
841  for (int i=0; i< progression_percentage_new / 2 ; i++) cout << "-";
842  }
843  }
844  // Else, if the DYNAMIC_SWITCH_END_LOOP is returned, set the local end_loop_flag boolean to true (which allows to quickly end the OMP parallel loop)
845  else if ( dynamic_switch_value == DYNAMIC_SWITCH_END_LOOP )
846  {
847  p_end_loop_flags[th] = true;
848  continue;
849  }
850  p_ChronoManager->StopIterativeDataUpdateStep2(th);
851  // Step 3: Compute the projection line
852  p_ChronoManager->StartIterativeDataUpdateStep3(th);
853  #ifdef CASTOR_DEBUG
854  if (m_verbose>=4) Cout("iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> Step3: Compute the projection line " << endl);
855  #endif
857  if (line==NULL)
858  {
859  Cerr("***** iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> A problem occurred while computing the projection line !" << endl);
860  // Specify that there was a problem
861  problem = true;
862  // We must continue here because we are inside an OpenMP loop
863  continue;
864  }
865  p_ChronoManager->StopIterativeDataUpdateStep3(th);
866  // Step 4: Optimize in the data space (forward-proj, update, backward-proj)
867  p_ChronoManager->StartIterativeDataUpdateStep4(th);
868  #ifdef CASTOR_DEBUG
869  if (m_verbose>=4) Cout("iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> Step4: Optimize in the data space " << endl);
870  #endif
871  if (line->NotEmptyLine()) mp_OptimizerManager->DataUpdateStep( line,
872  event,
873  bed,
877  th);
878  p_ChronoManager->StopIterativeDataUpdateStep4(th);
879  } // End of indices loop (OpenMP stops here)
880 
881  // Synchronize MPI processes
882  #ifdef CASTOR_MPI
883  MPI_Barrier(MPI_COMM_WORLD);
884  #endif
885  // Set back the number of threads for image computation
886  #ifdef CASTOR_OMP
887  omp_set_num_threads(mp_ID->GetNbThreadsForImageComputation());
888  #endif
889  // End of progression printing (do not log out with Cout here)
890  if (m_verbose>=2 && mp_ID->GetMPIRank()==0)
891  {
892  int progression_total_bars = 49;
893  for (int i=0; i<progression_total_bars-progression_nb_bars; i++) cout << "-";
894  cout << "|" << endl;
895  }
896  // If a problem was encountered, then report it here
897  if (problem)
898  {
899  Cerr("***** iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> A problem occurred inside the parallel loop over events !" << endl);
900  return 1;
901  }
902  } // End of beds loop
903 
904  // Synchronize MPI processes
905  #ifdef CASTOR_MPI
906  MPI_Barrier(MPI_COMM_WORLD);
907  #endif
908 
909  // Merge parallel results
911 
912  // If mask provided, apply mask to sensitivity, the CleanNeverVisitedVoxels will take care of the images
914 
915  // Perform here any image-based deformations related to motion on the backward image.
917 
918  // Apply convolver to backward images and sensitivity-if-needed
919  p_ChronoManager->StartConvolution();
921  {
922  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying convolver to backward images !" << endl);
923  return 1;
924  }
925  p_ChronoManager->StopConvolution();
926 
927  // Call the pre image update step function from the optimizer manager
929  {
930  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while performing the pre-image-update step !" << endl);
931  return 1;
932  }
933 
934  // Optimize in the image space (apply corrections, MAP and sensitivity); pass the number of subsets for list-mode sensitivity scaling
936  {
937  Cerr("***** iIterativeAlgorithm::StepPostProcessInsideSubsetLoop() -> A problem occurred while performing the image update step !" << endl);
938  return 1;
939  }
940 
941  // Clock stop for subset execution time
942  clock_t clock_stop_step = clock();
943  time_t time_stop_step = time(NULL);
944  if (m_verbose>=2) Cout ("iIterativeAlgorithm::PreOrPostIterationIndependentStep() -> Time spent for step | User: " << time_stop_step-time_start_step
945  << " sec | CPU: " << (clock_stop_step-clock_start_step)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
946 
947  return 0;
948 }
949 
950 // =====================================================================
951 // ---------------------------------------------------------------------
952 // ---------------------------------------------------------------------
953 // =====================================================================
954 
955 int iIterativeAlgorithm::InitSpecificOptions(string a_specificOptions)
956 {
957  (void)a_specificOptions; // avoid 'unused parameter' compil. warnings
958  if (m_verbose>=2) Cout("iIterativeAlgorithm::InitSpecificOptions ... " << endl);
959 
960  return 0;
961 }
int ApplyProcessingForward(oImageSpace *ap_ImageSpace)
void GetEventIndexStartAndStop(int64_t *ap_indexStart, int64_t *ap_indexStop, int a_subsetNum=0, int a_NbSubsets=1)
#define MODE_HISTOGRAM
int PerformDeformation(oImageSpace *ap_Image)
void DeallocateBackwardImageFromDynamicBasis()
Free memory for the backward image matrices.
FLTNB GetInitialValue()
Return the initial image value used by the optimizer, explaining why the eponym function of vOptimize...
bool NeedPreIteration()
Say if the optimizer needs a pre-process loop before iterations are done.
oImageDimensionsAndQuantification * mp_ID
#define Cerr(MESSAGE)
int PreImageUpdateStep()
A function that simply calls the eponym function from the vOptimizer.
void Reduce()
Merge parallel results into the matrix of the backward image matrix of the first thread. Also for MPI.
void StartIterativeDataUpdateStep4(int a_thread)
int ConvolveForward(oImageSpace *ap_ImageSpace)
int InitSpecificOptions(string a_specificOptions)
int ApplyOutputFOVMasking()
Mask the outside of the transaxial FOV based on the m_fovOutPercent.
int StepInnerLoopInsideSubsetLoop(int a_iteration, int a_subset, int a_bed)
oOptimizerManager * mp_OptimizerManager
void InstantiateBackwardImageFromDynamicBasis(int a_nbBackwardImages)
void StartIterativeDataUpdateStep3(int a_thread)
void SetImageConvolverManager(oImageConvolverManager *ap_ImageConvolverManager)
Set the Image Convolver Manager Object.
void StartIterativeDataUpdateStep2(int a_thread)
int ApplyMaskToSensitivity()
Apply the mask to the sensitivity image (only for the first thread, the image must be reduced beforeh...
void StartIterativeDataUpdateStep1(int a_thread)
int ApplyDeformationsToBackwardImage(oImageSpace *ap_Image)
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
int ConvolvePost(oImageSpace *ap_ImageSpace)
int ConvolveIntra(oImageSpace *ap_ImageSpace)
int ApplyProcessingPost(oImageSpace *ap_ImageSpace)
int InitImage(const string &a_pathToInitialImage, FLTNB a_value)
int DynamicSwitch(int64_t a_currentEventIndex, uint32_t a_currentTime, int a_bed, int a_th)
int StepAfterIterationLoop()
This function is called at the end of the RunCPU function.
int DataUpdateStep(oProjectionLine *ap_Line, vEvent *ap_Event, int a_bed, int a_timeFrame, int a_respGate, int a_cardGate, int a_thread)
void StopIterativeDataUpdateStep2(int a_thread)
int InitImageForDeformation(oImageSpace *ap_Image)
oImageProcessingManager * mp_ImageProcessingManager
bool GetRespStaticFlag()
Get the respiratory static flag that says if the reconstruction has only one respiratory gate or not...
int StepPostProcessInsideSubsetLoop(int a_iteration, int a_subset)
vEvent * GetEvent(int64_t a_eventIndex, int a_th=0)
void PrepareForwardImage()
Copy current image matrix in the forward-image buffer matrix.
int SaveOutputBasisCoefficientImage(int a_iteration, int a_subset=-1)
void StopIterativeDataUpdateStep1(int a_thread)
bool GetCardStaticFlag()
Get the cardiac static flag that says if the reconstruction has only one cardiac gate or not...
void CleanNeverVisitedVoxels()
Based on the visitedVoxelsImage, clean the never visited voxels in the image. This function must be c...
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
int StepPreProcessInsideSubsetLoop(int a_iteration, int a_subset)
int GetNbBackwardImages()
Return the number of backward images used by the optimizer, explaining why the eponym function of vOp...
int SaveOutputImage(int a_iteration, int a_subset=-1)
int PreDataUpdateStep()
A function that simply calls the eponym function from the vOptimizer.
void ResetCurrentDynamicIndices()
Call the eponym function from the oDynamicDataManager class using the member object.
virtual int StepBeforeIterationLoop()
This function is called at the beginning of the RunCPU function.
oProjectionLine * ComputeProjectionLine(vEvent *ap_Event, int a_th)
int ConvolveBackward(oImageSpace *ap_ImageSpace)
oImageConvolverManager * mp_ImageConvolverManager
int SaveParametricImages(int a_iteration, int a_subset=-1)
void SetCurrentIteration(int a_currentIteration)
void StartConvolution()
Start the timer for duration of convolution.
bool GetTimeStaticFlag()
Get the time static flag that says if the reconstruction has only one frame or not.
bool NotEmptyLine()
This function is used to know if the line contains any voxel contribution.
int GetNbSubIterationsInOneIteration()
Get the number of sub iterations in one iteration.
void InstantiateImageForDeformation(oImageSpace *ap_Image)
int ApplyProcessingIntra(oImageSpace *ap_ImageSpace)
oProjectorManager * mp_ProjectorManager
int StepBeforeIterationLoop()
This function is called at the beginning of the RunCPU function.
oDeformationManager * mp_DeformationManager
This class is designed to manage and store system matrix elements associated to a vEvent...
int ApplyOutputFlip()
Just flip the output image.
virtual int StepAfterIterationLoop()
This function is called at the end of the RunCPU function.
int StepImageOutput(int a_iteration, int a_subset=-1)
This is the base class for reconstructions, containing a framework with iteration and data subset loo...
void StopConvolution()
Stop the timer for duration of convolution.
void SetNumbersOfIterationsAndSubsets(int a_nbIterations, int *ap_nbSubsets)
Mother class for the Event objects.
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
void EnterPostIteration()
Set post-process flag of the vOptimizer to true.
void EnterPreIteration()
Set pre-process flag of the vOptimizer to true.
void ExitPostIteration()
Set post-process flag of the vOptimizer to false.
int ImageUpdateStep()
A function dedicated to the update step in the image space (performed after the loop on events) ...
int GetNbThreadsForProjection()
Get the number of threads used for projections.
bool NeedPostIteration()
Say if the optimizer needs a post-process loop after iterations are done.
This class is designed to manage some profiling of the code.
void ExitPreIteration()
Set pre-process flag of the vOptimizer to false.
int SaveSensitivityImage(const string &a_pathToSensitivityImage)
void InitBackwardImage()
Initialize each voxel of the backward images to 0, also for sensitivity if not loaded (estimated on t...
void DeallocateImageForDeformation(oImageSpace *ap_Image)
#define Cout(MESSAGE)
int PreOrPostIterationIndependentStep()
Perform an additional loop over data as well as the pre and post steps inside subset loop but without...
static sChronoManager * GetInstance()
Instantiate the singleton if not already done, then return the pointer to its instance.
int ApplyDynamicModel(oImageSpace *ap_ImageS, int a_iteration, int a_subset)
void SetOutputIterations(bool *ap_outputIterations)
Set the selected output iterations to the vOptimizer.
oDynamicModelManager * mp_DynamicModelManager