CASToR  3.1
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-2020 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF, Zacharias CHALAMPALAKIS
20 
21 This is CASToR version 3.1.
22 */
23 
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 ShowHelpModelSpecific
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 << " - LINE OPTIONS : (command-line options : _1TCM,options:)" << endl;
282  cout << " " << endl;
283  cout << " Command-line options just require the samples of the plasma input curve, separated by commas. " << endl;
284  cout << " All other options will be set to default. The optimization algorithm will be NNLS, the integration method for TAC will be WPO " << endl;
285  cout << " -> 1cptModel,val_cp_tf1 , val_cp_tf2 , ..., val_cp_tfn" << endl;
286  cout << endl;
287 
288  // Print general help for all dynamic models
289  ShowHelp();
290 }
291 
292 
293 
294 
295 // =====================================================================
296 // ---------------------------------------------------------------------
297 // ---------------------------------------------------------------------
298 // =====================================================================
299 /*
300  \fn ReadAndCheckConfigurationFileSpecific
301  \brief This function is used to read options from a configuration file.
302  \return 0 if success, other value otherwise.
303 */
305 {
306  if(m_verbose >=3) Cout("i1TCModel::ReadAndCheckConfigurationFileSpecific ..."<< endl);
307 
308  // The file will be processed in the Initialize() function
309 
310  ifstream in_file(m_fileOptions.c_str(), ios::in);
311 
312  if(in_file)
313  {
314  // Method to be used to compute integral of TACs
315  // (Check this first in case we must estimate plasma integral
316  // so we need to know which method to use)
317  // Default : WPO
318 
319  string dtag = "Integration_method";
321  dtag,
323  1,
324  KEYWORD_OPTIONAL) == 1)
325  {
326  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read '"<< dtag <<"' flag in "<< m_fileOptions << endl);
327  return 1;
328  }
329 
330  // Optimisation method for the estimation of parameters
331  dtag = "Optimisation_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  // Save blacklisted voxels images on disk ?
343  dtag = "Save_blacklisted_voxels_images";
345  dtag,
347  1,
348  KEYWORD_OPTIONAL) == 1)
349  {
350  Cerr("***** vDynamicModel::ReadAndCheckConfigurationFile() -> Error while trying to read '"<< dtag <<"' flag in " << m_fileOptions << endl);
351  return 1;
352  }
353 
354  // --- Constraints for ridge regression ---
355  const int nb_params = 3;
356  // Default upper/lower bounds
357  HPFLTNB p_bounds_params[ 6 ] = {0.1, 0., 0.1, 0., 1., 0.};
358 
359  // Ridge Regression parameter
360  dtag = "Ridge_Parameter";
362  dtag,
363  &m_RRcst,
364  1,
365  KEYWORD_OPTIONAL) == 1)
366  {
367  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read '"<< dtag <<"' flag in "<< m_fileOptions << endl);
368  return 1;
369  }
370 
371  // If a ridge regression parameter constant has been provided,
372  // set up the related flag and increase the number of LS fonction models
373  if (m_RRcst>=0) m_ridgeRegressionFlag = true;
374 
375  dtag = "Bounds";
377  dtag,
378  p_bounds_params,
379  2*nb_params,
380  KEYWORD_OPTIONAL) == 1)
381  {
382  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read '"<< dtag <<"' flag (upper/lower bounds) in "<< m_fileOptions << endl);
383  return 1;
384  }
385 
386  // init parameters bounds
387  mp_parUpperBounds = new FLTNB[ nb_params ];
388  mp_parLowerBounds = new FLTNB[ nb_params ];
389 
390  for (int p=0 ; p<nb_params ; p++ )
391  {
392  mp_parUpperBounds[ p ] = p_bounds_params[ p*2 ];
393  mp_parLowerBounds[ p ] = p_bounds_params[ p*2 + 1 ];
394  }
395  }
396  else
397  {
398  Cerr("***** i1TCModel::ReadAndCheckConfigurationFileSpecific() -> Error while trying to read configuration file at: " << m_fileOptions << endl);
399  return 1;
400  }
401 
402  return 0;
403 }
404 
405 
406 
407 
408 // =====================================================================
409 // ---------------------------------------------------------------------
410 // ---------------------------------------------------------------------
411 // =====================================================================
412 /*
413  \fn ReadAndCheckOptionsList
414  \param const string& a_optionsList : a list of parameters separated by commas
415  \brief This function is used to read parameters from a string.
416  \return 0 if success, other value otherwise.
417 */
418 int i1TCModel::ReadAndCheckOptionsList(string a_listOptions)
419 {
420  if(m_verbose >=2) Cout("i1TCModel::ReadAndCheckOptionsList ..."<< endl);
421 
422  // Just get the options here and perform initialization in InitializeSpecific()
423  // function, as some memory allocations must be performed first (images & TACs)
424  m_listOptions = a_listOptions;
425 
426  // Normal end
427  return 0;
428 }
429 
430 
431 
432 
433 // =====================================================================
434 // ---------------------------------------------------------------------
435 // ---------------------------------------------------------------------
436 // =====================================================================
437 /*
438  \fn CheckSpecificParameters
439  \brief This function is used to check whether all member variables
440  have been correctly initialized or not.
441  \return 0 if success, positive value otherwise.
442 */
444 {
445  if(m_verbose >=2) Cout("i1TCModel::CheckSpecificParameters ..."<< endl);
446 
447  // Check image dimensions
448  if (mp_ID==NULL)
449  {
450  Cerr("***** i1TCModel::CheckSpecificParameters() -> ImageDimensions object has not been provided !" << endl);
451  return 1;
452  }
453 
454  // Check number time basis functions
455  if (m_nbTimeBF<0)
456  {
457  Cerr("***** i1TCModel::CheckSpecificParameters() -> Wrong number of time frame basis functions !" << endl);
458  return 1;
459  }
460 
461  // Check if we have somehow both a file and a list of options for init...
462  if(m_listOptions != "" && m_fileOptions != "")
463  {
464  Cerr("***** i1TCModel::CheckSpecificParameters() -> Either a file or a list of options have to be selected to initialize the model, but not both ! " << endl);
465  return 1;
466  }
467 
468  // Check if we have no file not list of options for some reason...
469  if(m_listOptions == "" && m_fileOptions == "")
470  {
471  Cerr("***** i1TCModel::CheckSpecificParameters -> Either a file or a list of options should have been provided at this point ! " << endl);
472  return 1;
473  }
474 
475  // Check if we reconstruct gated data. Throw warning if it is the case
476  if(mp_ID->GetNbRespGates()>1 || mp_ID->GetNbCardGates()>1)
477  {
478  Cerr("***** i1TCModel::CheckSpecificParameters -> The implemented model is not compatible yet with gated reconstruction (parametric images will be the same for each gate)! " << endl);
479  return 1;
480  }
481 
482  // Check if ridge regression and NNLS has been enabled (RR only compatible with LS)
485  {
486  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);
487  return 1;
488  }
489 
490  // Normal end
491  return 0;
492 }
493 
494 
495 
496 
497 // =====================================================================
498 // ---------------------------------------------------------------------
499 // ---------------------------------------------------------------------
500 // =====================================================================
501 /*
502  \fn InitializeSpecific
503  \brief This function is used to initialize the model parametric images and basis functions
504  \return 0 if success, other value otherwise.
505 */
507 {
508  if(m_verbose >=2) Cout("i1TCModel::InitializeSpecific() ..."<< endl);
509 
510  // Forbid initialization without check
511  if (!m_checked)
512  {
513  Cerr("***** oDynamicModelManager::InitializeSpecific() -> Must call CheckParameters functions before Initialize() !" << endl);
514  return 1;
515  }
516 
517  // --- Memory Allocation --- //
518 
519  // Allocate memory for Parametric images and functions
522 
523  for(int b=0 ; b<m_nbTimeBF ; b++)
525 
526  // Init with negative values
527  for(int b=0 ; b<m_nbTimeBF ; b++)
528  for(int t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
530 
531  for(int p=0 ; p<m_nbModelParam ; p++)
533 
534 
535  // Initialize blacklisted voxels image to zero;
537 
538  for (int v = 0; v < mp_ID->GetNbVoxXYZ(); v++)
539  mp_blackListedvoxelsImage[v] = 0.0;
540 
541 
542  // --- Data Initialization with a configuration file --- //
543  // Data requiring memory allocation (Image and TACs initializations)
544 
545  if(m_fileOptions != "")
546  {
547  ifstream in_file(m_fileOptions.c_str(), ios::in);
548 
549  if(in_file)
550  {
551  // --- Plasma input functions ---
552 
553  // Recover Plasma input function TAC (mandatory)
554  string dtag = "Input_function";
556  dtag,
560  {
561  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read 1TCPM input functions !" << endl);
562  Cerr(" '"<< dtag <<"' keyword in " << m_fileOptions << endl);
563  return 1;
564  }
565 
566  // Recover Integral of plasma input function TAC (optional)
567  dtag = "Integral_input_function";
569  dtag,
572  KEYWORD_OPTIONAL) == 1 )
573  {
574  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read 1TCPM input functions !" << endl);
575  Cerr(" '"<< dtag <<"' keyword in " << m_fileOptions << endl);
576  return 1;
577  }
578 
579 
580  // Parametric images initialization
581  string input_image = "";
582  int return_value = 0;
583 
584  dtag = "Parametric_images_init";
585  return_value = ReadDataASCIIFile(m_fileOptions,
586  dtag,
587  &input_image,
588  1,
590 
591  if( return_value == 0) // Image have been provided
592  {
593  // Read image // INTF_LERP_DISABLED = interpolation disabled for input image reading
594  if( IntfReadImgDynCoeffFile(input_image,
596  mp_ID,
597  m_nbModelParam,
598  m_verbose,
599  INTF_LERP_DISABLED) ) // Image have been provided
600  {
601  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read the provided initialization parametric images : " << input_image << endl);
602  return 1;
603  }
604  }
605  else if( return_value == 1) // Error during reading
606  {
607  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read input functions coefficients !" << endl);
608  Cerr(" '"<< dtag <<"' keyword in " << m_fileOptions << endl);
609  return 1;
610  }
611  else //(return_value >= 1 ) // Keyword not found : no initialization provided
612  {
613  // Standard initialization
614  for(int p=0 ; p<m_nbModelParam ; p++)
615  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
616  m2p_parametricImages[p][v] = 0.;
617  }
618 
619 
620  string path_to_VAimage;
621  dtag = "VA_image";
623  dtag,
624  &path_to_VAimage,
625  1,
626  KEYWORD_OPTIONAL) == 1)
627  {
628  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read '"<< dtag <<"' keyword in " << m_fileOptions << endl);
629  return 1;
630  }
631 
632  if(!path_to_VAimage.empty())
633  {
634  mp_VaImage = new FLTNB[ mp_ID->GetNbVoxXYZ() ];
635 
637  {
638  Cerr("***** i1TCModel::InitializeSpecific()-> Error reading Interfile : " << path_to_VAimage << " !" << endl);
639  return 1;
640  }
641 
642  // Check all voxels in VaImage = [0 ,1]
643  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
644  {
645  if(mp_VaImage[ v ] <0.
646  ||mp_VaImage[ v ] >1.)
647  {
648  Cerr("***** i1TCModel::InitializeSpecific() -> Voxels values in provided Va image ;"<< path_to_VAimage << " must be between [0,1]. One voxel value is "<< m_fileOptions << endl);
649  Cerr(" Erroneous value found:"<< mp_VaImage[ v ] << endl);
650  return 1;
651  }
652  }
653 
654  // Set the number of parameters to estimate to 2
655  m_nnlsN=2;
656  }
657  }
658 
659  else
660  {
661  Cerr("***** i1TCModel::InitializeSpecific() -> Error while trying to read configuration file at: " << m_fileOptions << endl);
662  return 1;
663  }
664  }
665  // --- Data Initialization with a list of options --- //
666  else if( m_listOptions != "" )
667  {
668  // We expect here the plasma tac only
669 
670  // Read it
674  ",",
675  "1cpt model configuration"))
676  {
677  Cerr("***** i1TCModel::InitializeSpecific() -> Failed to correctly read the list of options !" << endl);
678  return 1;
679  }
680 
681  // Standard initialization for the parametric images
682  for(int p=0 ; p<m_nbModelParam ; p++)
683  for(int v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
684  m2p_parametricImages[p][v] = 0.;
685 
686  }
687 
688 
689  // WPO for integrals
690  // Half-frame time duration
691  mp_DT2 = new FLTNB[mp_ID->GetNbTimeFrames()];
692 
693  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
694  mp_DT2[t] = mp_ID->GetFrameDurationInSec(0,t) * 0.5 ;
695 
697  {
698  mp_wpoQ = new FLTNB[ mp_ID->GetNbTimeFrames() ];
699  mp_wpoA = new FLTNB[ mp_ID->GetNbTimeFrames() ];
703 
704  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
705  {
706  m2p_wpoP[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
707  m2p_wpoFD[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
708  m2p_wpoBD[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
709  }
710 
711  // Compute WPO_q and WPO_a
712  for(int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
713  {
714  if( t == mp_ID->GetNbTimeFrames() -1)
715  mp_wpoQ[t] = 1;
716  else
717  mp_wpoQ[t] = mp_DT2[t] / mp_DT2[t+1];
718  }
719 
720 
721  for(int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
722  {
723  if( t==0 )
724  mp_wpoA[t] = 1;
725  else if( t==(mp_ID->GetNbTimeFrames() -2) )
726  mp_wpoA[t] = 0.5;
727  else if( t==(mp_ID->GetNbTimeFrames() -1) )
728  mp_wpoA[t] = 0.;
729  else
730  mp_wpoA[t] = (mp_DT2[ t+1 ] + 2*mp_DT2[ t ] ) / (2 * (mp_DT2[ t+2 ] + mp_DT2[ t+1 ] + mp_DT2[ t ]) );
731  //mp_wpoA[t] = (mp_DT2[ t ] + 2*mp_DT2[ t-1 ] ) / (2 * (mp_DT2[ t+1 ] + mp_DT2[ t ] + mp_DT2[ t-1 ]) );
732  }
733  }
734 
735 
736  // Variables for Tissue TAC
739 
740  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
741  {
742  m2p_ct[ th ] = new FLTNB[mp_ID->GetNbTimeFrames()];
743  m2p_cti[ th ] = new FLTNB[mp_ID->GetNbTimeFrames()];
744  }
745 
746  // --- Input functions ---
747 
748  // Check IF has been provided
749  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
750  if(m2p_nestedModelTimeBasisFunctions[ 1 ][ t ] < 0)
751  {
752  Cerr("***** i1TCModel::InitializeSpecific() -> Error, plasma input curve has not been initialized !" << endl);
753  return 1;
754  }
755 
756  // Compute integration of IF if not provided
757  if( m2p_nestedModelTimeBasisFunctions[ 0 ][ 0 ] < 0)
758  {
759  // --- Compute integral ---
760  FLTNB* Icp = new FLTNB[ mp_ID->GetNbTimeFrames() ];
761 
762  // Compute plasma tac integral from plasma tac in m2p_nestedModelTimeBasisFunctions[ 1 ]
764 
765  // Recover in model function vectors
766  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
767  m2p_nestedModelTimeBasisFunctions[ 0 ][ t ] = Icp[ t ];
768 
769  delete[] Icp;
770  }
771 
772 
773  // NNLS variables
777 
778  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
779  {
780  // Init 2D coefficient matrix for NNLS estimation
781  m3p_nnlsA[ th ] = new FLTNB*[ m_nnlsN ];
782 
783  for(int n=0 ; n<m_nnlsN ; n++)
784  m3p_nnlsA[ th ][n] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
785 
786  // Init solution vector and working matrix for NNLS estimation
787  m2p_nnlsB[ th ] = new FLTNB[ mp_ID->GetNbTimeFrames() ];
788  m2p_nnlsMat[ th ] = new FLTNB[ (m_nnlsN+2) * mp_ID->GetNbTimeFrames() ];
789  }
790 
791 
792  // Compute weights for NNLS
793  mp_w = new FLTNB[ mp_ID->GetNbTimeFrames() ];
794 
795  for(int t=0; t<mp_ID->GetNbTimeFrames(); t++)
796  mp_w[t] = sqrt(mp_ID->GetFrameDurationInSec(0,t)) ; // (convert time in min);
797 
798  // Init matrix for LS method
800  {
809  mp_RRm = new oMatrix(m_nnlsN, 1);
810  mp_RRw = new oMatrix(m_nnlsN, m_nnlsN);
811 
812  for( int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++ )
813  {
814  mp_Y[ th ] = new oMatrix(mp_ID->GetNbTimeFrames(),1);
815  mp_X[ th ] = new oMatrix(mp_ID->GetNbTimeFrames(), m_nnlsN);
816  mp_Xt[ th ] = new oMatrix(m_nnlsN, mp_ID->GetNbTimeFrames());
817  mp_XtX[ th ] = new oMatrix(m_nnlsN, m_nnlsN);
818  mp_LSnum[ th ] = new oMatrix(m_nnlsN, 1);
819  mp_LSden[ th ] = new oMatrix(m_nnlsN, m_nnlsN);
820  mp_Theta[ th ] = new oMatrix(m_nnlsN, 1);
821  mp_RRnum[ th ] = new oMatrix(m_nnlsN, 1);
822  }
823 
824  // Init ridge regression matrices
826  {
827  // Init RR diagonal matrices
828  for (int pl=0 ; pl<m_nnlsN ; pl++)
829  for (int pc=0 ; pc<m_nnlsN ; pc++)
830  {
831  if( pl==pc)
832  {
833  mp_RRw->SetMatriceElt( pl , pc , 1 / ( (mp_parUpperBounds[ pl ]-mp_parLowerBounds[ pl ])*(mp_parUpperBounds[ pl ]-mp_parLowerBounds[ pl ]) ) ) ;
834  mp_RRm->SetMatriceElt( pl , 0 , (mp_parUpperBounds[ pl ] + mp_parLowerBounds[ pl ]) / 2. ) ;
835 
836  for(int th=0 ; th<mp_ID->GetNbThreadsForImageComputation() ; th++)
837  mp_RRnum[ th ]->SetMatriceElt( pl , 0 , 0. ) ;
838  }
839  else
840  {
841  mp_RRw->SetMatriceElt( pl , pc , 0 ) ;
842  }
843  }
844 
845  }
846  }
847 
848 
849  // Display TACs and other info
850  if(m_verbose >=2)
851  {
852  Cout("i1TCModel::InitializeSpecific() -> Input Plasma TAC coefficients :" << endl);
853  Cout(" ");
854  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
855  Cout(m2p_nestedModelTimeBasisFunctions[0][fr] << ", ");
856  Cout(endl);
857  Cout("i1TCModel::InitializeSpecific() -> Plasma TAC coefficients :" << endl);
858  Cout(" ");
859  for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
860  Cout(m2p_nestedModelTimeBasisFunctions[1][fr] << ", ");
861  Cout(endl);
862 
864  Cout("i1TCModel::InitializeSpecific() -> Image update from estimated parametric images is disabled" << endl);
865  }
866 
867  // Normal end
868  m_initialized = true;
869  return 0;
870 }
871 
872 
873 
874 // =====================================================================
875 // ---------------------------------------------------------------------
876 // ---------------------------------------------------------------------
877 // =====================================================================
878 /*
879  \fn EstimateModelParameters
880  \param ap_ImageS : pointer to the ImageSpace
881  \param a_ite : index of the actual iteration (not used)
882  \param a_sset : index of the actual subset (not used)
883  \brief Estimate K1, k2, Va parametric images
884  \return 0 if success, other value otherwise.
885 */
886 int i1TCModel::EstimateModelParameters(oImageSpace* ap_ImageS, int a_ite, int a_sset)
887 {
888  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParameters ..." <<endl);
889 
890  #ifdef CASTOR_DEBUG
891  if (!m_initialized)
892  {
893  Cerr("***** i1TCModel::EstimateModelParameters() -> Called while not initialized !" << endl);
894  Exit(EXIT_DEBUG);
895  }
896  #endif
897 
898  switch (m_OptimisationMethodFlag)
899  {
900  case METHOD_1CPT_LS:
901  if( EstimateModelParametersWithLS(ap_ImageS) )
902  {
903  Cerr("***** i1TCModel::EstimateModelParameters() -> A problem occured when estimating 1cpt model parameters with LS method !" << endl);
904  return 1;
905  }
906  break;
907 
908  case METHOD_1CPT_BF :
909  if ( EstimateModelParametersWithBF(ap_ImageS) )
910  {
911  Cerr("***** i1TCModel::EstimateModelParameters() -> A problem occured when estimating 1cpt model parameters with Basis functions method !" << endl);
912  return 1;
913  }
914  break;
915 
916  case METHOD_1CPT_NNLS :
917  if ( EstimateModelParametersWithNNLS(ap_ImageS) )
918  {
919  Cerr("***** i1TCModel::EstimateModelParameters() -> A problem occured when estimating 1cpt model parameters with NNLS method !" << endl);
920  return 1;
921  }
922  break;
923  }
924 
925 
926  // --- Voxel-based Constraints ---
927 
928  // Loop on all voxels
929  int v;
930  #pragma omp parallel for private(v) schedule(guided)
931  for(v=0; v<mp_ID->GetNbVoxXYZ(); v++)
932  {
933  // Set to 0. if value is Nan or < 0.
934  if (!isfinite(m2p_parametricImages[0][v])
935  || !isfinite(m2p_parametricImages[1][v])
936  || !isfinite(m2p_parametricImages[2][v])
937  )
938  {
939  m2p_parametricImages[2][v] = 0.;
940  m2p_parametricImages[1][v] = 0.;
941  m2p_parametricImages[0][v] = 0.;
942  }
943 
944  // Biological constraints on Va (positivity and max == 1)
945  if( m2p_parametricImages[2][v] < 0.) m2p_parametricImages[2][v]=0.;
946  if( m2p_parametricImages[2][v] > 1.) m2p_parametricImages[2][v]=1.;
947 
948 
949  // Biological constaints on k2
950  // (set to 0 if:
951  // -> Va ~= 1
952  // -> Va << && K1 <<
953  if( m2p_parametricImages[2][v] > 0.95 ||
954  ( m2p_parametricImages[2][v] < 0.1 && m2p_parametricImages[0][v]<0.00001) )
955  m2p_parametricImages[1][v] = 0;
956 
957  // Set debug image
958  if( // At least one parameter is non nil
959  ( m2p_parametricImages[ 0 ][ v ]>0.
960  || m2p_parametricImages[ 1 ][ v ]>0.
961  || m2p_parametricImages[ 2 ][ v ]>0.
962  )
963  // No negative parameter
964  && ( m2p_parametricImages[ 0 ][ v ]>=0.
965  && m2p_parametricImages[ 1 ][ v ]>=0.
966  && m2p_parametricImages[ 2 ][ v ]>=0. )
967  )
968  mp_blackListedvoxelsImage[ v ] = (mp_blackListedvoxelsImage[v] == 2) ? 2 : 0.;
969  else
970  mp_blackListedvoxelsImage[ v ] = (mp_blackListedvoxelsImage[v] == 2) ? 2 : 1.;
971 
972  }
973 
974 
975  return 0;
976 }
977 
978 
979 
980 // =====================================================================
981 // ---------------------------------------------------------------------
982 // ---------------------------------------------------------------------
983 // =====================================================================
984 /*
985  \fn EstimateModelParametersWithNNLS
986  \param ap_ImageS : pointer to the ImageSpace
987  \brief Estimate K1, k2, Va parametric images using NLLS
988  \return 0 if success, other value otherwise.
989 */
991 {
992  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParametersWithNNLS ..." <<endl);
993 
994  #ifdef CASTOR_DEBUG
995  if (!m_initialized)
996  {
997  Cerr("***** i1TCModel::EstimateModelParametersWithNNLS() -> Called while not initialized !" << endl);
998  Exit(EXIT_DEBUG);
999  }
1000  #endif
1001 
1002  int rg=0, cg=0;
1003 
1004 
1005  // Loop on all voxels
1006  int v;
1007  #pragma omp parallel for private(v) schedule(guided)
1008  for(v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1009  {
1010  // If a mask has been provided, check if the model applies in this voxel
1011  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1012  continue;
1013 
1014  int th=0;
1015  #ifdef CASTOR_OMP
1016  th = omp_get_thread_num();
1017  #endif
1018 
1019  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
1020 
1021  FLTNB** _2p_nnlsA = m3p_nnlsA[ th ];
1022  FLTNB* _p_nnlsB = m2p_nnlsB[ th ];
1023  FLTNB* _p_nnlsMat = m2p_nnlsMat[ th ];
1024 
1025  // m-array of working space
1026  FLTNB *nnls_zz;
1027 
1028  // Solution vectors
1029  FLTNB *nnls_x = new FLTNB[ m_nnlsN ];
1030 
1031  //n-array of working space for the dual solution y
1032  FLTNB *nnls_wp = new FLTNB[ m_nnlsN ];
1033 
1034  // n-array of working space
1035  int *nnls_index = new int[ m_nnlsN ];
1036 
1037  // Euclidean norm of the residual vector
1038  FLTNB nnls_rnorm;
1039 
1040  FLTNB *dptr=_p_nnlsMat;
1041  dptr=_p_nnlsMat;
1042  for(int n=0 ; n<m_nnlsN; n++)
1043  {
1044  _2p_nnlsA[n]=dptr;
1045  dptr+=nb_tf;
1046  }
1047 
1048  _p_nnlsB=dptr;
1049  dptr+=nb_tf;
1050  nnls_zz=dptr;
1051 
1052  // Integral of plasma TAC
1054  // Plasma TAC
1056 
1057  // Pointers for tissue TAC and integral of tissue TAC
1058  FLTNB* ct = m2p_ct[th];
1059  FLTNB* cti = m2p_cti[th];
1060 
1061  //------------------------
1062  // Estimate K1, k2 and Va
1063  //
1064  // Recover CPET value
1065  for(size_t t=0; t<nb_tf; t++)
1066  ct[t] = ap_ImageS->m4p_image[t][rg][cg][v];
1067 
1068  // If a VaImage has been provided, subtract Va*Ca from tissue TAC
1069  if( mp_VaImage != NULL)
1070  {
1071  for(size_t t=0; t<nb_tf; t++)
1072  {
1073  ct[t] -= mp_VaImage[v]*cp[t] ;
1074  if (ct[t]<0) ct[t]=0.;
1075  }
1076  }
1077 
1078  // Compute integral of tissue TAC
1079  IntegrateTAC(ct, cti, th);
1080 
1081  // Estimate K1, k2, Va parameters with NNLS
1082  if( mp_VaImage == NULL)
1083  {
1084  // Set up NNLS variables
1085  for(size_t t=0; t<nb_tf; t++)
1086  {
1087  if(cti[t]<0) cti[t] = 0.;
1088  // Fill NNLS A matrix:
1089  // function #1: tissue integral x -1
1090  _2p_nnlsA[0][t]=-cti[t]*mp_w[t];
1091  // function #2: integral of input
1092  _2p_nnlsA[1][t]=cpi[t]*mp_w[t];
1093  // function #3: input curve
1094  _2p_nnlsA[2][t]=cp[t]*mp_w[t];
1095 
1096  // Fill NNLS B array: tissue
1097  _p_nnlsB[t]=ct[t]*mp_w[t];
1098  }
1099 
1100  if(NNLS(_2p_nnlsA, nb_tf, m_nnlsN, _p_nnlsB, nnls_x, &nnls_rnorm,
1101  nnls_wp, nnls_zz, nnls_index) )
1102  continue; // no solution is possible
1103 
1104  // Computer 1cpt model K1, k2, Va from NNLS estimated parameters
1105  FLTNB Va=nnls_x[2];
1106 
1107  // Recover K1
1108  m2p_parametricImages[0][v] = nnls_x[1];
1109  // If Va>0, we fitted to CPET instead of CT
1110  // In this case we didn't estimate K1, but K1 + Va*k2 as there is a Va*k2 contribution to CPi
1111  // We have to substract this contribution to get K1
1112  m2p_parametricImages[0][v] -= Va*nnls_x[0];
1113 
1114  m2p_parametricImages[1][v] = nnls_x[0];
1115  m2p_parametricImages[2][v] = Va;
1116  }
1117 
1118  else // Va value from user provided image
1119  {
1120  FLTNB Va = mp_VaImage[v];
1121 
1122  // Fill matrix without blood volume contribution
1123  for(size_t t=0; t<nb_tf; t++)
1124  {
1125  // Fill NNLS A matrix:
1126  // function #1: tissue integral x -1
1127  _2p_nnlsA[0][t] = -cti[t]*mp_w[t];
1128  // function #2: integral of input
1129  _2p_nnlsA[1][t] = cpi[t]*mp_w[t];
1130 
1131  // Fill NNLS B array: tissue
1132  _p_nnlsB[t] = ct[t]*mp_w[t];
1133  }
1134 
1135  // Estimate with NNLS (nnls_n - 1 as Vb is already corrected)
1136  if(NNLS(_2p_nnlsA, nb_tf, m_nnlsN, _p_nnlsB, nnls_x, &nnls_rnorm,
1137  nnls_wp, nnls_zz, nnls_index) )
1138  continue; // no solution is possible
1139 
1140  // Recover K1
1141  m2p_parametricImages[0][v] = nnls_x[1];
1142  m2p_parametricImages[1][v] = nnls_x[0];
1143  m2p_parametricImages[2][v] = Va;
1144  }
1145 
1146  // Free memory
1147  if(nnls_x) delete[]nnls_x;
1148  if(nnls_wp) delete[]nnls_wp;
1149  if(nnls_index) delete[]nnls_index;
1150 
1151  } // multithreaded loop on voxels
1152 
1153  return 0;
1154 }
1155 
1156 
1157 
1158 
1159 // =====================================================================
1160 // ---------------------------------------------------------------------
1161 // ---------------------------------------------------------------------
1162 // =====================================================================
1163 /*
1164  \fn EstimateModelParametersWithBF
1165  \param ap_ImageS : pointer to the ImageSpace
1166  \brief Estimate K1, k2, Va parametric images using basis functions approach
1167  \return 0 if success, other value otherwise.
1168 */
1170 {
1171  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParametersWithBF ..." <<endl);
1172 
1173  #ifdef CASTOR_DEBUG
1174  if (!m_initialized)
1175  {
1176  Cerr("***** i1TCModel::EstimateModelParametersWithBF() -> Called while not initialized !" << endl);
1177  Exit(EXIT_DEBUG);
1178  }
1179  #endif
1180 
1181  Cerr("i1TCModel::EstimateModelParametersWithBF -> Not yet implemented !!!" <<endl);
1182  return 1;
1183 }
1184 
1185 
1186 
1187 
1188 
1189 
1190 
1191 
1192 
1193 
1194 // =====================================================================
1195 // ---------------------------------------------------------------------
1196 // ---------------------------------------------------------------------
1197 // =====================================================================
1198 /*
1199  \fn EstimateModelParametersWithLS
1200  \param ap_ImageS : pointer to the ImageSpace
1201  \brief Estimate K1, k2, Va parametric images using LS
1202  \return 0 if success, other value otherwise.
1203 */
1205 {
1206  if(m_verbose >=2) Cout("i1TCModel::EstimateModelParametersWithLS ..." <<endl);
1207 
1208  #ifdef CASTOR_DEBUG
1209  if (!m_initialized)
1210  {
1211  Cerr("***** i1TCModel::EstimateModelParametersWithLS() -> Called while not initialized !" << endl);
1212  Exit(EXIT_DEBUG);
1213  }
1214  #endif
1215 
1216  int rg=0, cg=0;
1217 
1218  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
1219 
1220  bool error_in_th_loop = false;
1221  string error_msg ="";
1222 
1223 
1224  // Loop on all voxels
1225  int v;
1226  #pragma omp parallel for private(v) schedule(guided)
1227  for(v=0; v<mp_ID->GetNbVoxXYZ(); v++)
1228  {
1229  // If a mask has been provided, check if the model applies in this voxel
1230  if(mp_maskModel != NULL && mp_maskModel[ v ] == 0)
1231  continue;
1232 
1233  int th=0;
1234  #ifdef CASTOR_OMP
1235  th = omp_get_thread_num();
1236  #endif
1237 
1238  // Pointers for tissue TAC and integral of tissue TAC
1239  FLTNB* ct = m2p_ct[th];
1240  FLTNB* cti = m2p_cti[th];
1241 
1242  // Integral of plasma TAC
1244  // Plasma TAC
1246 
1247  // Recover CPET value
1248  for(size_t t=0; t<nb_tf; t++)
1249  ct[t] = ap_ImageS->m4p_image[t][rg][cg][v];
1250 
1251  // If a VaImage has been provided, subtract Va*Ca from tissue TAC
1252 
1253  if( mp_VaImage != NULL)
1254  {
1255  for(size_t t=0; t<nb_tf; t++)
1256  {
1257  ct[t] -= mp_VaImage[v]*cp[t] ;
1258  if (ct[t]<0) ct[t]=0.;
1259  }
1260  }
1261 
1262  // Compute integral of tissue TAC
1263  IntegrateTAC(ct, cti, th);
1264 
1265  // Solution vector
1266  FLTNB *LS_res = new FLTNB[ m_nnlsN ];
1267 
1268  // Model matrix
1269  FLTNB** LS_Mod = m3p_nnlsA[ th ];
1270 
1271  for(int16_t t=0; t<mp_ID->GetNbTimeFrames(); t++)
1272  {
1273  // function #1: tissue integral x -1
1274  LS_Mod[0][t] = -cti[t];
1275  // function #2: integral of input
1276  LS_Mod[1][t] = cpi[t];
1277  // function #3: input curve
1278  if( m_nnlsN>2) // Va image not provided
1279  LS_Mod[2][t] = cp[t];
1280  }
1281 
1282  // LS optimization using ridge-regression or not
1283  if (m_ridgeRegressionFlag )
1284  {
1285  // Estimate parameters with LS with ridge-regression
1286  if(RRLS(m_nnlsN,
1288  LS_Mod,
1289  ct,
1290  mp_w,
1291  LS_res) )
1292  {
1293  error_in_th_loop = true;
1294  error_msg = "***** i1TCModel::EstimateModelParametersWithLS() -> An error occurred when minimizing WRSS function with ridge-regression!";
1295  }
1296  }
1297  else
1298  {
1299  // Estimate parameters with standard LS
1300  if(LS(m_nnlsN,
1302  LS_Mod,
1303  ct,
1304  mp_w,
1305  LS_res) )
1306  {
1307  error_in_th_loop = true;
1308  error_msg = "***** i1TCModel::EstimateModelParametersWithLS() -> An error occurred when minimizing WRSS function with standard LS!";
1309  }
1310  }
1311 
1312  // Recover Va from LS parameter, or Va image if provided
1313  m2p_parametricImages[2][v] = (mp_VaImage == NULL) ?
1314  LS_res[ 2 ] :
1315  mp_VaImage[v] ;
1316 
1317  // k2
1318  m2p_parametricImages[1][v] = LS_res[ 0 ];
1319  //K1
1320  m2p_parametricImages[0][v] = LS_res[ 1 ]
1321  - m2p_parametricImages[2][v]*LS_res[ 0 ];
1322 
1323  if( LS_res ) delete[] LS_res;
1324  }
1325 
1326  if ( error_in_th_loop == true )
1327  {
1328  Cerr(error_msg << endl);
1329  return 1;
1330  }
1331 
1332  return 0;
1333 }
1334 
1335 
1336 
1337 // =====================================================================
1338 // ---------------------------------------------------------------------
1339 // ---------------------------------------------------------------------
1340 // =====================================================================
1341 /*
1342  \fn RRLS
1343  \param a_nP : number of parameters (P)
1344  \param a_nT : number of data samples (T)
1345  \param a2p_model : matrix containing the model functions (TxP elts)
1346  \param ap_data : data vector (T elts)
1347  \param ap_w : vector containing the weights (T elts)
1348  \param ap_result : vector recovering the estimated parameters (P elts)
1349  \brief Non-linear least square estimator with Ridge-Regression
1350  \details This function will estimate a set of parameters (O) by
1351  minimizing the weighted residual sum of squares (WRSS)
1352  difference between the data and the model function
1353  Ô = [ X'*W*X + t.Rw ]-1 [ X'*W*y ]
1354  + [ X'*W*X + t.Rw ]-1 [ t.Rw*Rm ]
1355  y = data vector
1356  X = model matrix
1357  W = weights vector
1358  t = Ridge constant
1359  Rw= Ridge weights
1360  Rm= Ridge means
1361 
1362  \return 0 if success, other value otherwise.
1363 */
1364 int i1TCModel::RRLS(uint16_t a_nP,
1365  uint16_t a_nT,
1366  FLTNB **a2p_model,
1367  FLTNB *ap_data,
1368  FLTNB *ap_w,
1369  FLTNB *ap_result
1370  )
1371 {
1372  #ifdef CASTOR_DEBUG
1373  if(m_verbose >=4) Cout("i1TCModel::RRLS ..." <<endl);
1374 
1375  if (!m_initialized)
1376  {
1377  Cerr("***** i1TCModel::RRLS() -> Called while not initialized !" << endl);
1378  Exit(EXIT_DEBUG);
1379  }
1380  #endif
1381 
1382  // Check that the LS flag is on, so that we are sure that all variables
1383  // have been correctly allocated
1385  {
1386  Cerr("***** i1TCModel::RRLS() -> Function is called while the optimization method is not Least-square !" << endl);
1387  return 1;
1388  }
1389 
1390  int th=0;
1391  #ifdef CASTOR_OMP
1392  th = omp_get_thread_num();
1393  #endif
1394 
1395  // Get the working matrices
1396  oMatrix* Y = mp_Y[ th ];
1397  oMatrix* X = mp_X[ th ];
1398  oMatrix* Xt = mp_Xt[ th ];
1399  oMatrix* XtX = mp_XtX[ th ];
1400  oMatrix* Theta = mp_Theta[ th ];
1401  oMatrix* LSnum = mp_LSnum[ th ];
1402  oMatrix* LSden = mp_LSden[ th ];
1403 
1404  oMatrix *RRw = mp_RRw;
1405  oMatrix *RRm = mp_RRm;
1406  oMatrix *RRnum = mp_RRnum[ th ];
1407 
1408  // Init data vector
1409  for( uint16_t t=0 ; t<a_nT ; t++ )
1410  Y->SetMatriceElt( t , 0, ap_data[ t ]*ap_w[ t ] );
1411 
1412  // Init model matrix
1413  for( uint16_t p=0 ; p<a_nP ; p++ )
1414  for( uint16_t t=0 ; t<a_nT ; t++ )
1415  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] );
1416 
1417 
1418  #ifdef CASTOR_DEBUG
1419  if(m_verbose >=4)
1420  {
1421  cout << " ----------------------------------------------- " << endl;
1422  cout << " X->Describe() " << endl;
1423  cout << " ----------------------------------------------- " << endl;
1424  }
1425  #endif
1426 
1427  // Compute X'
1428  X->Transpose(Xt);
1429 
1430  // Once X' is computed, add weights to X
1431  for( uint16_t p=0 ; p<a_nP ; p++ )
1432  for( uint16_t t=0 ; t<a_nT ; t++ )
1433  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] * ap_w[ t ] );
1434 
1435 
1436  #ifdef CASTOR_DEBUG
1437  if(m_verbose >=4)
1438  {
1439  cout << " ----------------------------------------------- " << endl;
1440  cout << " Xt->Describe() " << endl;
1441  Xt->Describe();
1442  cout << " ----------------------------------------------- " << endl;
1443  }
1444  #endif
1445 
1446  // Compute (X'*W*X), recover the result in XtX matrix
1447  Xt->Multiplication( X, XtX );
1448 
1449  // Add Ridge weights, recover result in XtX
1450  for (int pl=0 ; pl<a_nP ; pl++)
1451  for (int pc=0 ; pc<a_nP ; pc++)
1452  XtX->SetMatriceElt( pl ,
1453  pc ,
1454  XtX->GetMatriceElt(pl,pc) + m_RRcst*RRw->GetMatriceElt(pl,pc) );
1455 
1456 
1457  #ifdef CASTOR_DEBUG
1458  if(m_verbose >=4)
1459  {
1460  cout << " ----------------------------------------------- " << endl;
1461  cout << " X'WX->Describe() " << endl;
1462  XtX->Describe();
1463  cout << " ----------------------------------------------- " << endl;
1464  }
1465  #endif
1466 
1467  // Compute LS denominator [X'*W*X + t.*Rw]-1, recover the result in LSden matrix
1468  // If nil on diagonal, set all elts of result vector to 0. and stop here
1469  if ( XtX->Inverse( LSden ) == 1 )
1470  {
1471  for( uint16_t p=0 ; p<a_nP ; p++ )
1472  ap_result[ p ] = 0.;
1473 
1474  return 0.;
1475  }
1476 
1477  #ifdef CASTOR_DEBUG
1478  if(m_verbose >=4)
1479  {
1480  cout << " ----------------------------------------------- " << endl;
1481  cout << " RRLS denominator ->Describe() " << endl;
1482  LSden->Describe();
1483  cout << " ----------------------------------------------- " << endl;
1484  }
1485  #endif
1486 
1487  // Compute LS numerator (X'*W*y), recover the result in LS_num matrix
1488  Xt->Multiplication( Y, LSnum );
1489 
1490  #ifdef CASTOR_DEBUG
1491  if(m_verbose >=4)
1492  {
1493  cout << " ----------------------------------------------- " << endl;
1494  cout << " LS numerator ->Describe() " << endl;
1495  LSnum->Describe();
1496  cout << " ----------------------------------------------- " << endl;
1497  }
1498  #endif
1499 
1500  // Compute t.Rw*Rm and write result in rrtWM
1501  RRw->Multiplication( RRm, RRnum );
1502 
1503  // Compute complete numerator [ X'*W*y ] [ t.Rw*Rm ], recover in LSnum
1504  for (int p=0 ; p<a_nP ; p++)
1505  LSnum->SetMatriceElt( p , 0 , LSnum->GetMatriceElt(p,0)
1506  + m_RRcst * RRnum->GetMatriceElt(p,0) ) ;
1507 
1508  #ifdef CASTOR_DEBUG
1509  if(m_verbose >=4)
1510  {
1511  cout << " ----------------------------------------------- " << endl;
1512  cout << " RRLS numerator ->Describe() " << endl;
1513  LSnum->Describe();
1514  cout << " ----------------------------------------------- " << endl;
1515  }
1516  #endif
1517 
1518 
1519  // Compute final parameters, recover in 'Theta'
1520  // Ô = [ X'*W*X + t.Rw ]-1 [ X'*W*y ]
1521  // + [ X'*W*X + t.Rw ]-1 [ t.Rw*Rm ],
1522  LSden->Multiplication( LSnum, Theta );
1523 ;
1524  #ifdef CASTOR_DEBUG
1525  if(m_verbose >=4)
1526  {
1527  cout << " ----------------------------------------------- " << endl;
1528  cout << " Theta->Describe() " << endl;
1529  Theta->Describe();
1530  }
1531  #endif
1532 
1533  // Recover estimated parameters in return vector
1534  for( uint16_t p=0 ; p<a_nP ; p++ )
1535  ap_result[ p ] = Theta->GetMatriceElt(p,0);
1536 
1537  return 0;
1538 }
1539 
1540 
1541 
1542 
1543 // =====================================================================
1544 // ---------------------------------------------------------------------
1545 // ---------------------------------------------------------------------
1546 // =====================================================================
1547 /*
1548  \fn LS
1549  \param a_nP : number of parameters (P)
1550  \param a_nT : number of data samples (T)
1551  \param a2p_model : matrix containing the model functions (TxP elts)
1552  \param ap_data : data vector (T elts)
1553  \param ap_w : vector containing the weights (T elts)
1554  \param ap_result : vector recovering the estimated parameters (P elts)
1555  \brief Non-linear least square estimator
1556  \details This function will estimate a set of parameters (O) by
1557  minimizing the weighted residual sum of squares (WRSS)
1558  difference between the data and the model function
1559  Ô = [ X'WX ]-1 X'Wy
1560  y = data vector
1561  X = model matrix
1562  W = weights vector
1563 
1564  \return 0 if success, other value otherwise.
1565 */
1566 int i1TCModel::LS(uint16_t a_nP,
1567  uint16_t a_nT,
1568  FLTNB **a2p_model,
1569  FLTNB *ap_data,
1570  FLTNB *ap_w,
1571  FLTNB *ap_result
1572  )
1573 {
1574  #ifdef CASTOR_DEBUG
1575  if(m_verbose >=4) Cout("i1TCModel::LS ..." <<endl);
1576 
1577  if (!m_initialized)
1578  {
1579  Cerr("***** i1TCModel::LS() -> Called while not initialized !" << endl);
1580  Exit(EXIT_DEBUG);
1581  }
1582  #endif
1583 
1584  // Check that the LS flag is on, so that we are sure that all variables
1585  // have been correctly allocated
1587  {
1588  Cerr("***** i1TCModel::LS() -> Function is called while the optimization method is not Least-square !" << endl);
1589  return 1;
1590  }
1591 
1592  int th=0;
1593  #ifdef CASTOR_OMP
1594  th = omp_get_thread_num();
1595  #endif
1596 
1597  // Get the working matrices
1598  oMatrix* Y = mp_Y[ th ];
1599  oMatrix* X = mp_X[ th ];
1600  oMatrix* Xt = mp_Xt[ th ];
1601  oMatrix* XtX = mp_XtX[ th ];
1602  oMatrix* Theta = mp_Theta[ th ];
1603  oMatrix* LSnum = mp_LSnum[ th ];
1604  oMatrix* LSden = mp_LSden[ th ];
1605 
1606  // Init data vector
1607  for( uint16_t t=0 ; t<a_nT ; t++ )
1608  Y->SetMatriceElt( t , 0, ap_data[ t ]*ap_w[ t ] );
1609 
1610  // Init model matrix
1611  for( uint16_t p=0 ; p<a_nP ; p++ )
1612  for( uint16_t t=0 ; t<a_nT ; t++ )
1613  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] );
1614 
1615 
1616  #ifdef CASTOR_DEBUG
1617  if(m_verbose >=4)
1618  {
1619  cout << " ----------------------------------------------- " << endl;
1620  cout << " X->Describe() " << endl;
1621  cout << " ----------------------------------------------- " << endl;
1622  }
1623  #endif
1624 
1625  // Compute X'
1626  X->Transpose(Xt);
1627 
1628  // Once X' is computed, add weights to X
1629  for( uint16_t p=0 ; p<a_nP ; p++ )
1630  for( uint16_t t=0 ; t<a_nT ; t++ )
1631  X->SetMatriceElt( t , p , a2p_model[ p ][ t ] * ap_w[ t ] );
1632 
1633 
1634  #ifdef CASTOR_DEBUG
1635  if(m_verbose >=4)
1636  {
1637  cout << " ----------------------------------------------- " << endl;
1638  cout << " Xt->Describe() " << endl;
1639  Xt->Describe();
1640  cout << " ----------------------------------------------- " << endl;
1641  }
1642  #endif
1643 
1644  // Compute (X'WX), recover the result in XtX matrix
1645  Xt->Multiplication( X, XtX );
1646 
1647  #ifdef CASTOR_DEBUG
1648  if(m_verbose >=4)
1649  {
1650  cout << " ----------------------------------------------- " << endl;
1651  cout << " X'WX->Describe() " << endl;
1652  XtX->Describe();
1653  cout << " ----------------------------------------------- " << endl;
1654  }
1655  #endif
1656 
1657  // Compute LS numerator (X'Wy), recover the result in LS_num matrix
1658  Xt->Multiplication( Y, LSnum );
1659 
1660  #ifdef CASTOR_DEBUG
1661  if(m_verbose >=4)
1662  {
1663  cout << " ----------------------------------------------- " << endl;
1664  cout << " LS numerator ->Describe() " << endl;
1665  LSnum->Describe();
1666  cout << " ----------------------------------------------- " << endl;
1667  }
1668  #endif
1669 
1670  // Compute LS denominator [X'WX]-1, recover the result in LSden matrix
1671  // If nil on diagonal, set all elts of result vector to 0. and stop here
1672  if ( XtX->Inverse( LSden ) == 1 )
1673  {
1674  for( uint16_t p=0 ; p<a_nP ; p++ )
1675  ap_result[ p ] = 0.;
1676 
1677  return 0.;
1678  }
1679 
1680  #ifdef CASTOR_DEBUG
1681  if(m_verbose >=4)
1682  {
1683  cout << " ----------------------------------------------- " << endl;
1684  cout << " LS denominator ->Describe() " << endl;
1685  LSden->Describe();
1686  cout << " ----------------------------------------------- " << endl;
1687  }
1688  #endif
1689 
1690  // Get final parameter Ô = [ X'WX ]-1 X'Wy
1691  LSden->Multiplication( LSnum, Theta );
1692 
1693  #ifdef CASTOR_DEBUG
1694  if(m_verbose >=4)
1695  {
1696  cout << " ----------------------------------------------- " << endl;
1697  cout << " Theta->Describe() " << endl;
1698  Theta->Describe();
1699  }
1700  #endif
1701 
1702  // Recover estimated parameters in return vector
1703  for( uint16_t p=0 ; p<a_nP ; p++ )
1704  ap_result[ p ] = Theta->GetMatriceElt(p,0);
1705 
1706  return 0;
1707 }
1708 
1709 
1710 
1711 // =====================================================================
1712 // ---------------------------------------------------------------------
1713 // ---------------------------------------------------------------------
1714 // =====================================================================
1715 /*
1716  \fn EstimateImageWithModel
1717  \param ap_ImageS : pointer to the ImageSpace
1718  \param a_ite : index of the actual iteration (not used)
1719  \param a_sset : index of the actual subset (not used)
1720  \brief Estimate image using model parametric images and basis functions
1721  \todo Maybe add the possibility to use specific thresholds
1722  ex: if Va~1 (eg >0.8) && K1~0 && k2~0 --> Va=1, K1=k2=0
1723  \return 0 if success, other value otherwise.
1724 */
1725 int i1TCModel::EstimateImageWithModel(oImageSpace* ap_ImageS, int a_ite, int a_sset)
1726 {
1727  if(m_verbose >= 2) Cout("i1TCModel::EstimateImageWithModel ... " <<endl);
1728 
1729  #ifdef CASTOR_DEBUG
1730  if (!m_initialized)
1731  {
1732  Cerr("***** i1TCModel::EstimateImageWithModel() -> Called while not initialized !" << endl);
1733  Exit(EXIT_DEBUG);
1734  }
1735  #endif
1736 
1737  if( !m_noImageUpdateFlag
1738  && a_ite >= m_startIteUpdateFlag)
1739  {
1740  // Compute average of time shift between two samples in TACs formulas
1741  // Required for TACs computation
1742  HPFLTNB* DTavg = new HPFLTNB[mp_ID->GetNbTimeFrames() ];
1743  DTavg[ 0 ] = mp_DT2[ 0 ];
1744  for (int32_t t=1 ; t<mp_ID->GetNbTimeFrames() ; t++)
1745  DTavg[ t ] = (mp_DT2[ t ] + mp_DT2[ t-1 ] ) / 2;
1746 
1747  for (int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
1748  for (int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
1749  {
1750  int v;
1751  #pragma omp parallel for private(v) schedule(guided)
1752  for (v=0 ; v<mp_ID->GetNbVoxXYZ() ; v++)
1753  {
1754  FLTNB K1 = m2p_parametricImages[0][v];
1755  FLTNB k2 = m2p_parametricImages[1][v];
1756  FLTNB Va = m2p_parametricImages[2][v];
1757 
1758  // Keep reconstructed image value if one parameter value <0
1759  // or if all parameters == 0
1760  if( ( K1>0. || k2>0. || Va>0.) // At least one parameter is non nil
1761  && ( K1>=0. && k2>=0. && Va>=0. ) ) // No negative parameter
1762  {
1763  FLTNB ct=0., // tissue tac
1764  b_ct=0., // tissue tac previous sample
1765  cti=0., // cumulative integral of tissue tac
1766  b=0., // output tissue concentration
1767  bb_ct=0., // tissue tact penultinum previous sample
1768  n_ct=0.; // tissue_tac next sample
1769 
1770  for (int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
1771  {
1772  bb_ct = b_ct;
1773  b_ct = ct;
1774  b = cti + DTavg[t]*b_ct ;
1775 
1776  ct = ( K1*m2p_nestedModelTimeBasisFunctions[ 0 ][ t ] - k2*b )
1777  / ( 1 + k2*DTavg[ t ] );
1778 
1779 
1780  // Approximating next cti with weighed parabola method
1782  {
1783  // Pre-estimate ct+1 (n_ct) with Trapz
1784  if(t>0)
1785  {
1786  FLTNB n_cti = cti + mp_DT2[t]*(ct + b_ct);
1787  FLTNB n_b = n_cti + mp_DT2[t]*ct ; // output tissue concentration
1788  n_ct = (K1*m2p_nestedModelTimeBasisFunctions[0][t] - k2*n_b)
1789  / (1 + k2*DTavg[t]);
1790  }
1791 
1792  cti += WPOinc( t, ct, b_ct, bb_ct, n_ct);
1793  }
1794  else // Approximating cti with trapezoidal rule
1795  cti += mp_DT2[t]*(ct + b_ct);
1796 
1797  // CPET (add blood contribution)
1798  FLTNB new_value = ct + m2p_nestedModelTimeBasisFunctions[1][t]*Va;
1799  ap_ImageS->m4p_image[t][rg][cg][v] = (new_value > 0) ?
1800  new_value :
1801  ap_ImageS->m4p_image[t][rg][cg][v] ;
1802  }
1803  } // end of condition on kinetic parameters
1804  else
1805  for (int32_t t=0 ; t<mp_ID->GetNbTimeFrames() ; t++)
1806  ap_ImageS->m4p_image[t][rg][cg][v] = 0;
1807 
1808  } // end of loop on voxels
1809 
1810  } // end of loop on rg, cg
1811 
1812  delete[] DTavg;
1813 
1814  } // update image condition
1815 
1816  return 0;
1817 }
1818 
1819 
1820 // =====================================================================
1821 // ---------------------------------------------------------------------
1822 // ---------------------------------------------------------------------
1823 // =====================================================================
1824 /*
1825  \fn WPOinc
1826  \param a_time : time point for which the integral will be computed
1827  \param tac : tac value at time t
1828  \param b_tac : tac value at time t-1
1829  \param bb_tac : tac value at time t-2
1830  \param n_tac : tac value at time t+1
1831  \brief Estimate the next integration value for a specific time point of a tac using WPO
1832  \return The estimated value of the integral for this time point
1833 */
1834 FLTNB i1TCModel::WPOinc(uint32_t a_time, FLTNB tac, FLTNB b_tac, FLTNB bb_tac, FLTNB n_tac)
1835 {
1836  FLTNB Itac=0.;
1837  FLTNB wpo_p=0., wpo_p_n=0, wpo_fd_n=0., wpo_bd=0.;
1838  uint32_t t = a_time;
1839 
1840  if(t==0) // first sample (b_tac, bb_tac not known)
1841  Itac = mp_DT2[t]*tac*0.5;
1842  else if( t==1 ) // second sample (bb_tac not known)
1843  {
1844  wpo_p_n = ( tac - ( mp_DT2[t+1]*b_tac + mp_DT2[t]*n_tac ) / ( mp_DT2[t+1]+mp_DT2[t] ) ) / 6;
1845  wpo_fd_n = 2*mp_DT2[1] * ( 0.5*( tac + b_tac ) + wpo_p_n * mp_wpoQ[t+1] );
1846  wpo_bd = mp_DT2[t-1] * ( b_tac + tac );
1847 
1848  Itac += (1-mp_wpoA[ t ]) * wpo_bd + mp_wpoA[ t ]*wpo_fd_n; // WPO rule
1849  }
1850  else if( t < ((uint32_t)mp_ID->GetNbTimeFrames()-1) )
1851  {
1852  // todo : try to get tac+1 with trapZ ?
1853  wpo_p_n = ( tac - ( mp_DT2[t+1]*b_tac + mp_DT2[t] *n_tac ) / ( mp_DT2[t+1]+ mp_DT2[t] ) ) / 6;
1854  wpo_p = ( b_tac - ( mp_DT2[t] *bb_tac + mp_DT2[t-1] *tac ) / ( mp_DT2[t] + mp_DT2[t-1] ) ) / 6;
1855  wpo_fd_n = 2*mp_DT2[t] * ( 0.5*( tac + b_tac ) + wpo_p_n * mp_wpoQ[t+1] );
1856  wpo_bd = 2*mp_DT2[t-1] * ( 0.5*( b_tac + tac ) + wpo_p / mp_wpoQ[t] );
1857 
1858  Itac += (1-mp_wpoA[ t ]) * wpo_bd + mp_wpoA[ t ]*wpo_fd_n; // WPO rule
1859  }
1860  else
1861  {
1862  wpo_p = ( b_tac - ( ( ( mp_DT2[t] *bb_tac ) + mp_DT2[t-1] *tac ) ) / ( mp_DT2[t]+mp_DT2[t-1] ) ) / 6;
1863  wpo_bd = 2*mp_DT2[t-1] * ( 0.5*( b_tac + tac ) + wpo_p / mp_wpoQ[t] );
1864 
1865  Itac += wpo_bd; // WPO rule
1866  }
1867  return Itac;
1868 
1869 }
1870 
1871 
1872 
1873 
1874 // =====================================================================
1875 // ---------------------------------------------------------------------
1876 // ---------------------------------------------------------------------
1877 // =====================================================================
1886 int i1TCModel::IntegrateTAC(FLTNB* ap_tac, FLTNB* ap_citac, int a_th)
1887 {
1889  return WPO(ap_tac, ap_citac, a_th);
1890  else // ( m_intMethodFlag == METHOD_INT_TRAP )
1891  return Trapz(ap_tac, ap_citac);
1892 
1893  return 0;
1894 }
1895 
1896 // =====================================================================
1897 // ---------------------------------------------------------------------
1898 // ---------------------------------------------------------------------
1899 // =====================================================================
1900 /*
1901  \fn WPO
1902  \param ap_tac : vector with nb time frames elements containing the time activity curve from which integral will be computed
1903  \param ap_citac : (returned) vector with nb time frames elements recovering the cumulative integral for each time point t
1904  \param a_th : thread index
1905  \brief return integral for each time point t (WPO_S), and cumulative integral (cumWPO_S )
1906  \return 0 if success, positive value otherwise
1907 */
1908 int i1TCModel::WPO(FLTNB* ap_tac, FLTNB* ap_citac, int a_th)
1909 {
1910  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
1911 
1912  FLTNB* DT2 = mp_DT2; // Frame duration / 2
1913  FLTNB itac = 0.; // integral at time point t
1914 
1915  // Init vectors
1916  FLTNB* WPO_P = m2p_wpoP[ a_th ];
1917  FLTNB* WPO_FD = m2p_wpoFD[ a_th ];
1918  FLTNB* WPO_BD = m2p_wpoBD[ a_th ];
1919 
1920  // WPO_P, WPO_BD, WPO_FD
1921  for(uint32_t t=0 ; t<nb_tf ; t++)
1922  {
1923  if( t==0 )
1924  {
1925  WPO_P[t] = 0. ;
1926  WPO_BD[t] = ap_tac[0] ;
1927  WPO_FD[t] = ap_tac[0] ;
1928  }
1929  else if( t==1 )
1930  {
1931  WPO_P[t] = 0. ;
1932  WPO_FD[t] = DT2[t-1] *( ( ap_tac[ t-1 ] ) );
1933  WPO_BD[t] = DT2[t-1] *( ( ap_tac[ t-1 ] + ap_tac[ t ] ));
1934  }
1935  else
1936  {
1937  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;
1938  WPO_FD[t] = 2*DT2[t-1] * ( 0.5 *( ap_tac[ t-1 ] + ap_tac[ t-2 ] ) + WPO_P[t] * mp_wpoQ[t] );
1939  WPO_BD[t] = 2*DT2[t-1] * ( 0.5 *( ap_tac[ t-1 ] + ap_tac[ t ] ) + WPO_P[t] / mp_wpoQ[t] );
1940  }
1941  }
1942 
1943  // TODO : adapt Simpson rule to the frame duration (only the isotropic frame in the peak)
1944  for(uint32_t t=0 ; t<nb_tf ; t++)
1945  {
1946  // Compute tac integral at time point t
1947  if( t==0 )
1948  itac = ap_tac[ t ] * DT2[ t ] ;
1949  else if( t == (nb_tf-1) )
1950  itac = WPO_BD[ t ];
1951  else
1952  itac = (1-mp_wpoA[ t ]) * WPO_BD[ t ] + mp_wpoA[ t ]*WPO_FD[ t+1 ]; // WPO rule
1953 
1954  // Compute cumumative integral
1955  if( t==0 )
1956  ap_citac[ t ] = itac ;
1957  else
1958  ap_citac[ t ] = ap_citac[ t-1] + itac;
1959  }
1960 
1961  return 0;
1962 }
1963 
1964 
1965 
1966 
1967 // =====================================================================
1968 // ---------------------------------------------------------------------
1969 // ---------------------------------------------------------------------
1970 // =====================================================================
1971 /*
1972  \fn Trapz
1973  \param ap_tac : vector with nb time frames elements containing the time activity curve from which integral will be computed
1974  \param ap_citac : (returned) vector with nb time frames elements recovering the cumulative integral for each time point t
1975  \brief return integral for each time point t (WPO_S), and cumulative integral (cumWPO_S )
1976  \return 0 if success, positive value otherwise
1977 */
1978 int i1TCModel::Trapz(FLTNB* ap_tac, FLTNB* ap_citac)
1979 {
1980  uint32_t nb_tf = mp_ID->GetNbTimeFrames();
1981 
1982  FLTNB* DT2 = mp_DT2; // Frame duration / 2
1983  FLTNB itac = 0.; // integral at time point t
1984 
1985  for(uint32_t t=0 ; t<nb_tf ; t++)
1986  {
1987  // Init
1988  ap_citac[t] = 0;
1989 
1990  if( t==0 )
1991  {
1992  itac = ap_tac[0] * DT2[0] *0.5;
1993  ap_citac[0] = itac;
1994  }
1995  else
1996  {
1997  itac = ( ap_tac[t]+ap_tac[t-1] ) * DT2[t];
1998  ap_citac[t] += ap_citac[t-1] + itac;
1999  }
2000  }
2001 
2002  return 0;
2003 }
2004 
2005 
2006 
#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:443
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:506
#define HPFLTNB
Definition: gVariables.hh:83
bool m_ridgeRegressionFlag
Definition: i1TCModel.hh:298
void ShowHelp()
This function is used to print out general help about dynamic models.
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:1978
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:1169
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:1364
#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:1908
#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:418
int EstimateModelParameters(oImageSpace *ap_Image, int a_ite, int a_sset)
Estimate K1, k2, Va parametric images.
Definition: i1TCModel.cc:886
int ReadAndCheckConfigurationFileSpecific()
This function is used to read options from a configuration file.
Definition: i1TCModel.cc:304
int EstimateImageWithModel(oImageSpace *ap_Image, int a_ite, int a_sset)
Estimate image using model parametric images and basis functions.
Definition: i1TCModel.cc:1725
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
#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:1204
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:990
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:1566
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:1834
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.
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.
Definition: i1TCModel.cc:201
#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:1886
int Transpose(oMatrix *a_MtxResult)
Transpose the elements of the matrix.
Definition: oMatrix.cc:228