CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iOptimizerMLEM.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 "iOptimizerMLEM.hh"
31 #include "sOutputManager.hh"
32 
33 // =====================================================================
34 // ---------------------------------------------------------------------
35 // ---------------------------------------------------------------------
36 // =====================================================================
37 
39 {
40  // ---------------------------
41  // Mandatory member parameters
42  // ---------------------------
43 
44  // Initial value at 1
45  m_initialValue = 1.;
46  // Only one backward image for MLEM
48  // MLEM is compatible with listmode and histogram data
51  // MLEM is compatible with both emission and log-converted transmission data
54 
55  // --------------------------
56  // Specific member parameters
57  // --------------------------
58 
62 }
63 
64 // =====================================================================
65 // ---------------------------------------------------------------------
66 // ---------------------------------------------------------------------
67 // =====================================================================
68 
70 {
71 }
72 
73 // =====================================================================
74 // ---------------------------------------------------------------------
75 // ---------------------------------------------------------------------
76 // =====================================================================
77 
79 {
80  cout << "This optimizer is the standard MLEM (for Maximum Likelihood Expectation Maximization)." << endl;
81  cout << "It is numerically implemented in the multiplicative form (as opposed to the gradient form)." << endl;
82  cout << "It truncates negative data to 0 to satisfy the positivity constraint." << endl;
83  cout << "If subsets are used, it naturally becomes the OSEM optimizer." << endl;
84  cout << "With transmission data, the log-converted pre-corrected data are used as in J. Nuyts et al: \"Iterative reconstruction" << endl;
85  cout << "for helical CT: a simulation study\", Phys. Med. Biol., vol. 43, pp. 729-737, 1998." << endl;
86  cout << "The following options can be used (in this particular order when provided as a list):" << endl;
87  cout << " initial image value: to set the uniform voxel value for the initial image" << endl;
88  cout << " denominator threshold: to set the threshold of the data space denominator under which the ratio is set to 1" << endl;
89  cout << " minimum image update: to set the minimum of the image update factor under which it stays constant (0 or a negative value" << endl;
90  cout << " means no minimum thus allowing a 0 update)" << endl;
91  cout << " maximum image update: to set the maximum of the image update factor over which it stays constant (0 or a negative value means" << endl;
92  cout << " no maximum)" << endl;
93 }
94 
95 // =====================================================================
96 // ---------------------------------------------------------------------
97 // ---------------------------------------------------------------------
98 // =====================================================================
99 
100 int iOptimizerMLEM::ReadConfigurationFile(const string& a_configurationFile)
101 {
102  string key_word = "";
103  // Read the initial image value option
104  key_word = "initial image value";
105  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_initialValue, 1, KEYWORD_MANDATORY))
106  {
107  Cerr("***** iOptimizerMLEM::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
108  return 1;
109  }
110  // Read the denominator threshold option
111  key_word = "denominator threshold";
112  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_dataSpaceDenominatorThreshold, 1, KEYWORD_MANDATORY))
113  {
114  Cerr("***** iOptimizerMLEM::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
115  return 1;
116  }
117  // Read the minimum image update option
118  key_word = "minimum image update";
119  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_minimumImageUpdateFactor, 1, KEYWORD_MANDATORY))
120  {
121  Cerr("***** iOptimizerMLEM::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
122  return 1;
123  }
124  // Read the maximum image update option
125  key_word = "maximum image update";
126  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_maximumImageUpdateFactor, 1, KEYWORD_MANDATORY))
127  {
128  Cerr("***** iOptimizerMLEM::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
129  return 1;
130  }
131  // Normal end
132  return 0;
133 }
134 
135 // =====================================================================
136 // ---------------------------------------------------------------------
137 // ---------------------------------------------------------------------
138 // =====================================================================
139 
140 int iOptimizerMLEM::ReadOptionsList(const string& a_optionsList)
141 {
142  // There are 4 floating point variables as options
143  const int nb_options = 4;
144  FLTNB options[nb_options];
145  // Read them
146  if (ReadStringOption(a_optionsList, options, nb_options, ",", "MLEM configuration"))
147  {
148  Cerr("***** iOptimizerMLEM::ReadOptionsList() -> Failed to correctly read the list of options !" << endl);
149  return 1;
150  }
151  // Affect options
152  m_initialValue = options[0];
153  m_dataSpaceDenominatorThreshold = options[1];
154  m_minimumImageUpdateFactor = options[2];
155  m_maximumImageUpdateFactor = options[3];
156  // Normal end
157  return 0;
158 }
159 
160 // =====================================================================
161 // ---------------------------------------------------------------------
162 // ---------------------------------------------------------------------
163 // =====================================================================
164 
166 {
167  // Check that initial image value is strictly positive
168  if (m_initialValue<=0.)
169  {
170  Cerr("***** iOptimizerMLEM->CheckSpecificParameters() -> Provided initial image value (" << m_initialValue << ") must be strictly positive !" << endl);
171  return 1;
172  }
173  // Check that denominator threshold value is strictly positive
175  {
176  Cerr("***** iOptimizerMLEM->CheckSpecificParameters() -> Provided data space denominator threshold (" << m_dataSpaceDenominatorThreshold << ") must be strictly positive !" << endl);
177  return 1;
178  }
179  // Check that maximum image update factor is higher than the minimum
181  {
182  Cerr("***** iOptimizerMLEM->CheckSpecificParameters() -> Provided minimum/maximum (" << m_minimumImageUpdateFactor << "/" << m_maximumImageUpdateFactor << " are inconsistent !" << endl);
183  return 1;
184  }
185  // Cannot deal with list-mode transmission data
187  {
188  Cerr("***** iOptimizerMLEM->CheckSpecificParameters() -> Cannot reconstruct list-mode transmission data !" << endl);
189  return 1;
190  }
191  // Normal end
192  return 0;
193 }
194 
195 // =====================================================================
196 // ---------------------------------------------------------------------
197 // ---------------------------------------------------------------------
198 // =====================================================================
199 
201 {
202  // Verbose
204  {
205  Cout("iOptimizerMLEM::InitializeSpecific() -> Use the MLEM optimizer" << endl);
207  {
208  Cout(" --> Initial image value: " << m_initialValue << endl);
209  Cout(" --> Data space denominator threshold: " << m_dataSpaceDenominatorThreshold << endl);
210  if (m_minimumImageUpdateFactor>0.) Cout(" --> Minimum image update factor: " << m_minimumImageUpdateFactor << endl);
211  else Cerr("!!!!! The minimum update value is not set, if using subsets, voxels could be trapped in 0 value causing some negative bias !" << endl);
212  if (m_maximumImageUpdateFactor>0.) Cout(" --> Maximum image update factor: " << m_maximumImageUpdateFactor << endl);
213  }
214  }
215  // Normal end
216  return 0;
217 }
218 
219 // =====================================================================
220 // ---------------------------------------------------------------------
221 // ---------------------------------------------------------------------
222 // =====================================================================
223 
224 int iOptimizerMLEM::SensitivitySpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_weight,
225  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
226  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
227 {
228  // Line weight here is simply 1
229  *ap_weight = 1.;
230  // That's all
231  return 0;
232 }
233 
234 // =====================================================================
235 // ---------------------------------------------------------------------
236 // ---------------------------------------------------------------------
237 // =====================================================================
238 
239 int iOptimizerMLEM::DataSpaceSpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_backwardValues,
240  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
241  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
242 {
243  // Case for emission tomography
245  {
246  // Truncate data to 0 if negative
247  if (a_data<0.) a_data = 0.;
248  // If the foward model is too close to zero, then ignore this data (set to 1 and not 0 because this line is taken into account in the sensitivity)
249  if (a_forwardModel>m_dataSpaceDenominatorThreshold) *ap_backwardValues = a_data / a_forwardModel;
250  else *ap_backwardValues = 1.;
251  }
252  // Case for transmission tomography (equivalent of log-converted MLEM from Nuyts et al 1998)
253  else if (m_dataSpec==SPEC_TRANSMISSION)
254  {
255  // Subtract scatters
256  a_data -= a_additiveCorrections;
257  a_forwardModel -= a_additiveCorrections;
258  // Safely ignore this data (set backward value to 1 and return) in 2 different cases:
259  // - if data or model is inferior to 1
260  // - if data or model is higher than blank value
261  if (a_data<1. || a_forwardModel<1. || a_data>a_blankValue || a_forwardModel>a_blankValue)
262  {
263  *ap_backwardValues = 1.;
264  return 0;
265  }
266  // Log-convert the data and the model
267  a_data = log(a_blankValue/a_data);
268  a_forwardModel = log(a_blankValue/a_forwardModel);
269  // If the foward model is to close to zero, then ignore this data (set to 1 and not 0 because this line is taken into account in the sensitivity)
270  if (a_forwardModel>m_dataSpaceDenominatorThreshold) *ap_backwardValues = a_data / a_forwardModel;
271  else *ap_backwardValues = 1.;
272  }
273  // That's all
274  return 0;
275 }
276 
277 // =====================================================================
278 // ---------------------------------------------------------------------
279 // ---------------------------------------------------------------------
280 // =====================================================================
281 
282 int iOptimizerMLEM::ImageSpaceSpecificOperations( FLTNB a_currentImageValue, FLTNB* ap_newImageValue,
283  FLTNB a_sensitivity, FLTNB* ap_correctionValues,
284  INTNB a_voxel, int a_tbf, int a_rbf, int a_cbf )
285 {
286  // Compute image update factor
287  FLTNB image_update_factor = *ap_correctionValues / a_sensitivity;
288  // Apply minimum image update factor
289  if ( m_minimumImageUpdateFactor > 0. && image_update_factor < m_minimumImageUpdateFactor ) image_update_factor = m_minimumImageUpdateFactor;
290  // Apply maximum image update factor
291  if ( m_maximumImageUpdateFactor > 0. && image_update_factor > m_maximumImageUpdateFactor ) image_update_factor = m_maximumImageUpdateFactor;
292  // Update image
293  *ap_newImageValue = a_currentImageValue * image_update_factor;
294  // End
295  return 0;
296 }
297 
298 // =====================================================================
299 // ---------------------------------------------------------------------
300 // ---------------------------------------------------------------------
301 // =====================================================================
FLTNB m_dataSpaceDenominatorThreshold
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.
#define MODE_LIST
Definition: vDataFile.hh:56
#define FLTNB
Definition: gVariables.hh:81
FLTNB m_initialValue
Definition: vOptimizer.hh:663
bool m_listmodeCompatibility
Definition: vOptimizer.hh:664
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
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...
#define VERBOSE_DETAIL
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child optimizer.
int m_dataMode
Definition: vOptimizer.hh:671
int m_dataSpec
Definition: vOptimizer.hh:673
#define Cerr(MESSAGE)
int InitializeSpecific()
This function is used to initialize specific stuff to the child optimizer.
bool m_emissionCompatibility
Definition: vOptimizer.hh:666
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
bool m_histogramCompatibility
Definition: vOptimizer.hh:665
#define VERBOSE_NORMAL
iOptimizerMLEM()
The constructor of iOptimizerMLEM.
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
Declaration of class iOptimizerMLEM.
#define SPEC_TRANSMISSION
Definition: vDataFile.hh:92
FLTNB m_maximumImageUpdateFactor
#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...
~iOptimizerMLEM()
The destructor of iOptimizerMLEM.
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
FLTNB m_minimumImageUpdateFactor
Declaration of class sOutputManager.
#define SPEC_EMISSION
Definition: vDataFile.hh:90
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) ...
#define Cout(MESSAGE)
void ShowHelpSpecific()
A function used to show help about the child optimizer.
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