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