CASToR  2.0
Tomographic Reconstruction (PET/SPECT/CT)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
iOptimizerMLTR.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-2018 all CASToR contributors listed below:
18 
19  --> current contributors: Thibaut MERLIN, Simon STUTE, Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Mael MILLARDET
20  --> past contributors: Valentin VIELZEUF
21 
22 This is CASToR version 2.0.
23 */
24 
31 #include "iOptimizerMLTR.hh"
32 #include "sOutputManager.hh"
33 
34 // =====================================================================
35 // ---------------------------------------------------------------------
36 // ---------------------------------------------------------------------
37 // =====================================================================
38 
40 {
41  // ---------------------------
42  // Mandatory member parameters
43  // ---------------------------
44 
45  // Initial value at 0
46  m_initialValue = 0.;
47  // Only one backward image for MLTR
49  // MLTR does not accept penalties
50  //m_penaltyEnergyFunctionDerivativesOrder = 0;
51  // MLTR is compatible with histogram data but not listmode
54  // MLTR is only compatible with transmission data
57 
58  // --------------------------
59  // Specific member parameters
60  // --------------------------
61 
62  // The alpha image
63  mp_alpha = NULL;
64  // The alpha ratio between exterior and interior of the eliptical FOV
65  m_alphaRatio = 1.;
66  // Relaxation parameters
70  // Non-negativity constraint
72 }
73 
74 // =====================================================================
75 // ---------------------------------------------------------------------
76 // ---------------------------------------------------------------------
77 // =====================================================================
78 
80 {
81 }
82 
83 // =====================================================================
84 // ---------------------------------------------------------------------
85 // ---------------------------------------------------------------------
86 // =====================================================================
87 
89 {
90  cout << "This optimizer is a version of the MLTR algorithm implemented from equation 16 of the paper from K. Van Slambrouck and J. Nuyts:" << endl;
91  cout << "\"Reconstruction scheme for accelerated maximum lihelihood reconstruction: the patchwork structure\"," << endl;
92  cout << "IEEE Trans. Nucl. Sci., vol. 61, pp. 173-81, 2014." << endl;
93  cout << "An additional empiric relaxation factor has been added onto the additive update. Its value onto the first and last updates" << endl;
94  cout << "can be parameterized. Its value for all updates in between is computed linearly from these first and last provided values." << endl;
95  cout << "Subsets can be used." << endl;
96  cout << "The following options can be used (in this particular order when provided as a list):" << endl;
97  cout << " initial image value: to set the uniform voxel value for the initial image" << endl;
98  cout << " alpha ratio: to set the ratio between exterior and interior of the cylindrical FOV alpha values (0 value means 0 inside exterior)" << endl;
99  cout << " initial relaxation factor: to set the empiric multiplicative factor on the additive update used at the first update" << endl;
100  cout << " final relaxation factor: to set the empiric multiplicative factor on the additive update used at the last update" << endl;
101  cout << " non-negativity constraint: 0 if no constraint or 1 in order to apply the constraint during the image update" << endl;
102 }
103 
104 // =====================================================================
105 // ---------------------------------------------------------------------
106 // ---------------------------------------------------------------------
107 // =====================================================================
108 
109 int iOptimizerMLTR::ReadConfigurationFile(const string& a_configurationFile)
110 {
111  string key_word = "";
112  // Read the initial image value option
113  key_word = "initial image value";
114  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_initialValue, 1, KEYWORD_MANDATORY))
115  {
116  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
117  return 1;
118  }
119  // Read the alpha ratio option
120  key_word = "alpha ratio";
121  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_alphaRatio, 1, KEYWORD_MANDATORY))
122  {
123  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
124  return 1;
125  }
126  // Read the initial relaxation factor option
127  key_word = "initial relaxation factor";
128  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_initialRelaxationFactor, 1, KEYWORD_MANDATORY))
129  {
130  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
131  return 1;
132  }
133  // Read the final relaxation factor option
134  key_word = "final relaxation factor";
135  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_finalRelaxationFactor, 1, KEYWORD_MANDATORY))
136  {
137  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
138  return 1;
139  }
140  // Read the non-negativity constraint option
141  key_word = "non-negativity constraint";
142  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_nonNegativityConstraint, 1, KEYWORD_MANDATORY))
143  {
144  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
145  return 1;
146  }
147  // Normal end
148  return 0;
149 }
150 
151 // =====================================================================
152 // ---------------------------------------------------------------------
153 // ---------------------------------------------------------------------
154 // =====================================================================
155 
156 int iOptimizerMLTR::ReadOptionsList(const string& a_optionsList)
157 {
158  // There are 5 floating point variables as options
159  const int nb_options = 5;
160  FLTNB options[nb_options];
161  // Read them
162  if (ReadStringOption(a_optionsList, options, nb_options, ",", "MLTR configuration"))
163  {
164  Cerr("***** iOptimizerMLTR::ReadOptionsList() -> Failed to correctly read the list of options !" << endl);
165  return 1;
166  }
167  // Affect options
168  m_initialValue = options[0];
169  m_alphaRatio = options[1];
170  m_initialRelaxationFactor = options[2];
171  m_finalRelaxationFactor = options[3];
172  if (options[4]==0.) m_nonNegativityConstraint = false;
173  else m_nonNegativityConstraint = true;
174  // Normal end
175  return 0;
176 }
177 
178 // =====================================================================
179 // ---------------------------------------------------------------------
180 // ---------------------------------------------------------------------
181 // =====================================================================
182 
184 {
185  // Check that the alpha ratio is positive
186  if (m_alphaRatio<0.)
187  {
188  Cerr("***** iOptimizerMLTR->CheckSpecificParameters() -> Provided alpha ratio (" << m_alphaRatio << ") must be positive !" << endl);
189  return 1;
190  }
191  // Check that the initial and final relaxation factors are strictly positive
193  {
194  Cerr("***** iOptimizerMLTR->CheckSpecificParameters() -> Provided initial relaxation factor (" << m_initialRelaxationFactor << ") must be positive !" << endl);
195  return 1;
196  }
198  {
199  Cerr("***** iOptimizerMLTR->CheckSpecificParameters() -> Provided final relaxation factor (" << m_finalRelaxationFactor << ") must be positive !" << endl);
200  return 1;
201  }
202  // Normal end
203  return 0;
204 }
205 
206 // =====================================================================
207 // ---------------------------------------------------------------------
208 // ---------------------------------------------------------------------
209 // =====================================================================
210 
212 {
213  // Verbose
215  {
216  Cout("iOptimizerMLTR::InitializeSpecific() -> Use the MLTR optimizer" << endl);
218  {
219  Cout(" --> Initial image value: " << m_initialValue << endl);
221  {
222  Cout(" --> Initial relaxation factor: " << m_initialRelaxationFactor << endl);
223  Cout(" --> Final relaxation factor: " << m_finalRelaxationFactor << endl);
224  }
225  if (m_nonNegativityConstraint) Cout(" --> Apply a non-negativity constraint during image update" << endl);
226  }
227  }
228 
229  // ===================================================================
230  // Step 1: Allocate the alpha image from a miscellaneous image
232 
233  // ===================================================================
234  // Step 2: Compute interior and exterior alpha values
235 
236  // We assume the interior value to be always 1, then we change the exterior value according to the alpha ratio provided
237  FLTNB alpha_interior = 1.;
238  FLTNB alpha_exterior = m_alphaRatio;
239  if (m_alphaRatio!=1. && m_verbose>=VERBOSE_DETAIL) Cout(" --> Use alpha values in interior/exterior of eliptical FOV of " << alpha_interior << "/" << alpha_exterior << endl);
240 
241  // ===================================================================
242  // Step 3: Set the alpha values inside and outside the cylindrical FOV
243 
244  // Precast half the number of voxels over X and Y minus 1 (for efficiency)
245  FLTNB flt_base_x = 0.5*((FLTNB)(mp_ImageDimensionsAndQuantification->GetNbVoxX()-1));
246  FLTNB flt_base_y = 0.5*((FLTNB)(mp_ImageDimensionsAndQuantification->GetNbVoxY()-1));
247  // Compute FOV elipse radius over X and Y, then squared
249  squared_radius_x *= squared_radius_x;
251  squared_radius_y *= squared_radius_y;
252  // We assume that the computation of the distance from the center for a given
253  // voxel and comparing it with the output FOV percentage costs more than performing
254  // the loops in an inverse order compared to how the image is stored in memory.
255  // Thus we begin the loops over X, then Y, then we test and if test passes, we
256  // do the remaining loop over Z and over all dynamic dimensions.
257  int x;
258  #pragma omp parallel for private(x) schedule(guided)
260  {
261  // Compute X distance from image center, then squared
262  FLTNB squared_distance_x = (((FLTNB)x)-flt_base_x) * mp_ImageDimensionsAndQuantification->GetVoxSizeX();
263  squared_distance_x *= squared_distance_x;
264  // Loop over Y
265  for (int y=0; y<mp_ImageDimensionsAndQuantification->GetNbVoxY(); y++)
266  {
267  // Compute Y distance from image center, then squared
268  FLTNB squared_distance_y = (((FLTNB)y)-flt_base_y) * mp_ImageDimensionsAndQuantification->GetVoxSizeY();
269  squared_distance_y *= squared_distance_y;
270  // The alpha value
271  FLTNB alpha_value = 0.;
272  // Set the alpha value to the interior if INSIDE the elipse
273  if ( squared_distance_x/squared_radius_x + squared_distance_y/squared_radius_y <= 1. ) alpha_value = alpha_interior;
274  // Else OUTSIDE the elipse
275  else alpha_value = alpha_exterior;
276  // Loop over Z
277  for (int z=0; z<mp_ImageDimensionsAndQuantification->GetNbVoxZ(); z++)
278  {
279  // Compute global voxel index
281  // Set the alpha value
282  mp_alpha[index] = alpha_value;
283  }
284  }
285  }
286 
287  // Normal end
288  return 0;
289 }
290 
291 // =====================================================================
292 // ---------------------------------------------------------------------
293 // ---------------------------------------------------------------------
294 // =====================================================================
295 
296 int iOptimizerMLTR::PostDataUpdateSpecificStep(int a_iteration, int a_nbIterations, int a_subset, int* ap_nbSubsets)
297 {
298  // ===================================================================
299  // In this function, we update the value of the relaxation factor to
300  // be used in the next image update.
301  // ===================================================================
302 
303  // Special case if only one update
304  if (a_nbIterations==1 && ap_nbSubsets[0]==1)
305  {
306  // Test if the initial and final relaxation factors differ, then throw an error
308  {
309  Cerr("***** iOptimizerMLTR::PostDataUpdateSpecificStep() -> Initial and final relaxation differ while there is only one update to do !" << endl);
310  return 1;
311  }
312  // Set the value
314  }
315 
316  // Compute the total number of updates minus 1
317  int total_number_of_updates_minus_one = -1;
318  for (int it=0; it<a_nbIterations; it++) total_number_of_updates_minus_one += ap_nbSubsets[it];
319  // Compute the current update index
320  int current_update_index = 0;
321  for (int it=0; it<a_iteration; it++) current_update_index += ap_nbSubsets[it];
322  current_update_index += a_subset;
323  // Compute the position on the slope of updates
324  FLTNB ratio_of_updates = ((FLTNB)current_update_index) / ((FLTNB)total_number_of_updates_minus_one);
325 
326  // Finally update the current relaxation factor to be used at this update, from the initial and final relaxation factors
327  m_currentRelaxationFactor = m_initialRelaxationFactor * (1.-ratio_of_updates) + m_finalRelaxationFactor * ratio_of_updates;
328 
329  // Verbose
330  if (m_verbose>=VERBOSE_DETAIL) Cout("iOptimizerMLTR::PostDataUpdateSpecificStep() -> Current relaxation factor value: " << m_currentRelaxationFactor << endl);
331 
332  // That's all
333  return 0;
334 }
335 
336 // =====================================================================
337 // ---------------------------------------------------------------------
338 // ---------------------------------------------------------------------
339 // =====================================================================
340 
341 int iOptimizerMLTR::SensitivitySpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_weight,
342  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
343  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
344 {
345  // Line weight here is a projection of a uniform 1 image multiplied by the model
346  *ap_weight = ForwardProject(ap_Line, mp_alpha) * a_forwardModel;
347  // That's all
348  return 0;
349 }
350 
351 // =====================================================================
352 // ---------------------------------------------------------------------
353 // ---------------------------------------------------------------------
354 // =====================================================================
355 
356 int iOptimizerMLTR::DataSpaceSpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_backwardValues,
357  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
358  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
359 {
360  // Truncate data to 0 if negative
361  if (a_data<0.) a_data = 0.;
362  // The correction value is simply the subtraction of the data from the model
363  *ap_backwardValues = a_forwardModel - a_data;
364  // That's all
365  return 0;
366 }
367 
368 // =====================================================================
369 // ---------------------------------------------------------------------
370 // ---------------------------------------------------------------------
371 // =====================================================================
372 
373 int iOptimizerMLTR::ImageSpaceSpecificOperations( FLTNB a_currentImageValue, FLTNB* ap_newImageValue,
374  FLTNB a_sensitivity, FLTNB* ap_correctionValues, INTNB a_voxel )
375 {
376  // Compute image update factor
377  FLTNB image_update_factor = *ap_correctionValues * mp_alpha[a_voxel] * m_currentRelaxationFactor / a_sensitivity;
378  // Update image
379  *ap_newImageValue = a_currentImageValue + image_update_factor;
380  // Apply non-negativity constraint if asked for
381  if (m_nonNegativityConstraint && *ap_newImageValue<0.) *ap_newImageValue = 0.;
382  // End
383  return 0;
384 }
385 
386 // =====================================================================
387 // ---------------------------------------------------------------------
388 // ---------------------------------------------------------------------
389 // =====================================================================
#define FLTNB
Definition: gVariables.hh:81
FLTNB GetVoxSizeX()
Get the voxel's size along the X axis, in mm.
~iOptimizerMLTR()
The destructor of iOptimizerMLTR.
FLTNB m_initialValue
Definition: vOptimizer.hh:619
bool m_listmodeCompatibility
Definition: vOptimizer.hh:620
#define VERBOSE_DETAIL
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Definition: vOptimizer.hh:625
FLTNB m_initialRelaxationFactor
void ShowHelpSpecific()
A function used to show help about the child optimizer.
FLTNB ForwardProject(oProjectionLine *ap_Line, FLTNB *ap_image=NULL)
A function used to forward project the provided image (or 1 if NULL), based on the provided oProjecti...
Definition: vOptimizer.cc:988
int DataSpaceSpecificOperations(FLTNB a_data, FLTNB a_forwardModel, FLTNB *ap_backwardValues, FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue, FLTNB a_quantificationFactor, oProjectionLine *ap_Line)
This function performs the data space operations specific to the optimizer (computes the values to be...
FLTNB m_currentRelaxationFactor
FLTNB GetVoxSizeY()
Get the voxel's size along the Y axis, in mm.
#define Cerr(MESSAGE)
bool m_emissionCompatibility
Definition: vOptimizer.hh:622
iOptimizerMLTR()
The constructor of iOptimizerMLTR.
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child optimizer.
int m_nbBackwardImages
Definition: vOptimizer.hh:615
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:123
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
Declaration of class iOptimizerMLTR.
int ImageSpaceSpecificOperations(FLTNB a_currentImageValue, FLTNB *ap_newImageValue, FLTNB a_sensitivity, FLTNB *ap_correctionValues, INTNB a_voxel)
This function perform the image update step specific to the optimizer.
bool m_histogramCompatibility
Definition: vOptimizer.hh:621
INTNB GetNbVoxXY()
Get the number of voxels in a slice.
#define VERBOSE_NORMAL
int InitializeSpecific()
This function is used to initialize specific stuff to the child optimizer.
#define KEYWORD_MANDATORY
Definition: gOptions.hh:48
int PostDataUpdateSpecificStep(int a_iteration, int a_nbIterations, int a_subset, int *ap_nbSubsets)
This function is overloaded from the vOptimizer that does nothing by default.
#define INTNB
Definition: gVariables.hh:92
bool m_transmissionCompatibility
Definition: vOptimizer.hh:623
This class is designed to generically described any iterative optimizer.
Definition: vOptimizer.hh:59
This class is designed to manage and store system matrix elements associated to a vEvent...
bool m_nonNegativityConstraint
FLTNB m_finalRelaxationFactor
Declaration of class sOutputManager.
oImageSpace * mp_ImageSpace
Definition: vOptimizer.hh:626
FLTNB * AllocateMiscellaneousImage()
Allocate a new miscellaneous image on m2p_miscellaneousImages and return the pointer to this image...
Definition: oImageSpace.cc:529
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
#define Cout(MESSAGE)
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the 'a_input' string corresponding to the 'a_option' into 'a_nbElts' elements, using the 'sep' separator. The results are returned in the templated 'ap_return' dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
Definition: gOptions.cc:62
int SensitivitySpecificOperations(FLTNB a_data, FLTNB a_forwardModel, FLTNB *ap_weight, FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue, FLTNB a_quantificationFactor, oProjectionLine *ap_Line)
This function compute the weight associated to the provided event (for sensitivity computation) ...
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.