CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
code/src/analytic_simulator/oComputeProjection.cc
Go to the documentation of this file.
1 
9 #include "oComputeProjection.hh"
10 
11 // =====================================================================
12 // ---------------------------------------------------------------------
13 // ---------------------------------------------------------------------
14 // =====================================================================
15 /*
16  \brief oComputeProjection constructor.
17  \param ap_ImageDimensionsAndQuantification
18  Initialize the member variables to their default values.
19 */
21 {
22  mp_ID = ap_ImageDimensionsAndQuantification;
23  m_noiseModelEnabled = false;
24 }
25 
26 
27 
28 
29 // =====================================================================
30 // ---------------------------------------------------------------------
31 // ---------------------------------------------------------------------
32 // =====================================================================
33 /*
34  \brief oComputeProjection destructor.
35 */
37 {
38 
39 }
40 
41 
42 
43 
44 // =====================================================================
45 // ---------------------------------------------------------------------
46 // ---------------------------------------------------------------------
47 // =====================================================================
48 /*
49  \fn DataUpdateStep
50  \param ap_DataFile : pointer to the datafile which manage output data writing
51  \param a2p_Line : pointer to the structure containing system matrix components
52  \param ap_Image : pointer to the image space to access forward/attenuation/projection images
53  \param ap_Event : pointer to the structure containing the event geometric indices
54  \param a_fr : frame index
55  \param a_rg : respiratory gate index
56  \param a_cg : cardiac gate index
57  \param a_timestamp : event timestamp
58  \param a_discardZeroEvent : if true, discard zero event
59  \brief Perform the forward-projection, and send the result to the datafile for writing.
60  Implements random Poisson noise.
61  Write the result in the projection image matrix (only implemented for SPECT).
62  \return 0 if success, positive value otherwise
63 */
65  oProjectionLine* a2p_Line,
66  oImageSpace* ap_Image,
67  vEvent* ap_Event,
68  int a_fr,
69  int a_rg,
70  int a_cg,
71  int th,
72  uint32_t a_timestamp,
73  bool a_discardZeroEvent )
74 {
75 
76  #ifdef CASTOR_VERBOSE
77  if(m_verbose >=4) Cout("oComputeProjection::DataUpdateStep ... " << endl);
78  #endif
79 
80  FLTNB fp = 0., fp_atn = 0.;
81 
82 
83  // Projection with attenuation for PET
84  //TODO : add fonction in oProjectionLine for PET attenuation ?
85  if(ap_DataFile->GetDataType() == TYPE_PET)
86  {
87  // Forward project input image
88  fp = a2p_Line->ForwardProject(ap_Image->m4p_forwardImage[a_fr][a_rg][a_cg]);
89 
90  // Forward project attenuation if atn map has been provided
91  if(ap_Image->m4p_attenuation != NULL && ap_Image->m4p_attenuation[a_fr][a_rg][a_cg] != NULL)
92  fp_atn = a2p_Line->ForwardProject(ap_Image->m4p_attenuation[a_fr][a_rg][a_cg]) * 0.1; // 0.1 -> convert in mm-1
93 
94  // Apply attenuation factor
95  fp *= std::exp(-fp_atn);
96 
97  // Store Atn correction factor in Event if PET (not recorded in datafile if no atn map provided by the user)
98  (dynamic_cast<iEventPET*>(ap_Event))->SetAttenuationCorrectionFactor(1/exp(-fp_atn));
99  }
100  // Project with attenuation for SPECT
101  else if (ap_DataFile->GetDataType() == TYPE_SPECT)
102  {
103  if(ap_Image->m4p_attenuation != NULL) // Attenuation has been provided
104  fp = a2p_Line->ForwardProjectWithSPECTAttenuation(ap_Image->m4p_attenuation[a_fr][a_rg][a_cg] ,
105  ap_Image->m4p_forwardImage[a_fr][a_rg][a_cg] );
106  else // No attenuation
107  fp = a2p_Line->ForwardProjectWithSPECTAttenuation(NULL ,
108  ap_Image->m4p_forwardImage[a_fr][a_rg][a_cg] );
109  }
110 
111  #ifdef CASTOR_VERBOSE
112  if(m_verbose >=4) Cout("oComputeProjection::DataUpdateStep : Projeted value = "<< fp << endl);
113  #endif
114 
115  // Poisson noise
117  {
118  fp = GetPoissonNoise(fp);
119 
120  #ifdef CASTOR_VERBOSE
121  if(m_verbose >=4) Cout("oComputeProjection::DataUpdateStep : Projeted value with Poisson noise = "<< fp << endl);
122  #endif
123  }
124 
125  // Write event in datafile
126  ap_Event->SetEventValue(0, fp);
127  ap_Event->SetTimeInMs(a_timestamp);
128 
129  // If we chose to discard zero event, write it only if fp > 0
130  if(!a_discardZeroEvent
131  || fp>0.)
132  {
133  if(ap_DataFile->WriteEvent(ap_Event, th) )
134  {
135  Cerr("*****oComputeProjection::DataUpdateStep()-> Error while trying to write a projeted event" << endl);
136  return 1;
137  }
138  }
139 
140  // Add the result to the projection image (only implemented for SPECT)
141  if(ap_DataFile->GetDataType() == TYPE_SPECT)
142  ap_Image->m2p_projectionImage[ap_Event->GetID1(0)][ap_Event->GetID2(0)] += fp;
143 
144  return 0;
145 }
146 
147 
148 
149 
150 // =====================================================================
151 // ---------------------------------------------------------------------
152 // ---------------------------------------------------------------------
153 // =====================================================================
154 /*
155  \fn InitNoiseModel
156  \param aNoiseModel : string corresponding to a noise model
157  \brief This is a premature implementation of noise model initialization
158  for analytic simulator.
159  Currently, only the Poisson noise can be selected.
160  It will be replaced with a more efficient system when a more
161  advanced analytical simulator will be implemented
162  \return 0 if success, positive value otherwise
163 */
164 int oComputeProjection::InitNoiseModel(string aNoiseModel)
165 {
166  if(m_verbose >=3) Cout("oComputeProjection::InitNoiseModel ... " << endl);
167 
168  // Get the noise model name in the options and isolate the real model's options
169 
170  // First check emptyness of the options
171  if (aNoiseModel=="")
172  {
173  m_noiseModelEnabled = false;
174  return 0;
175  }
176  else
177  {
178  string name_noise_model = "";
179  string list_options_noise_model = "";
180  string file_options_noise_model = "";
181 
182  // Search for a colon ":", this indicates that a configuration file is provided after the optimizer name
183  size_t colon = aNoiseModel.find_first_of(":");
184  size_t comma = aNoiseModel.find_first_of(",");
185 
186  // Case 1: we have a colon
187  if (colon!=string::npos)
188  {
189  // Get the optimizer name before the colon
190  name_noise_model = aNoiseModel.substr(0,colon);
191  // Get the configuration file after the colon
192  file_options_noise_model = aNoiseModel.substr(colon+1);
193  // List of options is empty
194  list_options_noise_model = "";
195  }
196  // Case 2: we have a comma
197  else if (comma!=string::npos)
198  {
199  // Get the optimizer name before the first comma
200  name_noise_model = aNoiseModel.substr(0,comma);
201  // Get the list of options after the first comma
202  list_options_noise_model = aNoiseModel.substr(comma+1);
203  // Configuration file is empty
204  file_options_noise_model = "";
205  }
206  // Case 3: no colon and no comma (a single model name)
207  else
208  {
209  // Get the optimizer name
210  name_noise_model = aNoiseModel;
211  // Configuration file is empty
212  file_options_noise_model = "";
213  // List of options is empty
214  list_options_noise_model = "";
215  }
216 
217  // Check if the noise model is known
218  if (aNoiseModel == "poisson")
219  {
220  m_noiseModelEnabled = true;
221  return 0;
222  }
223  else
224  {
225  Cerr("*****oComputeProjection::InitNoiseModel()-> Error while trying to initialize noise model. Model '"<<aNoiseModel<<"' is unknown"<<endl;);
226  return 1;
227  }
228  }
229 
230  return 0;
231 }
232 
233 
234 
235 
236 // =====================================================================
237 // ---------------------------------------------------------------------
238 // ---------------------------------------------------------------------
239 // =====================================================================
240 /*
241  \fn GetPoissonNoise
242  \param a_lambda : event rate
243  \brief Generate a Poisson random variable from a provided lambda
244  \details Depending on lambda, use either :
245  - Poisson noise if a_lambda <= 60
246  - Normal approximation otherwise
247  \return a random value
248  \todo Check Implementation
249  \todo Use C++11 Poisson noise distribution ?
250 */
251 //uint32_t oComputeProjection::GetPoissonNoise(int32_t a_lambda)
253 {
254  #ifdef CASTOR_VERBOSE
255  if(m_verbose >=4) Cout("oComputeProjection::GetPoissonNoise ... " << endl);
256  #endif
257 
258  // Get instance of Random Number Generator
260 
261  HPFLTNB return_value = 0.;
262 
263  // Normal approximation for projection with high value
264  if (a_lambda > 60.)
265  {
266  HPFLTNB rdm_1, rdm_2, w;
267 
268  do
269  {
270  rdm_1 = 2.0 * p_RNG->GenerateRdmNber() - 1.0;
271  rdm_2 = 2.0 * p_RNG->GenerateRdmNber() - 1.0;
272  w = rdm_1*rdm_1 + rdm_2*rdm_2;
273  } while (w > 1.0);
274 
275  w = sqrt( (-2.0 * log(w) ) / w );
276 
277  return_value = a_lambda + sqrt(a_lambda)*rdm_1*w;
278 
279  return_value = (return_value<0) ? 0 : round(return_value);
280  }
281  // Poisson noise
282  else
283  {
284  HPFLTNB L = exp(-(a_lambda));
285  int n = 0;
286  HPFLTNB p = 1;
287 
288  do
289  {
290  n++;
291  // Generate uniform random number u in [0,1] and let p ← p × u.
292  p *= p_RNG->GenerateRdmNber();
293  //cout << a_lambda << " ; " << L << " ; " << p << endl;
294  } while (p > L);
295 
296  return_value = n-1;
297  }
298 
299  return (uint32_t) return_value;
300 }
uint32_t GetID2(int a_line)
This class is designed to be a mother virtual class for DataFile.
static sRandomNumberGenerator * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
FLTNB ForwardProjectWithSPECTAttenuation(FLTNB *ap_attenuation, FLTNB *ap_image=NULL)
#define Cerr(MESSAGE)
virtual int WriteEvent(vEvent *ap_Event, int a_th=0)=0
int DataUpdateStep(vDataFile *ap_DataFile, oProjectionLine *a2p_Line, oImageSpace *ap_Image, vEvent *ap_Event, int a_fr, int a_rg, int a_cg, int th, uint32_t a_timestamp, bool a_discardZeroEvent)
virtual void SetEventValue(int a_bin, FLTNBDATA a_value)=0
HPFLTNB GenerateRdmNber()
Generate a random number for the thread which index is recovered from the OpenMP function.
oComputeProjection(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
oComputeProjection constructor.
Singleton class that generate a thread-safe random generator number for openMP As singleton...
Inherit from vEvent. Main PET class for the Event objects.
This class is designed to manage and store system matrix elements associated to a vEvent...
void SetTimeInMs(uint32_t a_value)
This class holds all the matrices in the image domain that can be used in the algorithm: image...
Mother class for the Event objects.
FLTNB ForwardProject(FLTNB *ap_image=NULL)
This class is designed to manage all dimensions and quantification related stuff. ...
uint32_t GetID1(int a_line)
~oComputeProjection()
oComputeProjection destructor.
#define Cout(MESSAGE)