CASToR  1.1
Tomographic Reconstruction (PET/SPECT)
 All Classes Files Functions Variables Typedefs Macros Groups Pages
oDynamicDataManager.cc
Go to the documentation of this file.
1 
2 /*
3  Implementation of class oDynamicDataManager
4 
5  - separators: X
6  - doxygen: X
7  - default initialization: X
8  - CASTOR_DEBUG: none
9  - CASTOR_VERBOSE: X
10 */
11 
12 
21 #include "oDynamicDataManager.hh"
23 
24 // =====================================================================
25 // ---------------------------------------------------------------------
26 // ---------------------------------------------------------------------
27 // =====================================================================
28 /*
29  Constructor oDynamicDataManager
30 */
32 {
33  mp_ID = NULL;
34  m_verbose = -1;
35  m_nbTimeFrames = 1;
36  mp_currentFrameIndex = NULL;
37  m_respGatingFlag = false;
38  m_rMotionCorrFlag = false;
39  m_nbRespGates = 1;
43  m_cardGatingFlag = false;
44  m_cMotionCorrFlag = false;
45  m_nbCardGates = 1;
49  m_pMotionCorrFlag = false;
53 }
54 
55 // =====================================================================
56 // ---------------------------------------------------------------------
57 // ---------------------------------------------------------------------
58 // =====================================================================
59 /*
60  Destructor oDynamicDataManager
61 */
63 {
64  for (int fr=0; fr<m_nbTimeFrames; fr++)
65  {
67  delete m2p_nbEventsPerRespGate[fr];
69  delete m2p_nbEventsPerCardGate[fr];
71  delete m2p_indexLastEventRespGate[fr];
73  delete m2p_indexLastEventCardGate[fr];
74  }
75 
85 }
86 
87 // =====================================================================
88 // ---------------------------------------------------------------------
89 // ---------------------------------------------------------------------
90 // =====================================================================
91 /*
92  \fn InitDynamicData
93  \param a_nbRespGates
94  \param a_nbCardGates
95  \param a_pathTo4DDataFile : path to an ASCII file containing dynamic metadata regarding the acquisition
96  \param a_rmCorrFlag : indicate whether respiratory motion correction is enabled (1) or disabled (0)
97  \param a_cmCorrFlag : indicate whether cardiac motion correction is enabled (1) or disabled (0)
98  \param a_dmCorrFlag : indicate whether simultaneous respiratory and cardiac motion corrections are enabled (1) or disabled (0)
99  \param a_pmCorrFlag : indicate whether involuntary patient motion correction is enabled (1) or disabled (0)
100  \brief Main function for instanciation and initialization of the member variables and arrays. Call the specific initialization function depending of the type of dataset.
101  \todo warning in the documentation that Respiratory motion correction is not supported for cardiac-gated data in the current implementation
102  \return 0 if success, positive value otherwise.
103 */
104 int oDynamicDataManager::InitDynamicData( int a_nbRespGates, int a_nbCardGates, const string& a_pathTo4DDataFile,
105  int a_rmCorrFlag, int a_cmCorrFlag, int a_dmCorrFlag, int a_pmCorrFlag)
106 {
107  if (m_verbose>=3) Cout("oDynamicDataManager::InitDynamicData() -> Initialize dynamic data management" << endl);
108 
109  // Initialization
111  m_nbRespGates = a_nbRespGates;
112  m_nbCardGates = a_nbCardGates;
113  if (m_nbRespGates > 1) m_respGatingFlag = true;
114  if (m_nbCardGates > 1) m_cardGatingFlag = true;
115  m_rMotionCorrFlag = a_rmCorrFlag;
116  m_cMotionCorrFlag = a_cmCorrFlag;
117  m_pMotionCorrFlag = a_pmCorrFlag;
118  if(a_dmCorrFlag)
119  {
120  m_rMotionCorrFlag = true;
121  m_cMotionCorrFlag = true;
122  }
123 
124  // Instanciation & initialization for current indices (used during the loop on events to know which frame/gate the event belongs to)
129  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
130  {
131  mp_currentFrameIndex [0] = 0;
132  mp_currentRespGateIndex [0] = 0;
133  mp_currentCardGateIndex [0] = 0;
134  mp_currentPMotionIndex[0] = 0;
135  }
136 
137  // Instanciation & initialization of number of events per frame for gated reconstructions
138  m2p_nbEventsPerRespGate = new int64_t*[m_nbTimeFrames];
139  m2p_nbEventsPerCardGate = new int64_t*[m_nbTimeFrames];
142 
143  for (int fr=0; fr<m_nbTimeFrames; fr++)
144  {
145  m2p_nbEventsPerRespGate[fr] = new int64_t[m_nbRespGates];
147  m2p_indexLastEventRespGate[fr] = new int64_t[m_nbRespGates];
149 
150  for (int rg=0; rg<m_nbRespGates; rg++)
151  {
152  m2p_nbEventsPerRespGate[fr][rg] = -1;
153  m2p_indexLastEventRespGate[fr][rg] = -1;
154 
155  for (int cg=0; cg<m_nbCardGates; cg++)
156  {
157  m2p_nbEventsPerCardGate[fr][rg*m_nbCardGates+cg] = -1;
158  m2p_indexLastEventCardGate[fr][rg*m_nbCardGates+cg] = -1;
159  }
160  }
161  }
162 
163  // Check coherence of motion initialization
164  // Throw error if cardiac gating is enabled alongside respiratory motion correction (not available)
165  // TODO : warning in the documentation that Respiratory motion correction is not supported for cardiac-gated data in the current implementation
167  {
168  Cerr("***** oDynamicDataManager::InitDynamicData()-> Respiratory motion correction is enabled for cardiac-gated data. This is not supported in the current implementation !" << endl);
169  return 1;
170  }
171 
172  // Check iPat motion vs physiological motion
173  if (a_pmCorrFlag && (m_rMotionCorrFlag || m_cMotionCorrFlag) )
174  {
175  Cerr("***** oDynamicDataManager::InitDynamicData()-> Involuntary patient image-based motion correction cannot be used along with respiratory or cardiac motion correction !" << endl);
176  return 1;
177  }
178 
179  // Incorrect motion initialization
180  if ( (a_rmCorrFlag + a_rmCorrFlag + a_dmCorrFlag) >1 )
181  {
182  Cerr("***** oDynamicDataManager::InitDynamicData()-> Incorrect image-based motion initialization."<<endl);
183  Cerr("***** Please use -rm for respiratory motion, -cm for cardiac motion, or -dm for double respiratory and cardiac motion correction !" << endl);
184  return 1;
185  }
186 
187  // Initialization for respiratory/cardiac gated reconstruction
188  // Note : for Analytic Projection, gating flag could be enabled in order to project an image containing several gates, but no description file is required
189  // InitDynamicDataGating() is restricted to reconstruction
190  // Check on the existence of a_pathTo4DDataFile during reconstruction is performed onnly in Grecon
191  if ((m_respGatingFlag || m_cardGatingFlag) && !a_pathTo4DDataFile.empty() )
192  {
193  if(m_verbose >=3) Cout("oDynamicDataManager::InitDynamicData()-> Initializing data for gated reconstruction... " << endl);
194 
195  if (InitDynamicDataGating(a_pathTo4DDataFile))
196  {
197  Cerr("***** oDynamicDataManager::InitDynamicData()-> A problem occured during the dynamic gating initialization !" << endl);
198  return 1;
199  }
200  }
201 
202  // Initialization with involuntary patient motion corrected reconstruction
203  if (m_pMotionCorrFlag && InitDynamicDataPatientMotion(a_pathTo4DDataFile) )
204  {
205  Cerr("***** oDynamicDataManager::InitDynamicData()-> A problem occured during the patient involuntary motion correction initialization !" << endl);
206  return 1;
207  }
208 
209  // Some feedback
210  if (m_verbose>=3)
211  {
212  if(m_respGatingFlag) Cout("oDynamicDataManager::InitDynamicData()-> Respiratory gating enabled" << endl);
213  if(m_rMotionCorrFlag) Cout("oDynamicDataManager::InitDynamicData()-> Respiratory image-based motion correction enabled" << endl);
214  if(m_cardGatingFlag) Cout("oDynamicDataManager::InitDynamicData()-> Cardiac gating enabled" << endl);
215  if(m_cMotionCorrFlag) Cout("oDynamicDataManager::InitDynamicData()-> Cardiac image-based motion correction enabled" << endl);
216  if(m_pMotionCorrFlag) Cout("oDynamicDataManager::InitDynamicData()-> Involuntary image-based patient motion correction enabled" << endl);
217  }
218 
219  // End
220  return 0;
221 }
222 
223 // =====================================================================
224 // ---------------------------------------------------------------------
225 // ---------------------------------------------------------------------
226 // =====================================================================
227 /*
228  \fn InitDynamicDataGating
229  \param a_pathToFile : path to an ASCII file containing dynamic metadata regarding the acquisition
230  \brief Initialisation of arrays containing informations about the data splitting and respiratory/cardiac gated reconstruction
231  \todo check the reconstruction with cardiac gated reconstruction. Some adjustements will be required for double gating
232  \return 0 if success, positive value otherwise.
233 */
234 int oDynamicDataManager::InitDynamicDataGating(const string& a_pathToFile)
235 {
236  if(m_verbose >=3) Cout("oDynamicDataManager::InitDynamicDataGating() ... " << endl);
237 
238  // Respiratory gating enabled
239  if (m_respGatingFlag)
240  {
241  // Read information about respiratory gating (number of events per respiratory gate per frame)
242  if ( ReadDataASCIIFile(a_pathToFile,
243  "respiratory_gates_data_splitting",
245  m_nbRespGates,
248  {
249  Cerr("***** oDynamicDataManager::InitDynamicDataGating() -> Error while trying to read 'respiratory_gates_data_splitting' in file '" << a_pathToFile << "' !" << endl);
250  return 1;
251  }
252 
253  // Get the index of the last event of each respiratory gate using the number of events inside each gate
254  uint64_t event_number_sum = 0;
255  if (m_verbose>=3) Cout("oDynamicDataManager::InitDynamicDataGating() :" << endl);
256 
257  for (int fr=0; fr<m_nbTimeFrames; fr++)
258  for (int rg=0; rg<m_nbRespGates; rg++)
259  {
260  m2p_indexLastEventRespGate[fr][rg] += m2p_nbEventsPerRespGate[fr][rg] + event_number_sum;
261  event_number_sum += m2p_nbEventsPerRespGate[fr][rg];
262  if (m_verbose>=3) Cout("Number of events for frame #" << fr << ", respiratory gate #" << rg << " = " << m2p_nbEventsPerRespGate[fr][rg] << endl);
263  }
264  }
265 
266 
267  // Cardiac gating enabled
268  if (m_cardGatingFlag)
269  {
270  // Read information about cardiac gating (number of events per cardiac gate per respiratory gates times frames)
271  if ( ReadDataASCIIFile(a_pathToFile,
272  "cardiac_gates_data_splitting",
274  m_nbCardGates,
277  {
278  Cerr("***** oDynamicDataManager::InitDynamicDataGating() -> Error while trying to read 'cardiac_gates_data_splitting' in file '" << a_pathToFile << "' !" << endl);
279  return 1;
280  }
281 
282  // Get the index of the last event of each cardiac gate using the number of events inside each gate
283  uint64_t event_number_sum = 0;
284  if (m_verbose >=3) Cout("oDynamicDataManager::InitDynamicDataGating() :" << endl);
285 
286  for (int fr=0; fr<m_nbTimeFrames; fr++)
287  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++)
288  {
289  m2p_indexLastEventCardGate[fr][g] += m2p_nbEventsPerCardGate[fr][g] + event_number_sum;
290  event_number_sum += m2p_nbEventsPerCardGate[fr][g];
291  if (m_verbose>=3) Cout("Number of events for frame #" << fr << ", cardiac gate #" << g << " = " << m2p_nbEventsPerCardGate[fr][g] << endl);
292  }
293  }
294 
295  // End
296  return 0;
297 }
298 
299 // =====================================================================
300 // ---------------------------------------------------------------------
301 // ---------------------------------------------------------------------
302 // =====================================================================
303 /*
304  \fn InitDynamicDataPatientMotion
305  \param a_pathToFile : path to an ASCII file containing dynamic metadata regarding the acquisition
306  \brief Initialisation of involuntary patient motion correction information, if any.
307  \todo Implementation currently IN PROGRESS. Will have to define how motion subset information is provided as it depends of each datafile, if the acquisition contain several bed steps.
308  \return 0 if success, positive value otherwise.
309 */
311 {
312  if(m_verbose >=3) Cout("oDynamicDataManager::InitDynamicDataPatientMotion() ... " << endl);
313  Cerr("!!!!! WARNING oDynamicDataManager::InitDynamicDataGating() -> Still WiP !" << endl);
314  // First get the number of time triggers into the dynamic file
315  if (ReadDataASCIIFile(a_pathToFile,
316  "nb_involuntary_motion_triggers",
318  1,
320  {
321  Cerr("***** oDynamicDataManager::InitDynamicDataPatientMotion() -> Involuntary motion correction enabled but could" << endl
322  << " not find the number of triggers in the dynamic file '" << a_pathToFile << "' !" << endl);
323  return 1;
324  }
325 
326  // Allocate time triggers and read them from the dynamic file
327  mp_listPMotionTriggers = (uint32_t*)malloc(m_nbPMotionTriggers*sizeof(uint32_t));
328 
329  if (ReadDataASCIIFile(a_pathToFile,
330  "list_involuntary_motion_triggers",
334  {
335  Cerr("***** oDynamicDataManager::InitDynamicDataPatientMotion() -> Involuntary motion correction enabled but list of triggers" << endl
336  << " not found in the dynamic file '" << a_pathToFile << "' !" << endl);
337  return 1;
338  }
339  // End
340  return 0;
341 }
342 
343 // =====================================================================
344 // ---------------------------------------------------------------------
345 // ---------------------------------------------------------------------
346 // =====================================================================
347 /*
348  \fn SetDynamicSpecificQuantificationFactors
349  \param FLTNB** a2p_quantificationFactors : 2 dimensional [timeframe][gate] set of quantitative factors to update
350  \brief Compute gate-specific quantificative factors using the number of events within each gate, and update the quantitative factors passed in argument
351  \return 0 if success, positive value otherwise.
352 */
354 {
355  if(m_verbose >=3) Cout("oDynamicDataManager::SetDynamicSpecificQuantificationFactors() ... " << endl);
356 
357  // COMPUTE GATE-SPECIFIC QUANTITATIVE FACTORS
358  if (m_nbRespGates>1 || m_nbCardGates>1)
359  {
360  for(int fr=0 ; fr<m_nbTimeFrames ; fr++)
361  {
362  // We have cardiac gates and don't correct for motion -> quantification factors required for cardiac gates
364  {
365  uint64_t total_events_in_frame = 0;
366  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++) total_events_in_frame += m2p_nbEventsPerCardGate[fr][g];
367 
368  if (m_verbose>=3) Cout("oDynamicDataManager::SetDynamicSpecificQuantificationFactors() -> Cardiac gating correction factors :" << endl);
369  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
370  {
371  a2p_quantificationFactors[fr][g] *= (FLTNB)total_events_in_frame/(FLTNB)m2p_nbEventsPerCardGate[fr][g];
372  if (m_verbose>=3) Cout("Frame #" << fr << ", cardiac gate #" << g << " = " << a2p_quantificationFactors[fr][g] << endl);
373  }
374  }
375 
376  // We have resp gates and don't correct for resp motion (and no independent cardiac gate reconstruction) -> quantification factors required for cardiac gates
378  {
379  uint64_t total_events_in_frame = 0;
380  for(int rg=0 ; rg<m_nbRespGates ; rg++) total_events_in_frame += m2p_nbEventsPerRespGate[fr][rg];
381 
382  if (m_verbose>=3) Cout("oDynamicDataManager::SetDynamicSpecificQuantificationFactors() -> Respiratory gating correction factors :" << endl);
383  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
384  {
385  int rg = int(g/m_nbCardGates);
386  a2p_quantificationFactors[fr][g] *= (FLTNB)total_events_in_frame/(FLTNB)m2p_nbEventsPerRespGate[fr][rg];
387  if (m_verbose>=3) Cout("Frame #" << fr << ", gate #" << g << " = " << a2p_quantificationFactors[fr][g] << endl);
388  }
389  }
390  }
391  }
392 
393  // End
394  return 0;
395 }
396 
397 // =====================================================================
398 // ---------------------------------------------------------------------
399 // ---------------------------------------------------------------------
400 // =====================================================================
408 {
409  if (m_verbose>=3) Cout("oDynamicDataManager::CheckParameters() -> Check parameters for dynamic data settings" << endl);
410 
411  // Check image dimensions
412  if (mp_ID==NULL)
413  {
414  Cerr("***** oDynamicDataManager::CheckParameters() -> No image dimensions provided !" << endl);
415  return 1;
416  }
417  // Check resp gates
418  if (m_nbRespGates<0)
419  {
420  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong number of respiratory gates !" << endl);
421  return 1;
422  }
423  // Check card gates
424  if (m_nbCardGates<0)
425  {
426  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong number of respiratory gates !" << endl);
427  return 1;
428  }
429  // Check involuntary motion triggers
430  if (m_nbPMotionTriggers<0)
431  {
432  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong number of involuntary motion subsets provided !" << endl);
433  return 1;
434  }
435  // Check verbosity
436  if (m_verbose<0)
437  {
438  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong verbosity level provided !" << endl);
439  return 1;
440  }
441  // Feedback regarding the enabled/disabled options for the reconstruction
442  if (m_verbose>=2)
443  {
444  if (m_respGatingFlag) Cout("oDynamicDataManager::CheckParameters() -> Respiratory gating is enabled" << endl);
445  if (m_rMotionCorrFlag) Cout("oDynamicDataManager::CheckParameters() -> Respiratory motion correction enabled" << endl);
446  if (m_cardGatingFlag) Cout("oDynamicDataManager::CheckParameters() -> Cardiac gating is enabled" << endl);
447  if (m_cMotionCorrFlag) Cout("oDynamicDataManager::CheckParameters() -> Cardiac motion correction is enabled" << endl);
448  if (m_pMotionCorrFlag) Cout("oDynamicDataManager::CheckParameters() -> Involuntary motion correction is enabled" << endl);
449  }
450 
451  // Check consistency between number of events in the datafile and the potential splitting of the dynamic data into respiratory or cardiac gates (if any gating is enabled)
452  if (m_respGatingFlag)
453  {
454  int last_fr = m_nbTimeFrames-1;
455  int last_rg = m_nbRespGates-1;
456  if (m2p_indexLastEventRespGate[last_fr][last_rg]+1 != a_nbEvents)
457  {
458  Cerr("***** oDynamicDataManager::CheckParameters() -> Problem while checking consistency of dynamic data !" << endl
459  << " The number of events in the datafile (" << a_nbEvents
460  << ") is different from the total number of events in respiratory gates (" << m2p_indexLastEventRespGate[last_fr][last_rg] << ") as initialized in the gating file !" << endl);
461  return 1;
462  }
463  }
464  if (m_cardGatingFlag)
465  {
466  int last_fr = m_nbTimeFrames-1;
467  int last_rg = m_nbCardGates-1;
468  if (m2p_indexLastEventCardGate[last_fr][last_rg]+1 != a_nbEvents)
469  {
470  Cerr("***** oDynamicDataManager::CheckParameters() -> Problem while checking consistency of dynamic data !" << endl
471  << " The number of events in the datafile (" << a_nbEvents
472  << ") is different to the total number of events in cardiac gates (" << m2p_indexLastEventRespGate[last_fr][last_rg] << ") as initialized in the gating file !" << endl);
473  return 1;
474  }
475  }
476 
477  // Normal end
478  return 0;
479 }
480 
481 // =====================================================================
482 // ---------------------------------------------------------------------
483 // ---------------------------------------------------------------------
484 // =====================================================================
490 {
491  #ifdef CASTOR_VERBOSE
492  if (m_verbose >=4) Cout("oDynamicDataManager::ResetCurrentDynamicIndices() -> Reset indices" << endl);
493  #endif
494 
495  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++)
496  {
497  // We initialize the frame index to -1 because at the beginning of the events loop, we don't know yet
498  // if we are in the first frame (the user can reconstruct only a part of the whole datafile).
499  mp_currentFrameIndex[th] = -1;
500  mp_currentPMotionIndex[th] = 0;
501  mp_currentRespGateIndex[th] = 0;
502  mp_currentCardGateIndex[th] = 0;
503  }
504 }
505 
506 // =====================================================================
507 // ---------------------------------------------------------------------
508 // ---------------------------------------------------------------------
509 // =====================================================================
510 /*
511  \fn DynamicSwitch
512  \param a_currentEventIndex : Index of the current event in the iterative reconstruction loop
513  \param a_currentTime : Timestamp of the current event in the iterative reconstruction loop
514  \param a_bed
515  \param a_th
516  \brief This function is called in the reconstruction event loop. It is used to check if the current event belongs to a new respiratory/cardiac/involuntary motion gate
517  Increment the index reporting the current relative gate in this case
518  \todo Check implementation for cardiac gating and involuntary patient motion correction
519  \return 0 if nothing to do,
520  1 if the current timestamp is inferior to the start time of the first frame,
521  2 if a deformation is required (gate has changed and motion correction is enabled)
522 */
523 int oDynamicDataManager::DynamicSwitch(int64_t a_currentEventIndex, uint32_t a_currentTime, int a_bed, int a_th)
524 {
525  #ifdef CASTOR_VERBOSE
526  if (m_verbose >=4) Cout("oDynamicDataManager::DynamicSwitch() -> Enter function" << endl);
527  #endif
528 
529  // The value that will be returned
530  int return_value = DYNAMIC_SWITCH_NOTHING;
531 
532  // -------------------------------------------------------------------------------------------------
533  // Step 1: involuntary motion management
534  // -------------------------------------------------------------------------------------------------
535 
536  // Only do this if the involuntary motion correction is enabled (meaning we must proceed to a deformation)
537  if (m_pMotionCorrFlag)
538  {
539  // Search if we passed one of the next motion triggers (starting at current index)
540  for (int mt=mp_currentPMotionIndex[a_th]; mt<m_nbPMotionTriggers; mt++)
541  {
542  // If we passed this trigger, set the return value to DEFORMATION. However, we continue the loop
543  // in the case where we passed multiple triggers.
544  if (a_currentTime>=mp_listPMotionTriggers[mt])
545  {
546  mp_currentPMotionIndex[a_th] = mt+1;
547  #ifdef CASTOR_VERBOSE
548  if (m_verbose >=4) Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", gate for patient motion correction switched to " << mp_currentPMotionIndex[a_th] << endl
549  << " at event #" << a_currentEventIndex << ", timestamp = " << a_currentTime << endl);
550  #endif
551  return_value = DYNAMIC_SWITCH_DEFORMATION;
552  }
553  }
554  }
555 
556  // -------------------------------------------------------------------------------------------------
557  // Step 2: frame management
558  // -------------------------------------------------------------------------------------------------
559 
560  // Special case if the frame number is still -1
561  if (mp_currentFrameIndex[a_th]==-1)
562  {
563  // We check if we are before the first frame, in this case we directly return a CONTINUE signal to go to the next event
564  if (a_currentTime<mp_ID->GetFrameTimeStartInMs(a_bed,0)) return DYNAMIC_SWITCH_CONTINUE;
565  // Otherwise, we now are at least in the first frame (which will be updated right after this if section)
566  else mp_currentFrameIndex[a_th] = 0;
567  }
568 
569  // A boolean to know later if the frame index has changed
570  bool frame_has_changed = false;
571 
572  // Now we search for the first frame index in which the event belongs, starting from the current frame index. Note that we do that
573  // this way (not simply incrementing the frame index) because we want to deal with the case where the next event managed by this
574  // thread could possibly skip multiple frames at once.
575  for (int fr=mp_currentFrameIndex[a_th]; fr<m_nbTimeFrames; fr++)
576  {
577  // If the current time is less than the time stop of the frame 'fr', then the event belongs to this frame
578  if (a_currentTime<mp_ID->GetFrameTimeStopInMs(a_bed,fr))
579  {
580  // Test if the frame has actually changed
581  if (mp_currentFrameIndex[a_th]!=fr)
582  {
583  // Set the boolean to true
584  frame_has_changed = true;
585  // Update the current frame index
586  mp_currentFrameIndex[a_th] = fr;
587  }
588  break;
589  }
590  }
591 
592  #ifdef CASTOR_VERBOSE
593  if (frame_has_changed && m_verbose >=4)
594  Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", frame switched to " << mp_currentFrameIndex[a_th] << endl);
595  #endif
596 
597  // -------------------------------------------------------------------------------------------------
598  // Step 3: respiratory gating management
599  // -------------------------------------------------------------------------------------------------
600 
601  // A boolean to know later if the respiratory index has changed
602  bool resp_gate_has_changed = false;
603 
604  // Do this only if respiratory gating is enabled
605  if (m_respGatingFlag)
606  {
607  // Test if the frame index has changed
608  if (frame_has_changed)
609  {
610  // Reset the respiratory gate index
611  mp_currentRespGateIndex[a_th] = 0;
612  // No deformation signal need to be sent, as the forward/backward image matrices for the next frame will be in the reference position as required
613  }
614  // For this frame, search the first gate (from the current gate index) for which the provided event index is below the event-index-stop
615  for (int rg=mp_currentRespGateIndex[a_th]; rg<m_nbRespGates; rg++)
616  {
617  // If the current event index is below the last event of this gate, then the event belongs to this gate
618  // (We won't enter again in the if statement due to the flag setting to true)
619  if (a_currentEventIndex<=m2p_indexLastEventRespGate[mp_currentFrameIndex[a_th]][rg] && resp_gate_has_changed == false)
620  {
621  // Test if the gate has actually changed
622  if (mp_currentRespGateIndex[a_th]!=rg)
623  {
624  // Update the current gate index
625  mp_currentRespGateIndex[a_th] = rg;
626  // Verbose
627  #ifdef CASTOR_VERBOSE
628  if (m_verbose>=4) Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", respiratory gate switch to " << mp_currentRespGateIndex[a_th]
629  << " on event " << a_currentEventIndex << endl
630  << "current frame : " << mp_currentFrameIndex[a_th] << endl
631  << "current respiratory gate index " << mp_currentRespGateIndex[a_th] << endl);
632  #endif
633  // If motion correction is enabled, then we should return a DEFORMATION signal
634  if (m_rMotionCorrFlag) return_value = DYNAMIC_SWITCH_DEFORMATION;
635  }
636  // Set the boolean to true
637  resp_gate_has_changed = true;
638  }
639  }
640  }
641 
642  // -------------------------------------------------------------------------------------------------
643  // Step 4: cardiac gating management
644  // -------------------------------------------------------------------------------------------------
645 
646  // A boolean to know later if the respiratory index has changed
647  bool card_gate_has_changed = false;
648 
649  // Do this only if cardiac gating is enabled
650  if (m_cardGatingFlag)
651  {
652  // Test if the frame or respiratory index have changed
653  if (frame_has_changed || resp_gate_has_changed)
654  {
655  // Reset the cardiac gate index
656  mp_currentCardGateIndex[a_th] = 0;
657  // Thus if one apply cardiac motion correction, we should return a DEFORMATION signal
658  if (m_cMotionCorrFlag) return_value = DYNAMIC_SWITCH_DEFORMATION;
659  }
660  // For this frame and respiratory gate, search the first gate (from the current gate index) for which the provided event index is below the event-index-stop
661  for (int cg=mp_currentCardGateIndex[a_th]; cg<m_nbCardGates; cg++)
662  {
663  // If the current event index is below the event-index-stop of this gate, then the event belongs to this gate
664  if (a_currentEventIndex<m2p_indexLastEventCardGate[mp_currentFrameIndex[a_th]][mp_currentRespGateIndex[a_th]*m_nbCardGates+cg] && card_gate_has_changed == false)
665  {
666  // Test if the gate has actually changed
667  if (mp_currentCardGateIndex[a_th]!=cg)
668  {
669  // Update the current gate index
670  mp_currentCardGateIndex[a_th] = cg;
671  // Verbose
672  #ifdef CASTOR_VERBOSE
673  if (m_verbose>=4) Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", cardiac gate switch to " << mp_currentCardGateIndex[a_th]
674  << " on event " << a_currentEventIndex << endl
675  << "current frame : " << mp_currentFrameIndex[a_th] << endl);
676  #endif
677  // If motion correction is enabled, then we should return a DEFORMATION signal
678  if (m_cMotionCorrFlag) return_value = DYNAMIC_SWITCH_DEFORMATION;
679  }
680  // Set the boolean to true
681  card_gate_has_changed = true;
682  }
683  }
684  }
685 
686  // -------------------------------------------------------------------------------------------------
687  // Step 5: just return the value !
688  // -------------------------------------------------------------------------------------------------
689 
690  // Return the status of the dynamic switch
691  return return_value;
692 }
int InitDynamicDataGating(const string &a_pathToGateFile)
Initialisation of arrays containing informations about the data splitting and respiratory/cardiac gat...
int CheckParameters(int64_t a_nbEvents)
Check all mandatory parameters.
Declaration of class oImageDimensionsAndQuantification.
#define FLTNB
Definition: gVariables.hh:55
#define DYNAMIC_SWITCH_NOTHING
#define DYNAMIC_SWITCH_CONTINUE
void ResetCurrentDynamicIndices()
Reset to 0 the multithreaded dynamic arrays gathering the indices of current frame, gates and involuntary motion.
int DynamicSwitch(int64_t a_index, uint32_t a_time, int a_bed, int a_th)
This function is called in the reconstruction event loop. It is used to check if the current event be...
#define Cerr(MESSAGE)
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
Definition: gOptions.cc:111
Declaration of class oDynamicDataManager.
~oDynamicDataManager()
oDynamicDataManager destructor.
oDynamicDataManager()
oDynamicDataManager constructor. Initialize the member variables to their default values...
oImageDimensionsAndQuantification * mp_ID
int SetDynamicSpecificQuantificationFactors(FLTNB **a2p_quantificationFactors)
Compute gate-specific quantificative factors using the number of events within each gate...
int64_t ** m2p_indexLastEventCardGate
#define KEYWORD_MANDATORY
Definition: gOptions.hh:25
int InitDynamicDataPatientMotion(const string &a_pathToFile)
Initialisation of involuntary patient motion correction information, if any.
int GetNbTimeFrames()
Get the number of time frames.
int64_t ** m2p_indexLastEventRespGate
int GetNbThreadsForProjection()
Get the number of threads used for projections.
int InitDynamicData(int a_nbRespGates, int a_nbCardGates, const string &a_pathTo4DDataSplittingFile, int a_rmMCorrFlag, int a_cMmCorrFlag, int a_dmCorrFlag, int a_pMotionCorrFlag)
Main function for instanciation and initialization of the member variables and arrays. Call the specific initialization function depending of the type of dataset.
#define Cout(MESSAGE)
#define DYNAMIC_SWITCH_DEFORMATION