CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
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-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 "iOptimizerMLTR.hh"
31 #include "sOutputManager.hh"
32 
33 // =====================================================================
34 // ---------------------------------------------------------------------
35 // ---------------------------------------------------------------------
36 // =====================================================================
37 
39 {
40  // ---------------------------
41  // Mandatory member parameters
42  // ---------------------------
43 
44  // Initial value at 0
45  m_initialValue = 0.;
46  // Only one backward image for MLTR
48  // MLTR is compatible with histogram data but not listmode
51  // MLTR is only compatible with transmission data
54 
55  // --------------------------
56  // Specific member parameters
57  // --------------------------
58 
59 /*
60  // The alpha image
61  mp_alpha = NULL;
62  // The alpha ratio between exterior and interior of the eliptical FOV
63  m_alphaRatio = 1.;
64 */
65  // Relaxation parameters
69  // Non-negativity constraint
71 }
72 
73 // =====================================================================
74 // ---------------------------------------------------------------------
75 // ---------------------------------------------------------------------
76 // =====================================================================
77 
79 {
80 }
81 
82 // =====================================================================
83 // ---------------------------------------------------------------------
84 // ---------------------------------------------------------------------
85 // =====================================================================
86 
88 {
89  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;
90  cout << "\"Reconstruction scheme for accelerated maximum lihelihood reconstruction: the patchwork structure\"," << endl;
91  cout << "IEEE Trans. Nucl. Sci., vol. 61, pp. 173-81, 2014." << endl;
92  cout << "An additional empiric relaxation factor has been added onto the additive update. Its value onto the first and last updates" << endl;
93  cout << "can be parameterized. Its value for all updates in between is computed linearly from these first and last provided values." << endl;
94  cout << "The design parameter 'alpha' is not used here." << 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 /*
120  // Read the alpha ratio option
121  key_word = "alpha ratio";
122  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_alphaRatio, 1, KEYWORD_MANDATORY))
123  {
124  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
125  return 1;
126  }
127 */
128  // Read the initial relaxation factor option
129  key_word = "initial relaxation factor";
130  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_initialRelaxationFactor, 1, KEYWORD_MANDATORY))
131  {
132  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
133  return 1;
134  }
135  // Read the final relaxation factor option
136  key_word = "final relaxation factor";
137  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_finalRelaxationFactor, 1, KEYWORD_MANDATORY))
138  {
139  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
140  return 1;
141  }
142  // Read the non-negativity constraint option
143  key_word = "non-negativity constraint";
144  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_nonNegativityConstraint, 1, KEYWORD_MANDATORY))
145  {
146  Cerr("***** iOptimizerMLTR::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
147  return 1;
148  }
149  // Normal end
150  return 0;
151 }
152 
153 // =====================================================================
154 // ---------------------------------------------------------------------
155 // ---------------------------------------------------------------------
156 // =====================================================================
157 
158 int iOptimizerMLTR::ReadOptionsList(const string& a_optionsList)
159 {
160  // There are 4 floating point variables as options
161  const int nb_options = 4;
162  FLTNB options[nb_options];
163  // Read them
164  if (ReadStringOption(a_optionsList, options, nb_options, ",", "MLTR configuration"))
165  {
166  Cerr("***** iOptimizerMLTR::ReadOptionsList() -> Failed to correctly read the list of options !" << endl);
167  return 1;
168  }
169  // Affect options
170  m_initialValue = options[0];
171 // m_alphaRatio = options[1];
172  m_initialRelaxationFactor = options[1];
173  m_finalRelaxationFactor = options[2];
174  if (options[3]==0.) m_nonNegativityConstraint = false;
175  else m_nonNegativityConstraint = true;
176  // Normal end
177  return 0;
178 }
179 
180 // =====================================================================
181 // ---------------------------------------------------------------------
182 // ---------------------------------------------------------------------
183 // =====================================================================
184 
186 {
187 /*
188  // Check that the alpha ratio is positive
189  if (m_alphaRatio<0.)
190  {
191  Cerr("***** iOptimizerMLTR->CheckSpecificParameters() -> Provided alpha ratio (" << m_alphaRatio << ") must be positive !" << endl);
192  return 1;
193  }
194 */
195  // Check that the initial and final relaxation factors are strictly positive
197  {
198  Cerr("***** iOptimizerMLTR->CheckSpecificParameters() -> Provided initial relaxation factor (" << m_initialRelaxationFactor << ") must be positive !" << endl);
199  return 1;
200  }
202  {
203  Cerr("***** iOptimizerMLTR->CheckSpecificParameters() -> Provided final relaxation factor (" << m_finalRelaxationFactor << ") must be positive !" << endl);
204  return 1;
205  }
206  // Normal end
207  return 0;
208 }
209 
210 // =====================================================================
211 // ---------------------------------------------------------------------
212 // ---------------------------------------------------------------------
213 // =====================================================================
214 
216 {
217  // Verbose
219  {
220  Cout("iOptimizerMLTR::InitializeSpecific() -> Use the MLTR optimizer" << endl);
222  {
223  Cout(" --> Initial image value: " << m_initialValue << endl);
225  {
226  Cout(" --> Initial relaxation factor: " << m_initialRelaxationFactor << endl);
227  Cout(" --> Final relaxation factor: " << m_finalRelaxationFactor << endl);
228  }
229  if (m_nonNegativityConstraint) Cout(" --> Apply a non-negativity constraint during image update" << endl);
230  }
231  }
232 
233 /*
234  // ===================================================================
235  // Step 1: Allocate the alpha image from a miscellaneous image
236  mp_alpha = mp_ImageSpace->AllocateMiscellaneousImage();
237 
238  // ===================================================================
239  // Step 2: Compute interior and exterior alpha values
240 
241  // We assume the interior value to be always 1, then we change the exterior value according to the alpha ratio provided
242  FLTNB alpha_interior = 1.;
243  FLTNB alpha_exterior = m_alphaRatio;
244  if (m_alphaRatio!=1. && m_verbose>=VERBOSE_DETAIL) Cout(" --> Use alpha values in interior/exterior of eliptical FOV of " << alpha_interior << "/" << alpha_exterior << endl);
245 
246  // ===================================================================
247  // Step 3: Set the alpha values inside and outside the cylindrical FOV
248 
249  // Precast half the number of voxels over X and Y minus 1 (for efficiency)
250  FLTNB flt_base_x = 0.5*((FLTNB)(mp_ImageDimensionsAndQuantification->GetNbVoxX()-1));
251  FLTNB flt_base_y = 0.5*((FLTNB)(mp_ImageDimensionsAndQuantification->GetNbVoxY()-1));
252  // Compute FOV elipse radius over X and Y, then squared
253  FLTNB squared_radius_x = 0.5 * ((FLTNB)(mp_ImageDimensionsAndQuantification->GetNbVoxX())) * mp_ImageDimensionsAndQuantification->GetVoxSizeX();
254  squared_radius_x *= squared_radius_x;
255  FLTNB squared_radius_y = 0.5 * ((FLTNB)(mp_ImageDimensionsAndQuantification->GetNbVoxY())) * mp_ImageDimensionsAndQuantification->GetVoxSizeY();
256  squared_radius_y *= squared_radius_y;
257  // We assume that the computation of the distance from the center for a given
258  // voxel and comparing it with the output FOV percentage costs more than performing
259  // the loops in an inverse order compared to how the image is stored in memory.
260  // Thus we begin the loops over X, then Y, then we test and if test passes, we
261  // do the remaining loop over Z and over all dynamic dimensions.
262  int x;
263  #pragma omp parallel for private(x) schedule(guided)
264  for (x=0; x<mp_ImageDimensionsAndQuantification->GetNbVoxX(); x++)
265  {
266  // Compute X distance from image center, then squared
267  FLTNB squared_distance_x = (((FLTNB)x)-flt_base_x) * mp_ImageDimensionsAndQuantification->GetVoxSizeX();
268  squared_distance_x *= squared_distance_x;
269  // Loop over Y
270  for (int y=0; y<mp_ImageDimensionsAndQuantification->GetNbVoxY(); y++)
271  {
272  // Compute Y distance from image center, then squared
273  FLTNB squared_distance_y = (((FLTNB)y)-flt_base_y) * mp_ImageDimensionsAndQuantification->GetVoxSizeY();
274  squared_distance_y *= squared_distance_y;
275  // The alpha value
276  FLTNB alpha_value = 0.;
277  // Set the alpha value to the interior if INSIDE the elipse
278  if ( squared_distance_x/squared_radius_x + squared_distance_y/squared_radius_y <= 1. ) alpha_value = alpha_interior;
279  // Else OUTSIDE the elipse
280  else alpha_value = alpha_exterior;
281  // Loop over Z
282  for (int z=0; z<mp_ImageDimensionsAndQuantification->GetNbVoxZ(); z++)
283  {
284  // Compute global voxel index
285  INTNB index = z*mp_ImageDimensionsAndQuantification->GetNbVoxXY() + y*mp_ImageDimensionsAndQuantification->GetNbVoxX() + x;
286  // Set the alpha value
287  mp_alpha[index] = alpha_value;
288  }
289  }
290  }
291 */
292  // Normal end
293  return 0;
294 }
295 
296 // =====================================================================
297 // ---------------------------------------------------------------------
298 // ---------------------------------------------------------------------
299 // =====================================================================
300 
302 {
303  // ===================================================================
304  // In this function, we update the value of the relaxation factor to
305  // be used in the next image update.
306  // ===================================================================
307 
308  // Special case if only one update!
309  if (m_nbIterations==1 && mp_nbSubsets[0]==1)
310  {
311  // Test if the initial and final relaxation factors differ, then throw an error
313  {
314  Cerr("***** iOptimizerMLTR::PreImageUpdateSpecificStep() -> Initial and final relaxation differ while there is only one update to do !" << endl);
315  return 1;
316  }
317  // Set the value
319  }
320 
321  // Compute the total number of updates minus 1
322  int total_number_of_updates_minus_one = -1;
323  for (int it=0; it<m_nbIterations; it++) total_number_of_updates_minus_one += mp_nbSubsets[it];
324  // Compute the current update index
325  int current_update_index = 0;
326  for (int it=0; it<m_currentIteration; it++) current_update_index += mp_nbSubsets[it];
327  current_update_index += m_currentSubset;
328  // Compute the position on the slope of updates
329  FLTNB ratio_of_updates = ((FLTNB)current_update_index) / ((FLTNB)total_number_of_updates_minus_one);
330 
331  // Finally update the current relaxation factor to be used at this update, from the initial and final relaxation factors
332  m_currentRelaxationFactor = m_initialRelaxationFactor * (1.-ratio_of_updates) + m_finalRelaxationFactor * ratio_of_updates;
333 
334  // Verbose
335  if (m_verbose>=VERBOSE_DETAIL) Cout("iOptimizerMLTR::PreImageUpdateSpecificStep() -> Current relaxation factor value: " << m_currentRelaxationFactor << endl);
336 
337  // That's all
338  return 0;
339 }
340 
341 // =====================================================================
342 // ---------------------------------------------------------------------
343 // ---------------------------------------------------------------------
344 // =====================================================================
345 
346 int iOptimizerMLTR::SensitivitySpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_weight,
347  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
348  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
349 {
350  // Line weight here is a projection of a uniform 1 image multiplied by the model
351 // *ap_weight = ForwardProject(ap_Line, mp_alpha) * a_forwardModel;
352  *ap_weight = ForwardProject(ap_Line) * a_forwardModel;
353  // That's all
354  return 0;
355 }
356 
357 // =====================================================================
358 // ---------------------------------------------------------------------
359 // ---------------------------------------------------------------------
360 // =====================================================================
361 
362 int iOptimizerMLTR::DataSpaceSpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_backwardValues,
363  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
364  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
365 {
366  // Truncate data to 0 if negative
367  if (a_data<0.) a_data = 0.;
368  // The correction value is simply the subtraction of the data from the model
369  *ap_backwardValues = a_forwardModel - a_data;
370  // That's all
371  return 0;
372 }
373 
374 // =====================================================================
375 // ---------------------------------------------------------------------
376 // ---------------------------------------------------------------------
377 // =====================================================================
378 
379 int iOptimizerMLTR::ImageSpaceSpecificOperations( FLTNB a_currentImageValue, FLTNB* ap_newImageValue,
380  FLTNB a_sensitivity, FLTNB* ap_correctionValues,
381  INTNB a_voxel, int a_tbf, int a_rbf, int a_cbf )
382 {
383  // Compute image update factor
384 // FLTNB image_update_factor = *ap_correctionValues * mp_alpha[a_voxel] * m_currentRelaxationFactor / a_sensitivity;
385  FLTNB image_update_factor = *ap_correctionValues * m_currentRelaxationFactor / a_sensitivity;
386  // Update image
387  *ap_newImageValue = a_currentImageValue + image_update_factor;
388  // Apply non-negativity constraint if asked for
389  if (m_nonNegativityConstraint && *ap_newImageValue<0.) *ap_newImageValue = 0.;
390  // End
391  return 0;
392 }
393 
394 // =====================================================================
395 // ---------------------------------------------------------------------
396 // ---------------------------------------------------------------------
397 // =====================================================================
#define FLTNB
Definition: gVariables.hh:81
~iOptimizerMLTR()
The destructor of iOptimizerMLTR.
FLTNB m_initialValue
Definition: vOptimizer.hh:663
bool m_listmodeCompatibility
Definition: vOptimizer.hh:664
#define VERBOSE_DETAIL
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:1120
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
#define Cerr(MESSAGE)
bool m_emissionCompatibility
Definition: vOptimizer.hh:666
iOptimizerMLTR()
The constructor of iOptimizerMLTR.
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child optimizer.
int m_nbIterations
Definition: vOptimizer.hh:676
int m_nbBackwardImages
Definition: vOptimizer.hh:659
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
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
Declaration of class iOptimizerMLTR.
bool m_histogramCompatibility
Definition: vOptimizer.hh:665
#define VERBOSE_NORMAL
int InitializeSpecific()
This function is used to initialize specific stuff to the child optimizer.
int m_currentSubset
Definition: vOptimizer.hh:679
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
#define INTNB
Definition: gVariables.hh:92
bool m_transmissionCompatibility
Definition: vOptimizer.hh:667
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
int m_currentIteration
Definition: vOptimizer.hh:678
FLTNB m_finalRelaxationFactor
Declaration of class sOutputManager.
int ImageSpaceSpecificOperations(FLTNB a_currentImageValue, FLTNB *ap_newImageValue, FLTNB a_sensitivity, FLTNB *ap_correctionValues, INTNB a_voxel, int a_tbf=-1, int a_rbf=-1, int a_cbf=-1)
This function perform the image update step specific to the optimizer.
int PreImageUpdateSpecificStep()
This function is overloaded from the vOptimizer that does nothing by default.
int * mp_nbSubsets
Definition: vOptimizer.hh:677
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
#define Cout(MESSAGE)
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 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) ...