CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/analytic_simulator/oAnalyticProjection.cc
Go to the documentation of this file.
1 
9 #include "gVariables.hh"
10 #include "oAnalyticProjection.hh"
11 
12 /*
13  \brief oAnalyticProjection constructor.
14  Initialize the member variables to their default values.
15 */
17 {
18  m_verbose = -1;
19  m_flagGPU = false;
20  m_discardZeroEvent = false;
21  mp_ID = NULL;
22  m2p_DataFile = NULL;
23  mp_ProjectorManager = NULL;
25  mp_ImageSpace = NULL;
26  mp_ComputeProjection = NULL;
27  m_nbBeds = -1;
28  m_pathToInitialImg = "";
29  m_pathToAtnImg = "";
30  mp_Scanner = NULL;
31 }
32 
33 
34 
35 /*
36  \brief oAnalyticProjection destructor.
37 */
39 {}
40 
41 
42 
43 /*
44  \fn Launch
45  \brief Just call either the LaunchCPU or the LaunchGPU function as asked for
46  \return 0 if success, positive value otherwise
47 */
49 {
50  #ifdef CASTOR_GPU
51  if (m_flagGPU) return LaunchGPU();
52  else return LaunchCPU();
53  #else
54  return LaunchCPU();
55  #endif
56 }
57 
58 
59 
60 
61 // =====================================================================
62 // ---------------------------------------------------------------------
63 // ---------------------------------------------------------------------
64 // =====================================================================
65 /*
66  \fn LaunchGPU
67  \brief Perform the projection by executing each possible LOR of the scanner,
68  call the different object for optimization, projection, update, etc.
69  Function designed to be executed on the GPU only.
70  Returns error by default as it is not implemented
71  \return 0 if success, positive value otherwise
72 */
73 #ifdef CASTOR_GPU
74 int oAnalyticProjection::LaunchGPU()
75 {
76  Cerr("***** oAnalyticProjection::LaunchGPU() -> The GPU specific function is not implemented !" << endl);
77  return 1;
78 }
79 #endif
80 
81 
82 
83 
84 // =====================================================================
85 // ---------------------------------------------------------------------
86 // ---------------------------------------------------------------------
87 // =====================================================================
88 /*
89  \fn LaunchCPU
90  \brief Perform the projection by executing each possible LOR of the scanner,
91  call the different object for optimization, projection, update, etc.
92  Function designed to be executed on the GPU only.
93  Returns error by default as it is not implemented
94  \return 0 if success, positive value otherwise
95 */
97 {
98  // Verbose
99  if (m_verbose>=1) Cout("oAnalyticProjection::LaunchCPU() -> Start analytic projection" << endl);
100 
101  // Spread verbosity
103 
104  // Image Space allocations
107 
108  // Instanciate and initialize projection image for SPECT
111 
112  // Initialize the image to project
114  {
115  Cerr("***** oAnalyticProjection::LaunchCPU()-> Error during image initialization !" << endl);
116  return 1;
117  }
118 
120  {
121  Cerr("***** oAnalyticProjection::LaunchCPU()-> Error during attenuation image initialization !" << endl);
122  return 1;
123  }
124 
125  // Copy current image in forward-image buffer
127 
129  {
130  Cerr("***** oAnalyticProjection::LaunchCPU() -> A problem occurred while applying image convolver to forward image !" << endl);
131  return 1;
132  }
133 
134 
135  // Initial clock
136  clock_t clock_start = clock();
137  time_t time_start = time(NULL);
138 
139  sScannerManager* p_scannerManager;
140  p_scannerManager = sScannerManager::GetInstance();
141 
142  for(int bed=0 ; bed<m_nbBeds ; bed++)
143  {
144  // Initialize main loop start and stop values
145  unsigned int main_loop_start_index = 0 ;
146  unsigned int main_loop_stop_index = 0;
147 
148  // Check beforehand any issue with the loop start/stop values
149  // (not possible to do this check in the inner multithreaded loop)
150  if(p_scannerManager->PROJ_GetModalityStopValueMainLoop() > 0)
151  main_loop_stop_index = p_scannerManager->PROJ_GetModalityStopValueMainLoop();
152  else
153  {
154  Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occurred when trying to initialize main loop stop index !" << endl);
155  return 1;
156  }
157 
158  if(p_scannerManager->PROJ_GetModalityStartValueInnerLoop(0) < 0)
159  {
160  Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occurred when trying to initialize inner loop start index !" << endl);
161  return 1;
162  }
163 
164  // Prepare pre-computed sums of events to avoid the exponential evolution of the percentage (for PET)
165  // TODO Perhaps replace this with a call to scannerManager for potential error management
166  int32_t nb_total_elts = mp_Scanner->GetSystemNbElts();
167  int64_t* elements_sum = (int64_t*)malloc(nb_total_elts*sizeof(int64_t));
168  elements_sum[0] = 0;
169 
170  for (int64_t idx_elt=1 ; idx_elt<nb_total_elts ; idx_elt++)
171  elements_sum[idx_elt] = elements_sum[idx_elt-1] + (uint64_t)(nb_total_elts-idx_elt);
172 
173  uint64_t* total_events = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
174  HPFLTNB* total_prompts = (HPFLTNB*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(HPFLTNB));
175 
176  // Index for progression printing
177  uint64_t printing_index = 0;
178  uint64_t progression_index_total = p_scannerManager->PROJ_GetProgressionFinalValue()*
182 
183  // Just a variable to track how many dynamic images have been processed
184  // for progression feedback calculation
185  uint16_t nb_dyn_img_processed=0;
186 
187  // look on the dynamic frames
188  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
189  {
190  // Time reference to initialize the timestamp of the events
191  uint32_t timestamp = mp_ID->GetFrameTimeStartInMs(bed,fr);
192  uint64_t* total_events_in_frame = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
193  HPFLTNB* total_prompts_in_frame = (HPFLTNB*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(HPFLTNB));
194 
195  for(int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
196  {
197  uint64_t* total_events_in_rgate = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
198  HPFLTNB* total_prompts_in_rgate = (HPFLTNB*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(HPFLTNB));
199 
200  for(int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
201  {
202  uint64_t* total_events_in_cgate = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
203  HPFLTNB* total_prompts_in_cgate = (HPFLTNB*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(HPFLTNB));
204 
205  if(mp_ID->GetNbTimeFrames() > 1)
206  if(m_verbose>=0) Cout(endl << "Processing frame #" << fr+1 << " out of " << mp_ID->GetNbTimeFrames() << endl);
207  if(mp_ID->GetNbRespGates() > 1)
208  if(m_verbose>=0) Cout(endl << "Processing resp gate #" << rg+1 << " out of " << mp_ID->GetNbRespGates() << endl);
209  if(mp_ID->GetNbCardGates() > 1)
210  if(m_verbose>=0) Cout(endl << "Processing card gate #" << cg+1 << " out of " << mp_ID->GetNbCardGates() << endl);
211 
212  // Used to catch errors inside the parallel loop
213  bool problem = false;
214 
215  // Multi-threaded loop on the scanner elements
216  // Use (static,1) as dynamic mode actually different output histogram with multithreading.
217  int64_t idx_elt1; //openMP throws error with unsigned
218  #pragma omp parallel for private(idx_elt1) schedule(static, 1)
219  for (idx_elt1=main_loop_start_index ; idx_elt1<(int)main_loop_stop_index ; idx_elt1++)
220  {
221  // Get the thread index
222  int th = 0;
223  #ifdef CASTOR_OMP
224  th = omp_get_thread_num();
225  #endif
226 
227  // Initialize inner loop start and stop values
228  int64_t inner_loop_start_index = p_scannerManager->PROJ_GetModalityStartValueInnerLoop(idx_elt1) ;
229  int64_t inner_loop_stop_index = mp_Scanner->GetSystemNbElts();
230 
231  // Inner loop on scanner elements (idx_elt2) which are used to form LOR using the first scanner element (idx_elt1)
232  for (int64_t idx_elt2=inner_loop_start_index ; idx_elt2<inner_loop_stop_index ; idx_elt2++)
233  {
234  // Print progression (do not log out with Cout here)
235  if (th==0)
236  {
237  if (printing_index%10000==0)
238  {
239  //int64_t progression_index_current = p_scannerManager->PROJ_GetCurrentProgression(idx_elt1, idx_elt2, elements_sum,
240  // mp_ID->GetNbRespGates(),
241  // mp_ID->GetNbCardGates(),
242  // fr, rg, cg);
243  int64_t progression_index_current = p_scannerManager->PROJ_GetCurrentProgression(idx_elt1, idx_elt2, elements_sum, nb_dyn_img_processed);
244 
245  FLTNB percent = ( (FLTNB)progression_index_current /((FLTNB)progression_index_total) ) * ((FLTNB)100);
246  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 "
247  << percent << " % ";
248 
249  }
250  printing_index++;
251  }
252 
253  // Allocate an event using the iDataFile
254  vEvent* event = m2p_DataFile[bed]->PROJ_GenerateEvent(idx_elt1, idx_elt2, th);
255 
256  // TODO : Perhaps replace this with a call to scannerManager for potential error management
257  if (!mp_Scanner->IsAvailableLOR(event->GetID1(0), event->GetID2(0))) continue;
258 
259  // Generate the projection event and compute the projection line
261  if (line==NULL)
262  {
263  Cerr("***** oAnalyticProjection::LaunchCPU() -> A problem occurred while computing the projection line !" << endl);
264  // Specify that there was a problem
265  problem = true;
266  // We must continue here because we are inside an OpenMP loop
267  continue;
268  }
269 
270  // Optimize in the data space (forward-proj, update, backward-proj) if the line has been performed
271  if (line->NotEmptyLine())
272  {
273  if(mp_ComputeProjection->DataUpdateStep(m2p_DataFile[bed], line, mp_ImageSpace, event, fr, rg, cg, th, timestamp, m_discardZeroEvent) )
274  {
275  Cerr("***** oAnalyticProjection::LaunchCPU() -> An error occurred during the data update step !" << endl);
276  //return 1; TODO : return in open MP structured block (stop all threads)
277  }
278 
279  // If we chose to discard zero event, write it only if event value > 0
280  if( !m_discardZeroEvent
281  || event->GetEventValue(0)>0. )
282  {
283  // Increment number of events
284  total_events[th]++;
285  total_events_in_cgate[th]++;
286  total_events_in_rgate[th]++;
287  total_events_in_frame[th]++;
288  HPFLTNB event_value = event->GetEventValue(0);
289  total_prompts[th] += event_value;
290  total_prompts_in_cgate[th] += event_value;
291  total_prompts_in_rgate[th] += event_value;
292  total_prompts_in_frame[th] += event_value;
293  }
294  }
295 
296  }
297 
298  } // End thread loop
299 
300  nb_dyn_img_processed++;
301 
302  // If a problem was encountered, then report it here
303  if (problem)
304  {
305  Cerr("***** oAnalyticProjection::LaunchCPU() -> A problem occurred inside the parallel loop over events !" << endl);
306  return 1;
307  }
308 
309  // Write data for this frame
310  if (m2p_DataFile[bed]->PROJ_WriteData() )
311  {
312  Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occurred during the data writing step" << endl);
313  return 1;
314  }
315 
316  // Merge counters
317  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events_in_cgate[0] += total_events_in_cgate[th];
318  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts_in_cgate[0] += total_prompts_in_cgate[th];
319 
320  if(m_verbose>=2 && mp_ID->GetNbCardGates() > 1)
321  {
322  Cout(endl << "Total events in cgate #" << cg+1 << ": " << total_events_in_cgate[0] << endl);
323  Cout("Total prompts in cgate #" << cg+1 << ": " << total_prompts_in_cgate[0] << endl);
324  }
325 
326  free(total_prompts_in_cgate);
327  free(total_events_in_cgate);
328  }
329 
330  // Merge counters
331  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events_in_rgate[0] += total_events_in_rgate[th];
332  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts_in_rgate[0] += total_prompts_in_rgate[th];
333 
334  if(m_verbose>=2 && mp_ID->GetNbRespGates() > 1)
335  {
336  Cout(endl << "Total events in rgate #" << rg+1 << ": " << total_events_in_rgate[0] << endl);
337  Cout("Total prompts in rgate #" << rg+1 << ": " << total_prompts_in_rgate[0] << endl);
338  }
339  free(total_prompts_in_rgate);
340  free(total_events_in_rgate);
341  }
342 
343  // Get time reference for this frame in order to initialize the timestamp (in ms) of the events
344  timestamp += mp_ID->GetFrameDurationInMs(bed,fr);
345 
346  // Merge counters
347  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events_in_frame[0] += total_events_in_frame[th];
348  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts_in_frame[0] += total_prompts_in_frame[th];
349 
350  if(m_verbose>=2 && mp_ID->GetNbTimeFrames() > 1)
351  {
352  Cout(endl << "Total events in frame #" << fr+1 << ": " << total_events_in_frame[0] << endl);
353  Cout("Total prompts in frame #" << fr+1 << ": " << total_prompts_in_frame[0] << endl);
354  }
355 
356  free(total_prompts_in_frame);
357  free(total_events_in_frame);
358  }
359 
360  // Compute simulated acquisition duration for 4D projection
361  int acquisition_duration = 0;
362  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
363  acquisition_duration += mp_ID->GetFrameDurationInMs(bed,fr);
364 
365  // If 3D, set the acquisition duration to 1 by default
366  if(acquisition_duration <= 0 ) acquisition_duration = 1; // Set default value for acquisition duration
367 
368  // End of progression printing (do not log out with Cout here)
369  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 100 % " << endl;
370 
371  // Save a projection image for output visualization (currently only for SPECT)
373 
374  // Merge counters
375  if (m_verbose>=2)
376  {
377  Cout("Total number of projected events:" << endl);
378  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++) Cout(" --> Thread " << th << " | " << total_events[th] << " events" << endl);
379  }
380  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events[0] += total_events[th];
381  if (m_verbose>=1) Cout("Final total number of events projected: " << total_events[0] << endl);
382  for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts[0] += total_prompts[th];
383  if (m_verbose>=1) Cout("Final total number of prompts: " << total_prompts[0] << endl);
384 
385  // Write header (Cdh)
386  // TODO : acquisition duration must depend on frames, and we should have one datafile by frame
389  m2p_DataFile[bed]->SetNbEvents(total_events[0]);
390  m2p_DataFile[bed]->WriteHeader();
392 
393  free(total_prompts);
394  free(total_events);
395  free(elements_sum);
396  }
397 
398  // Free memory
403 
404  // Clock total
405  clock_t clock_stop = clock();
406  time_t time_stop = time(NULL);
407  Cout (endl << endl << " Total Time spent - Analytic Projection | User: " << time_stop-time_start << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
408 
409  return 0;
410 }
void LMS_InstantiateImage()
Allocate memory for the main image matrices (for list-mode sensitivity generation) ...
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
oAnalyticProjection()
oAnalyticProjection constructor. Initialize the member variables to their default values...
int64_t PROJ_GetModalityStartValueInnerLoop(int64_t a_elt1)
int PROJ_InitImage(const string &a_pathToInitialImage)
#define Cerr(MESSAGE)
virtual int WriteHeader()=0
This function is implemented in child classes. Generate a header file according to the data output ...
void LMS_PrepareForwardImage()
Copy current image in forward-image buffer (for list-mode sensitivity generation) ...
int ConvolveForward(oImageSpace *ap_ImageSpace)
int64_t PROJ_GetProgressionFinalValue()
Get numerator value according to the modality to compute percent progression during the projection pr...
virtual uint16_t PROJ_GetSPECTNbProjections()
return the total number of projections for a SPECT acquisition
int PROJ_SaveProjectionImage()
Save an image of the projected data (for analytic projection)
void SetDuration(FLTNB a_value)
void LMS_InstantiateForwardImage()
Allocate memory for the forward image matrices (for list-mode sensitivity generation) ...
int InitAttenuationImage(const string &a_pathToAtnImage)
int DataUpdateStep(vDataFile *ap_DataFile, oProjectionLine *a2p_Line, oImageSpace *ap_Image, vEvent *ap_Event, int a_fr, int a_rg, int a_cg, int th, uint32_t a_timestamp, bool a_discardZeroEvent)
virtual uint16_t PROJ_GetSPECTNbPixels()
return the total number of pixels for a SPECT reconstruction
~oAnalyticProjection()
oAnalyticProjection destructor.
void PROJ_DeallocateProjectionImage(int a_nbProjs)
#define SCANNER_SPECT_CONVERGENT
void SetStartTime(FLTNB a_value)
Singleton class that Instantiate and initialize the scanner object.
void LMS_DeallocateImage()
Free memory for the main image matrices (for list-mode sensitivity generation)
int64_t PROJ_GetCurrentProgression(int64_t a_elt1, int64_t a_elt2, int64_t *ap_nbEltsArray, uint16_t a_nbDynImgProcessed)
oProjectionLine * ComputeProjectionLine(vEvent *ap_Event, int a_th)
int Launch()
Just call either the LaunchCPU or the LaunchGPU function as asked for.
vEvent * PROJ_GenerateEvent(int idx_elt1, int idx_elt2, int a_th)
bool NotEmptyLine()
This function is used to know if the line contains any voxel contribution.
void PROJ_InstantiateProjectionImage(int a_nbProjs, int a_nbPixels)
void SetNbEvents(int64_t a_value)
virtual int GetSystemNbElts()=0
This is a pure virtual method that must be implemented by children.
This class is designed to manage and store system matrix elements associated to a vEvent...
void LMS_DeallocateAttenuationImage()
Free memory for the Attenuation image matrices (for analytical projection or list-mode sensitivity ge...
Mother class for the Event objects.
int GetNbThreadsForProjection()
Get the number of threads used for projections.
int64_t PROJ_GetModalityStopValueMainLoop()
Get the stop value for the main loop of analytic projection depending on the modality.
int PROJ_DeleteTmpDataFile()
Delete temporary datafile used for multithreaded output writing if needed.
virtual int IsAvailableLOR(int a_elt1, int a_elt2)
#define Cout(MESSAGE)
void LMS_DeallocateForwardImage()
Free memory for the forward image matrices (for list-mode sensitivity generation) ...