CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iLinearSpectralModel.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
22 */
23 
30 #include "iLinearSpectralModel.hh"
31 #include "cmath"
32 #include "math.h"
33 
34 
35 
36 // =====================================================================
37 // ---------------------------------------------------------------------
38 // ---------------------------------------------------------------------
39 // =====================================================================
40 /*
41  \fn iDynamicModelTemplate
42  \brief Constructor of iDynamicModelTemplate. Simply set all data members to default values.
43 */
45 {
46  // --- Parameters inherited from vDynamicModel class --- //
47 
48  m_nbTimeBF = -1; // Number of basis functions in the model un-initialised
49 
50  // Negative initialization of specific model values
51  m_fast_exp=-1;
52  m_slow_exp=-1;
55  mp_spectral_bank=NULL;
56 
57  // Initialize number of additional basis functions to 0;
59 
60 }
61 
62 
63 
64 
65 // =====================================================================
66 // ---------------------------------------------------------------------
67 // ---------------------------------------------------------------------
68 // =====================================================================
69 /*
70  \fn ~iDynamicModelTemplate
71  \brief Destructor of iDynamicModelTemplate
72 */
74 {
75 
76 }
77 
78 
79 // =====================================================================
80 // ---------------------------------------------------------------------
81 // ---------------------------------------------------------------------
82 // =====================================================================
83 /*
84  \fn ShowHelp
85  \brief Print out specific help about the implementation of the model
86  and its initialization
87 */
89 {
90  cout << "-- This class implements the Spectral Model : " << endl;
91  cout << "-- By Adnrew Reader et al. " << endl;
92  cout << "-- It is used to model radiotracer dynamic behaviour with a set of decaying exponential functions ( exp(-β*t) ) " << endl;
93  cout << "-- The decaying exponential functions are logarithmically equally spaced within the selected range of β values " << endl;
94  cout << " All decaying exponentials are convolved with the interpolated Arterial Input Curve, before being discretised to the duration of the reconstruction frames " << endl;
95  cout << endl;
96  cout << " The model must be initialized using either an ASCII file with the following keywords and information :" << endl;
97  cout << " As this class inherits from the iLinearModel class, the following parameters must be declared inside the couple of the following specific tags: " << endl;
98  cout << " - DYNAMIC FRAMING/ENDDF " << endl;
99  cout << " - The ASCII file must contain the following keywords :" << endl;
100  cout << " 'AIC_input_file:' (mandatory) The file containing the sampled Arterial Input Function " << endl;
101  cout << " The file must contain the following information in successive lines, separated by ',' " << endl;
102  cout << " -> AIC_number_of_points: " << endl;
103  cout << " -> AIC_time_points: " << endl;
104  cout << " -> AIC_data_points: " << endl;
105  cout << " -> AIC_units: seconds " << endl;
106  cout << " 'Parametric_image_init: path ' (optional) path to an interfile image to be used as initialization for the parametric images." << endl;
107  cout << " -> Optimisation_method : x (mandatory) optimization method available options: " << endl;
108  cout << " x=0: Direct ( Implementation of basis functions side by system matrix in each tomographic iterative loop " << endl;
109  cout << " x=1: Nested EM " << endl;
110  cout << " x=2: Iterative non-negative Least-Square " << endl;
111  cout << " (C.L. Lawson and R.J. Hanson, Solving Least Squares Problems)" << endl;
112  cout << endl;
113  cout << " Parametric images will be initialized with 0.001 and 1.0 for Patlak slope and intercept by default " << endl;
114  cout << " The parametric images estimations will be written on disk for each iteration" << endl;
115  cout << " " << endl;
116  cout << " The following keywords are common to all dynamic models :" << endl;
117  cout << " 'Number of iterations before image update: x' Set a number 'x' of iteration to reach before using the model to generate the images at each frames/gates" << endl;
118  cout << " (Default x == 0) " << endl;
119  cout << " 'No image update: x' If set to 1, the reconstructed images for the next iteration/subset are not reestimated using the model" << endl;
120  cout << " (Default x == 0) (the code just performs standard independent reconstruction of each frames/gates) " << endl;
121  cout << " 'No parameters update: x' If set to 1, the parameters / functions of the model are not estimated with the image" << endl;
122  cout << " (Default x == 0) (this could be used to test The EstimateImageWithModel() function with specific user-provided parametric images) " << endl;
123  cout << " 'Save parametric images : x' Enable (1)/Disable(0) saving parametric images on disk" << endl;
124  cout << " (Default x == 1) " << endl;
125  cout << " 'Save blacklisted voxels images : x' Enable (1)/Disable(0) saving blacklisted voxels images on disk" << endl;
126  cout << " (Default x == 0) " << endl;
127  cout << " " << endl;
128 
129 }
130 
131 
132 // =====================================================================
133 // ---------------------------------------------------------------------
134 // ---------------------------------------------------------------------
135 // =====================================================================
136 /*
137  \fn ReadAndCheckConfigurationFileSpecific
138  \brief This function is used to read options from a configuration file.
139  \return 0 if success, other value otherwise.
140 */
142 {
143  if(m_verbose >=3) Cout("iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific ..."<< endl);
144 
145  // ===================================================================
146  // Implement here the reading of any options specific to this dynamic model
147  // (i.e : parameters or path to deformation files), through a configuration file
148  // The ReadDataASCIIFile() functions could be helpful to recover data from a file
149  // (check other dynamicModels for examples)
150  // ===================================================================
151 
152  // Apply the Generic linear Checks for all Linear Models
154  {
155  Cerr("***** iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read and check generic configuration for linear models " << m_fileOptions << endl);
156  return 1;
157  }
158 
159  // The file will be fully processed in the Initialize() function
160  ifstream in_file(m_fileOptions.c_str(), ios::in);
161 
162  if(in_file)
163  {
164  // Number of requested basis/spectral functions
165  if( ReadDataASCIIFile(m_fileOptions, "Number_spectral_functions", &m_spectral_bank_size, 1, KEYWORD_MANDATORY) == 1)
166  {
167  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Number_spectral_functions' flag in " << m_fileOptions << endl);
168  return 1;
169  }
170 
171  // A basis function is added to the total - to account for constant exponential -> Arterial Input Basis Function
173 
174  // Is constant value basis function requested ?
176  if( ReadDataASCIIFile(m_fileOptions, "Constant_basis_function", &m_constant_basis_value, 1, KEYWORD_OPTIONAL) == 1)
177  {
178  Cerr("***** iLinearModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Constant_basis_function' flag in " << m_fileOptions << endl);
179  return 1;
180  }
181  // Add another basis function if constant_basis value has been provided
183 
184  // Set model parameters and number of basis functions equal to the number of basis functions (including the additional)
187 
188  // Parameters for spectral Model -> Lower, Upper decay rate constants
189  if( ReadDataASCIIFile(m_fileOptions, "Fastest_rate", &m_fast_exp, 1, KEYWORD_MANDATORY) == 1)
190  {
191  Cerr("***** iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Fastest_rate' flag in " << m_fileOptions << endl);
192  return 1;
193  }
194  // Make rate units to 1/sec
195  m_fast_exp/=60. ;
196 
197  if( ReadDataASCIIFile(m_fileOptions, "Slowest_rate", &m_slow_exp, 1, KEYWORD_MANDATORY) == 1)
198  {
199  Cerr("***** iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read 'Slowest_rate' flag in " << m_fileOptions << endl);
200  return 1;
201  }
202  // Make rate units to 1/sec
203  m_slow_exp/=60. ;
204 
205  }
206  else
207  {
208  Cerr("***** iLinearPatlakModel::ReadAndCheckConfigurationFileSpecific -> Error while trying to read configuration file at: " << m_fileOptions << endl);
209  return 1;
210  }
211 
212 
213 return 0;
214 }
215 
216 // =====================================================================
217 // ---------------------------------------------------------------------
218 // ---------------------------------------------------------------------
219 // =====================================================================
220 /*
221  \fn ReadAndCheckOptionsList
222  \param a_optionsList : a list of parameters separated by commas
223  \brief This function is used to read parameters from a string.
224  \return 0 if success, other value otherwise.
225 */
227 {
228  if(m_verbose >=3) Cout("iLinearSpectralModel::ReadAndCheckConfigurationFileSpecific ..."<< endl);
229 
230  // Must be initialized with configuration file
231  Cerr("***** iLinearSpectralModel::ReadAndCheckOptionsList -> This model needs to use a file for its initialization. Use help for more info !" << endl);
232  return 1;
233 
234  // ===================================================================
235  // Implement here the reading of any options specific to this deformation model,
236  // through a list of options separated by commas
237  // The ReadStringOption() function could be helpful to parse the list of parameters in an array
238  // ===================================================================
239 
240  // Just recover the string here, it will be processed in the Initialize() function
241  //m_listOptions = a_listOptions;
242 
243  // Normal end
244  //return 0;
245 }
246 
247 
248 // =====================================================================
249 // ---------------------------------------------------------------------
250 // ---------------------------------------------------------------------
251 // =====================================================================
252 /*
253  \fn CheckSpecificParameters
254  \brief This function is used to check whether all member variables
255  have been correctly initialized or not.
256  \return 0 if success, positive value otherwise.
257 */
259 {
260  // ===================================================================
261  // Implement here checks over parameters which should be read using either
262  // ReadAndCheckConfigurationFile() and ReadAndCheckOptionsList() functions
263  // ===================================================================
264 
265  // Perform generic checks that apply for the Linear Models
267  {
268  Cerr("***** iLinearPatlakModel::CheckSpecificParameters -> A problem occurred while checking specific parameters ! " << endl);
269  return 1;
270  }
271 
272 
273  // Normal end
274  return 0;
275 }
276 
277 // =====================================================================
278 // ---------------------------------------------------------------------
279 // ---------------------------------------------------------------------
280 // =====================================================================
281 /*
282  \fn InitializeSpecific
283  \brief This function is used to initialize the model parametric images and basis functions
284  \return 0 if success, other value otherwise.
285 */
287 {
288  if (!m_checked)
289  {
290  Cerr("***** iLinearSpectralModel::InitializeSpecific() -> Must call CheckParameters functions before Initialize() !" << endl);
291  return 1;
292  }
293 
294 
295  if(m_verbose >=2) Cout("iLinearSpectralModel::InitializeSpecific ..."<< endl);
296  // Run generic Initialization for all Linear Models
298  {
299  Cerr("***** iLinearSpectralModel::InitializeSpecific() -> Error while performing generic initialisations for linear models !" << endl);
300  return 1;
301  }
302 
303  // ===================================================================
304  // Implement here the allocation/initialization of whatever member
305  // variables specifically used by this deformation model
306  // The set
307  // ===================================================================
308 
309  // --- Calculation of spectral function decay rates --------------------------------------------------------- //
310  // allocate spectral bank
312  // Calculate the spectral coefficients to be equally spaced in a log scale
313  HPFLTNB ln_slow_rate = log10(m_slow_exp);
314  HPFLTNB ln_fast_rate = log10(m_fast_exp);
315  HPFLTNB step = (ln_fast_rate - ln_slow_rate) / (HPFLTNB) (m_spectral_bank_size - 1);
316  for (int b = 0; b < m_spectral_bank_size; b++) mp_spectral_bank[b] = ln_slow_rate + step * b;
317  // Get spectral coefficients back to scale from log scale
318  for (int b = 0; b < m_spectral_bank_size; b++) mp_spectral_bank[b] = pow(10, mp_spectral_bank[b]);
319 
320  // --- Sample the exponential over the requested frames duration --------------------------------------------- //
321  if(m_verbose >=3) Cout( " iLinearSpectralModel::InitializeSpecific() -> Sampling exponential functions" << endl);
322  // Assumption of only one bed position at the moment !!
323  int bed = 0;
324  // Calculated total samples for interval of 0.1 seconds
325  int total_samples =
326  (int) ((((float) mp_ID->GetFrameTimeStopInMs(bed, mp_ID->GetNbTimeFrames() - 1)) / 1000.) * 10);
327 
328  HPFLTNB** spectral_exp = (HPFLTNB **) malloc((m_spectral_bank_size) * sizeof(HPFLTNB*));
329  for (int ex = 0; ex < m_spectral_bank_size; ex++)
330  {
331  spectral_exp[ex] = (HPFLTNB*) malloc(total_samples * sizeof(HPFLTNB));
332  }
333 
334  // Evaluating exponents over total time. Scaling time for 0.1 second intervals
335  for (int ex = 0; ex < m_spectral_bank_size; ex++)
336  {
337  for (int i = 0; i < total_samples; i++)
338  {
339  spectral_exp[ex][i] = (FLTNB) exp(-0.1 * mp_spectral_bank[ex] * i);
340  }
341  }
342 
343 
344  // --- Get Interpolated AIC and convolve with every spectral function --------------------------------------------- //
345 
346  // Allocate memory for the convolved spectral functions.
347  HPFLTNB** ConvSpectralFunctions = (HPFLTNB **) malloc((m_spectral_bank_size) * sizeof(HPFLTNB*));
348  for (int ex = 0; ex < m_spectral_bank_size; ex++)
349  {
350  ConvSpectralFunctions[ex] = (HPFLTNB*) malloc(total_samples * sizeof(HPFLTNB));
351  }
352  // Get Downsampled AIF and get it into a_AICIntrpY
355  // For every function perform the convolution ! use of multithreading for each spectral function convolution
356  int ex;
357  #pragma omp parallel for private(ex) schedule(static, 1)
358  for (ex = 0; ex < m_spectral_bank_size; ex++)
359  {
360  // Double loop for convolution of AICIntrpY with a sampled exponential function
361  for (int i = 0; i <= total_samples; i++)
362  {
363  for (int j = i; j <= total_samples; j++)
364  {
365  ConvSpectralFunctions[ex][j] += (a_AICIntrpY[i] * spectral_exp[ex][j - i]);
366  }
367  // Normalise each value with the size of each step
368  ConvSpectralFunctions[ex][i] *= 0.1;
369  // Check and remove negative values - might occur if multiplication results to lower/higher values than double !!
370  if ( ConvSpectralFunctions[ex][i] <0 )
371  {
372  Cout(" Negative spectral function value detected at " << i << ", with value: "<< ConvSpectralFunctions[ex][i] <<
373  " -> Setting value to zero "<< endl);
374  ConvSpectralFunctions[ex][i] = 0 ;
375  }
376  if (std::isnan(ConvSpectralFunctions[ex][i]))
377  {
378  Cout(" NaN spectral function value detected at " << i << "--> Setting value to zero "<< endl);
379  ConvSpectralFunctions[ex][i] = 0 ;
380  }
381  }
382  }
383 
384  // Average functions over frames to calculate Basis Functions
385  HPFLTNB RunningSum=0 ;
386  HPFLTNB halfDeltaT = (0.1/2);
387  // Loop over all spectral functions
388  for (int ex = 0; ex < m_spectral_bank_size; ex++)
389  {
390  // Loop over frames to get the sum of the values within each frame
391  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++)
392  {
393  RunningSum = 0 ;
394  // iteration over all the datapoints within the frame
395  for (uint32_t i = (mp_ID->GetFrameTimeStartInMs(bed, fr)/100)+1 ;i <(mp_ID->GetFrameTimeStopInMs(bed, fr)/100);i++)
396  {
397  RunningSum+=ConvSpectralFunctions[ex][i];
398  }
399  // Calculate integral using the trapezoidal method
400  m2p_nestedModelTimeBasisFunctions[ex][fr] = (FLTNB) ((ConvSpectralFunctions[ex][(mp_ID->GetFrameTimeStartInMs(bed, fr) / 100)] +
401  2 * RunningSum + ConvSpectralFunctions[ex][(mp_ID->GetFrameTimeStopInMs(bed, fr) / 100)]) * halfDeltaT);
402  // Average for the frame duration
404  }
405  }
406 
407  // Add basis function equal to AIF
408  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++)
409  {
410  RunningSum = 0;
411  // iteration over all the datapoints within the frame
412  for (uint32_t i = (mp_ID->GetFrameTimeStartInMs(bed, fr)/100)+1 ;i <(mp_ID->GetFrameTimeStopInMs(bed, fr)/100);i++)
413  {
414  RunningSum += a_AICIntrpY[i];
415  }
416  // Calculate integral using the trapezoidal method
418  2 * RunningSum + a_AICIntrpY[(mp_ID->GetFrameTimeStopInMs(bed, fr) / 100)]) * halfDeltaT);
419  // Average for the frame duration
421  }
422 
423  // Add constant basis function if requested
425  {
426  // Add constant value as the last basis function
427  for (int fr = 0; fr < mp_ID->GetNbTimeFrames(); fr++)
428  {
430  }
431  }
432 
433  // Print the basis functions
435 
436  // Normal end
437  m_initialized = true;
438  return 0;
439 }
int Downsample()
This function downsamples the interpolated arterial input function Currently needed to speed up convo...
int ReadAndCheckOptionsList(string a_listOptions)
This function is used to read parameters from a string.
#define FLTNB
Definition: gVariables.hh:81
iLinearSpectralModel()
Constructor of iLinearSpectralModel. Simply set all data members to default values.
#define HPFLTNB
Definition: gVariables.hh:83
oArterialInputCurve * mp_ArterialInputCurve
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.
uint32_t GetFrameTimeStartInMs(int a_bed, int a_frame)
Get the frame time start for the given bed, in milliseconds as a uint32_t.
oImageDimensionsAndQuantification * mp_ID
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...
Definition: iLinearModel.hh:62
string m_fileOptions
Declaration of class iLinearSpectralModel.
void ShowBasisFunctions()
This function is used to print the basis functions.
#define Cerr(MESSAGE)
int CheckSpecificParametersForAllLinearModels()
This function is used to check parameters for all Linear Models. .
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
~iLinearSpectralModel()
Destructor of iLinearSpectralModel.
FLTNB ** m2p_nestedModelTimeBasisFunctions
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:49
void ShowHelp()
This function is used to print out specific help about the model and its options. ...
int GetNbTimeFrames()
Get the number of time frames.
int CheckSpecificParameters()
This function is used to check whether all member variables have been correctly initialized or not...
#define Cout(MESSAGE)
int ReadAndCheckConfigurationFileSpecific()
This function is used to read options from a configuration file, specific to this model...
int ReadAndCheckConfigurationFileSpecificToAllLinearModels()
This function is used to read parameters that are generic for all Linear Models. ...
uint32_t GetFrameTimeStopInMs(int a_bed, int a_frame)
Get the frame time stop for the given bed, in milliseconds as a uint32_t.