CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/dynamic/iLinearModel.cc
Go to the documentation of this file.
1 
9 #include "iLinearModel.hh"
10 
11 // =====================================================================
12 // ---------------------------------------------------------------------
13 // ---------------------------------------------------------------------
14 // =====================================================================
15 /*
16  \fn iLinearModel
17  \brief Constructor of iLinearModel. Simply set all data members to default values.
18 */
20 {
21  m_nbRgateBF = -1;
22  m_nbCgateBF = -1;
23  m_nbRGModelParam = -1;
24  m_nbCGModelParam = -1;
25 
28 
31 
32  m_fileOptions = "";
33  m_listOptions = "";
34 
35  mp_corrBasisCoeffs = NULL;
36  mp_corrBasisFunctions = NULL;
41 }
42 
43 
44 
45 
46 // =====================================================================
47 // ---------------------------------------------------------------------
48 // ---------------------------------------------------------------------
49 // =====================================================================
50 /*
51  \fn ~iLinearModel
52  \brief Destructor of iLinearModel
53 */
55 {
56  if(m_initialized)
57  {
58  for(int rb=0 ; rb<m_nbRgateBF ; rb++)
59  {
60  if (m2p_respBasisFunctions[rb]) delete[] m2p_respBasisFunctions[rb];
61  if (m2p_RGParametricImages[rb]) delete[] m2p_RGParametricImages[rb];
62  }
63 
64  for(int cb=0 ; cb<m_nbCgateBF ; cb++)
65  {
66  if (m2p_cardBasisFunctions[cb]) delete[] m2p_cardBasisFunctions[cb];
67  if (m2p_CGParametricImages[cb]) delete[] m2p_CGParametricImages[cb];
68  }
69 
70  if (mp_corrBasisCoeffs != NULL) delete[] mp_corrBasisCoeffs;
71  if (mp_corrBasisFunctions != NULL) delete[] mp_corrBasisFunctions;
72 
73 
74  // Free NNLS variables
76  {
77  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
78  {
79  for(int n=0 ; n<m_nnlsN ; n++)
80  if ( m3p_nnlsA[ th ][ n ]) delete[] m3p_nnlsA[ th ][ n ];
81 
82  // Init 2D coefficient matrix for NNLS estimation
83  if( m3p_nnlsA[ th ] ) delete[] m3p_nnlsA[ th ];
84 
85  // Init solution vector and working matrix for NNLS estimation
86 
87  if( m2p_nnlsB[ th ] ) delete[] m2p_nnlsB[ th ];
88  if( m2p_nnlsMat[ th ] ) delete[] m2p_nnlsMat[ th ];
89  if( m2p_nnlsX[ th ] ) delete[] m2p_nnlsX[ th ];
90  if( m2p_nnlsWp[ th ] ) delete[] m2p_nnlsWp[ th ];
91  if( m2p_nnlsIdx[ th ] ) delete[] m2p_nnlsIdx[ th ];
92  }
93 
94  if( m3p_nnlsA ) delete[] m3p_nnlsA;
95  if( m2p_nnlsB ) delete[] m2p_nnlsB;
96  if( m2p_nnlsMat ) delete[] m2p_nnlsMat;
97  if( m2p_nnlsX ) delete[] m2p_nnlsX;
98  if( m2p_nnlsWp ) delete[] m2p_nnlsWp;
99  if( m2p_nnlsIdx ) delete[] m2p_nnlsIdx;
100  if( mp_w ) delete[] mp_w;
101  }
102  }
103 }
104 
105 
106 
107 
108 // =====================================================================
109 // ---------------------------------------------------------------------
110 // ---------------------------------------------------------------------
111 // =====================================================================
112 /*
113  \fn ShowHelpModelSpecific
114  \brief Print out specific help about the implementation of this model
115  and its initialization
116 */
118 {
119  cout << "-- This class implements a general linear dynamic model applied between the images of a dynamic acquisition." << endl;
120  cout << "-- The model is applied on a voxel-by-voxel basis between the images of the frames and/or respiratory/cardiac gates. " << endl;
121  cout << "-- The main keywords 'DYNAMIC FRAMING', 'RESPIRATORY GATING' and 'CARDIAC GATING' ahead of the parameters allow to define at which level the model parameters must be applied. " << endl;
122  cout << "-- Main parameters to define are:" << endl;
123  cout << " -> The number of basis functions / parametric images defined in the model" << endl;
124  cout << " -> Basis function initial values" << endl;
125  cout << " -> Parametric images initialization (optional)" << endl;
126  cout << " -> Optimisation_method : x (mandatory) optimization method for voxelwise parameters estimation." << endl;
127  cout << endl;
128  cout << " It can be initialized using a configuration text file with the following keywords and information :" << endl;
129  cout << " - Mandatory keywords :" << endl;
130  cout << " The following keywords are mandatory for at least one dynamic image level (Dynamic frame, Respiratory gating or Cardiac gating) :" << endl;
131  cout << " 'Number_basis_functions:' Enter the number of basis function for each image of time frame or respiratory/cardiac gate " << endl;
132  cout << " 'Basis_functions:' Enter the basis function (bf) coefficients for each image (im) of time frame or respiratory/cardiac gate " << endl;
133  cout << " on successive lines, separated by ',' :" << endl;
134  cout << " -> basis_functions: " << endl;
135  cout << " -> coeff_bf1_im1,coeff_bf1_im2,...,coeff_bf1_imn" << endl;
136  cout << " -> coeff_bf2_im1,coeff_bf2_im2,...,coeff_bf2_imn" << endl;
137  cout << " -> etc..." << endl;
138  cout << " Optimisation_method : x (mandatory) optimization method available options: " << endl;
139  cout << " x=0: Direct ( Implementation of basis functions side by system matrix in each tomographic iterative loop " << endl;
140  cout << " (! Currently not compatible with motion correction)" << endl;
141  cout << " x=1: Nested EM " << endl;
142  cout << " x=2: Iterative non-negative Least-Square " << endl;
143  cout << " (C.L. Lawson and R.J. Hanson, Solving Least Squares Problems)" << endl;
144  cout << endl;
145  cout << " - Optional keywords :" << endl;
146  cout << " 'Parametric_image_init: image_file' Set an image file to initialize the parametric images of each dynamic model. Default initialization: '1.0 for each voxel." << endl;
147  cout << " " << endl;
148  cout << " 'Number_weight_values:' Enter the number of weights to be applied for each dynamic frame for performing WLS optimisation (Optimisation method=2) " << endl;
149  cout << " 'Weight_values:' Enter the weight values to be applied for each dynamic frame ( within DYNAMIC FRAMING/ENDDF) " << endl;
150  cout << " on one single line, separated by ',' " << endl;
151  cout << " The previous parameters must be declared inside the couple of the following specific tags: " << endl;
152  cout << " - DYNAMIC FRAMING/ENDDF for chronological frame model (dynamic model applied to chronological frames of a dynamic aquisition)" << endl;
153  cout << " - RESPIRATORY GATING/ENDRG for respiratory model (dynamic model applied to respiratory gates of a dynamic aquisition)" << endl;
154  cout << " - CARDIAC GATING/ENDCG for cardiac model (dynamic model applied to cardiac gates of a dynamic aquisition)" << endl;
155  cout << " Different levels of dynamic model can be enabled simultaneously (i.e a dynamic frame model can be used simultaneously with respiratory and/or cardiac gating model) " << endl;
156  cout << " " << endl;
157  cout << " " << endl;
158  cout << " The following keywords are optional and common to each dynamic model (Dynamic frame, Respiratory gating or Cardiac gating) :" << endl;
159  cout << " 'Number_model_iterations: x' Number of iterations of the model parameters and basis functions updates in one cycle of Nested EM" << endl;
160  cout << " (Default x == 1) (one cycle consists in x iterations in which either the parametric images or the basis functions are updated) " << endl;
161  cout << " The ratio of parametric images / basis functions updates depends on the following parameter:" << endl;
162  cout << " 'Basis_function_update_ratio: x' Ratio of update between parametric images and basis functions updates cycle " << endl;
163  cout << " (Default x == 0) Cycles consist in x iterations of the parametric images, following by x iterations of the basis functions" << endl;
164  cout << " If x == 0, only the parametric images are updated" << endl;
165  cout << " 'Basis_function_start_ite : x' Starting iteration for the update of basis functions " << endl;
166  cout << " (Default x == -1) If negative, no update of the basis functions is performed (only parametric images are updated) " << endl;
167  cout << " " << endl;
168  cout << " " << endl;
169  cout << " ---------------------------" << endl;
170  cout << " Example of initialization: " << endl;
171  cout << " DYNAMIC FRAMING" << endl;
172  cout << " Number_basis_functions : 2" << endl;
173  cout << " Basis_functions :" << endl;
174  cout << " 23682.79, 25228.74, 26636.99, 27923.61, 29101.16" << endl;
175  cout << " 5.4, 4.91, 4.48, 4.1, 3.75" << endl;
176  cout << " ENDDF" << endl;
177  cout << " " << endl;
178  cout << " RESPIRATORY GATING" << endl;
179  cout << " Number_basis_functions : 6" << endl;
180  cout << " Basis_functions :" << endl;
181  cout << " 1, 0.8, 0.6, 0.4, 0.2, 0.01" << endl;
182  cout << " 0.7, 0.9, 0.7, 0.5, 0.3, 0.1" << endl;
183  cout << " 0.4, 0.6, 0.8, 0.6, 0.4, 0.2" << endl;
184  cout << " 0.2, 0.4, 0.6, 0.8, 0.6, 0.4" << endl;
185  cout << " 0.1, 0.3, 0.5, 0.7, 0.9, 0.7" << endl;
186  cout << " 0.01, 0.2, 0.4, 0.6, 0.8, 1" << endl;
187  cout << " ENDRG" << endl;
188  cout << " " << endl;
189  cout << " Number_model_iterations : 1" << endl;
190  cout << " Basis_function_start_ite : -1" << endl;
191  cout << " Basis_function_update_ratio : 0" << endl;
192  cout << " ---------------------------" << endl;
193  cout << " " << endl;
194 
195  // Print general help for all dynamic models
196  ShowHelp();
197 
198 }
199 
200 
201 
202 // =====================================================================
203 // ---------------------------------------------------------------------
204 // ---------------------------------------------------------------------
205 // =====================================================================
206 /*
207  \fn ReadAndCheckConfigurationFileSpecific
208  \brief This function is used to read options from a configuration file when the generic Linear Model is requested.
209  \return 0 if success, other value otherwise.
210 */
211 
213 {
214  if(m_verbose >=3) Cout("iLinearModel::ReadAndCheckConfigurationFileSpecific ..."<< endl);
215 
216  // Apply the Generic linear Checks for all Linear Models
218  {
219  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read and check specific configuration " << m_fileOptions << endl);
220  return 1;
221  }
222 
223  // Normal End
224  return 0 ;
225 }
226 
227 // =====================================================================
228 // ---------------------------------------------------------------------
229 // ---------------------------------------------------------------------
230 // =====================================================================
231 /*
232  \fn ReadAndCheckConfigurationFileSpecificToAllLinearModels
233  \brief This function is used to read options from a configuration file that are generic to all linear dynamic models.
234  \return 0 if success, other value otherwise.
235 */
237 {
238  if(m_verbose >=3) Cout("iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels ..."<< endl);
239 
240  // The file will be fully processed in the Initialize() function
241  ifstream in_file(m_fileOptions.c_str(), ios::in);
242 
243  if(in_file)
244  {
245  // Check first the file contains the mandatory keyword(s)
246  bool file_is_good = false;
247 
248  string line="", kword_to_search="";
249  while(!in_file.eof())
250  {
251  getline(in_file, line);
252 
253  //remove comment
254  if (line.find("#") != string::npos) line = line.substr(0, line.find_first_of("#")) ;
255 
256  if (line.find("DYNAMIC FRAMING") != string::npos)
257  {
258  if(kword_to_search != "")
259  {
260  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error, found an end tag 'END**' before 'DYNAMIC FRAMING' in configuration file: " << m_fileOptions << endl);
261  return 1;
262  }
263  else
264  kword_to_search = "ENDDF";
265  }
266 
267  else if (line.find("RESPIRATORY GATING") != string::npos)
268  {
269  if(kword_to_search != "")
270  {
271  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error, found an end tag 'END**' before 'RESPIRATORY GATING' in configuration file: " << m_fileOptions << endl);
272  return 1;
273  }
274  else
275  kword_to_search = "ENDRG";
276  }
277 
278  else if (line.find("CARDIAC GATING") != string::npos)
279  {
280  if(kword_to_search != "")
281  {
282  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error, found an end tag 'END**' before 'CARDIAC GATING' in configuration file: " << m_fileOptions << endl);
283  return 1;
284  }
285  else
286  kword_to_search = "ENDCG";
287  }
288 
289  else if (kword_to_search != ""
290  && line.find(kword_to_search) != string::npos)
291  {
292  kword_to_search = "";
293  file_is_good = true;
294  }
295  }
296 
297  if(!file_is_good)
298  {
299  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error, no mandatory tags ('DYNAMIC FRAMING'/'ENDDF', 'RESPIRATORY GATING'/'ENDRG', 'CARDIAC GATING'/'ENDCG' detected in configuration file: "
300  << m_fileOptions << ". Check help for more info" << endl);
301  return 1;
302  }
303 
304  if( ReadDataASCIIFile(m_fileOptions, "Number_basis_functions", &m_nbTimeBF, 1, KEYWORD_OPTIONAL, "DYNAMIC FRAMING", "ENDDF") == 1)
305  {
306  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Number_basis_functions' flag in " << m_fileOptions << endl);
307  return 1;
308  }
309 
310  if( ReadDataASCIIFile(m_fileOptions, "Number_basis_functions", &m_nbRgateBF, 1, KEYWORD_OPTIONAL, "RESPIRATORY GATING", "ENDRG") == 1)
311  {
312  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Number_basis_functions' flag in " << m_fileOptions << endl);
313  return 1;
314  }
315 
316  if( ReadDataASCIIFile(m_fileOptions, "Number_basis_functions", &m_nbCgateBF, 1, KEYWORD_OPTIONAL, "CARDIAC GATING", "ENDCG") == 1)
317  {
318  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Number_basis_functions' flag in " << m_fileOptions << endl);
319  return 1;
320  }
321 
322  if( ReadDataASCIIFile(m_fileOptions, "Number_weight_values", &m_nbWeightFactors, 1, KEYWORD_OPTIONAL, "DYNAMIC FRAMING", "ENDDF") == 1)
323  {
324  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Number_basis_functions' flag in " << m_fileOptions << endl);
325  return 1;
326  }
327 
328  if( ReadDataASCIIFile(m_fileOptions, "Number_model_iterations", &m_nbLinearModelCycles, 1, KEYWORD_OPTIONAL) == 1)
329  {
330  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Number_model_iterations' flag in " << m_fileOptions << endl);
331  return 1;
332  }
333 
334  if( ReadDataASCIIFile(m_fileOptions, "Basis_function_start_ite", &m_basisFunctionsUpdStartIte, 1, KEYWORD_OPTIONAL) == 1)
335  {
336  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Basis_function_start_ite' flag in " << m_fileOptions << endl);
337  return 1;
338  }
339 
340  if( ReadDataASCIIFile(m_fileOptions, "Basis_function_update_ratio", &m_basisFunctionsUpdRatio, 1, KEYWORD_OPTIONAL) == 1)
341  {
342  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Basis_function_update_ratio' flag in " << m_fileOptions << endl);
343  return 1;
344  }
345  // Optimisation method
346  if( ReadDataASCIIFile(m_fileOptions, "Optimisation_method", &m_OptimisationMethod, 1, KEYWORD_MANDATORY))
347  {
348  Cerr("***** ***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read 'Optimisation_method' keyword in " << m_fileOptions << endl);
349  return 1;
350  }
351 
352  // Print out information on optimisation method set
353  if(m_verbose >=2)
354  {
356  Cout("iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels() -> Selected optimization method : NNLS (2)" << endl);
358  Cout("iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels() -> Selected optimization method : Nested-EM (1)" << endl);
360  Cout("iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels() -> Selected optimization method : Direct Dynamic (0)" << endl);
361  }
362  }
363  else
364  {
365  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecificToAllLinearModels -> Error while trying to read configuration file at: " << m_fileOptions << endl);
366  return 1;
367  }
368 
369 
370  // Initialize the number of parameters of the linear model as the number of (frame) basis functions
371  // (Variable used during parametric images writing on disk)
375 
376  return 0;
377 }
378 
379 
380 
381 
382 // =====================================================================
383 // ---------------------------------------------------------------------
384 // ---------------------------------------------------------------------
385 // =====================================================================
386 /*
387  \fn ReadAndCheckOptionsList
388  \brief This function is used to read parameters from a string.
389  \return 0 if success, other value otherwise.
390 */
391 int iLinearModel::ReadAndCheckOptionsList(string a_listOptions)
392 {
393  if(m_verbose >=3) Cout("iLinearModel::ReadAndCheckOptionsList ..."<< endl);
394 
395  // Just recover the string here, it will be processed in the Initialize() function
396  m_listOptions = a_listOptions;
397 
398 
399  // For now, just restricts the initialization using a configuration file as there are quite a lot of parameters to initialize for a use with command line options
400  Cerr("***** iLinearModel::ReadAndCheckOptionsList() -> Initialization with command line options is not implemented for this class. Please use a configuration file instead" << endl);
401  return 1;
402 
403  // Normal end
404  //return 0;
405 }
406 
407 
408 
409 
410 // =====================================================================
411 // ---------------------------------------------------------------------
412 // ---------------------------------------------------------------------
413 // =====================================================================
414 /*
415  \fn CheckSpecificParameters
416  \brief This function is used to check whether all member variables
417  have been correctly initialized or not.
418  \return 0 if success, positive value otherwise.
419 */
421 {
422 
423  // Perform generic checks for the Linear Model
425  {
426  Cerr("***** iLinearModel::CheckSpecificParameters -> A problem occurred while checking specific parameters ! " << endl);
427  return 1;
428  }
429 
430  // Normal end
431  return 0;
432 }
433 
434 
435 
436 // =====================================================================
437 // ---------------------------------------------------------------------
438 // ---------------------------------------------------------------------
439 // =====================================================================
440 /*
441  \fn CheckSpecificParametersForAllLinearModels
442  \brief This function is used to check whether all member variables of a
443  generic linear model have been correctly initialized or not.
444  \return 0 if success, positive value otherwise.
445 */
446 
448 {
449 
450  if(m_verbose >=2) Cout("iLinearModel::CheckSpecificParametersForAllLinearModels ..."<< endl);
451 
452  // Check at least one basis function number has been initialized
453  if (m_nbTimeBF<=0 && m_nbRgateBF<=0 && m_nbCgateBF<=0)
454  {
455  Cerr("***** iLinearModel::CheckSpecificParametersForAllLinearModels() -> Error, the variables corresponding to the number of basis function has not been initialized. There might be an error in the configuration process/file !" << endl);
456  return 1;
457  }
458  else
459  {
460  // Set the other variables to 1 if not initialized
461  if(m_nbTimeBF<0) m_nbTimeBF =1;
462  if(m_nbRgateBF<0) m_nbRgateBF=1;
463  if(m_nbCgateBF<0) m_nbCgateBF=1;
464  }
465 
466  // if weights have been provided for WLS check if their lenght is equal to number of time basis functions
467  if (m_nbWeightFactors>1)
468  {
470  {
471  Cerr("***** iLinearModel::CheckSpecificParametersForAllLinearModels() -> Error, the number of weight factors doesn't match the number of input frames. There might be an error in the configuration process/file !" << endl);
472  return 1;
473  }
474  }
475 
476 
477  // Check if we have somehow both a file and a list of options for init...
478  if(m_listOptions != "" && m_fileOptions != "")
479  {
480  Cerr("***** iLinearModel::CheckSpecificParametersForAllLinearModels -> Either a file or a list of options have to be selected to initialize the model, but not both ! " << endl);
481  return 1;
482  }
483 
484  // Check if we have no file not list of options for some reason...
485  if(m_listOptions == "" && m_fileOptions == "")
486  {
487  Cerr("***** iLinearModel::CheckSpecificParametersForAllLinearModels -> Either a file or a list of options should have been provided at this point ! " << endl);
488  return 1;
489  }
490 
491  // In case of regular linear regression selected make sure a dynamic model with 2 basis functions is set
493  {
494  Cerr("***** iLinearModel::CheckSpecificParametersForAllLinearModels -> Optimization method Linear Regression (=3) only available when using two time basis functions ! " << endl);
495  return 1;
496  }
497 
498  // Check if the model has been called by imageDynamicTools and Direct optimization method has been selected
500  {
501  Cerr("***** iLinearModel::CheckSpecificParametersForAllLinearModels -> Optimization method 'Direct' (=0) only available when using castor-recon ! " << endl);
502  return 1;
503  }
504 
505  // Normal end
506  return 0;
507 }
508 
509 
510 // =====================================================================
511 // ---------------------------------------------------------------------
512 // ---------------------------------------------------------------------
513 // =====================================================================
514 /*
515  \fn InitializeSpecific
516  \brief This function is used to initialize the parametric images and basis functions
517  \return 0 if success, other value otherwise.
518 */
519 
521 {
522  if(m_verbose >=2) Cout("iLinearModel::InitializeSpecificToAllLinearModels ..."<< endl);
523 
524  // Forbid initialization without check
525  if (!m_checked)
526  {
527  Cerr("***** iLinearModel::InitializeSpecificToAllLinearModels() -> Must call CheckParameters functions before Initialize() !" << endl);
528  return 1;
529  }
530 
531  // Initialize blacklisted voxels image to zero;
533  for (int v = 0; v < mp_ID->GetNbVoxXYZ(); v++)
534  {
535  mp_blackListedvoxelsImage[v] = 0.0;
536  }
537 
538  // Allocate memory for Parametric images and time basis functions of the model
542 
543  for(int b=0 ; b<m_nbTimeBF ; b++)
544  {
547  }
548 
551 
552  for(int rb=0 ; rb<m_nbRgateBF ; rb++)
553  {
556  }
557 
560 
561 
562  for(int cb=0 ; cb<m_nbCgateBF ; cb++)
563  {
566  }
567 
568  // Memory allocation for correction images
571 
572  // Allocate output image matrices
573  for(int b=0 ; b<m_nbTimeBF ; b++)
575 
576 
577  // TODO: Consider other cases where linear models will be applied for Cardiac / Respiratory reconstruction.
578 
579  // --- Default Initialization basis functions --- //
580  for(int b=0 ; b<m_nbTimeBF ; b++)
581  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
583 
584  // --- Default Initialization of respiratory and cardiac basis functions --- //
585  for(int rb=0 ; rb<m_nbRgateBF ; rb++)
586  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
587  m2p_respBasisFunctions[rb][rg] = 1.;
588 
589  for(int cb=0 ; cb<m_nbCgateBF ; cb++)
590  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
591  m2p_cardBasisFunctions[cb][cg] = 1.;
592 
593 
594  // Initialize gate parametric images of linear model mother class
595  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
596  {
597  m2p_RGParametricImages[ 0 ][ v ] = 1.;
598  m2p_CGParametricImages[ 0 ][ v ] = 1.;
599  }
600 
601  // Allocate NNLS variables and weights.
603  {
610  mp_w = new FLTNB[ mp_ID->GetNbTimeFrames() ];
611 
612  // Get the largest nb of parameters and samples between time frames, resp or cardiac gates
613  // as all could be used as nnls samples depending on the linear model
614  int nnls_max_params = (m_nbTimeBF > m_nbRgateBF) ?
615  ( (m_nbTimeBF > m_nbCgateBF) ? m_nbTimeBF : m_nbCgateBF) :
616  ( (m_nbRgateBF > m_nbCgateBF) ? m_nbRgateBF : m_nbCgateBF) ;
617 
618  m_nnlsN = nnls_max_params;
619 
620  int nnls_max_samples = (mp_ID->GetNbTimeFrames() > mp_ID->GetNbRespGates()) ?
623 
624 
625  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
626  {
627  // Init 2D coefficient matrix for NNLS estimation
628  m3p_nnlsA[ th ] = new FLTNB*[ m_nnlsN ];
629 
630  for(int n=0 ; n<m_nnlsN ; n++)
631  m3p_nnlsA[ th ][n] = new FLTNB[ nnls_max_samples ];
632 
633  // Init solution vector and working matrix for NNLS estimation
634 
635  m2p_nnlsB[ th ] = new FLTNB[ nnls_max_samples ];
636  m2p_nnlsMat[ th ] = new FLTNB[ (m_nnlsN+2) * nnls_max_samples ];
637  m2p_nnlsX[ th ] = new FLTNB[ m_nnlsN ];
638  m2p_nnlsWp[ th ] = new FLTNB[ m_nnlsN ];
639  m2p_nnlsIdx[ th ] = new int[ m_nnlsN ];
640  }
641 
642  // Fill weights for NNLS to default values
643  // todo: specific weights for gate-related models (gate duration) - Also NECR related weights or user set weights (ZC)
644 
645  // Case of no weights input - set all weights to 1
646  if (m_nbWeightFactors<=1)
647  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
648  mp_w[t] = 1;
649  }
650 
651 
652  // --- Data Initialization with a configuration file --- //
653  if(m_fileOptions != "")
654  {
655  ifstream in_file(m_fileOptions.c_str(), ios::in);
656 
657  if(in_file)
658  {
659  // TODO: Implement a check of length of input basis functions
660  // Frame basis functions Initialization
661  if(m_nbTimeBF>1
663  "Basis_functions",
666  m_nbTimeBF,
668  "DYNAMIC FRAMING",
669  "ENDDF")==1)
670  {
671  Cerr("***** iLinearModel::Initialize -> Error while trying to read frame basis functions coefficients !" << endl);
672  Cerr(" 'Basis_functions' keyword inside DYNAMIC FRAMING / ENDDF paragraph in " << m_fileOptions << endl);
673  return 1;
674  }
675 
676  // Resp gates basis functions Initialization
677  if(m_nbRgateBF>1
679  "Basis_functions",
682  m_nbRgateBF,
684  "RESPIRATORY GATING",
685  "ENDRG") ==1 )
686  {
687  Cerr("***** iLinearModel::Initialize -> Error while trying to read respiratory gates basis functions coefficients !" << endl);
688  Cerr(" 'Basis_functions' keyword inside RESPIRATORY GATING / ENDRG paragraph in " << m_fileOptions << endl);
689  return 1;
690  }
691 
692  // Card gates basis functions Initialization
693  if(m_nbCgateBF>1
695  "Basis_functions",
698  m_nbCgateBF,
700  "CARDIAC GATING",
701  "ENDCG") ==1 )
702  {
703  Cerr("***** iLinearModel::Initialize -> Error while trying to read cardiac gates basis functions coefficients !" << endl);
704  Cerr(" 'Basis_functions' keyword inside CARDIAC GATING / ENDCG paragraph in " << m_fileOptions << endl);
705  return 1;
706  }
707 
708  // Optimisation method
710  "Optimisation_method",
712  1,
714  {
715  Cerr("***** iLinearModel::Initialize -> Error while trying to read 'Optimisation_method' keyword in " << m_fileOptions << endl);
716  return 1;
717  }
718 
719  //
720 
721  if(m_nbWeightFactors>1
723  "Weight_values",
724  mp_w,
726  KEYWORD_OPTIONAL,
727  "DYNAMIC FRAMING",
728  "ENDDF")==1)
729  {
730  Cerr("***** iLinearModel::Initialize -> Error while trying to read frame weight factor coefficients !" << endl);
731  Cerr(" 'Weight_values' keyword inside DYNAMIC FRAMING / ENDDF paragraph in " << m_fileOptions << endl);
732  return 1;
733  }
734 
735 
736  // --- Parametric image initialization --- //
737 
738  // Frame model
739  string input_image = "";
740  int return_value = 0;
741 
742  return_value = ReadDataASCIIFile(m_fileOptions,
743  "Parametric_images_init",
744  &input_image,
745  1,
746  KEYWORD_OPTIONAL,
747  "DYNAMIC FRAMING",
748  "ENDDF");
749 
750  if( return_value == 0) // Image have been provided
751  {
752  // Read image // INTF_LERP_DISABLED = interpolation disabled for input image reading
753  if( IntfReadImgDynCoeffFile(input_image,
755  mp_ID,
757  m_verbose,
758  INTF_LERP_DISABLED) ) // Image have been provided
759  {
760  Cerr("***** iLinearModel::Initialize -> Error while trying to read the provided initialization parametric images : " << input_image << endl);
761  return 1;
762  }
763  }
764  else if( return_value == 1) // Error during reading
765  {
766  Cerr("***** iLinearModel::Initialize -> Error while trying to read dynamic frame model parametric images !" << endl);
767  Cerr(" 'Parametric_image_init' keyword in " << m_fileOptions << endl);
768  return 1;
769  }
770  else //(return_value >= 1 ) // Keyword not found : no initialization provided
771  {
772  // Standard initialization
773  for(int b=0 ; b<m_nbTimeBF ; b++)
774  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
775  m2p_parametricImages[b][v] = 1.;
776  }
777 
778 
779  // Resp gate model
780  input_image = "";
781  return_value = 0;
782 
783  return_value = ReadDataASCIIFile(m_fileOptions,
784  "Parametric_images_init",
785  &input_image,
786  1,
787  KEYWORD_OPTIONAL,
788  "RESPIRATORY GATING",
789  "ENDRG");
790 
791  if( return_value == 0) // Image have been provided
792  {
793  // Read image // INTF_LERP_DISABLED = interpolation disabled for input image reading
794  if( IntfReadImgDynCoeffFile(input_image,
796  mp_ID,
798  m_verbose,
799  INTF_LERP_DISABLED) ) // Image have been provided
800  {
801  Cerr("***** iLinearModel::Initialize -> Error while trying to read the provided initialization parametric images : " << input_image << endl);
802  return 1;
803  }
804  }
805  else if( return_value == 1) // Error during reading
806  {
807  Cerr("***** iLinearModel::Initialize -> Error while trying to read respiratory gate model parametric images !" << endl);
808  Cerr(" 'Parametric_image_init' keyword in " << m_fileOptions << endl);
809  return 1;
810  }
811  else //(return_value >= 1 ) // Keyword not found : no initialization provided
812  {
813  // Standard initialization
814  for(int rb=0 ; rb<m_nbRgateBF ; rb++)
815  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
816  m2p_RGParametricImages[rb][v] = 1.;
817  }
818 
819 
820 
821  // Card gate model
822  input_image = "";
823  return_value = 0;
824 
825  return_value = ReadDataASCIIFile(m_fileOptions,
826  "Parametric_images_init",
827  &input_image,
828  1,
829  KEYWORD_OPTIONAL,
830  "CARDIAC GATING",
831  "ENDCG");
832 
833  if( return_value == 0) // Image have been provided
834  {
835  // Read image // INTF_LERP_DISABLED = interpolation disabled for input image reading
836  if( IntfReadImgDynCoeffFile(input_image,
838  mp_ID,
840  m_verbose,
841  INTF_LERP_DISABLED) ) // Image have been provided
842  {
843  Cerr("***** iLinearModel::Initialize -> Error while trying to read the provided initialization parametric images : " << input_image << endl);
844  return 1;
845  }
846  }
847  else if( return_value == 1) // Error during reading
848  {
849  Cerr("***** iLinearModel::Initialize -> Error while trying to read cardiac gate model parametric images !" << endl);
850  Cerr(" 'Parametric_image_init' keyword in " << m_fileOptions << endl);
851  return 1;
852  }
853  else //(return_value >= 1 ) // Keyword not found : no initialization provided
854  {
855  // Standard initialization
856  for(int cb=0 ; cb<m_nbCgateBF ; cb++)
857  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
858  m2p_CGParametricImages[cb][v] = 1.;
859  }
860  }
861 
862  else
863  {
864  Cerr("***** iLinearModel::Initialize() -> Error while trying to read configuration file at: " << m_fileOptions << endl);
865  return 1;
866  }
867  }
868 
869  // If weight values have been provided check they are not negative
870  if (m_nbWeightFactors>1)
871  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
872  {
873  if (mp_w[t]<=0)
874  {
875  Cerr("***** iLinearModel::CheckSpecificParametersForAllLinearModels() -> Error, negative weight factors found. Only positive non-zero factors allowed !" << endl);
876  return 1;
877  }
878  if(m_verbose >=4) Cout("iLinearModel::NNLS optimisation weight for frame: "<< t << " set at "<< mp_w[t]<< endl);
879  }
880 
881 
882 
883  // --- Data Initialization with a list of options --- //
884 
885  if(m_listOptions != "")
886  {
887  // TODO
888  }
889 
890  // Allocate output image matrices
891  for(int b=0 ; b<m_nbTimeBF ; b++)
893 
894  // If optimisation method requires an input of basis functions set m_ModelSpecificBasisFunctionsRequired flat
896  {
898  m_noImageUpdateFlag = true;
899  }
900 
901  // TODO : print output parametric images rg et cg
902 
903 
904  // Normal End
905  return 0;
906 }
907 
908 
909 // =====================================================================
910 // ---------------------------------------------------------------------
911 // ---------------------------------------------------------------------
912 // =====================================================================
913 /*
914  \fn ShowBasisFunctions
915  \brief This function is used to initialize the parametric images and basis functions
916  \return 0 if success, other value otherwise.
917 */
918 
920 {
921 
922  // Display Basis Functions set for the model
923  if ((m_verbose>=2) & (m_nbTimeBF>1))
924  {
925  Cout("***** iLinearModel::InitializeSpecificToAllLinearModels() -> Time Basis Function coefficients :" << endl);
926  for(int b=0 ; b<m_nbTimeBF ; b++)
927  {
928  Cout(" ");
929  Cout("Basis function["<<b+1<<"] : ");
930  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames()-1 ; fr++)
931  {
932  Cout(m2p_nestedModelTimeBasisFunctions[b][fr] << ", ");
933  }
934  // Print last value
936  }
937  }
938 
939 
940 }
941 
942 // =====================================================================
943 // ---------------------------------------------------------------------
944 // ---------------------------------------------------------------------
945 // =====================================================================
946 /*
947  \fn InitializeSpecific
948  \brief This function is used to initialize the parametric images and basis functions
949  \return 0 if success, other value otherwise.
950 */
952 {
953  if(m_verbose >=2) Cout("iLinearModel::InitializeSpecific ..."<< endl);
954 
955  // Forbid initialization without check
956  if (!m_checked)
957  {
958  Cerr("***** oDynamicModelManager::InitializeSpecific() -> Must call CheckParameters functions before Initialize() !" << endl);
959  return 1;
960  }
961 
962  // Run generic Initialization for all Linear Models
964  {
965  Cerr("***** iLinearModel::InitializeSpecific() -> Error while performing generic initialisations for linear models !" << endl);
966  return 1;
967  }
968 
969 
970  // Normal end
971  m_initialized = true;
972  return 0;
973 }
974 
975 
976 
977 
978 // =====================================================================
979 // ---------------------------------------------------------------------
980 // ---------------------------------------------------------------------
981 // =====================================================================
982 /*
983  \fn EstimateModelParameters
984  \param ap_ImageS : pointer to the ImageSpace
985  \param a_ite : index of the actual iteration (not used)
986  \param a_sset : index of the actual subset (not used)
987  \brief Estimate model parameters (parametric images and basis functions)
988  \return 0 if success, other value otherwise.
989 */
990 int iLinearModel::EstimateModelParameters(oImageSpace* ap_ImageS, int a_ite, int a_sset)
991 {
992 
993  #ifdef CASTOR_DEBUG
994  if (!m_initialized)
995  {
996  Cerr("***** iLinearModel::EstimateModelParameters() -> Called while not initialized !" << endl);
997  Exit(EXIT_DEBUG);
998  }
999  #endif
1000 
1001  if(m_verbose >=3) Cout(" iLinearModel::EstimateModelParameters -> Optimisation method to use: " << m_OptimisationMethod << endl) ;
1002 
1003  // Use the nested EM parametric image estimation method
1005  {
1006  if(NestedEM(ap_ImageS, a_ite) )
1007  {
1008  Cerr("***** iLinearModel::EstimateModelParameters() -> An error occured while using the nested EM parametric image estimation method !" << endl);
1009  return 1;
1010  }
1011  }
1012  // Direct reconstruction ( non nested ) - no update required
1014  {
1015  Cout("iLinearModel::EstimateModelParameters() -> Skipping nested calculation, not required for direct method " << endl);
1016  }
1017 
1018  // Least Square for Post Reconsutrction Patlak Analysis
1020  {
1021  if(EstimateParametersWithNNLS(ap_ImageS, a_ite) )
1022  {
1023  Cerr("***** iLinearModel::EstimateModelParameters() -> An error occured while using the Patlak LS Estimation Method !" << endl);
1024  return 1;
1025  }
1026  }
1027  // least-square linear regression
1029  {
1030  if(Patlak_LS(ap_ImageS, a_ite) )
1031  {
1032  Cerr("***** iLinearModel::EstimateModelParameters() -> An error occured while using the Least-Squares linear regression Estimation Method !" << endl);
1033  return 1;
1034  }
1035  }
1036 
1037  else
1038  {
1039  Cerr("***** iLinearModel::EstimateModelParameters() -> Error : unknown method to estimate images ! !" << endl);
1040  return 1;
1041  }
1042 
1043  if(m_verbose >=3)
1044  {
1045  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1046  {
1047  FLTNB avg = 0;
1048  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1049  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1050  avg += m2p_parametricImages[fb][v];
1051 
1052  avg /= m_nbVoxelsMask;
1053  Cout( "iLinearModel::EstimateModelParameters() -> Frame parametric image["<<fb<<"] avg value:" << avg << endl);
1054  }
1055  }
1056 
1057  return 0;
1058 }
1059 
1060 
1061 // =====================================================================
1062 // ---------------------------------------------------------------------
1063 // ---------------------------------------------------------------------
1064 // =====================================================================
1065 /*
1066  \fn NestedEM
1067  \param ap_ImageS : pointer to the ImageSpace
1068  \param a_ite : index of the actual iteration (not used)
1069  \brief Estimate parametric images and basis functions (if enabled) using the nested EM method
1070  \return 0 if success, other value otherwise.
1071 */
1072 int iLinearModel::NestedEM(oImageSpace* ap_ImageS, int a_ite)
1073 {
1074  if(m_verbose >=3) Cout("iLinearModel::NestedEM() ..." <<endl);
1075 
1076  for (uint32_t it=0 ; it<m_nbLinearModelCycles ; it++)
1077  {
1078  if(m_verbose >=3) Cout("iLinearModel::NestedEM() cycle "<< it+1 << "/" << m_nbLinearModelCycles <<endl);
1079 
1080  // Step 1 : Generate a difference image from voxel-by-voxel division of the current estimation of the image and the model image generated from the current parametric images / basis functions of the model
1081  // The backward image matrix (which is useless at this point of the reconstruction) is used as provisional image to gather the model image
1082 
1083  int v;
1084  #pragma omp parallel for private(v) schedule(static, 1)
1085  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1086  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1087  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1088  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1089  {
1090  // If a mask has been provided, check if the model applies in this voxel
1091  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1092  continue;
1093 
1094  // Reset this voxel to 0
1095  ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v] = 0;
1096 
1097  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1098  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1099  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1100  {
1101  // Retrieve current estimation of image according to coeffs/basis functions
1102  ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v] += m2p_parametricImages[fb][v]
1104  * m2p_RGParametricImages[rb][v]
1105  * m2p_respBasisFunctions[rb][rg]
1106  * m2p_CGParametricImages[cb][v]
1107  * m2p_cardBasisFunctions[cb][cg] ;
1108  }
1109 
1110  // Recover correction images using the ratio of the current estimation of the image (m4p_image) and the image generated with coffs/basis functions
1111  // (Again, use backward image as provisional image to recover the result)
1112 
1113  // TODO: Find where NaN is generated
1114  if (ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v] == 0 )
1115  {
1116  mp_blackListedvoxelsImage[v] = 1.0;
1117  ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v] = 1;
1118  }
1119  else
1120  {
1121  ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v] =
1122  ap_ImageS->m4p_image[fr][rg][cg][v] / ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v];
1123  }
1124  }
1125 
1126  // Step 2 : Estimate either basis functions or parametric images
1127 
1128  // Step 2a : Basis functions estimation // TODO: shouldn't this condition be a single & as we want all conditions ??
1129  if( m_basisFunctionsUpdStartIte >= 0 // if (m_basisFunctionsUpdStartIte < 0) --> no update of basis functions, just update the parametric images
1130  && m_basisFunctionsUpdStartIte <= a_ite // if (m_basisFunctionsUpdStartIte >= 0) --> Check condition of minimal iteration before starting to update the basis functions
1131  && !(int((m_basisFunctionsUpdIdx-1)/m_basisFunctionsUpdRatio)&1) ) // Check if we must estimate basis functions or coefficients at this stage (depends on m_basisFunctionsUpdRatio)
1132  {
1133 
1134  FLTNB parametric_image_norm = 0;
1135 
1136  if( m_nbTimeBF>1 )
1137  {
1138  if(m_verbose >=3) Cout("iLinearModel::NestedEM() -> Estimate Basis Functions - Frame basis functions estimation step" <<endl);
1139 
1140  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1141  {
1142  // Compute normalization related to the vowelwise time basis functions coefficients.
1143  parametric_image_norm = 0;
1144 
1145  // Compute normalization related to the parametric images.
1146  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1147  for (int cb=0 ; cb<m_nbRgateBF ; cb++)
1148  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1149  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1150  parametric_image_norm += m2p_parametricImages[fb][v]
1151  * m2p_RGParametricImages[rb][v]
1152  * m2p_CGParametricImages[cb][v];
1153 
1154  parametric_image_norm *= mp_ID->GetNbRespGates()
1155  * mp_ID->GetNbCardGates();
1156 
1157  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1158  {
1159  // Initialization of corrections factor for time basis functions
1160  for (int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
1161  mp_corrBasisFunctions[th]=0;
1162 
1163  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1164  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1165  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1166  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1167  {
1168  int v;
1169  #pragma omp parallel for private(v) schedule(static, 1)
1170  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1171  {
1172  // If a mask has been provided, check if the model applies in this voxel
1173  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1174  continue;
1175 
1176  int th = 0;
1177  #ifdef CASTOR_OMP
1178  th = omp_get_thread_num();
1179  #endif
1180 
1181  mp_corrBasisFunctions[th] += ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v]
1182  * m2p_parametricImages[fb][v]
1183  * m2p_RGParametricImages[rb][v]
1184  * m2p_CGParametricImages[cb][v];
1185  }
1186  }
1187 
1188  // Reduce
1189  for (int th=1 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
1191 
1192  // Apply corrections and normalization to the temporal basis functions values
1193  if (mp_corrBasisFunctions[0] > 0.)
1194  m2p_nestedModelTimeBasisFunctions[fb][fr] *= mp_corrBasisFunctions[0]/parametric_image_norm;
1195  }
1196 
1197  }
1198  }
1199 
1200  if( m_nbRgateBF>1 )
1201  {
1202  if(m_verbose >=3) Cout("iLinearModel::NestedEM() -> Estimate Basis Functions - Respiratory gate basis functions estimation step" <<endl);
1203 
1204  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1205  {
1206  // Compute normalization related to the vowelwise time basis functions coefficients.
1207  parametric_image_norm = 0;
1208 
1209  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1210  for (int cb=0 ; cb<m_nbRgateBF ; cb++)
1211  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1212  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1213  parametric_image_norm += m2p_parametricImages[fb][v]
1214  * m2p_RGParametricImages[rb][v]
1215  * m2p_CGParametricImages[cb][v];
1216 
1217  parametric_image_norm *= mp_ID->GetNbTimeFrames()
1218  * mp_ID->GetNbCardGates();
1219 
1220  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1221  {
1222  // Initialization of corrections for time basis functions
1223  for (int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
1224  mp_corrBasisFunctions[th]=0;
1225 
1226  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1227  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1228  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1229  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1230  {
1231  int v;
1232  #pragma omp parallel for private(v) schedule(static, 1)
1233  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1234  {
1235  // If a mask has been provided, check if the model applies in this voxel
1236  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1237  continue;
1238 
1239  int th = 0;
1240  #ifdef CASTOR_OMP
1241  th = omp_get_thread_num();
1242  #endif
1243  mp_corrBasisFunctions[th] += ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v]
1244  * m2p_parametricImages[fb][v]
1245  * m2p_RGParametricImages[rb][v]
1246  * m2p_CGParametricImages[cb][v];
1247  }
1248  }
1249 
1250  // Reduce
1251  for (int th=1 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
1253 
1254  // Apply corrections and normalization to the temporal basis functions values
1255  if (mp_corrBasisFunctions[0] > 0.)
1256  m2p_respBasisFunctions[rb][rg] *= mp_corrBasisFunctions[0]/parametric_image_norm;
1257 
1258  }
1259  }
1260  }
1261 
1262 
1263  if( m_nbCgateBF>1 )
1264  {
1265  if(m_verbose >=3) Cout("iLinearModel::NestedEM() -> Estimate Basis Functions - Cardiac gate basis functions estimation step" <<endl);
1266 
1267  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1268  {
1269  // Compute normalization related to the vowelwise time basis functions coefficients.
1270  parametric_image_norm = 0;
1271 
1272  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1273  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1274  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1275  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1276  parametric_image_norm += m2p_parametricImages[fb][v]
1277  * m2p_RGParametricImages[rb][v]
1278  * m2p_CGParametricImages[cb][v];
1279 
1280  parametric_image_norm *= mp_ID->GetNbTimeFrames()
1281  * mp_ID->GetNbRespGates();
1282 
1283  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1284  {
1285  // Initialization of corrections for time basis functions
1286  for (int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
1287  mp_corrBasisFunctions[th]=0;
1288 
1289  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1290  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1291  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1292  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1293  {
1294  int v;
1295  #pragma omp parallel for private(v) schedule(static, 1)
1296  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1297  {
1298  // If a mask has been provided, check if the model applies in this voxel
1299  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1300  continue;
1301 
1302  int th = 0;
1303  #ifdef CASTOR_OMP
1304  th = omp_get_thread_num();
1305  #endif
1306  mp_corrBasisFunctions[th] += ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v]
1307  * m2p_parametricImages[fb][v]
1308  * m2p_RGParametricImages[rb][v]
1309  * m2p_CGParametricImages[cb][v];
1310  }
1311  }
1312 
1313  // Reduce
1314  for (int th=1 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
1316 
1317  // Apply corrections and normalization to the temporal basis functions values
1318  if (mp_corrBasisFunctions[0] > 0.)
1319  m2p_cardBasisFunctions[cb][cg] *= mp_corrBasisFunctions[0]/parametric_image_norm;
1320 
1321  }
1322  }
1323  }
1324 
1326 
1327  // Some feedback :
1328  if(m_verbose >=3)
1329  {
1330  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1331  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1332  Cout( "iLinearModel::NestedEM() -> Basis function ["<<fb<<"] coefficients for frame ["<<fr<<"] :" << m2p_nestedModelTimeBasisFunctions[fb][fr] << endl);
1333 
1334  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1335  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1336  Cout( "iLinearModel::NestedEM() -> Basis function ["<<rb<<"] coefficients for resp gate ["<<rg<<"] :" << m2p_respBasisFunctions[rb][rg] << endl);
1337 
1338  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1339  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1340  Cout( "iLinearModel::NestedEM() -> Basis function ["<<cb<<"] coefficients for card gate ["<<cg<<"] :" << m2p_cardBasisFunctions[cb][cg] << endl);
1341  }
1342  } // end of if loop (basis functions update)
1343 
1344 
1345 
1346  // Step 2b : Image Coefficients estimation
1347  else
1348  {
1349  // Normalization general factor
1350  FLTNB basis_functions_norm = 0.;
1351 
1352  // Regularisation according to frame basis functions
1353  if( m_nbModelParam>1 )
1354  {
1355  if(m_verbose >=3) Cout("iLinearModel::NestedEM() -> Estimate Coefficients - Frame coeffs estimation step" <<endl);
1356 
1357  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1358  {
1359  // Initialization of voxelwise corrections coefficients for time basis functions coefficients
1360  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1361  mp_corrBasisCoeffs[v] = 0;
1362 
1363  basis_functions_norm = 0.;
1364 
1365 
1366  // Compute normalization related to the temporal basis functions.
1367  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1368  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1369  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1370  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1371  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1372  basis_functions_norm += m2p_nestedModelTimeBasisFunctions[fb][fr]
1373  * m2p_respBasisFunctions[rb][rg]
1374  * m2p_cardBasisFunctions[cb][cg];
1375 
1376  int v;
1377  #pragma omp parallel for private(v) schedule(static, 1)
1378  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1379  {
1380  // If a mask has been provided, check if the model applies in this voxel
1381  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1382  continue;
1383 
1384  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1385  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1386  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1387  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1388  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1389  {
1390  mp_corrBasisCoeffs[v] += ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v]
1392  * m2p_respBasisFunctions[rb][rg]
1393  * m2p_cardBasisFunctions[cb][cg];
1394  }
1395 
1396  // Apply corrections and normalization to the frame model parametric image.
1397  if (mp_corrBasisCoeffs[v] >= 0.) // could be == 0
1398  m2p_parametricImages[fb][v] *= mp_corrBasisCoeffs[v]/basis_functions_norm;
1399  }
1400  }
1401  }
1402 
1403  // Regularisation according to respiratory basis functions
1404 
1405  if( m_nbRGModelParam>1 )
1406  {
1407  if(m_verbose >=3) Cout("iLinearModel::NestedEM() -> Estimate Coefficients - Respiratory Gate coeffs estimation step" <<endl);
1408 
1409  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1410  {
1411  // Initialization of voxelwise corrections for time basis functions coefficients
1412  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1413  mp_corrBasisCoeffs[v] = 0;
1414 
1415  basis_functions_norm = 0.;
1416 
1417  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1418  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1419  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1420  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1421  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1422  basis_functions_norm += m2p_nestedModelTimeBasisFunctions[fb][fr]
1423  * m2p_respBasisFunctions[rb][rg]
1424  * m2p_cardBasisFunctions[cb][cg];
1425 
1426  // Compute corrections for the time vowelwise basis functions coefficients
1427  int v;
1428  #pragma omp parallel for private(v) schedule(static, 1)
1429  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1430  {
1431  // If a mask has been provided, check if the model applies in this voxel
1432  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1433  continue;
1434 
1435  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1436  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1437  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1438  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1439  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1440  {
1441  mp_corrBasisCoeffs[v] += ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v]
1443  * m2p_respBasisFunctions[rb][rg]
1444  * m2p_cardBasisFunctions[cb][cg];
1445  }
1446 
1447  // Apply corrections and normalization to the respiratory model parametric image.
1448  if (mp_corrBasisCoeffs[v] >= 0.)
1449  m2p_RGParametricImages[rb][v] *= mp_corrBasisCoeffs[v]/basis_functions_norm;
1450  }
1451  }
1452  }
1453 
1454 
1455  // Regularisation according to cardiac basis functions
1456  if( m_nbCGModelParam>1 )
1457  {
1458  if(m_verbose >=3) Cout("iLinearModel::NestedEM() -> Estimate Coefficients - Cardiac Gate coeffs estimation step" <<endl);
1459 
1460  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1461  {
1462  // Initialization of voxelwise corrections for time basis functions coefficients
1463  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1464  mp_corrBasisCoeffs[v] = 0;
1465 
1466  basis_functions_norm = 0.;
1467 
1468  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1469  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1470  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1471  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1472  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1473  basis_functions_norm += m2p_nestedModelTimeBasisFunctions[fb][fr]
1474  * m2p_respBasisFunctions[rb][rg]
1475  * m2p_cardBasisFunctions[cb][cg];
1476 
1477  // Compute corrections for the time vowelwise basis functions coefficients
1478  int v;
1479  #pragma omp parallel for private(v) schedule(static, 1)
1480  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1481  {
1482  // If a mask has been provided, check if the model applies in this voxel
1483  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1484  continue;
1485 
1486  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1487  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1488  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1489  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1490  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1491  {
1492  mp_corrBasisCoeffs[v] += ap_ImageS->m6p_backwardImage[0][0][fr][rg][cg][v]
1494  * m2p_respBasisFunctions[rb][rg]
1495  * m2p_cardBasisFunctions[cb][cg];
1496  }
1497 
1498  // Apply corrections and normalization to the cardiac model parametric image.
1499  if (mp_corrBasisCoeffs[v] >= 0.)
1500  m2p_CGParametricImages[cb][v] *= mp_corrBasisCoeffs[v]/basis_functions_norm;
1501  }
1502  }
1503  }
1504 
1506 
1507  // Some feedback :
1508  if(m_verbose >=3)
1509  {
1510  if( m_nbModelParam>1 )
1511  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1512  {
1513  FLTNB avg = 0;
1514  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1515  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1516  avg += m2p_parametricImages[fb][v];
1517 
1518  avg /= m_nbVoxelsMask;
1519  Cout( "iLinearModel::NestedEM() -> Frame parametric image["<<fb<<"] avg value:" << avg << endl);
1520  }
1521 
1522  if( m_nbRGModelParam>1 )
1523  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1524  {
1525  FLTNB avg = 0;
1526 
1527  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1528  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1529  avg += m2p_RGParametricImages[rb][v];
1530 
1531  avg /= m_nbVoxelsMask;
1532  Cout( "iLinearModel::NestedEM() -> Resp gate parametric image["<<rb<<"] avg value:" << avg << endl);
1533  }
1534 
1535  if( m_nbCGModelParam>1 )
1536  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1537  {
1538  FLTNB avg = 0;
1539 
1540  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1541  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1542  avg += m2p_CGParametricImages[cb][v];
1543 
1544  avg /= m_nbVoxelsMask;
1545  Cout( "iLinearModel::NestedEM() -> Card gate parametric image["<<cb<<"] avg value:" << avg << endl);
1546  }
1547  }
1548 
1549  } // end of else loop (parametric images update)
1550 
1551  } // end of loop on cycles (linear model iterations)
1552 
1553  return 0;
1554 }
1555 
1556 
1557 
1558 // =====================================================================
1559 // ---------------------------------------------------------------------
1560 // ---------------------------------------------------------------------
1561 // =====================================================================
1562 /*
1563  \fn EstimateParametersWithNNLS
1564  \param ap_ImageS : pointer to the ImageSpace
1565  \param a_ite : index of the actual iteration (not used)
1566  \brief Estimate parametric images using the NNLS method
1567  \return 0 if success, other value otherwise.
1568 */
1569 int iLinearModel::EstimateParametersWithNNLS(oImageSpace* ap_ImageS, int a_ite)
1570 {
1571  if(m_verbose >=3) Cout("iLinearModel::EstimateParametersWithNNLS() ..." <<endl);
1572 
1573  // Regularisation according to frame basis functions
1574  if( m_nbModelParam>1 )
1575  {
1576  if(m_verbose >=3) Cout("iLinearModel::EstimateParametersWithNNLS() -> Estimate Coefficients - Frame coeffs estimation step" <<endl);
1577 
1578  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1579  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1580  {
1581  int v;
1582  #pragma omp parallel for private(v) schedule(guided)
1583  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1584  {
1585  // If a mask has been provided, check if the model applies in this voxel
1586  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1587  continue;
1588 
1589  int th=0;
1590  #ifdef CASTOR_OMP
1591  th = omp_get_thread_num();
1592  #endif
1593 
1594  FLTNB** pp_nnls_A = m3p_nnlsA[ th ];
1595  FLTNB* p_nnls_B = m2p_nnlsB[ th ];
1596  FLTNB nnls_rnorm;
1597  FLTNB *p_nnls_zz=NULL;
1598 
1599  // Fill NNLS matrices
1600  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
1601  {
1602  // Fill NNLS A matrix:
1603  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1604  pp_nnls_A[ fb ][ t ] = m2p_nestedModelTimeBasisFunctions[ fb ][ t ] * mp_w[ t ];
1605 
1606  // Fill NNLS B array: tissue
1607  p_nnls_B[ t ]=ap_ImageS->m4p_image[ t ][ rg ][ cg ][ v ] * mp_w[ t ];
1608  }
1609 
1610  // Launch NNLS
1611  if(NNLS(pp_nnls_A,
1613  m_nbTimeBF,
1614  p_nnls_B,
1615  m2p_nnlsX[ th ],
1616  &nnls_rnorm,
1617  m2p_nnlsWp[ th ],
1618  p_nnls_zz,
1619  m2p_nnlsIdx[ th ]) )
1620  continue;
1621 
1622  // Recover parameters
1623  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1624  m2p_parametricImages[fb][v] = m2p_nnlsX[ th ][ fb ];
1625 
1626  } // end of voxel loop
1627 
1628  }
1629  }
1630 
1631  // Regularisation according to respiratory basis functions
1632 
1633  if( m_nbRGModelParam>1 )
1634  {
1635  if(m_verbose >=3) Cout("iLinearModel::EstimateParametersWithNNLS() -> Estimate Coefficients - Respiratory Gate coeffs estimation step" <<endl);
1636 
1637  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1638  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1639  {
1640  int v;
1641  #pragma omp parallel for private(v) schedule(guided)
1642  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1643  {
1644  // If a mask has been provided, check if the model applies in this voxel
1645  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1646  continue;
1647 
1648  int th=0;
1649  #ifdef CASTOR_OMP
1650  th = omp_get_thread_num();
1651  #endif
1652 
1653  FLTNB** pp_nnls_A = m3p_nnlsA[ th ];
1654  FLTNB* p_nnls_B = m2p_nnlsB[ th ];
1655  FLTNB nnls_rnorm;
1656  FLTNB *p_nnls_zz=NULL;
1657 
1658  // Fill NNLS matrices
1659  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1660  {
1661  // Fill NNLS A matrix:
1662  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1663  pp_nnls_A[ rb ][ rg ] = m2p_respBasisFunctions[ rb ][ rg ];
1664 
1665  // Fill NNLS B array: tissue
1666  p_nnls_B[ rg ]=ap_ImageS->m4p_image[ fr ][ rg ][ cg ][ v ];
1667  }
1668 
1669  // Launch NNLS
1670  if(NNLS(pp_nnls_A,
1671  mp_ID->GetNbRespGates(),
1672  m_nbRgateBF,
1673  p_nnls_B,
1674  m2p_nnlsX[ th ],
1675  &nnls_rnorm,
1676  m2p_nnlsWp[ th ],
1677  p_nnls_zz,
1678  m2p_nnlsIdx[ th ]) )
1679  continue;
1680 
1681  // Recover parameters
1682  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1683  m2p_RGParametricImages[rb][v] = m2p_nnlsX[ th ][ rb ];
1684 
1685  } // end of loop on voxels
1686 
1687  }
1688  }
1689 
1690 
1691  // Regularisation according to cardiac basis functions
1692  if( m_nbCGModelParam>1 )
1693  {
1694  if(m_verbose >=3) Cout("iLinearModel::EstimateParametersWithNNLS() -> Estimate Coefficients - Cardiac Gate coeffs estimation step" <<endl);
1695 
1696  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1697  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1698  {
1699  int v;
1700  #pragma omp parallel for private(v) schedule(guided)
1701  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1702  {
1703  // If a mask has been provided, check if the model applies in this voxel
1704  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1705  continue;
1706 
1707  int th=0;
1708  #ifdef CASTOR_OMP
1709  th = omp_get_thread_num();
1710  #endif
1711 
1712  FLTNB** pp_nnls_A = m3p_nnlsA[ th ];
1713  FLTNB* p_nnls_B = m2p_nnlsB[ th ];
1714  FLTNB nnls_rnorm;
1715  FLTNB *p_nnls_zz=NULL;
1716 
1717  // Fill NNLS matrices
1718  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1719  {
1720  // Fill NNLS A matrix:
1721  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1722  pp_nnls_A[ cb ][ cg ] = m2p_cardBasisFunctions[ cb ][ cg ];
1723 
1724  // Fill NNLS B array: tissue
1725  p_nnls_B[ cg ]=ap_ImageS->m4p_image[ fr ][ rg ][ cg ][ v ];
1726  }
1727 
1728  // Launch NNLS
1729  if(NNLS(pp_nnls_A,
1730  mp_ID->GetNbCardGates(),
1731  m_nbCgateBF,
1732  p_nnls_B,
1733  m2p_nnlsX[ th ],
1734  &nnls_rnorm,
1735  m2p_nnlsWp[ th ],
1736  p_nnls_zz,
1737  m2p_nnlsIdx[ th ]) )
1738  continue;
1739 
1740  // Recover parameters
1741  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1742  m2p_CGParametricImages[cb][v] = m2p_nnlsX[ th ][ cb ];
1743 
1744  } // end of loop on voxels
1745 
1746  }
1747  }
1748 
1749 
1750  // Some feedback :
1751  if(m_verbose >=3)
1752  {
1753  if( m_nbModelParam>1 )
1754  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1755  {
1756  FLTNB avg = 0;
1757  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1758  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1759  avg += m2p_parametricImages[fb][v];
1760 
1761  avg /= m_nbVoxelsMask;
1762  Cout( "iLinearModel::EstimateParametersWithNNLS() -> Frame parametric image["<<fb<<"] avg value:" << avg << endl);
1763  }
1764 
1765  if( m_nbRGModelParam>1 )
1766  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1767  {
1768  FLTNB avg = 0;
1769 
1770  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1771  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1772  avg += m2p_RGParametricImages[rb][v];
1773 
1774  avg /= m_nbVoxelsMask;
1775  Cout( "iLinearModel::EstimateParametersWithNNLS() -> Resp gate parametric image["<<rb<<"] avg value:" << avg << endl);
1776  }
1777 
1778  if( m_nbCGModelParam>1 )
1779  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1780  {
1781  FLTNB avg = 0;
1782 
1783  for (int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1784  if(mp_maskModel != NULL && mp_maskModel[ v ] == 1)
1785  avg += m2p_CGParametricImages[cb][v];
1786 
1787  avg /= m_nbVoxelsMask;
1788  Cout( "iLinearModel::EstimateParametersWithNNLS() -> Card gate parametric image["<<cb<<"] avg value:" << avg << endl);
1789  }
1790  }
1791 
1792  return 0;
1793 }
1794 
1795 
1796 // =====================================================================
1797 // ---------------------------------------------------------------------
1798 // ---------------------------------------------------------------------
1799 // =====================================================================
1800 /*
1801  \fn EstimateParametersWithLS (linear regresion)
1802  \param ap_ImageS : pointer to the ImageSpace
1803  \param a_ite : index of the actual iteration (not used)
1804  \brief Estimate parametric images using LS linear regression
1805  \return 0 if success, other value otherwise.
1806 */
1807 
1808 int iLinearModel::Patlak_LS(oImageSpace* ap_ImageS, int a_ite)
1809 {
1810  if(m_verbose >=3) Cout("iLinearPatlakModel::Patlak_LS ..." <<endl);
1811 
1812  int v;
1813  #pragma omp parallel for private(v) schedule(guided)
1814  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1815  {
1816  // If a mask has been provided, check if the model applies in this voxel
1817  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1818  continue;
1819 
1820  FLTNB xmean = 0.,
1821  ymean = 0.,
1822  K = 0.,
1823  Vd = 0.,
1824  Cov= 0.,
1825  Var = 0.;
1826 
1827 
1828  // Compute means for this voxel
1829  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1830  {
1832  ymean += ap_ImageS->m4p_image[fr][0][0][v]/m2p_nestedModelTimeBasisFunctions[1][fr];
1833  }
1834 
1835  xmean /= mp_ID->GetNbTimeFrames();
1836  ymean /= mp_ID->GetNbTimeFrames();
1837 
1838  FLTNB Yt,Xt ;
1839 
1840  // Compute covariance & variance
1841  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1842  {
1843  Yt = ap_ImageS->m4p_image[fr][0][0][v]/m2p_nestedModelTimeBasisFunctions[1][fr];
1845  Cov += (Xt-xmean) * (Yt-ymean);
1846  Var += (Xt-xmean) * (Xt-xmean);
1847  }
1848 
1849  // Slope
1850  K = (Var != 0) ? Cov/Var : 0.;
1851 
1852  // Non-negativity constraint
1853  if(K<0) K = 0.;
1854 
1855  // Intercept
1856  Vd = ymean - K*xmean;
1857 
1858  // Non-negativity constraint
1859  if(Vd<0) Vd = 0.;
1860 
1861  m2p_parametricImages[0][v] = K;
1862  m2p_parametricImages[1][v] = Vd;
1863  }
1864 
1865  return 0;
1866 }
1867 
1868 
1869 // =====================================================================
1870 // ---------------------------------------------------------------------
1871 // ---------------------------------------------------------------------
1872 // =====================================================================
1873 /*
1874  \fn EstimateImageWithModel
1875  \param ap_ImageS : pointer to the ImageSpace
1876  \param a_ite : index of the actual iteration (not used)
1877  \param a_sset : index of the actual subset (not used)
1878  \brief Re-estimate image using the linear parametric images and basis functions
1879  \return 0 if success, other value otherwise.
1880 */
1881 int iLinearModel::EstimateImageWithModel(oImageSpace* ap_ImageS, int a_ite, int a_sset)
1882 {
1883  if(m_verbose >= 3) Cout("iLinearModel::EstimateImageWithModel ... " <<endl);
1884 
1885  #ifdef CASTOR_DEBUG
1886  if (!m_initialized)
1887  {
1888  Cerr("***** iLinearModel::EstimateImageWithModel() -> Called while not initialized !" << endl);
1889  Exit(EXIT_DEBUG);
1890  }
1891  #endif
1892 
1893  int v;
1894  #pragma omp parallel for private(v) schedule(static, 1)
1895  for (int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
1896  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1897  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1898  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1899  {
1900  // If a mask has been provided, check if the model applies in this voxel
1901  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1902  continue;
1903 
1904  // Reset current estimated value
1905  ap_ImageS->m4p_image[fr][rg][cg][v] = 0.;
1906 
1907  for (int fb=0 ; fb<m_nbTimeBF ; fb++)
1908  for (int rb=0 ; rb<m_nbRgateBF ; rb++)
1909  for (int cb=0 ; cb<m_nbCgateBF ; cb++)
1910  {
1911  // Retrieve current estimation of image according to coeffs/basis functions
1912  ap_ImageS->m4p_image[fr][rg][cg][v] += m2p_parametricImages[fb][v]
1914  * m2p_RGParametricImages[rb][v]
1915  * m2p_respBasisFunctions[rb][rg]
1916  * m2p_CGParametricImages[cb][v]
1917  * m2p_cardBasisFunctions[cb][cg] ;
1918  }
1919  }
1920 
1921  return 0;
1922 }
#define INTF_LERP_DISABLED
int NNLS(FLTNB **A, int m, int n, FLTNB *B, FLTNB *X, FLTNB *rnorm, FLTNB *wp, FLTNB *zzp, int *indexp)
#define Cerr(MESSAGE)
oImageDimensionsAndQuantification * mp_ID
void ShowHelp()
This function is used to print out general help about dynamic models.
int EstimateImageWithModel(oImageSpace *ap_Image, int a_ite, int a_sset)
This is the mother class of dynamic model classes.
#define OPTIMISATION_METHOD_DR
int InitializeSpecificToAllLinearModels()
This function is used to initialize the parametric images and basis functions for all Linear Models...
void Exit(int code)
void ShowHelpModelSpecific()
This function is used to print out specific help about the dynamic model and its options. It is pure virtual so must be implemented by children.
int ReadAndCheckOptionsList(string a_listOptions)
iLinearModel()
Constructor of iLinearModel. Simply set all data members to default values.
void ShowBasisFunctions()
This function is used to print the basis functions.
int CheckSpecificParametersForAllLinearModels()
This function is used to check parameters for all Linear Models. .
virtual int ReadAndCheckConfigurationFileSpecific()
This function is used to read options from a configuration file.
int EstimateParametersWithNNLS(oImageSpace *ap_ImageS, int a_ite)
#define KEYWORD_MANDATORY
#define KEYWORD_OPTIONAL
#define OPTIMISATION_METHOD_LS
int InitializeSpecific()
This function is used to initialize the parametric images and basis functions for all Linear Models...
int IntfReadImgDynCoeffFile(const string &a_pathToHeaderFile, FLTNB **a2p_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int a_nbFbases, int vb, bool a_lerpFlag)
Function dedicated to Interfile image reading for dynamic coefficients images.
This class holds all the matrices in the image domain that can be used in the algorithm: image...
int CheckSpecificParameters()
This function is used to check whether all member variables have been correctly initialized or not...
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
~iLinearModel()
Destructor of iLinearModel.
int Patlak_LS(oImageSpace *ap_ImageS, int a_ite)
#define OPTIMISATION_METHOD_NNLS
int GetNbThreadsForProjection()
Get the number of threads used for projections.
#define OPTIMISATION_METHOD_NESTEM
int NestedEM(oImageSpace *ap_ImageS, int a_ite)
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...
int ReadAndCheckConfigurationFileSpecificToAllLinearModels()
This function is used to read parameters that are generic for all Linear Models. ...
#define Cout(MESSAGE)
int EstimateModelParameters(oImageSpace *ap_Image, int a_ite, int a_sset)