CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/dynamic/iLinearSpectralModel.cc
Go to the documentation of this file.
1 
8 #include "iLinearSpectralModel.hh"
9 #include "cmath"
10 #include "math.h"
11 
12 
13 
14 // =====================================================================
15 // ---------------------------------------------------------------------
16 // ---------------------------------------------------------------------
17 // =====================================================================
18 /*
19  \fn iDynamicModelTemplate
20  \brief Constructor of iDynamicModelTemplate. Simply set all data members to default values.
21 */
23 {
24  // --- Parameters inherited from vDynamicModel class --- //
25 
26  m_nbTimeBF = -1; // Number of basis functions in the model un-initialised
27 
28  // Negative initialization of specific model values
29  m_fast_exp = -1;
30  m_slow_exp = -1;
34  mp_spectral_bank=NULL;
35 
36  // Initialize number of additional basis functions to 0;
38 
39 }
40 
41 
42 
43 
44 // =====================================================================
45 // ---------------------------------------------------------------------
46 // ---------------------------------------------------------------------
47 // =====================================================================
48 /*
49  \fn ~iDynamicModelTemplate
50  \brief Destructor of iDynamicModelTemplate
51 */
53 {
54 
55 }
56 
57 
58 // =====================================================================
59 // ---------------------------------------------------------------------
60 // ---------------------------------------------------------------------
61 // =====================================================================
62 /*
63  \fn ShowHelpModelSpecific
64  \brief Print out specific help about the implementation of the model
65  and its initialization
66 */
68 {
69  cout << "-- This class implements the Spectral Model : " << endl;
70  cout << "-- first introduced by J. Cunningham et al. then used in PET reconstruction for temporal regularisation by Andrew Reader et al " << endl;
71  cout << "-- It is used to model radiotracer dynamic behaviour with a set of decaying exponential functions ( exp(-β*t) ) " << endl;
72  cout << "-- The decaying exponential coefficients β are logarithmically equally spaced within the selected range of values " << endl;
73  cout << " All decaying exponentials are convolved with the interpolated Arterial Input Curve, before being discretised to the duration of the reconstruction frames " << endl;
74  cout << endl;
75  cout << " The model must be initialized using a ASCII file with the following keywords and information :" << endl;
76  cout << " As this class inherits from the iLinearModel class, the following parameters must be declared inside the couple of the following specific tags: " << endl;
77  cout << " - DYNAMIC FRAMING/ENDDF " << endl;
78  cout << " - The ASCII file must contain the following keywords :" << endl;
79  cout << " 'AIC_input_file: path/to/file' (mandatory) The file containing the sampled Arterial Input Function " << endl;
80  cout << " The file must contain the following information in successive lines, separated by ',' " << endl;
81  cout << " -> AIC_number_of_points: " << endl;
82  cout << " -> AIC_time_points: " << endl;
83  cout << " -> AIC_data_points: " << endl;
84  cout << " -> AIC_units: 'seconds' or 'minutes' " << endl;
85  cout << " The following options are required for the specification of the spectral functions. "<< endl;
86  cout << " As in the spectral analysis method, the spectral functions are convolved with the interpolated Input Curve. "<< endl;
87  cout << " By default a function which is the interpolated Input Curve is added in the dataset (equivalent to convolution of the IC with a dirac function)" << endl;
88  cout << " The spectral functions created will be logarithmically spaced within the selected range " << endl;
89  cout << " Number_spectral_functions: x (mandatory) The number of spectral functions" << endl;
90  cout << " Fastest_rate: x (mandatory) The rate (1/min) for the fastest decaying exponential " << endl;
91  cout << " Slowest_rate: x (mandatory) The rate (1/min) for the slowest decaying exponential" << endl;
92  cout << " Full_trapping_basis_function: 1 or 0 (optional) Option to say whether we want to include a basis function to model full trapping of tracer (1) or not (0 by default) " << endl;
93  cout << " Blood_fraction_basis_function: 1 or 0 (optional) Option to say whether we want to include a basis function to model the blood fraction of the tracer (1) or not (0 by default) " << endl;
94  cout << " In this implementation and for the blood fraction it is assumed that the whole blood input curve and plasma input curve are the same (no change due to metabolism) " << endl;
95  cout << endl;
96  cout << " 'Parametric_image_init: path ' (optional) path to an interfile image to be used as initialization for the parametric images." << endl;
97  cout << endl;
98  cout << " -> Optimisation_method : x (mandatory) optimization method available options: " << endl;
99  cout << " x=0: Direct ( Implementation of basis functions side by system matrix in each tomographic iterative loop " << endl;
100  cout << " x=1: Nested EM " << endl;
101  cout << " x=2: Iterative non-negative Least-Square " << endl;
102  cout << " (C.L. Lawson and R.J. Hanson, Solving Least Squares Problems)" << endl;
103  cout << endl;
104 
105  // Print general help for all dynamic models
106  ShowHelp();
107 
108 }
109 
110 
111 // =====================================================================
112 // ---------------------------------------------------------------------
113 // ---------------------------------------------------------------------
114 // =====================================================================
115 /*
116  \fn ReadAndCheckConfigurationFileSpecific
117  \brief This function is used to read options from a configuration file.
118  \return 0 if success, other value otherwise.
119 */
121 {
122  if(m_verbose >=3) Cout("iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific ..."<< endl);
123 
124  // ===================================================================
125  // Implement here the reading of any options specific to this dynamic model
126  // (i.e : parameters or path to deformation files), through a configuration file
127  // The ReadDataASCIIFile() functions could be helpful to recover data from a file
128  // (check other dynamicModels for examples)
129  // ===================================================================
130 
131  // Apply the Generic linear Checks for all Linear Models
133  {
134  Cerr("***** iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read and check generic configuration for linear models " << m_fileOptions << endl);
135  return 1;
136  }
137 
138  // The file will be fully processed in the Initialize() function
139  ifstream in_file(m_fileOptions.c_str(), ios::in);
140 
141  if(in_file)
142  {
143  // Number of requested basis/spectral functions
144  if( ReadDataASCIIFile(m_fileOptions, "Number_spectral_functions", &m_spectral_bank_size, 1, KEYWORD_MANDATORY) == 1)
145  {
146  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Number_spectral_functions' flag in " << m_fileOptions << endl);
147  return 1;
148  }
149 
150 
151  // Is trapping basis function requested ?
152  int full_trapping_basis_input =-1 ;
153  if( ReadDataASCIIFile(m_fileOptions, "Full_trapping_basis_function", &full_trapping_basis_input, 1, KEYWORD_OPTIONAL) == 1)
154  {
155  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Full_trapping_basis_function' flag in " << m_fileOptions << endl);
156  return 1;
157  }
158  if (full_trapping_basis_input>0) m_full_trapping_basis_flag = true;
159 
160  // Is blood fraction basis function requested ?
161  int blood_fraction_basis_input =-1 ;
162  if( ReadDataASCIIFile(m_fileOptions, "Blood_fraction_basis_function", &blood_fraction_basis_input, 1, KEYWORD_OPTIONAL) == 1)
163  {
164  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Blood_fraction_basis_function' flag in " << m_fileOptions << endl);
165  return 1;
166  }
167  if (blood_fraction_basis_input>0) m_blood_fraction_fasis_flag = true;
168 
169  // Set model parameters and number of basis functions equal to the number of basis functions (including the additional)
170  // Add another basis function if the trapping basis flag has been provided
172  // Add another basis function if the blood fraction flag has been provided
174 
177 
178  // Parameters for spectral Model -> Lower, Upper decay rate constants
179  if( ReadDataASCIIFile(m_fileOptions, "Fastest_rate", &m_fast_exp, 1, KEYWORD_MANDATORY) == 1)
180  {
181  Cerr("***** iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Fastest_rate' flag in " << m_fileOptions << endl);
182  return 1;
183  }
184  // Make rate units to 1/sec
185  m_fast_exp/=60. ;
186 
187  if( ReadDataASCIIFile(m_fileOptions, "Slowest_rate", &m_slow_exp, 1, KEYWORD_MANDATORY) == 1)
188  {
189  Cerr("***** iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Slowest_rate' flag in " << m_fileOptions << endl);
190  return 1;
191  }
192  // Make rate units to 1/sec
193  m_slow_exp/=60. ;
194  }
195  else
196  {
197  Cerr("***** iLinearPatlakModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read configuration file at: " << m_fileOptions << endl);
198  return 1;
199  }
200 
201 
202 return 0;
203 }
204 
205 // =====================================================================
206 // ---------------------------------------------------------------------
207 // ---------------------------------------------------------------------
208 // =====================================================================
209 /*
210  \fn ReadAndCheckOptionsList
211  \param a_optionsList : a list of parameters separated by commas
212  \brief This function is used to read parameters from a string.
213  \return 0 if success, other value otherwise.
214 */
215 int iLinearSpectralModel::ReadAndCheckOptionsList(string a_listOptions)
216 {
217  if(m_verbose >=3) Cout("iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific ..."<< endl);
218 
219  // Must be initialized with configuration file
220  Cerr("***** iLinearSpectralModel::ReadAndCheckOptionsList -> This model needs to use a file for its initialization. Use help for more info !" << endl);
221  return 1;
222 
223  // ===================================================================
224  // Implement here the reading of any options specific to this deformation model,
225  // through a list of options separated by commas
226  // The ReadStringOption() function could be helpful to parse the list of parameters in an array
227  // ===================================================================
228 
229  // Just recover the string here, it will be processed in the Initialize() function
230  //m_listOptions = a_listOptions;
231 
232  // Normal end
233  //return 0;
234 }
235 
236 
237 // =====================================================================
238 // ---------------------------------------------------------------------
239 // ---------------------------------------------------------------------
240 // =====================================================================
241 /*
242  \fn CheckSpecificParameters
243  \brief This function is used to check whether all member variables
244  have been correctly initialized or not.
245  \return 0 if success, positive value otherwise.
246 */
248 {
249  // ===================================================================
250  // Implement here checks over parameters which should be read using either
251  // ReadAndCheckConfigurationFile() and ReadAndCheckOptionsList() functions
252  // ===================================================================
253 
254  // Perform generic checks that apply for the Linear Models
256  {
257  Cerr("***** iLinearPatlakModel::CheckSpecificParameters -> A problem occurred while checking specific parameters ! " << endl);
258  return 1;
259  }
260 
261 
262  // Normal end
263  return 0;
264 }
265 
266 // =====================================================================
267 // ---------------------------------------------------------------------
268 // ---------------------------------------------------------------------
269 // =====================================================================
270 /*
271  \fn InitializeSpecific
272  \brief This function is used to initialize the model parametric images and basis functions
273  \return 0 if success, other value otherwise.
274 */
276 {
277  if (!m_checked)
278  {
279  Cerr("***** iLinearSpectralModel::InitializeSpecific() -> Must call CheckParameters functions before Initialize() !" << endl);
280  return 1;
281  }
282 
283 
284  if(m_verbose >=2) Cout("iLinearSpectralModel::InitializeSpecific ..."<< endl);
285  // Run generic Initialization for all Linear Models
287  {
288  Cerr("***** iLinearSpectralModel::InitializeSpecific() -> Error while performing generic initialisations for linear models !" << endl);
289  return 1;
290  }
291 
292  // Assumption of framing set equally to all bed position at the moment !!
293  int bed = 0;
294  // Get pointer to interpolated Arterial Input Curve
296  // Get Downsampled AIF and get it into a_AICIntrpYDown
298  HPFLTNB* a_AICIntrpYDown = mp_ArterialInputCurve->GetDownsampledAIC();
299 
301  {
302  // --- Calculation of φ0 function for estimation of the tracer trapping ( if included ) -------------------------- //
303 
304  // Calculation of the 'Intagrated Time' integral in the discrete level with the trapezoidal method
305  // For uniform spacing that is $ \frac{\Delta T}{2} ( f(x_0) + \sum_{k=0}^{N-1} f(x_k) + f(x_N)) $
306  HPFLTNB* a_IntegAICY = new HPFLTNB[(mp_ID->GetFrameTimeStopInMs(bed, mp_ID->GetNbTimeFrames() - 1))];
307 
308  HPFLTNB RunningSum = 0;
309  HPFLTNB halfDeltaT = (0.001 / 2);
310  // First value of integration over the AIC will be the first value of the AIC * DeltaT
311  a_IntegAICY[0] = a_AICIntrpY[0] * 0.001;
312  // Then iterate though all the other points using the trapezoidal function for uniform spacing
313  for (uint32_t i = 1; i < (mp_ID->GetFrameTimeStopInMs(bed, mp_ID->GetNbTimeFrames() - 1)); i++)
314  {
315  RunningSum += a_AICIntrpY[i - 1];
316  a_IntegAICY[i] = (a_AICIntrpY[0] + 2 * RunningSum + a_AICIntrpY[i]) * halfDeltaT;
317  }
318 
319  // Calculate first basis function - Integration of sampled integral of AIC over each frame and division by duration-//
320  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++)
321  {
322  RunningSum = 0;
323  // iteration over all the datapoints within the frame
324  for (uint32_t i = mp_ID->GetFrameTimeStartInMs(bed, fr) + 1; i < (mp_ID->GetFrameTimeStopInMs(bed, fr)); i++)
325  {
326  RunningSum += a_IntegAICY[i];
327  }
328  m2p_nestedModelTimeBasisFunctions[0][fr] = (FLTNB) ((a_IntegAICY[mp_ID->GetFrameTimeStartInMs(bed, fr)] +
329  2 * RunningSum +
330  a_IntegAICY[mp_ID->GetFrameTimeStopInMs(bed, fr)]) *
331  halfDeltaT);
333  ((FLTNB) (mp_ID->GetFrameTimeStopInMs(bed, fr) - mp_ID->GetFrameTimeStartInMs(bed, fr))) / 1000;
334  }
335  // Clear Integrated TAC from memory
336  if (a_IntegAICY) delete [] (a_IntegAICY);
337  }
338 
339  // --- Calculation of spectral functions φ1 .. φN-1-------------------------------------------------------- //
340  // Allocate spectral bank
342  // Calculate the spectral coefficients to be equally spaced in a log scale
343  HPFLTNB ln_slow_rate = log(m_slow_exp);
344  HPFLTNB ln_fast_rate = log(m_fast_exp);
345  HPFLTNB step = (ln_fast_rate - ln_slow_rate) / (HPFLTNB) (m_spectral_bank_size - 1);
346  for (int b = 0; b < m_spectral_bank_size; b++) mp_spectral_bank[b] = ln_slow_rate + step * b;
347  // Get spectral coefficients back to scale from log scale
348  for (int b = 0; b < m_spectral_bank_size; b++) mp_spectral_bank[b] = exp(mp_spectral_bank[b]);
349 
350  // --- Sample the exponential over the requested frames duration --------------------------------------------- //
351  if(m_verbose >=3) Cout( " iLinearSpectralModel::InitializeSpecific() -> Sampling exponential functions" << endl);
352 
353  // Calculated total samples for interval of 0.1 seconds
354  int total_samples =
355  (int) ((((float) mp_ID->GetFrameTimeStopInMs(bed, mp_ID->GetNbTimeFrames() - 1)) / 1000.) * 10);
356 
357  HPFLTNB** spectral_exp = new HPFLTNB*[m_spectral_bank_size];
358  for (int ex = 0; ex < m_spectral_bank_size; ex++)
359  {
360  spectral_exp[ex] = new HPFLTNB[total_samples];
361  }
362 
363  // Evaluating exponents over total time. Scaling time for 0.1 second intervals
364  for (int ex = 0; ex < m_spectral_bank_size; ex++)
365  {
366  for (int i = 0; i < total_samples; i++)
367  {
368  spectral_exp[ex][i] = (FLTNB) exp(-0.1 * mp_spectral_bank[ex] * i);
369  }
370  }
371  // --- Get Interpolated AIC and convolve with every spectral function ----------------------------------------- //
372 
373  // Allocate memory for the convolved spectral functions.
374  HPFLTNB** ConvSpectralFunctions = new HPFLTNB*[m_spectral_bank_size];
375  for (int ex = 0; ex < m_spectral_bank_size; ex++)
376  {
377  ConvSpectralFunctions[ex] = new HPFLTNB[total_samples];
378  }
379 
380  // For every function perform the convolution ! use of multithreading for each spectral function convolution
381  int ex;
382  #pragma omp parallel for private(ex) schedule(static, 1)
383  for (ex = 0; ex < m_spectral_bank_size; ex++)
384  {
385  // Double loop for convolution of AICIntrpY with a sampled exponential function
386  for (int i = 0; i < total_samples; i++)
387  {
388  for (int j = i; j < total_samples; j++)
389  {
390  ConvSpectralFunctions[ex][j] += (a_AICIntrpYDown[i] * spectral_exp[ex][j - i]);
391  }
392  // Normalise each value with the size of each step
393  ConvSpectralFunctions[ex][i] *= 0.1;
394  // Check and remove negative values - might occur if multiplication results to lower/higher values than double !!
395  if ( ConvSpectralFunctions[ex][i] <0 )
396  {
397  Cout(" Negative spectral function value detected at " << i << ", with value: "<< ConvSpectralFunctions[ex][i] <<
398  " -> Setting value to zero "<< endl);
399  ConvSpectralFunctions[ex][i] = 0 ;
400  }
401  if (std::isnan(ConvSpectralFunctions[ex][i]))
402  {
403  Cout(" NaN spectral function value detected at " << i << "--> Setting value to zero "<< endl);
404  ConvSpectralFunctions[ex][i] = 0 ;
405  }
406  }
407  }
408  // --- Average convolved basis functions into the study frames
409  // If full trapping BF set, then start spectral functions index 1 as φ1 ; else from index 0 as φ0.
410  int bf_index_start = (m_full_trapping_basis_flag==true) ? 1 : 0 ;
411  // Average functions over frames to calculate Basis Functions
412  HPFLTNB RunningSum;
413  HPFLTNB halfDeltaT = (0.1/2);
414  // Loop over all spectral functions
415  for (int ex = 0; ex < m_spectral_bank_size; ex++)
416  {
417  // Loop over frames to get the sum of the values within each frame
418  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++)
419  {
420  RunningSum = 0 ;
421  // iteration over all the datapoints within the frame
422  for (uint32_t i = (mp_ID->GetFrameTimeStartInMs(bed, fr)/100)+1 ;i <(mp_ID->GetFrameTimeStopInMs(bed, fr)/100);i++)
423  {
424  RunningSum+=ConvSpectralFunctions[ex][i];
425  }
426  // Calculate integral using the trapezoidal method
427  m2p_nestedModelTimeBasisFunctions[ex+bf_index_start][fr] = (FLTNB) ((ConvSpectralFunctions[ex][(mp_ID->GetFrameTimeStartInMs(bed, fr) / 100)] +
428  2 * RunningSum + ConvSpectralFunctions[ex][(mp_ID->GetFrameTimeStopInMs(bed, fr) / 100)]) * halfDeltaT);
429  // Average for the frame duration
430  m2p_nestedModelTimeBasisFunctions[ex+bf_index_start][fr] /= (FLTNB)((mp_ID->GetFrameTimeStopInMs(bed, fr)) - (mp_ID->GetFrameTimeStartInMs(bed, fr)))/1000 ;
431  }
432  }
433 
434  // Clear memory from ConvSpectralFunctions
435  if (mp_spectral_bank) delete [] (mp_spectral_bank);
436  for (ex = 0; ex < m_spectral_bank_size; ex++)
437  if (spectral_exp[ex]) delete[] (spectral_exp[ex]);
438  if (spectral_exp) delete[] (spectral_exp);
439 
440  for (ex = 0; ex < m_spectral_bank_size; ex++)
441  {
442  if (ConvSpectralFunctions[ex]) delete[] (ConvSpectralFunctions[ex]);
443  }
444  if (ConvSpectralFunctions) delete[] (ConvSpectralFunctions);
445 
446  // If blood fraction BF set, set it to the φN coefficient
448  {
449  // --- Calculation of functions φN for estimation of blood fraction ------------------------------------------- //
450  halfDeltaT = (0.001 / 2);
451  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++)
452  {
453  RunningSum = 0;
454  // iteration over all the datapoints within the frame
455  for (uint32_t i = (mp_ID->GetFrameTimeStartInMs(bed, fr)) + 1; i < (mp_ID->GetFrameTimeStopInMs(bed, fr)); i++)
456  {
457  RunningSum += a_AICIntrpY[i];
458  }
459  // Calculate integral using the trapezoidal method
461  (a_AICIntrpY[(mp_ID->GetFrameTimeStartInMs(bed, fr))] +
462  2 * RunningSum + a_AICIntrpY[(mp_ID->GetFrameTimeStopInMs(bed, fr))]) * halfDeltaT);
463  // Average for the frame duration
465  (FLTNB) ((mp_ID->GetFrameTimeStopInMs(bed, fr)) - (mp_ID->GetFrameTimeStartInMs(bed, fr))) / 1000;
466  }
467  }
468 
469  // Print the basis functions
471 
472  // Normal end
473  m_initialized = true;
474  return 0;
475 }
int Downsample()
This function downsamples the interpolated arterial input function Currently needed to speed up convo...
int ReadAndCheckOptionsList(string a_listOptions)
iLinearSpectralModel()
Constructor of iLinearSpectralModel. Simply set all data members to default values.
#define Cerr(MESSAGE)
oImageDimensionsAndQuantification * mp_ID
void ShowHelp()
This function is used to print out general help about dynamic models.
int InitializeSpecific()
This function is used to initialize the model parametric images and basis functions.
HPFLTNB * GetDownsampledAIC()
Set the framing of the reconstruction for the AIC object.
int InitializeSpecificToAllLinearModels()
This function is used to initialize the parametric images and basis functions for all Linear Models...
This class implements a general linear dynamic model applied between the images of a dynamic acquisit...
void ShowBasisFunctions()
This function is used to print the basis functions.
int CheckSpecificParametersForAllLinearModels()
This function is used to check parameters for all Linear Models. .
~iLinearSpectralModel()
Destructor of iLinearSpectralModel.
#define KEYWORD_MANDATORY
#define KEYWORD_OPTIONAL
oArterialInputCurve * mp_ArterialInputCurve
int CheckSpecificParameters()
This function is used to check whether all member variables have been correctly initialized or not...
int ReadAndCheckConfigurationFileSpecific()
This function is used to read options from a configuration file, specific to this model...
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
int ReadAndCheckConfigurationFileSpecificToAllLinearModels()
This function is used to read parameters that are generic for all Linear Models. ...
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.
HPFLTNB * GetInterpolatedAIC()
Set the framing of the reconstruction for the AIC object.
#define Cout(MESSAGE)