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