CASToR  2.0
Tomographic Reconstruction (PET/SPECT/CT)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
oDynamicDataManager.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2018 all CASToR contributors listed below:
18 
19  --> current contributors: Thibaut MERLIN, Simon STUTE, Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Mael MILLARDET
20  --> past contributors: Valentin VIELZEUF
21 
22 This is CASToR version 2.0.
23 */
24 
32 #include "oDynamicDataManager.hh"
34 
35 // =====================================================================
36 // ---------------------------------------------------------------------
37 // ---------------------------------------------------------------------
38 // =====================================================================
39 
41 {
42  mp_ID = NULL;
43  m_verbose = -1;
44  m_nbTimeFrames = -1;
45  mp_currentFrameIndex = NULL;
46  m_respGatingFlag = false;
47  m_rMotionCorrFlag = false;
48  m_nbRespGates = -1;
52  m_cardGatingFlag = false;
53  m_cMotionCorrFlag = false;
54  m_nbCardGates = -1;
58  m_pMotionCorrFlag = false;
64 }
65 
66 // =====================================================================
67 // ---------------------------------------------------------------------
68 // ---------------------------------------------------------------------
69 // =====================================================================
70 
72 {
73  for (int fr=0; fr<m_nbTimeFrames; fr++)
74  {
76  delete m2p_nbEventsPerRespGate[fr];
78  delete m2p_nbEventsPerCardGate[fr];
80  delete m2p_indexLastEventRespGate[fr];
82  delete m2p_indexLastEventCardGate[fr];
83  }
84 
96 }
97 
98 // =====================================================================
99 // ---------------------------------------------------------------------
100 // ---------------------------------------------------------------------
101 // =====================================================================
102 
103 int oDynamicDataManager::InitDynamicData( int a_nbRespGates, int a_nbCardGates, const string& a_pathTo4DDataFile,
104  int a_rmCorrFlag, int a_cmCorrFlag, int a_dmCorrFlag, int a_pmCorrFlag)
105 {
106  // Verbose
108  if (m_verbose>=VERBOSE_DETAIL) Cout("oDynamicDataManager::InitDynamicData() -> Initialize dynamic data management" << endl);
109 
110  // Initialization
112  m_nbRespGates = a_nbRespGates;
113  m_nbCardGates = a_nbCardGates;
115 
116  if (m_nbRespGates > 1) m_respGatingFlag = true;
117  if (m_nbCardGates > 1) m_cardGatingFlag = true;
118  m_rMotionCorrFlag = a_rmCorrFlag;
119  m_cMotionCorrFlag = a_cmCorrFlag;
120  m_pMotionCorrFlag = a_pmCorrFlag;
121  if(a_dmCorrFlag)
122  {
123  m_rMotionCorrFlag = true;
124  m_cMotionCorrFlag = true;
125  }
126 
127  // Instanciation & initialization for current indices (used during the loop on events to know which frame/gate the event belongs to)
132  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
133  {
134  mp_currentFrameIndex [0] = 0;
135  mp_currentRespGateIndex [0] = 0;
136  mp_currentCardGateIndex [0] = 0;
137  mp_currentPMotionIndex[0] = 0;
138  }
139 
140  // Instanciation & initialization of number of events per frame for gated reconstructions
141  m2p_nbEventsPerRespGate = new int64_t*[m_nbTimeFrames];
142  m2p_nbEventsPerCardGate = new int64_t*[m_nbTimeFrames];
145 
146  for (int fr=0; fr<m_nbTimeFrames; fr++)
147  {
148  m2p_nbEventsPerRespGate[fr] = new int64_t[m_nbRespGates];
150  m2p_indexLastEventRespGate[fr] = new int64_t[m_nbRespGates];
152 
153  for (int rg=0; rg<m_nbRespGates; rg++)
154  {
155  m2p_nbEventsPerRespGate[fr][rg] = -1;
156  m2p_indexLastEventRespGate[fr][rg] = -1;
157 
158  for (int cg=0; cg<m_nbCardGates; cg++)
159  {
160  m2p_nbEventsPerCardGate[fr][rg*m_nbCardGates+cg] = -1;
161  m2p_indexLastEventCardGate[fr][rg*m_nbCardGates+cg] = -1;
162  }
163  }
164  }
165 
166  // Check coherence of motion initialization
167  // Throw error if cardiac gating is enabled alongside respiratory motion correction (not available)
168  // TODO : warning in the documentation that Respiratory motion correction is not supported for cardiac-gated data in the current implementation
170  {
171  Cerr("***** oDynamicDataManager::InitDynamicData() -> Respiratory motion correction is enabled for cardiac-gated data. This is not supported in the current implementation !" << endl);
172  return 1;
173  }
174 
175  // Check iPat motion vs physiological motion
176  if (a_pmCorrFlag && (m_rMotionCorrFlag || m_cMotionCorrFlag) )
177  {
178  Cerr("***** oDynamicDataManager::InitDynamicData() -> Involuntary patient image-based motion correction cannot be used along with respiratory or cardiac motion correction !" << endl);
179  return 1;
180  }
181 
182  // Incorrect motion initialization
183  if ( (a_rmCorrFlag + a_cmCorrFlag + a_dmCorrFlag) >1 )
184  {
185  Cerr("***** oDynamicDataManager::InitDynamicData() -> Incorrect image-based motion initialization."<<endl);
186  Cerr("***** Please use -rm for respiratory motion, -cm for cardiac motion, or -dm for double respiratory and cardiac motion correction !" << endl);
187  return 1;
188  }
189 
190  // Initialization for respiratory/cardiac gated reconstruction
191  // Note : for Analytic Projection, gating flag could be enabled in order to project an image containing several gates, but no description file is required
192  // InitDynamicDataGating() is restricted to reconstruction
193  // Check on the existence of a_pathTo4DDataFile during reconstruction is performed onnly in Grecon
194  if ((m_respGatingFlag || m_cardGatingFlag) && !a_pathTo4DDataFile.empty() )
195  {
196  if(m_verbose>=VERBOSE_DETAIL) Cout("oDynamicDataManager::InitDynamicData() -> Initializing data for gated reconstruction... " << endl);
197 
198  if (InitDynamicDataGating(a_pathTo4DDataFile))
199  {
200  Cerr("***** oDynamicDataManager::InitDynamicData() -> A problem occured during the dynamic gating initialization !" << endl);
201  return 1;
202  }
203  }
204 
205  // Initialization with involuntary patient motion corrected reconstruction
206  if (m_pMotionCorrFlag && InitDynamicDataPatientMotion(a_pathTo4DDataFile) )
207  {
208  Cerr("***** oDynamicDataManager::InitDynamicData() -> A problem occured during the patient involuntary motion correction initialization !" << endl);
209  return 1;
210  }
211 
212  // Some feedback
214  {
215  if (m_respGatingFlag) Cout("oDynamicDataManager::InitDynamicData() -> Respiratory gating enabled" << endl);
216  if (m_rMotionCorrFlag) Cout("oDynamicDataManager::InitDynamicData() -> Respiratory image-based motion correction enabled" << endl);
217  if (m_cardGatingFlag) Cout("oDynamicDataManager::InitDynamicData() -> Cardiac gating enabled" << endl);
218  if (m_cMotionCorrFlag) Cout("oDynamicDataManager::InitDynamicData() -> Cardiac image-based motion correction enabled" << endl);
219  if (m_pMotionCorrFlag) Cout("oDynamicDataManager::InitDynamicData() -> Involuntary image-based patient motion correction enabled" << endl);
220  }
221 
222  // End
223  return 0;
224 }
225 
226 // =====================================================================
227 // ---------------------------------------------------------------------
228 // ---------------------------------------------------------------------
229 // =====================================================================
230 
231 int oDynamicDataManager::InitDynamicDataGating(const string& a_pathToFile)
232 {
233  // Verbose
235  if (m_verbose>=VERBOSE_DETAIL) Cout("oDynamicDataManager::InitDynamicDataGating() ... " << endl);
236 
237  // Respiratory gating enabled
238  if (m_respGatingFlag)
239  {
240  // Read information about respiratory gating (number of events per respiratory gate per frame)
241  if ( ReadDataASCIIFile(a_pathToFile,
242  "respiratory_gates_data_splitting",
244  m_nbRespGates,
247  {
248  Cerr("***** oDynamicDataManager::InitDynamicDataGating() -> Error while trying to read 'respiratory_gates_data_splitting' in file '" << a_pathToFile << "' !" << endl);
249  return 1;
250  }
251 
252  // Get the index of the last event of each respiratory gate using the number of events inside each gate
253  uint64_t event_number_sum = 0;
254  if (m_verbose>=VERBOSE_DETAIL) Cout("oDynamicDataManager::InitDynamicDataGating() :" << endl);
255 
256  for (int fr=0; fr<m_nbTimeFrames; fr++)
257  for (int rg=0; rg<m_nbRespGates; rg++)
258  {
259  m2p_indexLastEventRespGate[fr][rg] += m2p_nbEventsPerRespGate[fr][rg] + event_number_sum;
260  event_number_sum += m2p_nbEventsPerRespGate[fr][rg];
261  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Number of events for frame #" << fr << ", respiratory gate #" << rg << " = " << m2p_nbEventsPerRespGate[fr][rg] << endl);
262  }
263  }
264 
265  // Cardiac gating enabled
266  if (m_cardGatingFlag)
267  {
268  // Read information about cardiac gating (number of events per cardiac gate per respiratory gates times frames)
269  if ( ReadDataASCIIFile(a_pathToFile,
270  "cardiac_gates_data_splitting",
272  m_nbCardGates,
275  {
276  Cerr("***** oDynamicDataManager::InitDynamicDataGating() -> Error while trying to read 'cardiac_gates_data_splitting' in file '" << a_pathToFile << "' !" << endl);
277  return 1;
278  }
279  // Get the index of the last event of each cardiac gate using the number of events inside each gate
280  uint64_t event_number_sum = 0;
281  if (m_verbose>=VERBOSE_DETAIL) Cout("oDynamicDataManager::InitDynamicDataGating() :" << endl);
282 
283  for (int fr=0; fr<m_nbTimeFrames; fr++)
284  for (int g=0; g<m_nbRespGates*m_nbCardGates; g++)
285  {
286  m2p_indexLastEventCardGate[fr][g] += m2p_nbEventsPerCardGate[fr][g] + event_number_sum;
287  event_number_sum += m2p_nbEventsPerCardGate[fr][g];
288  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Number of events for frame #" << fr << ", cardiac gate #" << g << " = " << m2p_nbEventsPerCardGate[fr][g] << endl);
289  }
290  }
291 
292  // End
293  return 0;
294 }
295 
296 // =====================================================================
297 // ---------------------------------------------------------------------
298 // ---------------------------------------------------------------------
299 // =====================================================================
300 
302 {
303  // Verbose
305  if (m_verbose>=VERBOSE_DETAIL) Cout("oDynamicDataManager::InitDynamicDataPatientMotion() ... " << endl);
306 
307  // First get the number of time triggers into the dynamic file
308  if (ReadDataASCIIFile(a_pathToFile,
309  "nb_involuntary_motion_triggers",
311  1,
313  {
314  Cerr("***** oDynamicDataManager::InitDynamicDataPatientMotion() -> Involuntary motion correction enabled but"
315  << " number of triggers could not be found in the dynamic file '" << a_pathToFile << "' !" << endl);
316  return 1;
317  }
318  // Allocate time triggers and read them from the dynamic file
319  mp_listPMotionTriggers = (uint32_t*)malloc(m_nbPMotionTriggers*sizeof(uint32_t));
320  mp_frameNbPMotionTriggers = (uint16_t*)malloc(m_nbTimeFrames*sizeof(uint16_t));
321  mp_framePMotionFirstIndex = (uint16_t*)malloc(m_nbTimeFrames*sizeof(uint16_t));
322  for(int fr=0 ; fr<m_nbTimeFrames ; fr++)
323  {
326  }
327 
328  // Get timestamp of motion trigger
329  if (ReadDataASCIIFile(a_pathToFile,
330  "list_involuntary_motion_triggers",
334  {
335  Cerr("***** oDynamicDataManager::InitDynamicDataPatientMotion() -> Involuntary motion correction enabled but list of triggers"
336  << " not found in the dynamic file '" << a_pathToFile << "' !" << endl);
337  return 1;
338  }
339 
340  // Recover the number of transformation required for each frame
341  uint16_t fr=0;
342 
343  for(int t=0 ; t<m_nbPMotionTriggers ; t++)
344  {
346  {
347  fr++;
350  }
351 
353 
354  // TODO check this (motion trigger after last frameTimeStop)
355  if(fr >= m_nbTimeFrames)
356  break;
357  }
358 
359  if(fr<m_nbTimeFrames)
360  for(int f=fr ; f<m_nbTimeFrames ; f++)
362 
363  // End
364  return 0;
365 }
366 
367 // =====================================================================
368 // ---------------------------------------------------------------------
369 // ---------------------------------------------------------------------
370 // =====================================================================
371 
373 {
375  if (m_verbose>=VERBOSE_NORMAL) Cout("oDynamicDataManager::SetDynamicSpecificQuantificationFactors() ... " << endl);
376 
377  // COMPUTE GATE-SPECIFIC QUANTITATIVE FACTORS
378  if (m_nbRespGates>1 || m_nbCardGates>1)
379  {
380  for(int fr=0 ; fr<m_nbTimeFrames ; fr++)
381  {
382  // We have cardiac gates and don't correct for motion -> quantification factors required for cardiac gates
384  {
385  uint64_t total_events_in_frame = 0;
386  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++) total_events_in_frame += m2p_nbEventsPerCardGate[fr][g];
387 
388  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Cardiac gating correction factors :" << endl);
389  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
390  {
391  a2p_quantificationFactors[fr][g] *= (FLTNB)total_events_in_frame/(FLTNB)m2p_nbEventsPerCardGate[fr][g];
392  if (m_verbose>=VERBOSE_DETAIL) Cout(" Frame #" << fr << ", cardiac gate #" << g << " = " << a2p_quantificationFactors[fr][g] << endl);
393  }
394  }
395 
396  // We have resp gates and don't correct for resp motion (and no independent cardiac gate reconstruction) -> quantification factors required for cardiac gates
398  {
399  uint64_t total_events_in_frame = 0;
400  for(int rg=0 ; rg<m_nbRespGates ; rg++) total_events_in_frame += m2p_nbEventsPerRespGate[fr][rg];
401 
402  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Respiratory gating correction factors :" << endl);
403  for(int g=0 ; g<m_nbRespGates*m_nbCardGates ; g++)
404  {
405  int rg = int(g/m_nbCardGates);
406  a2p_quantificationFactors[fr][g] *= (FLTNB)total_events_in_frame/(FLTNB)m2p_nbEventsPerRespGate[fr][rg];
407  if (m_verbose>=VERBOSE_DETAIL) Cout(" Frame #" << fr << ", gate #" << g << " = " << a2p_quantificationFactors[fr][g] << endl);
408  }
409  }
410  }
411  }
412 
413  // End
414  return 0;
415 }
416 
417 // =====================================================================
418 // ---------------------------------------------------------------------
419 // ---------------------------------------------------------------------
420 // =====================================================================
421 
423 {
424  // Verbose
426  if (m_verbose>=VERBOSE_DETAIL) Cout("oDynamicDataManager::CheckParameters() -> Check parameters for dynamic data settings" << endl);
427  // Check image dimensions
428  if (mp_ID==NULL)
429  {
430  Cerr("***** oDynamicDataManager::CheckParameters() -> No image dimensions provided !" << endl);
431  return 1;
432  }
433  // Check time frames
434  if (m_nbTimeFrames<0)
435  {
436  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong number of time frames !" << endl);
437  return 1;
438  }
439  // Check resp gates
440  if (m_nbRespGates<0)
441  {
442  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong number of respiratory gates !" << endl);
443  return 1;
444  }
445  // Check card gates
446  if (m_nbCardGates<0)
447  {
448  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong number of respiratory gates !" << endl);
449  return 1;
450  }
451  // Check involuntary motion triggers
452  if (m_nbPMotionTriggers<0)
453  {
454  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong number of involuntary motion subsets provided !" << endl);
455  return 1;
456  }
457  // Check verbosity
458  if (m_verbose<0)
459  {
460  Cerr("***** oDynamicDataManager::CheckParameters() -> Wrong verbosity level provided !" << endl);
461  return 1;
462  }
463 
464  // Feedback regarding the enabled/disabled options for the reconstruction
465  if (m_verbose>=2)
466  {
467  if (m_respGatingFlag) Cout("oDynamicDataManager::CheckParameters() -> Respiratory gating is enabled" << endl);
468  if (m_rMotionCorrFlag) Cout("oDynamicDataManager::CheckParameters() -> Respiratory motion correction enabled" << endl);
469  if (m_cardGatingFlag) Cout("oDynamicDataManager::CheckParameters() -> Cardiac gating is enabled" << endl);
470  if (m_cMotionCorrFlag) Cout("oDynamicDataManager::CheckParameters() -> Cardiac motion correction is enabled" << endl);
471  if (m_pMotionCorrFlag) Cout("oDynamicDataManager::CheckParameters() -> Involuntary motion correction is enabled" << endl);
472  }
473 
474  // 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)
475  if (m_respGatingFlag)
476  {
477  int last_fr = m_nbTimeFrames-1;
478  int last_rg = m_nbRespGates-1;
479  if (m2p_indexLastEventRespGate[last_fr][last_rg]+1 != a_nbEvents)
480  {
481  Cerr("***** oDynamicDataManager::CheckParameters() -> Problem while checking consistency of dynamic data !" << endl
482  << " The number of events in the datafile (" << a_nbEvents
483  << ") is different from the total number of events in respiratory gates (" << m2p_indexLastEventRespGate[last_fr][last_rg] << ") as initialized in the gating file !" << endl);
484  return 1;
485  }
486  }
487  if (m_cardGatingFlag)
488  {
489  int last_fr = m_nbTimeFrames-1;
490  int last_rg = m_nbCardGates-1;
491  if (m2p_indexLastEventCardGate[last_fr][last_rg]+1 != a_nbEvents)
492  {
493  Cerr("***** oDynamicDataManager::CheckParameters() -> Problem while checking consistency of dynamic data !" << endl
494  << " The number of events in the datafile (" << a_nbEvents
495  << ") is different to the total number of events in cardiac gates (" << m2p_indexLastEventRespGate[last_fr][last_rg] << ") as initialized in the gating file !" << endl);
496  return 1;
497  }
498  }
499  // Normal end
500  return 0;
501 }
502 
503 // =====================================================================
504 // ---------------------------------------------------------------------
505 // ---------------------------------------------------------------------
506 // =====================================================================
507 
509 {
511  // Reset indices for each thread (this is a thread-safe implementation)
512  for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++)
513  {
514  // We initialize the frame index to -1 because at the beginning of the events loop, we don't know yet
515  // if we are in the first frame (the user can reconstruct only a part of the whole datafile).
516  mp_currentFrameIndex[th] = -1;
517  mp_currentPMotionIndex[th] = 0;
518  mp_currentRespGateIndex[th] = 0;
519  mp_currentCardGateIndex[th] = 0;
520  }
521 }
522 
523 // =====================================================================
524 // ---------------------------------------------------------------------
525 // ---------------------------------------------------------------------
526 // =====================================================================
527 
528 int oDynamicDataManager::DynamicSwitch(int64_t a_currentEventIndex, uint32_t a_currentTime, int a_bed, int a_th)
529 {
531 
532  // The value that will be returned
533  int return_value = DYNAMIC_SWITCH_NOTHING;
534 
535  // -------------------------------------------------------------------------------------------------
536  // Step 1: involuntary motion management
537  // -------------------------------------------------------------------------------------------------
538 
539  // Only do this if the involuntary motion correction is enabled (meaning we must proceed to a deformation)
540  if (m_pMotionCorrFlag)
541  {
542  // Search if we passed one of the next motion triggers (starting at current index)
543  for (int mt=mp_currentPMotionIndex[a_th]; mt<m_nbPMotionTriggers; mt++)
544  {
545 
546  // If we passed this trigger, set the return value to DEFORMATION. However, we continue the loop
547  // in the case where we passed multiple triggers.
548  if (a_currentTime>=mp_listPMotionTriggers[mt])
549  {
550  mp_currentPMotionIndex[a_th] = mt+1;
551  #ifdef CASTOR_DEBUG
552  if (m_verbose>=VERBOSE_DEBUG_EVENT) Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", gate for patient motion correction switched to " << mp_currentPMotionIndex[a_th] << endl
553  << " at event #" << a_currentEventIndex << ", timestamp = " << a_currentTime << endl);
554  #endif
555  return_value = DYNAMIC_SWITCH_DEFORMATION;
556  }
557  }
558  }
559 
560  // -------------------------------------------------------------------------------------------------
561  // Step 2: frame management
562  // -------------------------------------------------------------------------------------------------
563 
564  // Special case if the frame number is still -1
565  if (mp_currentFrameIndex[a_th]==-1)
566  {
567  // We check if we are before the first frame, in this case we directly return a CONTINUE signal to go to the next event
568  if (a_currentTime<mp_ID->GetFrameTimeStartInMs(a_bed,0)) return DYNAMIC_SWITCH_CONTINUE;
569  // Otherwise, we now are at least in the first frame (which will be updated right after this 'if' section)
570  else mp_currentFrameIndex[a_th] = 0;
571  }
572 
573  // A boolean to know later if the frame index has changed
574  bool frame_has_changed = false;
575 
576 
577  // Now we search for the first frame index in which the event belongs, starting from the current frame index. Note that we do that
578  // this way (not simply incrementing the frame index) because we want to deal with the case where the next event managed by this
579  // thread could possibly skip multiple frames at once.
580  for (int fr=mp_currentFrameIndex[a_th]; fr<m_nbTimeFrames; fr++)
581  {
582  // If the current time is less than the time stop of the frame 'fr', then the event belongs to this frame
583  if (a_currentTime<mp_ID->GetFrameTimeStopInMs(a_bed,fr))
584  {
585  // Test if the frame has actually changed
586  if (mp_currentFrameIndex[a_th]!=fr)
587  {
588  // Set the boolean to true
589  frame_has_changed = true;
590  // Update the current frame index
591  mp_currentFrameIndex[a_th] = fr;
592  }
593  break;
594  }
595  }
596 
597  #ifdef CASTOR_DEBUG
598  if (frame_has_changed && m_verbose >=VERBOSE_DEBUG_EVENT)
599  Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", frame switched to " << mp_currentFrameIndex[a_th] << endl);
600  #endif
601 
602  // -------------------------------------------------------------------------------------------------
603  // Step 3: respiratory gating management
604  // -------------------------------------------------------------------------------------------------
605 
606  // A boolean to know later if the respiratory index has changed
607  bool resp_gate_has_changed = false;
608 
609  // Do this only if respiratory gating is enabled
610  if (m_respGatingFlag)
611  {
612  // Test if the frame index has changed
613  if (frame_has_changed)
614  {
615  // Reset the respiratory gate index
616  mp_currentRespGateIndex[a_th] = 0;
617  // 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
618  }
619  // For this frame, search the first gate (from the current gate index) for which the provided event index is below the event-index-stop
620  for (int rg=mp_currentRespGateIndex[a_th]; rg<m_nbRespGates; rg++)
621  {
622  // If the current event index is below the last event of this gate, then the event belongs to this gate
623  // (We won't enter again in the if statement due to the flag setting to true)
624  if (a_currentEventIndex<=m2p_indexLastEventRespGate[mp_currentFrameIndex[a_th]][rg] && resp_gate_has_changed == false)
625  {
626  // Test if the gate has actually changed
627  if (mp_currentRespGateIndex[a_th]!=rg)
628  {
629  // Update the current gate index
630  mp_currentRespGateIndex[a_th] = rg;
631  // Verbose
632  #ifdef CASTOR_DEBUG
633  if (m_verbose>=VERBOSE_DEBUG_EVENT) Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", respiratory gate switch to " << mp_currentRespGateIndex[a_th]
634  << " on event " << a_currentEventIndex << endl
635  << "current frame : " << mp_currentFrameIndex[a_th] << endl
636  << "current respiratory gate index " << mp_currentRespGateIndex[a_th] << endl);
637  #endif
638  // If motion correction is enabled, then we should return a DEFORMATION signal
639  if (m_rMotionCorrFlag) return_value = DYNAMIC_SWITCH_DEFORMATION;
640  }
641  // Set the boolean to true
642  resp_gate_has_changed = true;
643  }
644  }
645  }
646 
647  // -------------------------------------------------------------------------------------------------
648  // Step 4: cardiac gating management
649  // -------------------------------------------------------------------------------------------------
650 
651  // A boolean to know later if the respiratory index has changed
652  bool card_gate_has_changed = false;
653 
654  // Do this only if cardiac gating is enabled
655  if (m_cardGatingFlag)
656  {
657  // Test if the frame or respiratory index have changed
658  if (frame_has_changed || resp_gate_has_changed)
659  {
660  // Reset the cardiac gate index
661  mp_currentCardGateIndex[a_th] = 0;
662  // Thus if one apply cardiac motion correction, we should return a DEFORMATION signal
663  if (m_cMotionCorrFlag) return_value = DYNAMIC_SWITCH_DEFORMATION;
664  }
665  // 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
666  for (int cg=mp_currentCardGateIndex[a_th]; cg<m_nbCardGates; cg++)
667  {
668  // If the current event index is below the event-index-stop of this gate, then the event belongs to this gate
669  if (a_currentEventIndex<m2p_indexLastEventCardGate[mp_currentFrameIndex[a_th]][mp_currentRespGateIndex[a_th]*m_nbCardGates+cg] && card_gate_has_changed == false)
670  {
671  // Test if the gate has actually changed
672  if (mp_currentCardGateIndex[a_th]!=cg)
673  {
674  // Update the current gate index
675  mp_currentCardGateIndex[a_th] = cg;
676  // Verbose
677  #ifdef CASTOR_DEBUG
678  if (m_verbose>=VERBOSE_DEBUG_EVENT) Cout("oDynamicDataManager::DynamicSwitch() -> Thread " << a_th << ", cardiac gate switch to " << mp_currentCardGateIndex[a_th]
679  << " on event " << a_currentEventIndex << endl
680  << "current frame : " << mp_currentFrameIndex[a_th] << endl);
681  #endif
682  // If motion correction is enabled, then we should return a DEFORMATION signal
683  if (m_cMotionCorrFlag) return_value = DYNAMIC_SWITCH_DEFORMATION;
684  }
685  // Set the boolean to true
686  card_gate_has_changed = true;
687  }
688  }
689  }
690 
691  // -------------------------------------------------------------------------------------------------
692  // Step 5: just return the value !
693  // -------------------------------------------------------------------------------------------------
694 
695  // Return the status of the dynamic switch
696  return return_value;
697 }
698 
699 // =====================================================================
700 // ---------------------------------------------------------------------
701 // ---------------------------------------------------------------------
702 // =====================================================================
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 VERBOSE_DEBUG_EVENT
#define FLTNB
Definition: gVariables.hh:81
#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.
#define VERBOSE_DETAIL
uint16_t * mp_framePMotionFirstIndex
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)
#define VERBOSE_DEBUG_LIGHT
uint16_t * mp_frameNbPMotionTriggers
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:123
Declaration of class oDynamicDataManager.
~oDynamicDataManager()
oDynamicDataManager destructor.
oDynamicDataManager()
oDynamicDataManager constructor. Initialize the member variables to their default values...
#define VERBOSE_NORMAL
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:48
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.
#define DEBUG_VERBOSE(IGNORED1, IGNORED2)
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
uint32_t GetFrameTimeStopInMs(int a_bed, int a_frame)
Get the frame time stop for the given bed, in milliseconds as a uint32_t.