CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/dynamic/i1TCModel.cc
Go to the documentation of this file.
1 
2 /*
3  Implementation of class i1TCModel
4 
5  - separators: X
6  - doxygen: X
7  - default initialization: none (require user inputs (basis functions), no sense to provide 'standard' configuration file as for optimizer/projector)
8  - CASTOR_DEBUG: X
9  - CASTOR_VERBOSE: X
10 */
11 
19 #include "i1TCModel.hh"
20 
21 // =====================================================================
22 // ---------------------------------------------------------------------
23 // ---------------------------------------------------------------------
24 // =====================================================================
25 /*
26  \fn i1TCModel
27  \brief Constructor of i1TCModel. Simply set all data members to default values.
28 */
30 {
31  m_nbTimeBF = 2; // Two basis functions (Plasma curve (Cp) and integral of plasma curve (Cpi))
32  m_nbModelParam = 3; // K1, k2, Va
33  m2p_parametricImages = NULL;
35 
36  mp_parUpperBounds = NULL;
37  mp_parLowerBounds = NULL;
38  m_RRcst = -1.;
39  m_ridgeRegressionFlag = false;
40 
41  m2p_ct = NULL;
42  m2p_cti = NULL;
43  mp_w = NULL;
44  m3p_nnlsA = NULL;
45  m2p_nnlsB = NULL;
46  m2p_nnlsMat = NULL;
47  mp_VaImage = NULL;
48 
49  // Matrices for LS optimization method
50  mp_Y = NULL;
51  mp_X = NULL;
52  mp_Xt = NULL;
53  mp_XtX = NULL;
54  mp_LSnum = NULL;
55  mp_LSden = NULL;
56  mp_Theta = NULL;
57  // RRLS
58  mp_RRm = NULL;
59  mp_RRw = NULL;
60  mp_RRnum = NULL;
61 
62  m_fileOptions = "";
63  m_listOptions = "";
65 
66  mp_DT2 = NULL;
67 
69  mp_wpoQ = NULL;
70  mp_wpoA = NULL;
71  m2p_wpoP = NULL;
72  m2p_wpoFD = NULL;
73  m2p_wpoBD = NULL;
74 
75 }
76 
77 
78 
79 
80 // =====================================================================
81 // ---------------------------------------------------------------------
82 // ---------------------------------------------------------------------
83 // =====================================================================
84 /*
85  \fn ~i1TCModel
86  \brief Destructor of i1TCModel
87 */
89 {
90  if(m_initialized)
91  {
92  if(m2p_ct && m2p_cti)
93  {
94  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
95  {
96  if(m2p_ct[ th ]) delete[] m2p_ct[ th ];
97  if(m2p_cti[ th ]) delete[] m2p_cti[ th ];
98  }
99 
100  delete[] m2p_ct;
101  delete[] m2p_cti;
102  }
103 
104  // NNLS variables
106  {
107  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
108  {
109  //for( int n=0 ; n<m_nnlsN ; n++ )
110  // if(m3p_nnlsA[ th ][ n ]) delete[] m3p_nnlsA[ th ][ n ];
111 
112  if(m3p_nnlsA[ th ] ) delete[] m3p_nnlsA[ th ];
113  if(m2p_nnlsB[ th ] ) delete[] m2p_nnlsB[ th ];
114  if(m2p_nnlsMat[ th ] ) delete[] m2p_nnlsMat[ th ];
115  }
116 
117  delete[] m3p_nnlsA;
118  delete[] m2p_nnlsB;
119  delete[] m2p_nnlsMat;
120  }
121 
122  if( mp_w ) delete[] mp_w;
123 
124  // Matrices for LS method
126  {
127  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
128  {
129  if( mp_Y[ th ] ) delete mp_Y[ th ] ;
130  if( mp_X[ th ] ) delete mp_X[ th ] ;
131  if( mp_Xt[ th ] ) delete mp_Xt[ th ] ;
132  if( mp_XtX[ th ] ) delete mp_XtX[ th ] ;
133  if( mp_LSnum[ th ] ) delete mp_LSnum[ th ] ;
134  if( mp_LSden[ th ] ) delete mp_LSden[ th ] ;
135  if( mp_Theta[ th ] ) delete mp_Theta[ th ] ;
136  if( mp_RRnum[ th ] ) delete mp_RRnum[ th ] ;
137  }
138 
139  if( mp_Y ) delete[] mp_Y ;
140  if( mp_X ) delete[] mp_X ;
141  if( mp_Xt ) delete[] mp_Xt;
142  if( mp_XtX ) delete[] mp_XtX;
143  if( mp_LSnum ) delete[] mp_LSnum;
144  if( mp_LSden ) delete[] mp_LSden;
145  if( mp_Theta ) delete[] mp_Theta;
146  if( mp_RRnum ) delete[] mp_RRnum;
147  if( mp_RRm ) delete[] mp_RRm;
148  if( mp_RRw ) delete[] mp_RRw;
149  }
150 
151  if( mp_parUpperBounds ) delete[]mp_parUpperBounds;
152  if( mp_parLowerBounds ) delete[]mp_parLowerBounds;
153 
154  if( mp_VaImage ) delete[] mp_VaImage;
156 
157  if( mp_DT2 ) delete[] mp_DT2;
158  if(mp_wpoQ) delete[] mp_wpoQ;
159  if(mp_wpoA) delete[] mp_wpoA;
160  if(m2p_wpoP) delete[] m2p_wpoP;
161  if(m2p_wpoFD) delete[] m2p_wpoFD;
162  if(m2p_wpoBD) delete[] m2p_wpoBD;
163  }
164 
165 }
166 
167 
168 
169 
170 // =====================================================================
171 // ---------------------------------------------------------------------
172 // ---------------------------------------------------------------------
173 // =====================================================================
174 /*
175  \fn ShowHelpModelSpecific
176  \brief Print out specific help about the implementation of the model
177  and its initialization
178 */
180 {
181  cout << "-- This class implements a 2 compartments kinetic model " << endl;
182  cout << "-- or 1 Tissue Compartment Model (1TCM) for perfusion quantitation " << endl;
183  cout << "-- for radiotracers such as [15-O]H2O. " << endl;
184  cout << endl;
185  cout << "-- *--------* K1 *--------* " << endl;
186  cout << "-- | | -----> | | " << endl;
187  cout << "-- | Cp | | Ct | " << endl;
188  cout << "-- | | <----- | | " << endl;
189  cout << "-- *--------* k2 *--------* " << endl;
190  cout << endl;
191  cout << "-- This model contains 3 parameters, including 2 rate constants, " << endl;
192  cout << "-- K1 (v/s/v, where v is a volume unit), k2 (s-1) and the arterial volume fraction in tissue (Va), " << endl;
193  cout << "-- as described by the following equations : " << endl;
194  cout << "-- (1) Cpet ( t ) = Ct( t ) + Va*Cp( t ) " << endl;
195  cout << "-- (2) dCt( t ) / dt = K1*Cp( t ) - k2*Ct( t ) " << endl;
196  cout << "-- where Cpet( t ) is the image value in voxel for frame t, and" << endl;
197  cout << "-- Ct and Cp are activity concentration in tissue and plasma," << endl;
198  cout << "-- The model estimates K1, k2 and Va parametric images." << endl;
199  cout << "-- The input function (Cp) must be provided by the user." << endl;
200  cout << endl;
201  cout << "--------------------------------------" << endl;
202  cout << " The model can be initialized using either a configuration text file, or a list of options :" << endl;
203  cout << endl;
204  cout << " - CONFIGURATION FILE : (command-line options : _1TCM:path/to/conf/file.txt:)" << endl;
205  cout << " " << endl;
206  cout << " 'Input_function:' (mandatory) " << endl;
207  cout << " Enter the activity concentration values (kBq/v/s) of the plasma function (cp) for each time frame (tf)," << endl;
208  cout << " separated by ','. Time unit is seconds : " << endl;
209  cout << " -> Input functions: " << endl;
210  cout << " -> val_cp_tf1 , val_cp_tf2 , ..., val_cp_tfn" << endl;
211  cout << endl;
212  cout << " 'Integral_input_function:' (optional) " << endl;
213  cout << " Enter the activity concentration values of the integral of the plasma function (Icp) for each time frame (tf)," << endl;
214  cout << " separated by ','. Time unit is seconds :" << endl;
215  cout << " -> Integral_input_function: " << endl;
216  cout << " -> val_Icp_tf1, val_Icp_tf2, ..., val_Icp_tfn" << endl;
217  cout << " NOTE: If not provided, the integral will be estimated from the input function" << endl;
218  cout << " Set the 'Integral method' flag below to select the method for TAC integration (default: WPO))" << endl;
219  cout << endl;
220  cout << " 'Optimisation_method:' (optional) Define the method to use for parameters estimation. Only least-squares methods " << endl;
221  cout << " (default: NNLS) are currently available to estimate O = (K1, k2, Va)" << endl;
222  cout << " Ô = [ X'WX ]-1 X'Wy, with " << endl;
223  cout << " with y = data vector " << endl;
224  cout << " X = model matrix (Cp, Icp, Cpet) " << endl;
225  cout << " W = weights vector (frame duration) " << endl;
226  cout << " If the estimated parameter values are negative, the activity value for the related voxels will be kept to their original number" << endl;
227  cout << " 0 (default) = Non-negative least-square (NNLS)" << endl;
228  cout << " Derived from Turku PET center libraries, authors: Vesa Oikonen and Kaisa Sederholm" << endl;
229  cout << " (http://www.turkupetcentre.net/petanalysis/index.html)" << endl;
230  cout << " Routine based on the text and fortran code in C.L. Lawson and R.J. Hanson," << endl;
231  cout << " Solving Least Squares Problems, Prentice-Hall, Englewood Cliffs, New Jersey, 1974." << endl;
232  cout << " Note: K1 estimated values could still be negative as they are computed from a substraction of the estimated NNLS parameters." << endl;
233  cout << " Activity values will be kept to their original values for the voxels involved" << endl;
234  cout << " 1 = Standard Least Square (LS) optimization " << endl;
235  cout << endl;
236  cout << " 'Integration_method:' (optional) Define the method to use for TAC integration over the time samples" << endl;
237  cout << " 0 (default) = Weighed parabola overlapping (WPO) (Z.Wang, D.Feng, Int. Sys. Sci 23 (1992), pp.1361-69)" << endl;
238  cout << " 1 = Trapezoidal " << endl;
239  cout << " 'Ridge_parameter:' (optional) Constant for Ridge Regression during Least-Square optimization (only available with Least-Square algorithm and not NNLS ) " << endl;
240  cout << " (default: 0) Bounds must be provided with the eponym options below in order to compute ridge weights and means for the new cost function: " << endl;
241  cout << " Ô = [ X'*W*X + t.Rw ]-1 [ X'*W*y ] " << endl;
242  cout << " + [ X'*W*X + t.Rw ]-1 [ t.Rw*Rm ] " << endl;
243  cout << " with y = data vector " << endl;
244  cout << " X = model matrix (Cp, Icp, Cpet) " << endl;
245  cout << " W = weights vector (frame duration) " << endl;
246  cout << " t = Ridge constant " << endl;
247  cout << " Rw= Ridge weights " << endl;
248  cout << " Rm= Ridge means " << endl;
249  cout << " 'Bounds:' (optional) Upper / Lower Bounds for each 3 parameters, to define ridge means mr0, and weights wr0," << endl;
250  cout << " such as mr0 = (Max+Min)/2 and wr0 = 1 / (Max-Min)^2 " << endl;
251  cout << " They must be provided as in the following syntax: " << endl;
252  cout << " Bounds: K1Max, K1Min, k2Max, k2Min, VaMax, VaMin" << endl;
253  cout << " Default: K1(Max,Min) = 0.1, 0. " << endl;
254  cout << " K2(Max,Min) = 0.1, 0. " << endl;
255  cout << " Va(Max,Min) = 1. , 0. " << endl;
256  cout << " 'VA_image:' (optional) Path to an interfile image containing the arterial volume fraction value in tissue for each voxel," << endl;
257  cout << " only K1 and k2 rate constants will be estimated (Default: All parameters are estimated) " << endl;
258  cout << endl;
259  cout << " - LINE OPTIONS : (command-line options : _1TCM,options:)" << endl;
260  cout << " " << endl;
261  cout << " Command-line options just require the samples of the plasma input curve, separated by commas. " << endl;
262  cout << " All other options will be set to default. The optimization algorithm will be NNLS, the integration method for TAC will be WPO " << endl;
263  cout << " -> 1cptModel,val_cp_tf1 , val_cp_tf2 , ..., val_cp_tfn" << endl;
264  cout << endl;
265 
266  // Print general help for all dynamic models
267  ShowHelp();
268 }
269 
270 
271 
272 
273 // =====================================================================
274 // ---------------------------------------------------------------------
275 // ---------------------------------------------------------------------
276 // =====================================================================
277 /*
278  \fn ReadAndCheckConfigurationFileSpecific
279  \brief This function is used to read options from a configuration file.
280  \return 0 if success, other value otherwise.
281 */
283 {
284  if(m_verbose >=3) Cout("i1TCModel::ReadAndCheckConfigurationFileSpecific ..."<< endl);
285 
286  // The file will be processed in the Initialize() function
287 
288  ifstream in_file(m_fileOptions.c_str(), ios::in);
289 
290  if(in_file)
291  {
292  // Method to be used to compute integral of TACs
293  // (Check this first in case we must estimate plasma integral
294  // so we need to know which method to use)
295  // Default : WPO
296 
297  string dtag = "Integration_method";
299  dtag,
301  1,
302  KEYWORD_OPTIONAL) == 1)
303  {
304  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read '"<< dtag <<"' flag in "<< m_fileOptions << endl);
305  return 1;
306  }
307 
308  // Optimisation method for the estimation of parameters
309  dtag = "Optimisation_method";
311  dtag,
313  1,
314  KEYWORD_OPTIONAL) == 1)
315  {
316  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read '"<< dtag <<"' flag in "<< m_fileOptions << endl);
317  return 1;
318  }
319 
320  // Save blacklisted voxels images on disk ?
321  dtag = "Save_blacklisted_voxels_images";
323  dtag,
325  1,
326  KEYWORD_OPTIONAL) == 1)
327  {
328  Cerr("***** vDynamicModel::ReadAndCheckConfigurationFile() -> Error while trying to read '"<< dtag <<"' flag in " << m_fileOptions << endl);
329  return 1;
330  }
331 
332  // --- Constraints for ridge regression ---
333  const int nb_params = 3;
334  // Default upper/lower bounds
335  HPFLTNB p_bounds_params[ 6 ] = {0.1, 0., 0.1, 0., 1., 0.};
336 
337  // Ridge Regression parameter
338  dtag = "Ridge_Parameter";
340  dtag,
341  &m_RRcst,
342  1,
343  KEYWORD_OPTIONAL) == 1)
344  {
345  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read '"<< dtag <<"' flag in "<< m_fileOptions << endl);
346  return 1;
347  }
348 
349  // If a ridge regression parameter constant has been provided,
350  // set up the related flag and increase the number of LS fonction models
351  if (m_RRcst>=0) m_ridgeRegressionFlag = true;
352 
353  dtag = "Bounds";
355  dtag,
356  p_bounds_params,
357  2*nb_params,
358  KEYWORD_OPTIONAL) == 1)
359  {
360  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read '"<< dtag <<"' flag (upper/lower bounds) in "<< m_fileOptions << endl);
361  return 1;
362  }
363 
364  // init parameters bounds
365  mp_parUpperBounds = new FLTNB[ nb_params ];
366  mp_parLowerBounds = new FLTNB[ nb_params ];
367 
368  for (int p=0 ; p<nb_params ; p++ )
369  {
370  mp_parUpperBounds[ p ] = p_bounds_params[ p*2 ];
371  mp_parLowerBounds[ p ] = p_bounds_params[ p*2 + 1 ];
372  }
373  }
374  else
375  {
376  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read configuration file at: " << m_fileOptions << endl);
377  return 1;
378  }
379 
380  return 0;
381 }
382 
383 
384 
385 
386 // =====================================================================
387 // ---------------------------------------------------------------------
388 // ---------------------------------------------------------------------
389 // =====================================================================
390 /*
391  \fn ReadAndCheckOptionsList
392  \param const string& a_optionsList : a list of parameters separated by commas
393  \brief This function is used to read parameters from a string.
394  \return 0 if success, other value otherwise.
395 */
396 int i1TCModel::ReadAndCheckOptionsList(string a_listOptions)
397 {
398  if(m_verbose >=2) Cout("i1TCModel::ReadAndCheckOptionsList ..."<< endl);
399 
400  // Just get the options here and perform initialization in InitializeSpecific()
401  // function, as some memory allocations must be performed first (images & TACs)
402  m_listOptions = a_listOptions;
403 
404  // Normal end
405  return 0;
406 }
407 
408 
409 
410 
411 // =====================================================================
412 // ---------------------------------------------------------------------
413 // ---------------------------------------------------------------------
414 // =====================================================================
415 /*
416  \fn CheckSpecificParameters
417  \brief This function is used to check whether all member variables
418  have been correctly initialized or not.
419  \return 0 if success, positive value otherwise.
420 */
422 {
423  if(m_verbose >=2) Cout("i1TCModel::CheckSpecificParameters ..."<< endl);
424 
425  // Check image dimensions
426  if (mp_ID==NULL)
427  {
428  Cerr("***** i1TCModel::CheckSpecificParameters() -> ImageDimensions object has not been provided !" << endl);
429  return 1;
430  }
431 
432  // Check number time basis functions
433  if (m_nbTimeBF<0)
434  {
435  Cerr("***** i1TCModel::CheckSpecificParameters() -> Wrong number of time frame basis functions !" << endl);
436  return 1;
437  }
438 
439  // Check if we have somehow both a file and a list of options for init...
440  if(m_listOptions != "" && m_fileOptions != "")
441  {
442  Cerr("***** i1TCModel::CheckSpecificParameters() -> Either a file or a list of options have to be selected to initialize the model, but not both ! " << endl);
443  return 1;
444  }
445 
446  // Check if we have no file not list of options for some reason...
447  if(m_listOptions == "" && m_fileOptions == "")
448  {
449  Cerr("***** i1TCModel::CheckSpecificParameters -> Either a file or a list of options should have been provided at this point ! " << endl);
450  return 1;
451  }
452 
453  // Check if we reconstruct gated data. Throw warning if it is the case
454  if(mp_ID->GetNbRespGates()>1 || mp_ID->GetNbCardGates()>1)
455  {
456  Cerr("***** i1TCModel::CheckSpecificParameters -> The implemented model is not compatible yet with gated reconstruction (parametric images will be the same for each gate)! " << endl);
457  return 1;
458  }
459 
460  // Check if ridge regression and NNLS has been enabled (RR only compatible with LS)
463  {
464  Cerr("***** i1TCModel::CheckSpecificParameters() -> Error, ridge regression is not compatible with NNLS algorithm. Switch 'Optimisation method' flag to LS in order to use ridge-regression !" << endl);
465  return 1;
466  }
467 
468  // Normal end
469  return 0;
470 }
471 
472 
473 
474 
475 // =====================================================================
476 // ---------------------------------------------------------------------
477 // ---------------------------------------------------------------------
478 // =====================================================================
479 /*
480  \fn InitializeSpecific
481  \brief This function is used to initialize the model parametric images and basis functions
482  \return 0 if success, other value otherwise.
483 */
485 {
486  if(m_verbose >=2) Cout("i1TCModel::InitializeSpecific() ..."<< endl);
487 
488  // Forbid initialization without check
489  if (!m_checked)
490  {
491  Cerr("***** oDynamicModelManager::InitializeSpecific() -> Must call CheckParameters functions before Initialize() !" << endl);
492  return 1;
493  }
494 
495  // --- Memory Allocation --- //
496 
497  // Allocate memory for Parametric images and functions
500 
501  for(int b=0 ; b<m_nbTimeBF ; b++)
503 
504  // Init with negative values
505  for(int b=0 ; b<m_nbTimeBF ; b++)
506  for(int t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
508 
509  for(int p=0 ; p<m_nbModelParam ; p++)
511 
512 
513  // Initialize blacklisted voxels image to zero;
515 
516  for (int v = 0; v < mp_ID->GetNbVoxXYZ(); v++)
517  mp_blackListedvoxelsImage[v] = 0.0;
518 
519 
520  // --- Data Initialization with a configuration file --- //
521  // Data requiring memory allocation (Image and TACs initializations)
522 
523  if(m_fileOptions != "")
524  {
525  ifstream in_file(m_fileOptions.c_str(), ios::in);
526 
527  if(in_file)
528  {
529  // --- Plasma input functions ---
530 
531  // Recover Plasma input function TAC (mandatory)
532  string dtag = "Input_function";
534  dtag,
538  {
539  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read 1TCPM input functions !" << endl);
540  Cerr(" '"<< dtag <<"' keyword in " << m_fileOptions << endl);
541  return 1;
542  }
543 
544  // Recover Integral of plasma input function TAC (optional)
545  dtag = "Integral_input_function";
547  dtag,
550  KEYWORD_OPTIONAL) == 1 )
551  {
552  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read 1TCPM input functions !" << endl);
553  Cerr(" '"<< dtag <<"' keyword in " << m_fileOptions << endl);
554  return 1;
555  }
556 
557 
558  // Parametric images initialization
559  string input_image = "";
560  int return_value = 0;
561 
562  dtag = "Parametric_images_init";
563  return_value = ReadDataASCIIFile(m_fileOptions,
564  dtag,
565  &input_image,
566  1,
568 
569  if( return_value == 0) // Image have been provided
570  {
571  // Read image // INTF_LERP_DISABLED = interpolation disabled for input image reading
572  if( IntfReadImgDynCoeffFile(input_image,
574  mp_ID,
575  m_nbModelParam,
576  m_verbose,
577  INTF_LERP_DISABLED) ) // Image have been provided
578  {
579  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read the provided initialization parametric images : " << input_image << endl);
580  return 1;
581  }
582  }
583  else if( return_value == 1) // Error during reading
584  {
585  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read input functions coefficients !" << endl);
586  Cerr(" '"<< dtag <<"' keyword in " << m_fileOptions << endl);
587  return 1;
588  }
589  else //(return_value >= 1 ) // Keyword not found : no initialization provided
590  {
591  // Standard initialization
592  for(int p=0 ; p<m_nbModelParam ; p++)
593  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
594  m2p_parametricImages[p][v] = 0.;
595  }
596 
597 
598  string path_to_VAimage;
599  dtag = "VA_image";
601  dtag,
602  &path_to_VAimage,
603  1,
604  KEYWORD_OPTIONAL) == 1)
605  {
606  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read '"<< dtag <<"' keyword in " << m_fileOptions << endl);
607  return 1;
608  }
609 
610  if(!path_to_VAimage.empty())
611  {
612  mp_VaImage = new FLTNB[ mp_ID->GetNbVoxXYZ() ];
613 
615  {
616  Cerr("***** i1TCModel::InitializeSpecific()-> Error reading Interfile : " << path_to_VAimage << " !" << endl);
617  return 1;
618  }
619 
620  // Check all voxels in VaImage = [0 ,1]
621  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
622  {
623  if(mp_VaImage[ v ] <0.
624  ||mp_VaImage[ v ] >1.)
625  {
626  Cerr("***** i1TCModel::InitializeSpecific() -> Voxels values in provided Va image ;"<< path_to_VAimage << " must be between [0,1]. One voxel value is "<< m_fileOptions << endl);
627  Cerr(" Erroneous value found:"<< mp_VaImage[ v ] << endl);
628  return 1;
629  }
630  }
631 
632  // Set the number of parameters to estimate to 2
633  m_nnlsN=2;
634  }
635  }
636 
637  else
638  {
639  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read configuration file at: " << m_fileOptions << endl);
640  return 1;
641  }
642  }
643  // --- Data Initialization with a list of options --- //
644  else if( m_listOptions != "" )
645  {
646  // We expect here the plasma tac only
647 
648  // Read it
652  ",",
653  "1cpt model configuration"))
654  {
655  Cerr("***** i1TCModel::InitializeSpecific() -> Failed to correctly read the list of options !" << endl);
656  return 1;
657  }
658 
659  // Standard initialization for the parametric images
660  for(int p=0 ; p<m_nbModelParam ; p++)
661  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
662  m2p_parametricImages[p][v] = 0.;
663 
664  }
665 
666 
667  // WPO for integrals
668  // Half-frame time duration
669  mp_DT2 = new FLTNB[mp_ID->GetNbTimeFrames()];
670 
671  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
672  mp_DT2[t] = mp_ID->GetFrameDurationInSec(0,t) * 0.5 ;
673 
675  {
676  mp_wpoQ = new FLTNB[ mp_ID->GetNbTimeFrames() ];
677  mp_wpoA = new FLTNB[ mp_ID->GetNbTimeFrames() ];
681 
682  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
683  {
684  m2p_wpoP[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
685  m2p_wpoFD[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
686  m2p_wpoBD[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
687  }
688 
689  // Compute WPO_q and WPO_a
690  for(int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
691  {
692  if( t == mp_ID->GetNbTimeFrames() -1)
693  mp_wpoQ[t] = 1;
694  else
695  mp_wpoQ[t] = mp_DT2[t] / mp_DT2[t+1];
696  }
697 
698 
699  for(int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
700  {
701  if( t==0 )
702  mp_wpoA[t] = 1;
703  else if( t==(mp_ID->GetNbTimeFrames() -2) )
704  mp_wpoA[t] = 0.5;
705  else if( t==(mp_ID->GetNbTimeFrames() -1) )
706  mp_wpoA[t] = 0.;
707  else
708  mp_wpoA[t] = (mp_DT2[ t+1 ] + 2*mp_DT2[ t ] ) / (2 * (mp_DT2[ t+2 ] + mp_DT2[ t+1 ] + mp_DT2[ t ]) );
709  //mp_wpoA[t] = (mp_DT2[ t ] + 2*mp_DT2[ t-1 ] ) / (2 * (mp_DT2[ t+1 ] + mp_DT2[ t ] + mp_DT2[ t-1 ]) );
710  }
711  }
712 
713 
714  // Variables for Tissue TAC
717 
718  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
719  {
720  m2p_ct[ th ] = new FLTNB[mp_ID->GetNbTimeFrames()];
721  m2p_cti[ th ] = new FLTNB[mp_ID->GetNbTimeFrames()];
722  }
723 
724  // --- Input functions ---
725 
726  // Check IF has been provided
727  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
728  if(m2p_nestedModelTimeBasisFunctions[ 1 ][ t ] < 0)
729  {
730  Cerr("***** i1TCModel::InitializeSpecific() -> Error, plasma input curve has not been initialized !" << endl);
731  return 1;
732  }
733 
734  // Compute integration of IF if not provided
735  if( m2p_nestedModelTimeBasisFunctions[ 0 ][ 0 ] < 0)
736  {
737  // --- Compute integral ---
738  FLTNB* Icp = new FLTNB[ mp_ID->GetNbTimeFrames() ];
739 
740  // Compute plasma tac integral from plasma tac in m2p_nestedModelTimeBasisFunctions[ 1 ]
742 
743  // Recover in model function vectors
744  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
745  m2p_nestedModelTimeBasisFunctions[ 0 ][ t ] = Icp[ t ];
746 
747  delete[] Icp;
748  }
749 
750 
751  // NNLS variables
755 
756  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
757  {
758  // Init 2D coefficient matrix for NNLS estimation
759  m3p_nnlsA[ th ] = new FLTNB*[ m_nnlsN ];
760 
761  for(int n=0 ; n<m_nnlsN ; n++)
762  m3p_nnlsA[ th ][n] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
763 
764  // Init solution vector and working matrix for NNLS estimation
765  m2p_nnlsB[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
766  m2p_nnlsMat[ th ] = new FLTNB[ (m_nnlsN+2) * mp_ID->GetNbTimeFrames() ];
767  }
768 
769 
770  // Compute weights for NNLS
771  mp_w = new FLTNB[ mp_ID->GetNbTimeFrames() ];
772 
773  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
774  mp_w[t] = sqrt(mp_ID->GetFrameDurationInSec(0,t)) ; // (convert time in min);
775 
776  // Init matrix for LS method
778  {
787  mp_RRm = new oMatrix(m_nnlsN, 1);
788  mp_RRw = new oMatrix(m_nnlsN, m_nnlsN);
789 
790  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
791  {
792  mp_Y[ th ] = new oMatrix(mp_ID->GetNbTimeFrames(),1);
793  mp_X[ th ] = new oMatrix(mp_ID->GetNbTimeFrames(), m_nnlsN);
794  mp_Xt[ th ] = new oMatrix(m_nnlsN, mp_ID->GetNbTimeFrames());
795  mp_XtX[ th ] = new oMatrix(m_nnlsN, m_nnlsN);
796  mp_LSnum[ th ] = new oMatrix(m_nnlsN, 1);
797  mp_LSden[ th ] = new oMatrix(m_nnlsN, m_nnlsN);
798  mp_Theta[ th ] = new oMatrix(m_nnlsN, 1);
799  mp_RRnum[ th ] = new oMatrix(m_nnlsN, 1);
800  }
801 
802  // Init ridge regression matrices
804  {
805  // Init RR diagonal matrices
806  for (int pl=0 ; pl<m_nnlsN ; pl++)
807  for (int pc=0 ; pc<m_nnlsN ; pc++)
808  {
809  if( pl==pc)
810  {
811  mp_RRw->SetMatriceElt( pl , pc , 1 / ( (mp_parUpperBounds[ pl ]-mp_parLowerBounds[ pl ])*(mp_parUpperBounds[ pl ]-mp_parLowerBounds[ pl ]) ) ) ;
812  mp_RRm->SetMatriceElt( pl , 0 , (mp_parUpperBounds[ pl ] + mp_parLowerBounds[ pl ]) / 2. ) ;
813 
814  for(int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
815  mp_RRnum[ th ]->SetMatriceElt( pl , 0 , 0. ) ;
816  }
817  else
818  {
819  mp_RRw->SetMatriceElt( pl , pc , 0 ) ;
820  }
821  }
822 
823  }
824  }
825 
826 
827  // Display TACs and other info
828  if(m_verbose >=2)
829  {
830  Cout("i1TCModel::InitializeSpecific() -> Input Plasma TAC coefficients :" << endl);
831  Cout(" ");
832  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
833  Cout(m2p_nestedModelTimeBasisFunctions[0][fr] << ", ");
834  Cout(endl);
835  Cout("i1TCModel::InitializeSpecific() -> Plasma TAC coefficients :" << endl);
836  Cout(" ");
837  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
838  Cout(m2p_nestedModelTimeBasisFunctions[1][fr] << ", ");
839  Cout(endl);
840 
842  Cout("i1TCModel::InitializeSpecific() -> Image update from estimated parametric images is disabled" << endl);
843  }
844 
845  // Normal end
846  m_initialized = true;
847  return 0;
848 }
849 
850 
851 
852 // =====================================================================
853 // ---------------------------------------------------------------------
854 // ---------------------------------------------------------------------
855 // =====================================================================
856 /*
857  \fn EstimateModelParameters
858  \param ap_ImageS : pointer to the ImageSpace
859  \param a_ite : index of the actual iteration (not used)
860  \param a_sset : index of the actual subset (not used)
861  \brief Estimate K1, k2, Va parametric images
862  \return 0 if success, other value otherwise.
863 */
864 int i1TCModel::EstimateModelParameters(oImageSpace* ap_ImageS, int a_ite, int a_sset)
865 {
866  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParameters ..." <<endl);
867 
868  #ifdef CASTOR_DEBUG
869  if (!m_initialized)
870  {
871  Cerr("***** i1TCModel::EstimateModelParameters() -> Called while not initialized !" << endl);
872  Exit(EXIT_DEBUG);
873  }
874  #endif
875 
876  switch (m_OptimisationMethodFlag)
877  {
878  case METHOD_1CPT_LS:
879  if( EstimateModelParametersWithLS(ap_ImageS) )
880  {
881  Cerr("***** i1TCModel::EstimateModelParameters() -> A problem occured when estimating 1cpt model parameters with LS method !" << endl);
882  return 1;
883  }
884  break;
885 
886  case METHOD_1CPT_BF :
887  if ( EstimateModelParametersWithBF(ap_ImageS) )
888  {
889  Cerr("***** i1TCModel::EstimateModelParameters() -> A problem occured when estimating 1cpt model parameters with Basis functions method !" << endl);
890  return 1;
891  }
892  break;
893 
894  case METHOD_1CPT_NNLS :
895  if ( EstimateModelParametersWithNNLS(ap_ImageS) )
896  {
897  Cerr("***** i1TCModel::EstimateModelParameters() -> A problem occured when estimating 1cpt model parameters with NNLS method !" << endl);
898  return 1;
899  }
900  break;
901  }
902 
903 
904  // --- Voxel-based Constraints ---
905 
906  // Loop on all voxels
907  int v;
908  #pragma omp parallel for private(v) schedule(guided)
909  for(v=0; v<mp_ID->GetNbVoxXYZ(); v++)
910  {
911  // Set to 0. if value is Nan or < 0.
912  if (!isfinite(m2p_parametricImages[0][v])
913  || !isfinite(m2p_parametricImages[1][v])
914  || !isfinite(m2p_parametricImages[2][v])
915  )
916  {
917  m2p_parametricImages[2][v] = 0.;
918  m2p_parametricImages[1][v] = 0.;
919  m2p_parametricImages[0][v] = 0.;
920  }
921 
922  // Biological constraints on Va (positivity and max == 1)
923  if( m2p_parametricImages[2][v] < 0.) m2p_parametricImages[2][v]=0.;
924  if( m2p_parametricImages[2][v] > 1.) m2p_parametricImages[2][v]=1.;
925 
926 
927  // Biological constaints on k2
928  // (set to 0 if:
929  // -> Va ~= 1
930  // -> Va << && K1 <<
931  if( m2p_parametricImages[2][v] > 0.95 ||
932  ( m2p_parametricImages[2][v] < 0.1 && m2p_parametricImages[0][v]<0.00001) )
933  m2p_parametricImages[1][v] = 0;
934 
935  // Set debug image
936  if( // At least one parameter is non nil
937  ( m2p_parametricImages[ 0 ][ v ]>0.
938  || m2p_parametricImages[ 1 ][ v ]>0.
939  || m2p_parametricImages[ 2 ][ v ]>0.
940  )
941  // No negative parameter
942  && ( m2p_parametricImages[ 0 ][ v ]>=0.
943  && m2p_parametricImages[ 1 ][ v ]>=0.
944  && m2p_parametricImages[ 2 ][ v ]>=0. )
945  )
946  mp_blackListedvoxelsImage[ v ] = (mp_blackListedvoxelsImage[v] == 2) ? 2 : 0.;
947  else
948  mp_blackListedvoxelsImage[ v ] = (mp_blackListedvoxelsImage[v] == 2) ? 2 : 1.;
949 
950  }
951 
952 
953  return 0;
954 }
955 
956 
957 
958 // =====================================================================
959 // ---------------------------------------------------------------------
960 // ---------------------------------------------------------------------
961 // =====================================================================
962 /*
963  \fn EstimateModelParametersWithNNLS
964  \param ap_ImageS : pointer to the ImageSpace
965  \brief Estimate K1, k2, Va parametric images using NLLS
966  \return 0 if success, other value otherwise.
967 */
969 {
970  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParametersWithNNLS ..." <<endl);
971 
972  #ifdef CASTOR_DEBUG
973  if (!m_initialized)
974  {
975  Cerr("***** i1TCModel::EstimateModelParametersWithNNLS() -> Called while not initialized !" << endl);
976  Exit(EXIT_DEBUG);
977  }
978  #endif
979 
980  int rg=0, cg=0;
981 
982 
983  // Loop on all voxels
984  int v;
985  #pragma omp parallel for private(v) schedule(guided)
986  for(v=0; v<mp_ID->GetNbVoxXYZ(); v++)
987  {
988  // If a mask has been provided, check if the model applies in this voxel
989  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
990  continue;
991 
992  int th=0;
993  #ifdef CASTOR_OMP
994  th = omp_get_thread_num();
995  #endif
996 
997  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
998 
999  FLTNB** _2p_nnlsA = m3p_nnlsA[ th ];
1000  FLTNB* _p_nnlsB = m2p_nnlsB[ th ];
1001  FLTNB* _p_nnlsMat = m2p_nnlsMat[ th ];
1002 
1003  // m-array of working space
1004  FLTNB *nnls_zz;
1005 
1006  // Solution vectors
1007  FLTNB *nnls_x = new FLTNB[ m_nnlsN ];
1008 
1009  //n-array of working space for the dual solution y
1010  FLTNB *nnls_wp = new FLTNB[ m_nnlsN ];
1011 
1012  // n-array of working space
1013  int *nnls_index = new int[ m_nnlsN ];
1014 
1015  // Euclidean norm of the residual vector
1016  FLTNB nnls_rnorm;
1017 
1018  FLTNB *dptr=_p_nnlsMat;
1019  dptr=_p_nnlsMat;
1020  for(int n=0 ; n<m_nnlsN; n++)
1021  {
1022  _2p_nnlsA[n]=dptr;
1023  dptr+=nb_tf;
1024  }
1025 
1026  _p_nnlsB=dptr;
1027  dptr+=nb_tf;
1028  nnls_zz=dptr;
1029 
1030  // Integral of plasma TAC
1032  // Plasma TAC
1034 
1035  // Pointers for tissue TAC and integral of tissue TAC
1036  FLTNB* ct = m2p_ct[th];
1037  FLTNB* cti = m2p_cti[th];
1038 
1039  //------------------------
1040  // Estimate K1, k2 and Va
1041  //
1042  // Recover CPET value
1043  for(size_t t=0; t<nb_tf; t++)
1044  ct[t] = ap_ImageS->m4p_image[t][rg][cg][v];
1045 
1046  // If a VaImage has been provided, subtract Va*Ca from tissue TAC
1047  if( mp_VaImage != NULL)
1048  {
1049  for(size_t t=0; t<nb_tf; t++)
1050  {
1051  ct[t] -= mp_VaImage[v]*cp[t] ;
1052  if (ct[t]<0) ct[t]=0.;
1053  }
1054  }
1055 
1056  // Compute integral of tissue TAC
1057  IntegrateTAC(ct, cti, th);
1058 
1059  // Estimate K1, k2, Va parameters with NNLS
1060  if( mp_VaImage == NULL)
1061  {
1062  // Set up NNLS variables
1063  for(size_t t=0; t<nb_tf; t++)
1064  {
1065  if(cti[t]<0) cti[t] = 0.;
1066  // Fill NNLS A matrix:
1067  // function #1: tissue integral x -1
1068  _2p_nnlsA[0][t]=-cti[t]*mp_w[t];
1069  // function #2: integral of input
1070  _2p_nnlsA[1][t]=cpi[t]*mp_w[t];
1071  // function #3: input curve
1072  _2p_nnlsA[2][t]=cp[t]*mp_w[t];
1073 
1074  // Fill NNLS B array: tissue
1075  _p_nnlsB[t]=ct[t]*mp_w[t];
1076  }
1077 
1078  if(NNLS(_2p_nnlsA, nb_tf, m_nnlsN, _p_nnlsB, nnls_x, &nnls_rnorm,
1079  nnls_wp, nnls_zz, nnls_index) )
1080  continue; // no solution is possible
1081 
1082  // Computer 1cpt model K1, k2, Va from NNLS estimated parameters
1083  FLTNB Va=nnls_x[2];
1084 
1085  // Recover K1
1086  m2p_parametricImages[0][v] = nnls_x[1];
1087  // If Va>0, we fitted to CPET instead of CT
1088  // In this case we didn't estimate K1, but K1 + Va*k2 as there is a Va*k2 contribution to CPi
1089  // We have to substract this contribution to get K1
1090  m2p_parametricImages[0][v] -= Va*nnls_x[0];
1091 
1092  m2p_parametricImages[1][v] = nnls_x[0];
1093  m2p_parametricImages[2][v] = Va;
1094  }
1095 
1096  else // Va value from user provided image
1097  {
1098  FLTNB Va = mp_VaImage[v];
1099 
1100  // Fill matrix without blood volume contribution
1101  for(size_t t=0; t<nb_tf; t++)
1102  {
1103  // Fill NNLS A matrix:
1104  // function #1: tissue integral x -1
1105  _2p_nnlsA[0][t] = -cti[t]*mp_w[t];
1106  // function #2: integral of input
1107  _2p_nnlsA[1][t] = cpi[t]*mp_w[t];
1108 
1109  // Fill NNLS B array: tissue
1110  _p_nnlsB[t] = ct[t]*mp_w[t];
1111  }
1112 
1113  // Estimate with NNLS (nnls_n - 1 as Vb is already corrected)
1114  if(NNLS(_2p_nnlsA, nb_tf, m_nnlsN, _p_nnlsB, nnls_x, &nnls_rnorm,
1115  nnls_wp, nnls_zz, nnls_index) )
1116  continue; // no solution is possible
1117 
1118  // Recover K1
1119  m2p_parametricImages[0][v] = nnls_x[1];
1120  m2p_parametricImages[1][v] = nnls_x[0];
1121  m2p_parametricImages[2][v] = Va;
1122  }
1123 
1124  // Free memory
1125  if(nnls_x) delete[]nnls_x;
1126  if(nnls_wp) delete[]nnls_wp;
1127  if(nnls_index) delete[]nnls_index;
1128 
1129  } // multithreaded loop on voxels
1130 
1131  return 0;
1132 }
1133 
1134 
1135 
1136 
1137 // =====================================================================
1138 // ---------------------------------------------------------------------
1139 // ---------------------------------------------------------------------
1140 // =====================================================================
1141 /*
1142  \fn EstimateModelParametersWithBF
1143  \param ap_ImageS : pointer to the ImageSpace
1144  \brief Estimate K1, k2, Va parametric images using basis functions approach
1145  \return 0 if success, other value otherwise.
1146 */
1148 {
1149  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParametersWithBF ..." <<endl);
1150 
1151  #ifdef CASTOR_DEBUG
1152  if (!m_initialized)
1153  {
1154  Cerr("***** i1TCModel::EstimateModelParametersWithBF() -> Called while not initialized !" << endl);
1155  Exit(EXIT_DEBUG);
1156  }
1157  #endif
1158 
1159  Cerr("i1TCModel::EstimateModelParametersWithBF -> Not yet implemented !!!" <<endl);
1160  return 1;
1161 }
1162 
1163 
1164 
1165 
1166 
1167 
1168 
1169 
1170 
1171 
1172 // =====================================================================
1173 // ---------------------------------------------------------------------
1174 // ---------------------------------------------------------------------
1175 // =====================================================================
1176 /*
1177  \fn EstimateModelParametersWithLS
1178  \param ap_ImageS : pointer to the ImageSpace
1179  \brief Estimate K1, k2, Va parametric images using LS
1180  \return 0 if success, other value otherwise.
1181 */
1183 {
1184  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParametersWithLS ..." <<endl);
1185 
1186  #ifdef CASTOR_DEBUG
1187  if (!m_initialized)
1188  {
1189  Cerr("***** i1TCModel::EstimateModelParametersWithLS() -> Called while not initialized !" << endl);
1190  Exit(EXIT_DEBUG);
1191  }
1192  #endif
1193 
1194  int rg=0, cg=0;
1195 
1196  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
1197 
1198  bool error_in_th_loop = false;
1199  string error_msg ="";
1200 
1201 
1202  // Loop on all voxels
1203  int v;
1204  #pragma omp parallel for private(v) schedule(guided)
1205  for(v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1206  {
1207  // If a mask has been provided, check if the model applies in this voxel
1208  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1209  continue;
1210 
1211  int th=0;
1212  #ifdef CASTOR_OMP
1213  th = omp_get_thread_num();
1214  #endif
1215 
1216  // Pointers for tissue TAC and integral of tissue TAC
1217  FLTNB* ct = m2p_ct[th];
1218  FLTNB* cti = m2p_cti[th];
1219 
1220  // Integral of plasma TAC
1222  // Plasma TAC
1224 
1225  // Recover CPET value
1226  for(size_t t=0; t<nb_tf; t++)
1227  ct[t] = ap_ImageS->m4p_image[t][rg][cg][v];
1228 
1229  // If a VaImage has been provided, subtract Va*Ca from tissue TAC
1230 
1231  if( mp_VaImage != NULL)
1232  {
1233  for(size_t t=0; t<nb_tf; t++)
1234  {
1235  ct[t] -= mp_VaImage[v]*cp[t] ;
1236  if (ct[t]<0) ct[t]=0.;
1237  }
1238  }
1239 
1240  // Compute integral of tissue TAC
1241  IntegrateTAC(ct, cti, th);
1242 
1243  // Solution vector
1244  FLTNB *LS_res = new FLTNB[ m_nnlsN ];
1245 
1246  // Model matrix
1247  FLTNB** LS_Mod = m3p_nnlsA[ th ];
1248 
1249  for(int16_t t=0; t<mp_ID->GetNbTimeFrames(); t++)
1250  {
1251  // function #1: tissue integral x -1
1252  LS_Mod[0][t] = -cti[t];
1253  // function #2: integral of input
1254  LS_Mod[1][t] = cpi[t];
1255  // function #3: input curve
1256  if( m_nnlsN>2) // Va image not provided
1257  LS_Mod[2][t] = cp[t];
1258  }
1259 
1260  // LS optimization using ridge-regression or not
1261  if (m_ridgeRegressionFlag )
1262  {
1263  // Estimate parameters with LS with ridge-regression
1264  if(RRLS(m_nnlsN,
1266  LS_Mod,
1267  ct,
1268  mp_w,
1269  LS_res) )
1270  {
1271  error_in_th_loop = true;
1272  error_msg = "***** i1TCModel::EstimateModelParametersWithLS() -> An error occurred when minimizing WRSS function with ridge-regression!";
1273  }
1274  }
1275  else
1276  {
1277  // Estimate parameters with standard LS
1278  if(LS(m_nnlsN,
1280  LS_Mod,
1281  ct,
1282  mp_w,
1283  LS_res) )
1284  {
1285  error_in_th_loop = true;
1286  error_msg = "***** i1TCModel::EstimateModelParametersWithLS() -> An error occurred when minimizing WRSS function with standard LS!";
1287  }
1288  }
1289 
1290  // Recover Va from LS parameter, or Va image if provided
1291  m2p_parametricImages[2][v] = (mp_VaImage == NULL) ?
1292  LS_res[ 2 ] :
1293  mp_VaImage[v] ;
1294 
1295  // k2
1296  m2p_parametricImages[1][v] = LS_res[ 0 ];
1297  //K1
1298  m2p_parametricImages[0][v] = LS_res[ 1 ]
1299  - m2p_parametricImages[2][v]*LS_res[ 0 ];
1300 
1301  if( LS_res ) delete[] LS_res;
1302  }
1303 
1304  if ( error_in_th_loop == true )
1305  {
1306  Cerr(error_msg << endl);
1307  return 1;
1308  }
1309 
1310  return 0;
1311 }
1312 
1313 
1314 
1315 // =====================================================================
1316 // ---------------------------------------------------------------------
1317 // ---------------------------------------------------------------------
1318 // =====================================================================
1319 /*
1320  \fn RRLS
1321  \param a_nP : number of parameters (P)
1322  \param a_nT : number of data samples (T)
1323  \param a2p_model : matrix containing the model functions (TxP elts)
1324  \param ap_data : data vector (T elts)
1325  \param ap_w : vector containing the weights (T elts)
1326  \param ap_result : vector recovering the estimated parameters (P elts)
1327  \brief Non-linear least square estimator with Ridge-Regression
1328  \details This function will estimate a set of parameters (O) by
1329  minimizing the weighted residual sum of squares (WRSS)
1330  difference between the data and the model function
1331  Ô = [ X'*W*X + t.Rw ]-1 [ X'*W*y ]
1332  + [ X'*W*X + t.Rw ]-1 [ t.Rw*Rm ]
1333  y = data vector
1334  X = model matrix
1335  W = weights vector
1336  t = Ridge constant
1337  Rw= Ridge weights
1338  Rm= Ridge means
1339 
1340  \return 0 if success, other value otherwise.
1341 */
1342 int i1TCModel::RRLS(uint16_t a_nP,
1343  uint16_t a_nT,
1344  FLTNB **a2p_model,
1345  FLTNB *ap_data,
1346  FLTNB *ap_w,
1347  FLTNB *ap_result
1348  )
1349 {
1350  #ifdef CASTOR_DEBUG
1351  if(m_verbose >=4) Cout("i1TCModel::RRLS ..." <<endl);
1352 
1353  if (!m_initialized)
1354  {
1355  Cerr("***** i1TCModel::RRLS() -> Called while not initialized !" << endl);
1356  Exit(EXIT_DEBUG);
1357  }
1358  #endif
1359 
1360  // Check that the LS flag is on, so that we are sure that all variables
1361  // have been correctly allocated
1363  {
1364  Cerr("***** i1TCModel::RRLS() -> Function is called while the optimization method is not Least-square !" << endl);
1365  return 1;
1366  }
1367 
1368  int th=0;
1369  #ifdef CASTOR_OMP
1370  th = omp_get_thread_num();
1371  #endif
1372 
1373  // Get the working matrices
1374  oMatrix* Y = mp_Y[ th ];
1375  oMatrix* X = mp_X[ th ];
1376  oMatrix* Xt = mp_Xt[ th ];
1377  oMatrix* XtX = mp_XtX[ th ];
1378  oMatrix* Theta = mp_Theta[ th ];
1379  oMatrix* LSnum = mp_LSnum[ th ];
1380  oMatrix* LSden = mp_LSden[ th ];
1381 
1382  oMatrix *RRw = mp_RRw;
1383  oMatrix *RRm = mp_RRm;
1384  oMatrix *RRnum = mp_RRnum[ th ];
1385 
1386  // Init data vector
1387  for( uint16_t t=0 ; t<a_nT ; t++ )
1388  Y->SetMatriceElt( t , 0, ap_data[ t ]*ap_w[ t ] );
1389 
1390  // Init model matrix
1391  for( uint16_t p=0 ; p<a_nP ; p++ )
1392  for( uint16_t t=0 ; t<a_nT ; t++ )
1393  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] );
1394 
1395 
1396  #ifdef CASTOR_DEBUG
1397  if(m_verbose >=4)
1398  {
1399  cout << " ----------------------------------------------- " << endl;
1400  cout << " X->Describe() " << endl;
1401  cout << " ----------------------------------------------- " << endl;
1402  }
1403  #endif
1404 
1405  // Compute X'
1406  X->Transpose(Xt);
1407 
1408  // Once X' is computed, add weights to X
1409  for( uint16_t p=0 ; p<a_nP ; p++ )
1410  for( uint16_t t=0 ; t<a_nT ; t++ )
1411  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] * ap_w[ t ] );
1412 
1413 
1414  #ifdef CASTOR_DEBUG
1415  if(m_verbose >=4)
1416  {
1417  cout << " ----------------------------------------------- " << endl;
1418  cout << " Xt->Describe() " << endl;
1419  Xt->Describe();
1420  cout << " ----------------------------------------------- " << endl;
1421  }
1422  #endif
1423 
1424  // Compute (X'*W*X), recover the result in XtX matrix
1425  Xt->Multiplication( X, XtX );
1426 
1427  // Add Ridge weights, recover result in XtX
1428  for (int pl=0 ; pl<a_nP ; pl++)
1429  for (int pc=0 ; pc<a_nP ; pc++)
1430  XtX->SetMatriceElt( pl ,
1431  pc ,
1432  XtX->GetMatriceElt(pl,pc) + m_RRcst*RRw->GetMatriceElt(pl,pc) );
1433 
1434 
1435  #ifdef CASTOR_DEBUG
1436  if(m_verbose >=4)
1437  {
1438  cout << " ----------------------------------------------- " << endl;
1439  cout << " X'WX->Describe() " << endl;
1440  XtX->Describe();
1441  cout << " ----------------------------------------------- " << endl;
1442  }
1443  #endif
1444 
1445  // Compute LS denominator [X'*W*X + t.*Rw]-1, recover the result in LSden matrix
1446  // If nil on diagonal, set all elts of result vector to 0. and stop here
1447  if ( XtX->Inverse( LSden ) == 1 )
1448  {
1449  for( uint16_t p=0 ; p<a_nP ; p++ )
1450  ap_result[ p ] = 0.;
1451 
1452  return 0.;
1453  }
1454 
1455  #ifdef CASTOR_DEBUG
1456  if(m_verbose >=4)
1457  {
1458  cout << " ----------------------------------------------- " << endl;
1459  cout << " RRLS denominator ->Describe() " << endl;
1460  LSden->Describe();
1461  cout << " ----------------------------------------------- " << endl;
1462  }
1463  #endif
1464 
1465  // Compute LS numerator (X'*W*y), recover the result in LS_num matrix
1466  Xt->Multiplication( Y, LSnum );
1467 
1468  #ifdef CASTOR_DEBUG
1469  if(m_verbose >=4)
1470  {
1471  cout << " ----------------------------------------------- " << endl;
1472  cout << " LS numerator ->Describe() " << endl;
1473  LSnum->Describe();
1474  cout << " ----------------------------------------------- " << endl;
1475  }
1476  #endif
1477 
1478  // Compute t.Rw*Rm and write result in rrtWM
1479  RRw->Multiplication( RRm, RRnum );
1480 
1481  // Compute complete numerator [ X'*W*y ] [ t.Rw*Rm ], recover in LSnum
1482  for (int p=0 ; p<a_nP ; p++)
1483  LSnum->SetMatriceElt( p , 0 , LSnum->GetMatriceElt(p,0)
1484  + m_RRcst * RRnum->GetMatriceElt(p,0) ) ;
1485 
1486  #ifdef CASTOR_DEBUG
1487  if(m_verbose >=4)
1488  {
1489  cout << " ----------------------------------------------- " << endl;
1490  cout << " RRLS numerator ->Describe() " << endl;
1491  LSnum->Describe();
1492  cout << " ----------------------------------------------- " << endl;
1493  }
1494  #endif
1495 
1496 
1497  // Compute final parameters, recover in 'Theta'
1498  // Ô = [ X'*W*X + t.Rw ]-1 [ X'*W*y ]
1499  // + [ X'*W*X + t.Rw ]-1 [ t.Rw*Rm ],
1500  LSden->Multiplication( LSnum, Theta );
1501 ;
1502  #ifdef CASTOR_DEBUG
1503  if(m_verbose >=4)
1504  {
1505  cout << " ----------------------------------------------- " << endl;
1506  cout << " Theta->Describe() " << endl;
1507  Theta->Describe();
1508  }
1509  #endif
1510 
1511  // Recover estimated parameters in return vector
1512  for( uint16_t p=0 ; p<a_nP ; p++ )
1513  ap_result[ p ] = Theta->GetMatriceElt(p,0);
1514 
1515  return 0;
1516 }
1517 
1518 
1519 
1520 
1521 // =====================================================================
1522 // ---------------------------------------------------------------------
1523 // ---------------------------------------------------------------------
1524 // =====================================================================
1525 /*
1526  \fn LS
1527  \param a_nP : number of parameters (P)
1528  \param a_nT : number of data samples (T)
1529  \param a2p_model : matrix containing the model functions (TxP elts)
1530  \param ap_data : data vector (T elts)
1531  \param ap_w : vector containing the weights (T elts)
1532  \param ap_result : vector recovering the estimated parameters (P elts)
1533  \brief Non-linear least square estimator
1534  \details This function will estimate a set of parameters (O) by
1535  minimizing the weighted residual sum of squares (WRSS)
1536  difference between the data and the model function
1537  Ô = [ X'WX ]-1 X'Wy
1538  y = data vector
1539  X = model matrix
1540  W = weights vector
1541 
1542  \return 0 if success, other value otherwise.
1543 */
1544 int i1TCModel::LS(uint16_t a_nP,
1545  uint16_t a_nT,
1546  FLTNB **a2p_model,
1547  FLTNB *ap_data,
1548  FLTNB *ap_w,
1549  FLTNB *ap_result
1550  )
1551 {
1552  #ifdef CASTOR_DEBUG
1553  if(m_verbose >=4) Cout("i1TCModel::LS ..." <<endl);
1554 
1555  if (!m_initialized)
1556  {
1557  Cerr("***** i1TCModel::LS() -> Called while not initialized !" << endl);
1558  Exit(EXIT_DEBUG);
1559  }
1560  #endif
1561 
1562  // Check that the LS flag is on, so that we are sure that all variables
1563  // have been correctly allocated
1565  {
1566  Cerr("***** i1TCModel::LS() -> Function is called while the optimization method is not Least-square !" << endl);
1567  return 1;
1568  }
1569 
1570  int th=0;
1571  #ifdef CASTOR_OMP
1572  th = omp_get_thread_num();
1573  #endif
1574 
1575  // Get the working matrices
1576  oMatrix* Y = mp_Y[ th ];
1577  oMatrix* X = mp_X[ th ];
1578  oMatrix* Xt = mp_Xt[ th ];
1579  oMatrix* XtX = mp_XtX[ th ];
1580  oMatrix* Theta = mp_Theta[ th ];
1581  oMatrix* LSnum = mp_LSnum[ th ];
1582  oMatrix* LSden = mp_LSden[ th ];
1583 
1584  // Init data vector
1585  for( uint16_t t=0 ; t<a_nT ; t++ )
1586  Y->SetMatriceElt( t , 0, ap_data[ t ]*ap_w[ t ] );
1587 
1588  // Init model matrix
1589  for( uint16_t p=0 ; p<a_nP ; p++ )
1590  for( uint16_t t=0 ; t<a_nT ; t++ )
1591  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] );
1592 
1593 
1594  #ifdef CASTOR_DEBUG
1595  if(m_verbose >=4)
1596  {
1597  cout << " ----------------------------------------------- " << endl;
1598  cout << " X->Describe() " << endl;
1599  cout << " ----------------------------------------------- " << endl;
1600  }
1601  #endif
1602 
1603  // Compute X'
1604  X->Transpose(Xt);
1605 
1606  // Once X' is computed, add weights to X
1607  for( uint16_t p=0 ; p<a_nP ; p++ )
1608  for( uint16_t t=0 ; t<a_nT ; t++ )
1609  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] * ap_w[ t ] );
1610 
1611 
1612  #ifdef CASTOR_DEBUG
1613  if(m_verbose >=4)
1614  {
1615  cout << " ----------------------------------------------- " << endl;
1616  cout << " Xt->Describe() " << endl;
1617  Xt->Describe();
1618  cout << " ----------------------------------------------- " << endl;
1619  }
1620  #endif
1621 
1622  // Compute (X'WX), recover the result in XtX matrix
1623  Xt->Multiplication( X, XtX );
1624 
1625  #ifdef CASTOR_DEBUG
1626  if(m_verbose >=4)
1627  {
1628  cout << " ----------------------------------------------- " << endl;
1629  cout << " X'WX->Describe() " << endl;
1630  XtX->Describe();
1631  cout << " ----------------------------------------------- " << endl;
1632  }
1633  #endif
1634 
1635  // Compute LS numerator (X'Wy), recover the result in LS_num matrix
1636  Xt->Multiplication( Y, LSnum );
1637 
1638  #ifdef CASTOR_DEBUG
1639  if(m_verbose >=4)
1640  {
1641  cout << " ----------------------------------------------- " << endl;
1642  cout << " LS numerator ->Describe() " << endl;
1643  LSnum->Describe();
1644  cout << " ----------------------------------------------- " << endl;
1645  }
1646  #endif
1647 
1648  // Compute LS denominator [X'WX]-1, recover the result in LSden matrix
1649  // If nil on diagonal, set all elts of result vector to 0. and stop here
1650  if ( XtX->Inverse( LSden ) == 1 )
1651  {
1652  for( uint16_t p=0 ; p<a_nP ; p++ )
1653  ap_result[ p ] = 0.;
1654 
1655  return 0.;
1656  }
1657 
1658  #ifdef CASTOR_DEBUG
1659  if(m_verbose >=4)
1660  {
1661  cout << " ----------------------------------------------- " << endl;
1662  cout << " LS denominator ->Describe() " << endl;
1663  LSden->Describe();
1664  cout << " ----------------------------------------------- " << endl;
1665  }
1666  #endif
1667 
1668  // Get final parameter Ô = [ X'WX ]-1 X'Wy
1669  LSden->Multiplication( LSnum, Theta );
1670 
1671  #ifdef CASTOR_DEBUG
1672  if(m_verbose >=4)
1673  {
1674  cout << " ----------------------------------------------- " << endl;
1675  cout << " Theta->Describe() " << endl;
1676  Theta->Describe();
1677  }
1678  #endif
1679 
1680  // Recover estimated parameters in return vector
1681  for( uint16_t p=0 ; p<a_nP ; p++ )
1682  ap_result[ p ] = Theta->GetMatriceElt(p,0);
1683 
1684  return 0;
1685 }
1686 
1687 
1688 
1689 // =====================================================================
1690 // ---------------------------------------------------------------------
1691 // ---------------------------------------------------------------------
1692 // =====================================================================
1693 /*
1694  \fn EstimateImageWithModel
1695  \param ap_ImageS : pointer to the ImageSpace
1696  \param a_ite : index of the actual iteration (not used)
1697  \param a_sset : index of the actual subset (not used)
1698  \brief Estimate image using model parametric images and basis functions
1699  \todo Maybe add the possibility to use specific thresholds
1700  ex: if Va~1 (eg >0.8) && K1~0 && k2~0 --> Va=1, K1=k2=0
1701  \return 0 if success, other value otherwise.
1702 */
1703 int i1TCModel::EstimateImageWithModel(oImageSpace* ap_ImageS, int a_ite, int a_sset)
1704 {
1705  if(m_verbose >= 2) Cout("i1TCModel::EstimateImageWithModel ... " <<endl);
1706 
1707  #ifdef CASTOR_DEBUG
1708  if (!m_initialized)
1709  {
1710  Cerr("***** i1TCModel::EstimateImageWithModel() -> Called while not initialized !" << endl);
1711  Exit(EXIT_DEBUG);
1712  }
1713  #endif
1714 
1715  if( !m_noImageUpdateFlag
1716  && a_ite >= m_startIteUpdateFlag)
1717  {
1718  // Compute average of time shift between two samples in TACs formulas
1719  // Required for TACs computation
1720  HPFLTNB* DTavg = new HPFLTNB[mp_ID->GetNbTimeFrames() ];
1721  DTavg[ 0 ] = mp_DT2[ 0 ];
1722  for (int32_t t=1 ; t<mp_ID->GetNbTimeFrames() ; t++)
1723  DTavg[ t ] = (mp_DT2[ t ] + mp_DT2[ t-1 ] ) / 2;
1724 
1725  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1726  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1727  {
1728  int v;
1729  #pragma omp parallel for private(v) schedule(guided)
1730  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1731  {
1732  FLTNB K1 = m2p_parametricImages[0][v];
1733  FLTNB k2 = m2p_parametricImages[1][v];
1734  FLTNB Va = m2p_parametricImages[2][v];
1735 
1736  // Keep reconstructed image value if one parameter value <0
1737  // or if all parameters == 0
1738  if( ( K1>0. || k2>0. || Va>0.) // At least one parameter is non nil
1739  && ( K1>=0. && k2>=0. && Va>=0. ) ) // No negative parameter
1740  {
1741  FLTNB ct=0., // tissue tac
1742  b_ct=0., // tissue tac previous sample
1743  cti=0., // cumulative integral of tissue tac
1744  b=0., // output tissue concentration
1745  bb_ct=0., // tissue tact penultinum previous sample
1746  n_ct=0.; // tissue_tac next sample
1747 
1748  for (int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
1749  {
1750  bb_ct = b_ct;
1751  b_ct = ct;
1752  b = cti + DTavg[t]*b_ct ;
1753 
1754  ct = ( K1*m2p_nestedModelTimeBasisFunctions[ 0 ][ t ] - k2*b )
1755  / ( 1 + k2*DTavg[ t ] );
1756 
1757 
1758  // Approximating next cti with weighed parabola method
1760  {
1761  // Pre-estimate ct+1 (n_ct) with Trapz
1762  if(t>0)
1763  {
1764  FLTNB n_cti = cti + mp_DT2[t]*(ct + b_ct);
1765  FLTNB n_b = n_cti + mp_DT2[t]*ct ; // output tissue concentration
1766  n_ct = (K1*m2p_nestedModelTimeBasisFunctions[0][t] - k2*n_b)
1767  / (1 + k2*DTavg[t]);
1768  }
1769 
1770  cti += WPOinc( t, ct, b_ct, bb_ct, n_ct);
1771  }
1772  else // Approximating cti with trapezoidal rule
1773  cti += mp_DT2[t]*(ct + b_ct);
1774 
1775  // CPET (add blood contribution)
1776  FLTNB new_value = ct + m2p_nestedModelTimeBasisFunctions[1][t]*Va;
1777  ap_ImageS->m4p_image[t][rg][cg][v] = (new_value > 0) ?
1778  new_value :
1779  ap_ImageS->m4p_image[t][rg][cg][v] ;
1780  }
1781  } // end of condition on kinetic parameters
1782  else
1783  for (int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
1784  ap_ImageS->m4p_image[t][rg][cg][v] = 0;
1785 
1786  } // end of loop on voxels
1787 
1788  } // end of loop on rg, cg
1789 
1790  delete[] DTavg;
1791 
1792  } // update image condition
1793 
1794  return 0;
1795 }
1796 
1797 
1798 // =====================================================================
1799 // ---------------------------------------------------------------------
1800 // ---------------------------------------------------------------------
1801 // =====================================================================
1802 /*
1803  \fn WPOinc
1804  \param a_time : time point for which the integral will be computed
1805  \param tac : tac value at time t
1806  \param b_tac : tac value at time t-1
1807  \param bb_tac : tac value at time t-2
1808  \param n_tac : tac value at time t+1
1809  \brief Estimate the next integration value for a specific time point of a tac using WPO
1810  \return The estimated value of the integral for this time point
1811 */
1812 FLTNB i1TCModel::WPOinc(uint32_t a_time, FLTNB tac, FLTNB b_tac, FLTNB bb_tac, FLTNB n_tac)
1813 {
1814  FLTNB Itac=0.;
1815  FLTNB wpo_p=0., wpo_p_n=0, wpo_fd_n=0., wpo_bd=0.;
1816  uint32_t t = a_time;
1817 
1818  if(t==0) // first sample (b_tac, bb_tac not known)
1819  Itac = mp_DT2[t]*tac*0.5;
1820  else if( t==1 ) // second sample (bb_tac not known)
1821  {
1822  wpo_p_n = ( tac - ( mp_DT2[t+1]*b_tac + mp_DT2[t]*n_tac ) / ( mp_DT2[t+1]+mp_DT2[t] ) ) / 6;
1823  wpo_fd_n = 2*mp_DT2[1] * ( 0.5*( tac + b_tac ) + wpo_p_n * mp_wpoQ[t+1] );
1824  wpo_bd = mp_DT2[t-1] * ( b_tac + tac );
1825 
1826  Itac += (1-mp_wpoA[ t ]) * wpo_bd + mp_wpoA[ t ]*wpo_fd_n; // WPO rule
1827  }
1828  else if( t < ((uint32_t)mp_ID->GetNbTimeFrames()-1) )
1829  {
1830  // todo : try to get tac+1 with trapZ ?
1831  wpo_p_n = ( tac - ( mp_DT2[t+1]*b_tac + mp_DT2[t] *n_tac ) / ( mp_DT2[t+1]+ mp_DT2[t] ) ) / 6;
1832  wpo_p = ( b_tac - ( mp_DT2[t] *bb_tac + mp_DT2[t-1] *tac ) / ( mp_DT2[t] + mp_DT2[t-1] ) ) / 6;
1833  wpo_fd_n = 2*mp_DT2[t] * ( 0.5*( tac + b_tac ) + wpo_p_n * mp_wpoQ[t+1] );
1834  wpo_bd = 2*mp_DT2[t-1] * ( 0.5*( b_tac + tac ) + wpo_p / mp_wpoQ[t] );
1835 
1836  Itac += (1-mp_wpoA[ t ]) * wpo_bd + mp_wpoA[ t ]*wpo_fd_n; // WPO rule
1837  }
1838  else
1839  {
1840  wpo_p = ( b_tac - ( ( ( mp_DT2[t] *bb_tac ) + mp_DT2[t-1] *tac ) ) / ( mp_DT2[t]+mp_DT2[t-1] ) ) / 6;
1841  wpo_bd = 2*mp_DT2[t-1] * ( 0.5*( b_tac + tac ) + wpo_p / mp_wpoQ[t] );
1842 
1843  Itac += wpo_bd; // WPO rule
1844  }
1845  return Itac;
1846 
1847 }
1848 
1849 
1850 
1851 
1852 // =====================================================================
1853 // ---------------------------------------------------------------------
1854 // ---------------------------------------------------------------------
1855 // =====================================================================
1864 int i1TCModel::IntegrateTAC(FLTNB* ap_tac, FLTNB* ap_citac, int a_th)
1865 {
1867  return WPO(ap_tac, ap_citac, a_th);
1868  else // ( m_intMethodFlag == METHOD_INT_TRAP )
1869  return Trapz(ap_tac, ap_citac);
1870 
1871  return 0;
1872 }
1873 
1874 // =====================================================================
1875 // ---------------------------------------------------------------------
1876 // ---------------------------------------------------------------------
1877 // =====================================================================
1878 /*
1879  \fn WPO
1880  \param ap_tac : vector with nb time frames elements containing the time activity curve from which integral will be computed
1881  \param ap_citac : (returned) vector with nb time frames elements recovering the cumulative integral for each time point t
1882  \param a_th : thread index
1883  \brief return integral for each time point t (WPO_S), and cumulative integral (cumWPO_S )
1884  \return 0 if success, positive value otherwise
1885 */
1886 int i1TCModel::WPO(FLTNB* ap_tac, FLTNB* ap_citac, int a_th)
1887 {
1888  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
1889 
1890  FLTNB* DT2 = mp_DT2; // Frame duration / 2
1891  FLTNB itac = 0.; // integral at time point t
1892 
1893  // Init vectors
1894  FLTNB* WPO_P = m2p_wpoP[ a_th ];
1895  FLTNB* WPO_FD = m2p_wpoFD[ a_th ];
1896  FLTNB* WPO_BD = m2p_wpoBD[ a_th ];
1897 
1898  // WPO_P, WPO_BD, WPO_FD
1899  for(uint32_t t=0 ; t<nb_tf ; t++)
1900  {
1901  if( t==0 )
1902  {
1903  WPO_P[t] = 0. ;
1904  WPO_BD[t] = ap_tac[0] ;
1905  WPO_FD[t] = ap_tac[0] ;
1906  }
1907  else if( t==1 )
1908  {
1909  WPO_P[t] = 0. ;
1910  WPO_FD[t] = DT2[t-1] *( ( ap_tac[ t-1 ] ) );
1911  WPO_BD[t] = DT2[t-1] *( ( ap_tac[ t-1 ] + ap_tac[ t ] ));
1912  }
1913  else
1914  {
1915  WPO_P[t] = ( ap_tac[ t-1 ] - ( DT2[t]*ap_tac[ t-2 ] + DT2[t-1]*ap_tac[ t ] ) / ( DT2[t] + DT2[t-1] ) ) / 6;
1916  WPO_FD[t] = 2*DT2[t-1] * ( 0.5 *( ap_tac[ t-1 ] + ap_tac[ t-2 ] ) + WPO_P[t] * mp_wpoQ[t] );
1917  WPO_BD[t] = 2*DT2[t-1] * ( 0.5 *( ap_tac[ t-1 ] + ap_tac[ t ] ) + WPO_P[t] / mp_wpoQ[t] );
1918  }
1919  }
1920 
1921  // TODO : adapt Simpson rule to the frame duration (only the isotropic frame in the peak)
1922  for(uint32_t t=0 ; t<nb_tf ; t++)
1923  {
1924  // Compute tac integral at time point t
1925  if( t==0 )
1926  itac = ap_tac[ t ] * DT2[ t ] ;
1927  else if( t == (nb_tf-1) )
1928  itac = WPO_BD[ t ];
1929  else
1930  itac = (1-mp_wpoA[ t ]) * WPO_BD[ t ] + mp_wpoA[ t ]*WPO_FD[ t+1 ]; // WPO rule
1931 
1932  // Compute cumumative integral
1933  if( t==0 )
1934  ap_citac[ t ] = itac ;
1935  else
1936  ap_citac[ t ] = ap_citac[ t-1] + itac;
1937  }
1938 
1939  return 0;
1940 }
1941 
1942 
1943 
1944 
1945 // =====================================================================
1946 // ---------------------------------------------------------------------
1947 // ---------------------------------------------------------------------
1948 // =====================================================================
1949 /*
1950  \fn Trapz
1951  \param ap_tac : vector with nb time frames elements containing the time activity curve from which integral will be computed
1952  \param ap_citac : (returned) vector with nb time frames elements recovering the cumulative integral for each time point t
1953  \brief return integral for each time point t (WPO_S), and cumulative integral (cumWPO_S )
1954  \return 0 if success, positive value otherwise
1955 */
1956 int i1TCModel::Trapz(FLTNB* ap_tac, FLTNB* ap_citac)
1957 {
1958  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
1959 
1960  FLTNB* DT2 = mp_DT2; // Frame duration / 2
1961  FLTNB itac = 0.; // integral at time point t
1962 
1963  for(uint32_t t=0 ; t<nb_tf ; t++)
1964  {
1965  // Init
1966  ap_citac[t] = 0;
1967 
1968  if( t==0 )
1969  {
1970  itac = ap_tac[0] * DT2[0] *0.5;
1971  ap_citac[0] = itac;
1972  }
1973  else
1974  {
1975  itac = ( ap_tac[t]+ap_tac[t-1] ) * DT2[t];
1976  ap_citac[t] += ap_citac[t-1] + itac;
1977  }
1978  }
1979 
1980  return 0;
1981 }
1982 
1983 
1984 
#define INTF_LERP_DISABLED
int NNLS(FLTNB **A, int m, int n, FLTNB *B, FLTNB *X, FLTNB *rnorm, FLTNB *wp, FLTNB *zzp, int *indexp)
int Inverse(oMatrix *ap_MtxResult)
int CheckSpecificParameters()
This function is used to check whether all member variables have been correctly initialized or not...
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the &#39;a_input&#39; string corresponding to the &#39;a_option&#39; into &#39;a_nbElts&#39; elements, using the &#39;sep&#39; separator. The results are returned in the templated &#39;ap_return&#39; dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
#define METHOD_1CPT_NNLS
#define Cerr(MESSAGE)
i1TCModel()
Constructor of i1TCModel. Simply set all data members to default values.
int InitializeSpecific()
This function is used to initialize parametric images and basis functions.
oImageDimensionsAndQuantification * mp_ID
void ShowHelp()
This function is used to print out general help about dynamic models.
This is the mother class of dynamic model classes.
~i1TCModel()
Destructor of i1TCModel.
int Trapz(FLTNB *ap_tac, FLTNB *ap_citac)
void Exit(int code)
int EstimateModelParametersWithBF(oImageSpace *ap_ImageS)
int RRLS(uint16_t a_nP, uint16_t a_nT, FLTNB **a2p_model, FLTNB *ap_data, FLTNB *ap_w, FLTNB *ap_result)
int IntfReadImage(const string &a_pathToHeaderFile, FLTNB *ap_ImgMatrix, oImageDimensionsAndQuantification *ap_ID, int vb, bool a_lerpFlag)
Main function dedicated to Interfile 3D image loading.
int WPO(FLTNB *ap_tac, FLTNB *ap_citac, int a_th)
#define METHOD_INT_WPO
int ReadAndCheckOptionsList(string a_listOptions)
int EstimateModelParameters(oImageSpace *ap_Image, int a_ite, int a_sset)
int ReadAndCheckConfigurationFileSpecific()
This function is used to read options from a configuration file.
int EstimateImageWithModel(oImageSpace *ap_Image, int a_ite, int a_sset)
HPFLTNB GetMatriceElt(uint16_t l, uint16_t c)
void Describe()
Display the element of the matrix.
#define KEYWORD_MANDATORY
#define KEYWORD_OPTIONAL
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 EstimateModelParametersWithLS(oImageSpace *ap_ImageS)
This class holds all the matrices in the image domain that can be used in the algorithm: image...
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
Structure designed for basic matrices operations.
int EstimateModelParametersWithNNLS(oImageSpace *ap_ImageS)
int LS(uint16_t a_nP, uint16_t a_nT, FLTNB **a2p_model, FLTNB *ap_data, FLTNB *ap_w, FLTNB *ap_result)
int Multiplication(oMatrix *ap_Mtx, oMatrix *ap_MtxResult)
FLTNB WPOinc(uint32_t a_time, FLTNB tac, FLTNB b_tac, FLTNB bb_tac, FLTNB n_tac)
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...
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.
#define METHOD_1CPT_LS
int SetMatriceElt(uint16_t l, uint16_t c, HPFLTNB a_val)
#define Cout(MESSAGE)
#define METHOD_1CPT_BF
int IntegrateTAC(FLTNB *ap_tac, FLTNB *ap_citac, int a_th)
int Transpose(oMatrix *a_MtxResult)