CASToR  3.0
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-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
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;
51 
52  // Diagonal basis functions
56 
60 
61  // Verbosity
62  m_verbose = -1;
63  m_checked = false;
64  m_initialized = false;
65 }
66 
67 // =====================================================================
68 // ---------------------------------------------------------------------
69 // ---------------------------------------------------------------------
70 // =====================================================================
71 /*
72  \fn ~oDynamicModelManager
73  \brief Destructor of oDynamicModelManager. Free memory from all allocated tabs.
74 */
76 {
77  // Delete model objects
78  if (mp_DynamicModel) delete mp_DynamicModel;
79 }
80 
81 // =====================================================================
82 // ---------------------------------------------------------------------
83 // ---------------------------------------------------------------------
84 // =====================================================================
85 /*
86  \fn CheckParameters
87  \brief This function is used to check parameters after the latter
88  have been all set using Set functions.
89  \return 0 if success, positive value otherwise.
90 */
92 #ifdef CASTOR_VERBOSE
93  if (m_verbose>=2) Cout("oDynamicModelManager::CheckParameters() ..."<< endl);
94 #endif
95 
96  // Check image dimensions
97  if (mp_ID == NULL) {
98  Cerr("***** oDynamicModelManager::CheckParameters() -> No image dimensions provided !" << endl);
99  return 1;
100  }
101  // Check if any options have been provided
102  //if (m_options =="")
103  //{
104  // Cerr("***** oDynamicModelManager::CheckParameters() -> No options provided !" << endl);
105  //return 1;
106  // }
107  // Check verbosity
108  if (m_verbose<0)
109  {
110  Cerr("***** oDynamicModelManager::CheckParameters() -> Wrong verbosity level provided !" << endl);
111  return 1;
112  }
113 
114  // Normal end
115  m_checked = true;
116  return 0;
117 }
118 
119 // =====================================================================
120 // ---------------------------------------------------------------------
121 // ---------------------------------------------------------------------
122 // =====================================================================
123 /*
124  \fn Initialize
125  \brief Set the dynamic model flag and instanciate/initialize model objects
126  through the ParseOptionsAndInitializeDeformations() private function.
127  \return 0 if success, positive value otherwise.
128 */
130 {
131  // Forbid initialization without check
132  if (!m_checked)
133  {
134  Cerr("***** oDynamicModelManager::Initialize() -> Must call CheckParameters() before Initialize() !" << endl);
135  return 1;
136  }
137  // Check options
138  if (m_options=="")
139  {
140  // If no options have been provided then no dynamic model will be used
141  m_initialized = true;
142  m_UseModel = false;
143  // Set default diagonal Basis Functions.
145  mp_ID->SetTimeStaticFlag(true);
148  // Exit the dynamic manager after that
149  return 2;
150  }
151 
152  // Else we have some model options
153  m_UseModel = true;
154 
155  // Verbose
156  if (m_verbose>=1) Cout("oDynamicModelManager::Initialize() -> Initialize models" << endl);
157 
158  // Parse model options and initialize them
160  {
161  Cerr("***** oDynamicModelManager::Initialize() -> A problem occurred while parsing model options and initializing them !" << endl);
162  return 1;
163  }
164 
165  // Normal end
166  m_initialized = true;
167  return 0;
168 
169 }
170 
171 // =====================================================================
172 // ---------------------------------------------------------------------
173 // ---------------------------------------------------------------------
174 // =====================================================================
175 /*
176  \fn ParseOptionsAndInitializeProjectors
177  \brief Parse dynamic model options contained in the previously provided
178  strings. This function is called inside the Initialize() function.
179  \details Manage the options reading and initialize specific vDynamicModel
180  Options are a string containing first the name of the model,
181  then either a ':' and a configuration file specific to the model
182  - or - as many ',' as needed parameters for this model.
183  Specific pure virtual functions of the vDynamicModel are used to read parameters and initialize them.
184  \return 0 if success, positive value otherwise
185 */
187 {
188  #ifdef CASTOR_VERBOSE
189  if (m_verbose>=3) Cout("oDynamicModelManager::ParseOptionsAndInitializeModel ..."<< endl);
190  #endif
191 
192  string dynamic_model = "";
193  string list_options = "";
194  string file_options = "";
195 
196  // This is for the automatic initialization of the models
197  typedef vDynamicModel *(*maker_dynamic_model) ();
198 
199  // Get model's list from addon manager
200  std::map <string,maker_dynamic_model> list = sAddonManager::GetInstance()->mp_listOfDynamicModels;
201 
202  size_t colon, comma;
203 
204  // ---------------------------------------------------------------------------------------------------
205  // Manage model for respiratory motion
206  // ---------------------------------------------------------------------------------------------------
207 
208  // First, check if we have dynamic data
209  if (mp_ID->GetNbTimeFrames() <= 1 &&
210  mp_ID->GetNbRespGates() <= 1 &&
211  mp_ID->GetNbCardGates() <= 1)
212  {
213  Cerr("***** oDynamicModelManager::CheckParameters() -> Dynamic model should be used with more than one time frame/dynamic gate !" << endl);
214  return 1;
215  }
216 
217  // ______________________________________________________________________________
218  // Get the model name in the options and isolate the real model's options
219 
220  // Search for a colon ":", this indicates that a configuration file is provided after the model name
221  colon = m_options.find_first_of(":");
222  comma = m_options.find_first_of(",");
223 
224  // Case 1: we have a colon
225  if (colon!=string::npos)
226  {
227  // Get the model name before the colon
228  dynamic_model = m_options.substr(0,colon);
229  // Get the configuration file after the colon
230  file_options = m_options.substr(colon+1);
231  // List of options is empty
232  list_options = "";
233  }
234  // Case 2: we have a comma
235  else if (comma!=string::npos)
236  {
237  // Get the model name before the first comma
238  dynamic_model = m_options.substr(0,comma);
239  // Get the list of options after the first comma
240  list_options = m_options.substr(comma+1);
241  // Configuration file is empty
242  file_options = "";
243  }
244  // Case 3: no colon and no comma (a single model name)
245  else
246  {
247  // Get the model name
248  dynamic_model = m_options;
249  // Configuration file is empty
250  file_options = "";
251  // List of options is empty
252  list_options = "";
253  }
254 
255  // Create the model
256  if (list[dynamic_model]) mp_DynamicModel = list[dynamic_model]();
257  else
258  {
259  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> Model '" << dynamic_model << "' does not exist !" << endl);
261  return 1;
262  }
265  // Provide configuration file if any
266  if (file_options!="" && mp_DynamicModel->ReadAndCheckConfigurationFile(file_options))
267  {
268  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while reading and checking frame dynamic model's configuration file !" << endl);
269  return 1;
270  }
271  // Provide options if any
272  if (list_options!="" && mp_DynamicModel->ReadAndCheckOptionsList(list_options))
273  {
274  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while parsing and reading frame dynamic model's options !" << endl);
275  return 1;
276  }
277  // Check parameters
279  {
280  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while checking frame dynamic model parameters !" << endl);
281  return 1;
282  }
283  // Initialize the model
285  {
286  Cerr("***** oDynamicModelManager::ParseOptionsAndInitializeModel() -> A problem occurred while initializing frame dynamic model !" << endl);
287  return 1;
288  }
289  // Check if model specific BasisFunctions are required for the ImageDimensionsAndQuantification object (normaly the case for non-nested recons )
290  // if there is no requirement for model specific basis functions -> Set Diagonal Basis Functions (normaly the case for nested recons )
292  {
293  if(m_verbose>=2) Cout("oDynamicModelManager:: Setting Diagonal basis functions for the tomographic update "<< endl);
297 
298  }
299  // Case where specific basis functions are required
300  else
301  {
302  if(m_verbose>=2) Cout("oDynamicModelManager:: Seting model specific Time basis functions "<< endl);
305 
306  // At the moment all dynamic models relate to kinetic modeling, so seting other basis functions to diagonal
309 
310  // TODO: Validate this
311  // When the basis functions have been set there is no further need for the dynamic model
312  m_UseModel=false;
313  }
314 
315  // Normal end
316  return 0;
317 }
318 
319 // =====================================================================
320 // ---------------------------------------------------------------------
321 // ---------------------------------------------------------------------
322 // =====================================================================
323 /*
324  \fn ApplyDynamicModel
325  \param ap_ImageS : pointer to the ImageSpace
326  \param a_iteration : index of the actual iteration
327  \param a_subset : index of the actual subset
328  \brief Call successively EstimateModelParameters() ans EstimateImageWithModel()
329  functions of the dynamic model object is 'm_UseModel' is on.
330  \return 0 if success, positive value otherwise
331 */
332 int oDynamicModelManager::ApplyDynamicModel(oImageSpace* ap_ImageS, int a_iteration, int a_subset)
333 {
334  #ifdef CASTOR_DEBUG
335  if (!m_initialized)
336  {
337  Cerr("***** oDynamicModelManager::ApplyDynamicModel() -> Called while not initialized !" << endl);
338  Exit(EXIT_DEBUG);
339  }
340  #endif
341 
342  if (m_UseModel)
343  {
344  // Verbose
345  if(m_verbose>=2) Cout("oDynamicModelManager::ApplyDynamicModel ..."<< endl);
346 
347  // Estimate model parameters
348  if( mp_DynamicModel->EstimateModel(ap_ImageS, a_iteration, a_subset) )
349  {
350  Cerr("***** oDynamicModelManager::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying dynamic model to current estimate images !" << endl);
351  return 1;
352  }
353 
354  // Generate the serie of dynamic images using the model parameters
355  if( mp_DynamicModel->EstimateImage(ap_ImageS, a_iteration, a_subset) )
356  {
357  Cerr("***** oDynamicModelManager::StepPostProcessInsideSubsetLoop() -> A problem occurred while applying dynamic model to current estimate images !" << endl);
358  return 1;
359  }
360  }
361 
362  return 0;
363 }
364 
365 // =====================================================================
366 // ---------------------------------------------------------------------
367 // ---------------------------------------------------------------------
368 // =====================================================================
369 /*
370  \fn SaveParametricImages
371  \param a_iteration : current iteration index
372  \param a_subset : current number of subsets (or -1 by default)
373  \brief Call SaveCoeffImages() function of the dynamic model object is
374  'm_UseModel' is on, in order to save any parameter image
375  \return 0 if success, positive value otherwise
376 */
377 int oDynamicModelManager::SaveParametricImages(int a_iteration, int a_subset)
378 {
379  if (m_UseModel)
380  {
381  // Verbose
382  if(m_verbose>=2) Cout("oDynamicModelManager::SaveParametricImages ..."<< endl);
383 
384  // Write output parametric images if required
386 
387  // Apply Masking if required
389  {
390  Cerr("***** oDynamicModelManager::SaveParametricImages() -> A problem occurred while trying to apply FOV masking on parametric images !" << endl);
391  return 1;
392  }
393 
394  // Save images
395  if (mp_DynamicModel->SaveParametricImages(a_iteration, a_subset))
396  {
397  Cerr("***** oDynamicModelManager::SaveParametricImages() -> A problem occurred while trying to save image coefficients !" << endl);
398  return 1;
399  }
400  }
401  return 0;
402 }
403 
405 {
406  // This function is used to set the BasisFunctions in ImageDimensionsAndQuantification to diagonal values
407  // Set the number of time basis functions to the number of frames
409  // Allocate the conversion matrix from basis functions to frames
410  m2p_timeBasisFunctions = (FLTNB **) malloc(m_nbTimeBasisFunctions * sizeof(FLTNB *));
411  for (int tbf = 0; tbf < m_nbTimeBasisFunctions; tbf++) {
412  m2p_timeBasisFunctions[tbf] = (FLTNB *) malloc(mp_ID->GetNbTimeFrames() * sizeof(FLTNB));
413  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++) {
414  // Set diagonal to 1, the rest to 0
415  if (tbf == fr) m2p_timeBasisFunctions[tbf][fr] = 1.;
416  else m2p_timeBasisFunctions[tbf][fr] = 0.;
417  }
418  }
419  // In the diagonal case the number of time basis functions is always equal to the number of frames
420  mp_ID->SetNbTimeBasisFunctions(m_nbTimeBasisFunctions);
422  // Exit
423  return 0;
424 }
425 
427 {
428 
429  // This function is used to set the BasisFunctions in ImageDimensionsAndQuantification to diagonal values
430  // Set the number of respiratory basis functions to the number of frames
432  // Allocate the conversion matrix from basis functions to frames
433  m2p_respBasisFunctions = (FLTNB **) malloc(m_nbRespBasisFunctions * sizeof(FLTNB *));
434  for (int rbf = 0; rbf < m_nbRespBasisFunctions; rbf++) {
435  m2p_respBasisFunctions[rbf] = (FLTNB *) malloc(mp_ID->GetNbRespGates() * sizeof(FLTNB));
436  for (int rg = 0; rg < mp_ID->GetNbRespGates(); rg++) {
437  // Set diagonal to 1, the rest to 0
438  if (rbf == rg) m2p_respBasisFunctions[rbf][rg] = 1.;
439  else m2p_respBasisFunctions[rbf][rg] = 0.;
440  }
441  }
442  // Set the static flag to true, the number of basis functions and the basis functions
443  // TODO: Do I realy need to set this flag ?
444  mp_ID->SetRespStaticFlag(true);
445  mp_ID->SetNbRespBasisFunctions(m_nbRespBasisFunctions);
447  // Exit
448  return 0;
449 }
450 
452 {
453 //-------------------------------------------------------------------------------
454  // Diagonal Cardiac Basis Function - for the provided number of respiratory gates
455  // Set the number of cardiac basis functions to the number of cardiac gates
457  // Allocate the conversion matrix from cardiac basis functions to cardiac gates
458  m2p_cardBasisFunctions = (FLTNB**)malloc(mp_ID->GetNbCardGates()*sizeof(FLTNB*));
459  for (int cbf=0; cbf<m_nbCardBasisFunctions; cbf++)
460  {
461  m2p_cardBasisFunctions[cbf] = (FLTNB*)malloc(mp_ID->GetNbCardGates()*sizeof(FLTNB));
462  for (int cg=0; cg<mp_ID->GetNbCardGates(); cg++)
463  {
464  // Set diagonal to 1, the rest to 0
465  if (cbf==cg) m2p_cardBasisFunctions[cbf][cg] = 1.;
466  else m2p_cardBasisFunctions[cbf][cg] = 0.;
467  }
468  }
469  // Set the static flag to true, the number of basis functions and the basis functions
470  // TODO: Do I realy need to set this flag ?
471  mp_ID->SetCardStaticFlag(true);
472  mp_ID->SetNbCardBasisFunctions(m_nbCardBasisFunctions);
474  // Exit
475  return 0;
476 }
477 
478 // =====================================================================
479 // ---------------------------------------------------------------------
480 // ---------------------------------------------------------------------
481 // =====================================================================
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 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...