CASToR  3.1
Tomographic Reconstruction (PET/SPECT/CT)
oDynamicModelManager.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-2020 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF, Zacharias CHALAMPALAKIS
20 
21 This is CASToR version 3.1.
22 */
23 
30 #include "oDynamicModelManager.hh"
31 #include "sAddonManager.hh"
32 
33 // =====================================================================
34 // ---------------------------------------------------------------------
35 // ---------------------------------------------------------------------
36 // =====================================================================
37 /*
38  \fn oDynamicModelManager
39  \brief Constructor of oDynamicModelManager. Simply set all data members to default values.
40 */
42 {
43  // Image dimensions
44  mp_ID = NULL;
45  // Options for each model type
46  m_options = "";
47 
48  // Model object and associated bool
49  mp_DynamicModel = NULL;
50  m_useModel = false;
52 
53  // Diagonal basis functions
57 
61 
62  // Verbosity
63  m_verbose = -1;
64  m_checked = false;
65  m_initialized = false;
66 }
67 
68 // =====================================================================
69 // ---------------------------------------------------------------------
70 // ---------------------------------------------------------------------
71 // =====================================================================
72 /*
73  \fn ~oDynamicModelManager
74  \brief Destructor of oDynamicModelManager. Free memory from all allocated tabs.
75 */
77 {
78  // Delete model objects
79  if (mp_DynamicModel) delete mp_DynamicModel;
80 }
81 
82 // =====================================================================
83 // ---------------------------------------------------------------------
84 // ---------------------------------------------------------------------
85 // =====================================================================
86 /*
87  \fn CheckParameters
88  \brief This function is used to check parameters after the latter
89  have been all set using Set functions.
90  \return 0 if success, positive value otherwise.
91 */
93 #ifdef CASTOR_VERBOSE
94  if (m_verbose>=2) Cout("oDynamicModelManager::CheckParameters() ..."<< endl);
95 #endif
96 
97  // Check image dimensions
98  if (mp_ID == NULL) {
99  Cerr("***** oDynamicModelManager::CheckParameters() -> No image dimensions provided !" << endl);
100  return 1;
101  }
102  // Check if any options have been provided
103  //if (m_options =="")
104  //{
105  // Cerr("***** oDynamicModelManager::CheckParameters() -> No options provided !" << endl);
106  //return 1;
107  // }
108  // Check verbosity
109  if (m_verbose<0)
110  {
111  Cerr("***** oDynamicModelManager::CheckParameters() -> Wrong verbosity level provided !" << endl);
112  return 1;
113  }
114 
115  // Normal end
116  m_checked = true;
117  return 0;
118 }
119 
120 // =====================================================================
121 // ---------------------------------------------------------------------
122 // ---------------------------------------------------------------------
123 // =====================================================================
124 /*
125  \fn Initialize
126  \brief Set the dynamic model flag and instanciate/initialize model objects
127  through the ParseOptionsAndInitializeDeformations() private function.
128  \return 0 if success, positive value otherwise.
129 */
131 {
132  // Forbid initialization without check
133  if (!m_checked)
134  {
135  Cerr("***** oDynamicModelManager::Initialize() -> Must call CheckParameters() before Initialize() !" << endl);
136  return 1;
137  }
138  // Check options
139  if (m_options=="")
140  {
141  // If no options have been provided then no dynamic model will be used
142  m_initialized = true;
143  m_useModel = false;
144  // Set default diagonal Basis Functions.
146  mp_ID->SetTimeStaticFlag(true);
149  // Exit the dynamic manager after that
150  return 2;
151  }
152 
153  // Else we have some model options
154  m_useModel = true;
155 
156  // Verbose
157  if (m_verbose>=1) Cout("oDynamicModelManager::Initialize() -> Initialize models" << endl);
158 
159  // Parse model options and initialize them
161  {
162  Cerr("***** oDynamicModelManager::Initialize() -> A problem occurred while parsing model options and initializing them !" << endl);
163  return 1;
164  }
165 
166  // Normal end
167  m_initialized = true;
168  return 0;
169 
170 }
171 
172 // =====================================================================
173 // ---------------------------------------------------------------------
174 // ---------------------------------------------------------------------
175 // =====================================================================
176 /*
177  \fn ParseOptionsAndInitializeProjectors
178  \brief Parse dynamic model options contained in the previously provided
179  strings. This function is called inside the Initialize() function.
180  \details Manage the options reading and initialize specific vDynamicModel
181  Options are a string containing first the name of the model,
182  then either a ':' and a configuration file specific to the model
183  - or - as many ',' as needed parameters for this model.
184  Specific pure virtual functions of the vDynamicModel are used to read parameters and initialize them.
185  \return 0 if success, positive value otherwise
186 */
188 {
189  #ifdef CASTOR_VERBOSE
190  if (m_verbose>=3) Cout("oDynamicModelManager::ParseOptionsAndInitializeModel ..."<< endl);
191  #endif
192 
193  string dynamic_model = "";
194  string list_options = "";
195  string file_options = "";
196 
197  // This is for the automatic initialization of the models
198  typedef vDynamicModel *(*maker_dynamic_model) ();
199 
200  // Get model's list from addon manager
201  std::map <string,maker_dynamic_model> list = sAddonManager::GetInstance()->mp_listOfDynamicModels;
202 
203  size_t colon, comma;
204 
205  // ---------------------------------------------------------------------------------------------------
206  // Manage model for respiratory motion
207  // ---------------------------------------------------------------------------------------------------
208 
209  // First, check if we have dynamic data
210  if (mp_ID->GetNbTimeFrames() <= 1 &&
211  mp_ID->GetNbRespGates() <= 1 &&
212  mp_ID->GetNbCardGates() <= 1)
213  {
214  Cerr("***** oDynamicModelManager::CheckParameters() -> Dynamic model should be used with more than one time frame/dynamic gate !" << endl);
215  return 1;
216  }
217 
218  // ______________________________________________________________________________
219  // Get the model name in the options and isolate the real model's options
220 
221  // Search for a colon ":", this indicates that a configuration file is provided after the model name
222  colon = m_options.find_first_of(":");
223  comma = m_options.find_first_of(",");
224 
225  // Case 1: we have a colon
226  if (colon!=string::npos)
227  {
228  // Get the model name before the colon
229  dynamic_model = m_options.substr(0,colon);
230  // Get the configuration file after the colon
231  file_options = m_options.substr(colon+1);
232  // List of options is empty
233  list_options = "";
234  }
235  // Case 2: we have a comma
236  else if (comma!=string::npos)
237  {
238  // Get the model name before the first comma
239  dynamic_model = m_options.substr(0,comma);
240  // Get the list of options after the first comma
241  list_options = m_options.substr(comma+1);
242  // Configuration file is empty
243  file_options = "";
244  }
245  // Case 3: no colon and no comma (a single model name)
246  else
247  {
248  // Get the model name
249  dynamic_model = m_options;
250  // Configuration file is empty
251  file_options = "";
252  // List of options is empty
253  list_options = "";
254  }
255 
256  // Create the model
257  if (list[dynamic_model]) mp_DynamicModel = list[dynamic_model]();
258  else
259  {
260  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> Model '" << dynamic_model << "' does not exist !" << endl);
262  return 1;
263  }
266  // Provide configuration file if any
267  if (file_options!="" && mp_DynamicModel->ReadAndCheckConfigurationFile(file_options))
268  {
269  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while reading and checking frame dynamic model's configuration file !" << endl);
270  return 1;
271  }
272  // Provide options if any
273  if (list_options!="" && mp_DynamicModel->ReadAndCheckOptionsList(list_options))
274  {
275  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while parsing and reading frame dynamic model's options !" << endl);
276  return 1;
277  }
278  // Provide flag to know if the model has been called for use in reconstruction or post-reconstruction
280  // Check parameters
282  {
283  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while checking frame dynamic model parameters !" << endl);
284  return 1;
285  }
286  // Initialize the model
288  {
289  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while initializing frame dynamic model !" << endl);
290  return 1;
291  }
292  // Check if model specific BasisFunctions are required for the ImageDimensionsAndQuantification object (normaly the case for non-nested recons )
293  // if there is no requirement for model specific basis functions -> Set Diagonal Basis Functions (normaly the case for nested recons )
295  {
296  if(m_verbose>=2) Cout("oDynamicModelManager:: Setting Diagonal basis functions for the tomographic update "<< endl);
300 
301  }
302  // Case where specific basis functions are required
303  else
304  {
305  if(m_verbose>=2) Cout("oDynamicModelManager:: Seting model specific Time basis functions "<< endl);
308 
309  // At the moment all dynamic models relate to kinetic modeling, so seting other basis functions to diagonal
312 
313  // TODO: Validate this
314  // When the basis functions have been set in oImageDimensionsAndQuantification there is no further need for the dynamic model
315  m_useModel=false;
316  }
317 
318  // Normal end
319  return 0;
320 }
321 
322 // =====================================================================
323 // ---------------------------------------------------------------------
324 // ---------------------------------------------------------------------
325 // =====================================================================
326 /*
327  \fn ApplyDynamicModel
328  \param ap_ImageS : pointer to the ImageSpace
329  \param a_iteration : index of the actual iteration
330  \param a_subset : index of the actual subset
331  \brief Call successively EstimateModelParameters() ans EstimateImageWithModel()
332  functions of the dynamic model object if 'm_useModel' is on.
333  \return 0 if success, positive value otherwise
334 */
335 int oDynamicModelManager::ApplyDynamicModel(oImageSpace* ap_ImageS, int a_iteration, int a_subset)
336 {
337  #ifdef CASTOR_DEBUG
338  if (!m_initialized)
339  {
340  Cerr("***** oDynamicModelManager::ApplyDynamicModel() -> Called while not initialized !" << endl);
341  Exit(EXIT_DEBUG);
342  }
343  #endif
344 
345  if (m_useModel)
346  {
347  // Verbose
348  if(m_verbose>=2) Cout("oDynamicModelManager::ApplyDynamicModel ..."<< endl);
349 
350  // Estimate model parameters
351  if( mp_DynamicModel->EstimateModel(ap_ImageS, a_iteration, a_subset) )
352  {
353  Cerr("***** oDynamicModelManager::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying dynamic model to current estimate images !" << endl);
354  return 1;
355  }
356 
357  // Generate the serie of dynamic images using the model parameters
358  if( mp_DynamicModel->EstimateImage(ap_ImageS, a_iteration, a_subset) )
359  {
360  Cerr("***** oDynamicModelManager::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying dynamic model to current estimate images !" << endl);
361  return 1;
362  }
363  }
364 
365  return 0;
366 }
367 
368 // =====================================================================
369 // ---------------------------------------------------------------------
370 // ---------------------------------------------------------------------
371 // =====================================================================
372 /*
373  \fn SaveParametricImages
374  \param a_iteration : current iteration index
375  \param a_subset : current number of subsets (or -1 by default)
376  \brief Call SaveCoeffImages() function of the dynamic model object is
377  'm_useModel' is on, in order to save any parameter image
378  \return 0 if success, positive value otherwise
379 */
380 int oDynamicModelManager::SaveParametricImages(int a_iteration, int a_subset)
381 {
382  if (m_useModel)
383  {
384  // Verbose
385  if(m_verbose>=2) Cout("oDynamicModelManager::SaveParametricImages ..."<< endl);
386 
387  // Write output parametric images if required
389 
390  // Apply Masking if required
392  {
393  Cerr("***** oDynamicModelManager::SaveParametricImages() -> A problem occurred while trying to apply FOV masking on parametric images !" << endl);
394  return 1;
395  }
396 
397  // Save images
398  if (mp_DynamicModel->SaveParametricImages(a_iteration, a_subset))
399  {
400  Cerr("***** oDynamicModelManager::SaveParametricImages() -> A problem occurred while trying to save image coefficients !" << endl);
401  return 1;
402  }
403  }
404  return 0;
405 }
406 
408 {
409  // This function is used to set the BasisFunctions in ImageDimensionsAndQuantification to diagonal values
410  // Set the number of time basis functions to the number of frames
412  // Allocate the conversion matrix from basis functions to frames
413  m2p_timeBasisFunctions = (FLTNB **) malloc(m_nbTimeBasisFunctions * sizeof(FLTNB *));
414  for (int tbf = 0; tbf < m_nbTimeBasisFunctions; tbf++) {
415  m2p_timeBasisFunctions[tbf] = (FLTNB *) malloc(mp_ID->GetNbTimeFrames() * sizeof(FLTNB));
416  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++) {
417  // Set diagonal to 1, the rest to 0
418  if (tbf == fr) m2p_timeBasisFunctions[tbf][fr] = 1.;
419  else m2p_timeBasisFunctions[tbf][fr] = 0.;
420  }
421  }
422  // In the diagonal case the number of time basis functions is always equal to the number of frames
423  mp_ID->SetNbTimeBasisFunctions(m_nbTimeBasisFunctions);
425  // Exit
426  return 0;
427 }
428 
430 {
431 
432  // This function is used to set the BasisFunctions in ImageDimensionsAndQuantification to diagonal values
433  // Set the number of respiratory basis functions to the number of frames
435  // Allocate the conversion matrix from basis functions to frames
436  m2p_respBasisFunctions = (FLTNB **) malloc(m_nbRespBasisFunctions * sizeof(FLTNB *));
437  for (int rbf = 0; rbf < m_nbRespBasisFunctions; rbf++) {
438  m2p_respBasisFunctions[rbf] = (FLTNB *) malloc(mp_ID->GetNbRespGates() * sizeof(FLTNB));
439  for (int rg = 0; rg < mp_ID->GetNbRespGates(); rg++) {
440  // Set diagonal to 1, the rest to 0
441  if (rbf == rg) m2p_respBasisFunctions[rbf][rg] = 1.;
442  else m2p_respBasisFunctions[rbf][rg] = 0.;
443  }
444  }
445  // Set the static flag to true, the number of basis functions and the basis functions
446  // TODO: Do I realy need to set this flag ?
447  mp_ID->SetRespStaticFlag(true);
448  mp_ID->SetNbRespBasisFunctions(m_nbRespBasisFunctions);
450  // Exit
451  return 0;
452 }
453 
455 {
456 //-------------------------------------------------------------------------------
457  // Diagonal Cardiac Basis Function - for the provided number of respiratory gates
458  // Set the number of cardiac basis functions to the number of cardiac gates
460  // Allocate the conversion matrix from cardiac basis functions to cardiac gates
461  m2p_cardBasisFunctions = (FLTNB**)malloc(mp_ID->GetNbCardGates()*sizeof(FLTNB*));
462  for (int cbf=0; cbf<m_nbCardBasisFunctions; cbf++)
463  {
464  m2p_cardBasisFunctions[cbf] = (FLTNB*)malloc(mp_ID->GetNbCardGates()*sizeof(FLTNB));
465  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
466  {
467  // Set diagonal to 1, the rest to 0
468  if (cbf==cg) m2p_cardBasisFunctions[cbf][cg] = 1.;
469  else m2p_cardBasisFunctions[cbf][cg] = 0.;
470  }
471  }
472  // Set the static flag to true, the number of basis functions and the basis functions
473  // TODO: Do I realy need to set this flag ?
474  mp_ID->SetCardStaticFlag(true);
475  mp_ID->SetNbCardBasisFunctions(m_nbCardBasisFunctions);
477  // Exit
478  return 0;
479 }
480 
481 // =====================================================================
482 // ---------------------------------------------------------------------
483 // ---------------------------------------------------------------------
484 // =====================================================================
bool GetModelBasisFunctionsRequiredFlag()
int SetDiagonalCardBasisFunctions()
Set diagonal Cardiac Basis Functions for regular gated reconstruction.
Declaration of class oDynamicModelManager.
virtual int CheckParameters()
This function is used to check parameters after the latter have been all set using Set functions...
#define FLTNB
Definition: gVariables.hh:81
void ShowHelpDynamicModel()
Show help about all implemented dynamic models.
int GetNbTimeBasisFunctions()
void SetNbTimeBasisFunctions(int a_nbTimeBasisFunctions)
Set the number of time basis functions.
void SetUseModelInReconstruction(bool a_useModelInReconstruction)
Set flag to indicate if the dynamic model is used in the tomographic reconstruction.
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
Set the image dimensions in use.
int ReadAndCheckConfigurationFile(string a_fileOptions)
This function is used to read options from a configuration file. It looks for the parameters implem...
This is the mother class of dynamic model classes.
virtual int ApplyOutputFOVMaskingOnParametricImages()
Mask the outside of the transaxial FOV based on the m_fovOutPercent.
virtual int ReadAndCheckOptionsList(string a_listOptions)=0
This function is used to read parameters from a string. It is pure virtual so must be implemented b...
oDynamicModelManager()
Constructor of oDynamicModelManager. Simply set all data members to default values.
int Initialize()
Set the dynamic model flag and instanciate/initialize model objects through the ParseOptionsAndInitia...
void Exit(int code)
static sAddonManager * GetInstance()
void SetNbRespBasisFunctions(int a_nbRespBasisFunctions)
Set the number of respiratory basis functions.
virtual int EstimateImage(oImageSpace *ap_Image, int a_ite, int a_sset)
int ParseOptionsAndInitializeModel()
Parse dynamic model options contained in the previously provided strings. This function is called ins...
#define Cerr(MESSAGE)
virtual int EstimateModel(oImageSpace *ap_Image, int a_ite, int a_sset)
This function checks if the EstimateModelParameters() function (specific to each model) must be calle...
void SetCardStaticFlag(bool a_flag)
Set the Cardiac Static Flag.
void SetCardBasisFunctions(FLTNB **a_m2pCardBasisFunctions)
int SaveParametricImages(int a_iteration, int a_subset=-1)
Call SaveParametricImages() function of the dynamic model object is &#39;m_useModel&#39; is on...
oImageDimensionsAndQuantification * mp_ID
int Initialize()
A public function used to initialize the dynamic model.
void SetRespStaticFlag(bool a_flag)
Set the Respiratory Static Flag.
void SetVerbose(int a_verbose)
Set the verbose level.
vDynamicModel * mp_DynamicModel
int CheckParameters()
This function is used to check parameters after the latter have been all set using Set functions...
int SetDiagonalRespBasisFunctions()
Set diagonal Respiratory Basis Functions for regular gated reconstruction.
int GetNbCardGates()
Get the number of cardiac gates.
FLTNB ** GetTimeBasisFunctions()
std::map< string, maker_dynamic_model > mp_listOfDynamicModels
This class holds all the matrices in the image domain that can be used in the algorithm: image...
Definition: oImageSpace.hh:60
void SetTimeBasisFunctions(FLTNB **a_m2pTimeBasisFunctions)
Set the basis functions array, calculated by the DynamicModelManager or the DynamicModel.
int GetNbTimeFrames()
Get the number of time frames.
#define EXIT_DEBUG
Definition: gVariables.hh:97
void SetNbCardBasisFunctions(int a_nbCardBasisFunctions)
Set the number of cardiac basis functions.
int GetNbRespGates()
Get the number of respiratory gates.
#define Cout(MESSAGE)
virtual void ComputeOutputParImage()
Compute output image using the m2p_parametricImages matrix Store the result in the m2p_outputParImage...
~oDynamicModelManager()
Destructor of oDynamicModelManager. Free memory from all allocated tabs.
void SetRespBasisFunctions(FLTNB **a_m2pRespBasisFunctions)
Set the basis functions array, calculated by the DynamicModelManager or the DynamicModel.
int SetDiagonalTimeBasisFunctions()
Set diagonal Time Basis Functions for regular frame-by-frame reconstruction.
int SaveParametricImages(int a_iteration, int a_subset=-1)
This function is virtual it can be overloaded by children if required.
int ApplyDynamicModel(oImageSpace *ap_ImageS, int a_iteration, int a_subset)
Declaration of class sAddonManager.
void SetTimeStaticFlag(bool a_flag)
Set the Time Static Flag - to be used with Time Basis functions for direct dynamic reconstruction...