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