CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
iOptimizerMLMuMap.cc
Go to the documentation of this file.
1 
8 #include "iOptimizerMLMuMap.hh"
9 #include "sOutputManager.hh"
10 #include "iDataFilePET.hh"
11 
12 // =====================================================================
13 // ---------------------------------------------------------------------
14 // ---------------------------------------------------------------------
15 // =====================================================================
16 
18 {
19  // ---------------------------
20  // Mandatory member parameters
21  // ---------------------------
22 
23  // Initial value at 1
24  m_initialValue = 1.;
25  // Only one backward image for MLMuMap
27  // MLMuMap is compatible only with histogram data
30  // MLMuMap is compatible only with log-converted ACF from PET histogram data
33 
34  // --------------------------
35  // Specific member parameters
36  // --------------------------
37 
41 }
42 
43 // =====================================================================
44 // ---------------------------------------------------------------------
45 // ---------------------------------------------------------------------
46 // =====================================================================
47 
49 {
50 }
51 
52 // =====================================================================
53 // ---------------------------------------------------------------------
54 // ---------------------------------------------------------------------
55 // =====================================================================
56 
58 {
59  cout << "This optimizer is a modified MLEM algorithm where the image voxel values are attenuation factors" << endl;
60  cout << "and the data are taken as the logarithm of ACF values associated to PET histogram data." << endl;
61  cout << "It is numerically implemented in the multiplicative form (as opposed to the gradient form)." << endl;
62  cout << "It truncates ACF lower than 1 to 1 to satisfy the positivity constraint." << endl;
63  cout << "Note that although physicists are used to attenuation values in 1/cm, the reconstructed image units" << endl;
64  cout << "are 1/mm as the whole castor code is using mm." << endl;
65  cout << "Subsets can be used." << endl;
66  cout << "The following options can be used (in this particular order when provided as a list):" << endl;
67  cout << " initial image value: to set the uniform voxel value for the initial image" << endl;
68  cout << " denominator threshold: to set the threshold of the data space denominator under which the ratio is set to 1" << endl;
69  cout << " minimum image update: to set the minimum of the image update factor under which it stays constant (0 or a negative value" << endl;
70  cout << " means no minimum thus allowing a 0 update)" << endl;
71  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;
72  cout << " no maximum)" << endl;
73 }
74 
75 // =====================================================================
76 // ---------------------------------------------------------------------
77 // ---------------------------------------------------------------------
78 // =====================================================================
79 
80 int iOptimizerMLMuMap::ReadConfigurationFile(const string& a_configurationFile)
81 {
82  string key_word = "";
83  // Read the initial image value option
84  key_word = "initial image value";
85  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_initialValue, 1, KEYWORD_MANDATORY))
86  {
87  Cerr("***** iOptimizerMLMuMap::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
88  return 1;
89  }
90  // Read the denominator threshold option
91  key_word = "denominator threshold";
92  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_dataSpaceDenominatorThreshold, 1, KEYWORD_MANDATORY))
93  {
94  Cerr("***** iOptimizerMLMuMap::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
95  return 1;
96  }
97  // Read the minimum image update option
98  key_word = "minimum image update";
99  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_minimumImageUpdateFactor, 1, KEYWORD_MANDATORY))
100  {
101  Cerr("***** iOptimizerMLMuMap::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
102  return 1;
103  }
104  // Read the maximum image update option
105  key_word = "maximum image update";
106  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_maximumImageUpdateFactor, 1, KEYWORD_MANDATORY))
107  {
108  Cerr("***** iOptimizerMLMuMap::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
109  return 1;
110  }
111  // Normal end
112  return 0;
113 }
114 
115 // =====================================================================
116 // ---------------------------------------------------------------------
117 // ---------------------------------------------------------------------
118 // =====================================================================
119 
120 int iOptimizerMLMuMap::ReadOptionsList(const string& a_optionsList)
121 {
122  // There are 4 floating point variables as options
123  const int nb_options = 4;
124  FLTNB options[nb_options];
125  // Read them
126  if (ReadStringOption(a_optionsList, options, nb_options, ",", "MLEM configuration"))
127  {
128  Cerr("***** iOptimizerMLMuMap::ReadOptionsList() -> Failed to correctly read the list of options !" << endl);
129  return 1;
130  }
131  // Affect options
132  m_initialValue = options[0];
133  m_dataSpaceDenominatorThreshold = options[1];
134  m_minimumImageUpdateFactor = options[2];
135  m_maximumImageUpdateFactor = options[3];
136  // Normal end
137  return 0;
138 }
139 
140 // =====================================================================
141 // ---------------------------------------------------------------------
142 // ---------------------------------------------------------------------
143 // =====================================================================
144 
146 {
147  // Check that initial image value is strictly positive
148  if (m_initialValue<=0.)
149  {
150  Cerr("***** iOptimizerMLMuMap->CheckSpecificParameters() -> Provided initial image value (" << m_initialValue << ") must be strictly positive !" << endl);
151  return 1;
152  }
153  // Check that denominator threshold value is strictly positive
155  {
156  Cerr("***** iOptimizerMLMuMap->CheckSpecificParameters() -> Provided data space denominator threshold (" << m_dataSpaceDenominatorThreshold << ") must be strictly positive !" << endl);
157  return 1;
158  }
159  // Check that maximum image update factor is higher than the minimum
161  {
162  Cerr("***** iOptimizerMLMuMap->CheckSpecificParameters() -> Provided minimum/maximum (" << m_minimumImageUpdateFactor << "/" << m_maximumImageUpdateFactor << " are inconsistent !" << endl);
163  return 1;
164  }
165  // Can only deal with PET histogram data where ACF values are provideed
167  {
168  Cerr("***** iOptimizerMLMuMap->CheckSpecificParameters() -> Can only reconstruct ACF from PET histogram data !" << endl);
169  return 1;
170  }
171  // Get the datafile and check that ACF are provided
172  if (!(dynamic_cast<iDataFilePET*>(mp_DataFile))->GetAtnCorrectionFlag())
173  {
174  Cerr("***** iOptimizerMLMuMap->CheckSpecificParameters() -> ACF must be provided into the PET histogram data !" << endl);
175  return 1;
176  }
177  // Normal end
178  return 0;
179 }
180 
181 // =====================================================================
182 // ---------------------------------------------------------------------
183 // ---------------------------------------------------------------------
184 // =====================================================================
185 
187 {
188  // In order to get quantitative attenuation values in the image space, we have to disable all PET related corrections.
189  // We cannot disable attenuation correction though. Because the effect of ignoring a correction from the datafile means
190  // that when reading the value in the datafile, the value is replaced by a neutral one (1 for ACF), so this would mean
191  // that we cannot have access to the ACF anymore within the optimizer. A consequence is that the attenuation will be
192  // automatically integrated into the system matrix and we have to remove it manually in the computation of the sensitivity
193  // at least. No need to do it in forward and backward projections as the multiplicative factor of the system matrix cancel
194  // out in the computation of the ratio (also because there is no scatter or random here).
202  // Have to do it also inside the datafile
203  (dynamic_cast<iDataFilePET*>(mp_DataFile))->SetIgnoreNormCorrectionFlag(true);
204  (dynamic_cast<iDataFilePET*>(mp_DataFile))->SetIgnoreRandCorrectionFlag(true);
205  (dynamic_cast<iDataFilePET*>(mp_DataFile))->SetIgnoreScatCorrectionFlag(true);
207  {
208  Cerr("***** oOptimizerMLMuMap->InitializeSpecific() -> A problem occured while reseting the quantification factors !" << endl);
209  return 1;
210  }
211  // Verbose
213  {
214  Cout("iOptimizerMLMuMap::InitializeSpecific() -> Use the MLEM optimizer" << endl);
216  {
217  Cout(" --> Initial image value: " << m_initialValue << endl);
218  Cout(" --> Data space denominator threshold: " << m_dataSpaceDenominatorThreshold << endl);
219  if (m_minimumImageUpdateFactor>0.) Cout(" --> Minimum image update factor: " << m_minimumImageUpdateFactor << endl);
220  else Cerr("!!!!! The minimum update value is not set, if using subsets, voxels could be trapped in 0 value causing some negative bias !" << endl);
221  if (m_maximumImageUpdateFactor>0.) Cout(" --> Maximum image update factor: " << m_maximumImageUpdateFactor << endl);
222  }
223  }
224  // Normal end
225  return 0;
226 }
227 
228 // =====================================================================
229 // ---------------------------------------------------------------------
230 // ---------------------------------------------------------------------
231 // =====================================================================
232 
233 int iOptimizerMLMuMap::SensitivitySpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_weight,
234  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
235  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
236 {
237  // We multiply by the multiplicative_corrections as they are already in the system matrix and we want to remove them
238  *ap_weight = a_multiplicativeCorrections;
239  // That's all
240  return 0;
241 }
242 
243 // =====================================================================
244 // ---------------------------------------------------------------------
245 // ---------------------------------------------------------------------
246 // =====================================================================
247 
248 int iOptimizerMLMuMap::DataSpaceSpecificOperations( FLTNB a_data, FLTNB a_forwardModel, FLTNB* ap_backwardValues,
249  FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue,
250  FLTNB a_quantificationFactor, oProjectionLine* ap_Line )
251 {
252  // Do nothing as it is not used
253  return 0;
254 }
255 
256 // =====================================================================
257 // ---------------------------------------------------------------------
258 // ---------------------------------------------------------------------
259 // =====================================================================
260 
262  int a_bed, int a_timeFrame, int a_respGate, int a_cardGate,
263  int a_th )
264 {
265  // Note: This algorithm only works for PET data and only ACF are used, so we ignore TOF and overload this function.
266  // We cast the event to PET in order to specifically get the ACF value.
267 
268  // Default to NO TOF
269  int NOTOF = 0;
270  ap_Line->SetCurrentTOFBin(NOTOF);
271  // Get the ACF
272  FLTNB acf = (dynamic_cast<iEventPET*>(ap_Event))->GetAtnCorrFactor();
273  // Get the forward model
274  FLTNB forward = m2p_forwardValues[a_th][NOTOF];
275  // Truncate acf to 1 if lower
276  if (acf<1.) acf = 1.;
277  // Take the log
278  acf = log(acf);
279  // 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)
280  if (forward>m_dataSpaceDenominatorThreshold) *m3p_backwardValues[a_th][NOTOF] = acf / forward;
281  else *m3p_backwardValues[a_th][NOTOF] = 1.;
282  // End
283  return 0;
284 }
285 
286 // =====================================================================
287 // ---------------------------------------------------------------------
288 // ---------------------------------------------------------------------
289 // =====================================================================
290 
291 int iOptimizerMLMuMap::ImageSpaceSpecificOperations( FLTNB a_currentImageValue, FLTNB* ap_newImageValue,
292  FLTNB a_sensitivity, FLTNB* ap_correctionValues,
293  INTNB a_voxel, int a_tbf, int a_rbf, int a_cbf )
294 {
295  // Compute image update factor
296  FLTNB image_update_factor = *ap_correctionValues / a_sensitivity;
297  // Apply minimum image update factor
298  if ( m_minimumImageUpdateFactor > 0. && image_update_factor < m_minimumImageUpdateFactor ) image_update_factor = m_minimumImageUpdateFactor;
299  // Apply maximum image update factor
300  if ( m_maximumImageUpdateFactor > 0. && image_update_factor > m_maximumImageUpdateFactor ) image_update_factor = m_maximumImageUpdateFactor;
301  // Update image
302  *ap_newImageValue = a_currentImageValue * image_update_factor;
303  // End
304  return 0;
305 }
306 
307 // =====================================================================
308 // ---------------------------------------------------------------------
309 // ---------------------------------------------------------------------
310 // =====================================================================
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child optimizer.
~iOptimizerMLMuMap()
The destructor of iOptimizerMLMuMap.
#define MODE_HISTOGRAM
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.
int DataStep5ComputeCorrections(oProjectionLine *ap_Line, vEvent *ap_Event, int a_bed, int a_timeFrame, int a_respGate, int a_cardGate, int a_thread)
Overload of the vOptimizer function. See implementation for more details.
#define Cerr(MESSAGE)
void SetIgnoreNormCorrectionFlag(bool a_ignoreNormCorrectionFlag)
Set the boolean m_ignoreNormCorrectionFlag to a_ignoreNormCorrectionFlag.
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.
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
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) ...
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
void ShowHelpSpecific()
A function used to show help about the child optimizer.
void SetIgnoreScatCorrectionFlag(bool a_ignoreScatCorrectionFlag)
Set the boolean m_ignoreScatCorrectionFlag to a_ignoreScatCorrectionFlag.
int ResetQuantificationFactors()
If already initialized, set the quantification factors to 1.
void SetIgnoreFdurCorrectionFlag(bool a_ignoreFdurCorrectionFlag)
Set the boolean m_ignoreFdurCorrectionFlag to a_ignoreFdurCorrectionFlag.
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...
void SetIgnoreBratCorrectionFlag(bool a_ignoreBratCorrectionFlag)
Set the boolean m_ignoreBratCorrectionFlag to a_ignoreBratCorrectionFlag.
#define KEYWORD_MANDATORY
Inherit from vEvent. Main PET class for the Event objects.
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...
void SetIgnoreCaliCorrectionFlag(bool a_ignoreCaliCorrectionFlag)
Set the boolean m_ignoreCaliCorrectionFlag to a_ignoreCaliCorrectionFlag.
void SetIgnoreRandCorrectionFlag(bool a_ignoreRandCorrectionFlag)
Set the boolean m_ignoreRandCorrectionFlag to a_ignoreRandCorrectionFlag.
int InitializeSpecific()
This function is used to initialize specific stuff to the child optimizer.
Mother class for the Event objects.
void SetIgnoreDecaCorrectionFlag(bool a_ignoreDecaCorrectionFlag)
Set the boolean m_ignoreDecaCorrectionFlag to a_ignoreDecaCorrectionFlag.
iOptimizerMLMuMap()
The constructor of iOptimizerMLMuMap.
Declaration of class iOptimizerMLMuMap.
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...
Inherit from vDataFile. Class that manages the reading of a PET input file (header + data)...
#define Cout(MESSAGE)
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.