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