CASToR  1.1
Tomographic Reconstruction (PET/SPECT)
 All Classes Files Functions Variables Typedefs Macros Groups Pages
iProjectorIncrementalSiddon.cc
Go to the documentation of this file.
1 
2 /*
3  Implementation of class iProjectorIncrementalSiddon
4 
5  - separators: done
6  - doxygen: done
7  - default initialization: done
8  - CASTOR_DEBUG:
9  - CASTOR_VERBOSE:
10 */
11 
19 #include "sOutputManager.hh"
20 
21 // =====================================================================
22 // ---------------------------------------------------------------------
23 // ---------------------------------------------------------------------
24 // =====================================================================
25 
27 {
28  // This projector is compatible with SPECT attenuation correction because
29  // all voxels contributing to a given line are ordered from point 1 (focal)
30  // to point 2 (scanner). Note that there can be some aliasing problem with
31  // this incremental method, but which will depend on the voxel size and number.
32  // However, these problems are quite negligible and pretty uncommon. So we still
33  // say that this projector is compatible.
35 }
36 
37 // =====================================================================
38 // ---------------------------------------------------------------------
39 // ---------------------------------------------------------------------
40 // =====================================================================
41 
43 {
44 }
45 
46 // =====================================================================
47 // ---------------------------------------------------------------------
48 // ---------------------------------------------------------------------
49 // =====================================================================
50 
51 int iProjectorIncrementalSiddon::ReadConfigurationFile(const string& a_configurationFile)
52 {
53  // No options for incremental siddon
54  ;
55  // Normal end
56  return 0;
57 }
58 
59 // =====================================================================
60 // ---------------------------------------------------------------------
61 // ---------------------------------------------------------------------
62 // =====================================================================
63 
64 int iProjectorIncrementalSiddon::ReadOptionsList(const string& a_optionsList)
65 {
66  // No options for incremental siddon
67  ;
68  // Normal end
69  return 0;
70 }
71 
72 // =====================================================================
73 // ---------------------------------------------------------------------
74 // ---------------------------------------------------------------------
75 // =====================================================================
76 
78 {
79  cout << "This projector is a simple line projector that computes the exact path length of a line through the voxels." << endl;
80  cout << "It is basically an incremental version of the original algorithm proposed by R. L. Siddon (see the classicSiddon projector)." << endl;
81  cout << "It is implemented from the following published paper:" << endl;
82  cout << "F. Jacobs et al, \"A fast algorithm to calculate the exact radiological path through a pixel or voxel space\", J. Comput. Inf. Technol., vol. 6, pp. 89-94, 1998." << endl;
83 }
84 
85 // =====================================================================
86 // ---------------------------------------------------------------------
87 // ---------------------------------------------------------------------
88 // =====================================================================
89 
91 {
92  // Nothing to check for this projector
93  ;
94  // Normal end
95  return 0;
96 }
97 
98 // =====================================================================
99 // ---------------------------------------------------------------------
100 // ---------------------------------------------------------------------
101 // =====================================================================
102 
104 {
105  // Verbose
106  if (m_verbose>=2) Cout("iProjectorIncrementalSiddon::InitializeSpecific() -> Use incremental Siddon projector" << endl);
107  // Normal end
108  return 0;
109 }
110 
111 // =====================================================================
112 // ---------------------------------------------------------------------
113 // ---------------------------------------------------------------------
114 // =====================================================================
115 
117 {
118  // Find the maximum number of voxels along a given dimension
119  INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
120  if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
121  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
122  // We should have at most 4 voxels in a given plane, so multiply by 4
123  // (note: this is not true however it ensures no overflow and is already quite optimized for RAM usage !)
124  max_nb_voxels_in_dimension *= 4;
125  // Return the value
126  return max_nb_voxels_in_dimension;
127 }
128 
129 // =====================================================================
130 // ---------------------------------------------------------------------
131 // ---------------------------------------------------------------------
132 // =====================================================================
133 
134 int iProjectorIncrementalSiddon::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
135 {
136  #ifdef CASTOR_DEBUG
137  if (!m_initialized)
138  {
139  Cerr("***** iProjectorIncrementalSiddon::ProjectWithoutTOF() -> Called while not initialized !" << endl);
140  Exit(EXIT_DEBUG);
141  }
142  #endif
143 
144  #ifdef CASTOR_VERBOSE
145  if (m_verbose>=10)
146  {
147  string direction = "";
148  if (a_direction==FORWARD) direction = "forward";
149  else direction = "backward";
150  Cout("iProjectorIncrementalSiddon::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
151  }
152  #endif
153 
154  // Get event positions
155  FLTNB* event1Float = ap_ProjectionLine->GetPosition1();
156  FLTNB* event2Float = ap_ProjectionLine->GetPosition2();
157  double event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
158  double event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
159 
160  // **************************************
161  // STEP 1: LOR length calculation
162  // **************************************
163  double length_LOR = ap_ProjectionLine->GetLength();
164 
165  // **************************************
166  // STEP 2: Compute entrance voxel indexes
167  // **************************************
168  double alphaFirst[] = { 0.0, 0.0, 0.0 };
169  double alphaLast[] = { 0.0, 0.0, 0.0 };
170 
171  double alphaMin = 0.0f, alphaMax = 1.0f;
172  double delta_pos[] = {
173  event2[ 0 ] - event1[ 0 ],
174  event2[ 1 ] - event1[ 1 ],
175  event2[ 2 ] - event1[ 2 ]
176  };
177 
178  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
179  for( int i = 0; i < 3; ++i )
180  {
181  if( delta_pos[ i ] != 0.0 )
182  {
183  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
184  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
185  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
186  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
187  }
188  }
189 
190  // if alphaMax is less than or equal to alphaMin no intersection
191  // and return an empty buffer
192  if( alphaMax <= alphaMin ) return 0;
193 
194  // Now we have to find the indices of the particular plane
195  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
196  int iMin = 0, iMax = 0;
197  int jMin = 0, jMax = 0;
198  int kMin = 0, kMax = 0;
199 
200  // For the X-axis
201  if( delta_pos[ 0 ] > 0.0f )
202  {
203  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
204  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
205  }
206  else if( delta_pos[ 0 ] < 0.0f )
207  {
208  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
209  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
210  }
211  if( delta_pos[ 0 ] == 0 )
212  {
213  iMin = 1, iMax = 0;
214  }
215 
216  // For the Y-axis
217  if( delta_pos[ 1 ] > 0 )
218  {
219  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
220  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
221  }
222  else if( delta_pos[ 1 ] < 0 )
223  {
224  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
225  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
226  }
227  else if( delta_pos[ 1 ] == 0 )
228  {
229  jMin = 1, jMax = 0;
230  }
231 
232  // For the Z-axis
233  if( delta_pos[ 2 ] > 0 )
234  {
235  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
236  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
237  }
238  else if( delta_pos[ 2 ] < 0 )
239  {
240  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
241  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
242  }
243  else if( delta_pos[ 2 ] == 0 )
244  {
245  kMin = 1, kMax = 0;
246  }
247 
248  // Computing the last term n number of intersection
249  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
250  + ( kMax - kMin + 1 );
251 
252  // Array storing the first alpha in X, Y and Z
253  double alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
254 
255  // Computing the first alpha in X
256  if( delta_pos[ 0 ] > 0 )
257  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
258  else if( delta_pos[ 0 ] < 0 )
259  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
260 
261  // Computing the first alpha in Y
262  if( delta_pos[ 1 ] > 0 )
263  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
264  else if( delta_pos[ 1 ] < 0 )
265  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
266 
267  // Computing the first alpha in Z
268  if( delta_pos[ 2 ] > 0 )
269  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
270  else if( delta_pos[ 2 ] < 0 )
271  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
272 
273  // Computing the alpha updating
274  double alpha_u[ 3 ] = {
275  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
276  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
277  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
278  };
279 
280  // Computing the index updating
281  INTNB index_u[ 3 ] = {
282  (delta_pos[ 0 ] < 0) ? -1 : 1,
283  (delta_pos[ 1 ] < 0) ? -1 : 1,
284  (delta_pos[ 2 ] < 0) ? -1 : 1
285  };
286 
287  // Check which alpha is the min/max and increment
288  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
289  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
290  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
291 
292  // Computing the minimum value in the alpha_XYZ buffer
293  double const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ],
294  (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
295 
296  // Computing the first index of intersection
297  double const alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
298  INTNB index_ijk[ 3 ] = {
299  1 + (int)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
300  1 + (int)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
301  1 + (int)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
302  };
303 
304  INTNB const w = mp_nbVox[ 0 ];
305  INTNB const wh = w * mp_nbVox[ 1 ];
306 
307  // Loop over the number of plane to cross
308  double alpha_c = alphaMin;
309  FLTNB coeff = 0.0f;
310  INTNB numVox = 0;
311  for( int nP = 0; nP < n - 1; ++nP )
312  {
313  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
314  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
315  {
316  // Storing values
317  if( ( alpha_XYZ[ 0 ] >= alphaMin )
318  && ( index_ijk[ 0 ] - 1 >= 0 )
319  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
320  && ( index_ijk[ 1 ] - 1 >= 0 )
321  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
322  && ( index_ijk[ 2 ] - 1 >= 0 )
323  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
324  {
325  coeff = ( alpha_XYZ[ 0 ] - alpha_c ) * length_LOR;
326  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
327  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
328  }
329 
330  // Increment values
331  alpha_c = alpha_XYZ[ 0 ];
332  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
333  index_ijk[ 0 ] += index_u[ 0 ];
334  }
335  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
336  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
337  {
338  // Storing values
339  if( ( alpha_XYZ[ 1 ] >= alphaMin )
340  && ( index_ijk[ 0 ] - 1 >= 0 )
341  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
342  && ( index_ijk[ 1 ] - 1 >= 0 )
343  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
344  && ( index_ijk[ 2 ] - 1 >= 0 )
345  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
346  {
347  coeff = ( alpha_XYZ[ 1 ] - alpha_c ) * length_LOR;
348  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
349  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
350  }
351 
352  // Increment values
353  alpha_c = alpha_XYZ[ 1 ];
354  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
355  index_ijk[ 1 ] += index_u[ 1 ];
356  }
357  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
358  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
359  {
360  // Storing values
361  if( ( alpha_XYZ[ 2 ] >= alphaMin )
362  && ( index_ijk[ 0 ] - 1 >= 0 )
363  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
364  && ( index_ijk[ 1 ] - 1 >= 0 )
365  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
366  && ( index_ijk[ 2 ] - 1 >= 0 )
367  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
368  {
369  coeff = ( alpha_XYZ[ 2 ] - alpha_c ) * length_LOR;
370  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
371  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
372  }
373 
374  // Increment values
375  alpha_c = alpha_XYZ[ 2 ];
376  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
377  index_ijk[ 2 ] += index_u[ 2 ];
378  }
379  }
380 
381  return 0;
382 }
383 
384 // =====================================================================
385 // ---------------------------------------------------------------------
386 // ---------------------------------------------------------------------
387 // =====================================================================
388 
389 int iProjectorIncrementalSiddon::ProjectWithTOFPos(int a_direction, oProjectionLine* ap_ProjectionLine)
390 {
391  #ifdef CASTOR_DEBUG
392  if (!m_initialized)
393  {
394  Cerr("***** iProjectorIncrementalSiddon::ProjectWithTOFPos() -> Called while not initialized !" << endl);
395  Exit(EXIT_DEBUG);
396  }
397  #endif
398 
399  #ifdef CASTOR_VERBOSE
400  if (m_verbose>=10)
401  {
402  string direction = "";
403  if (a_direction==FORWARD) direction = "forward";
404  else direction = "backward";
405  Cout("iProjectorIncrementalSiddon::Project with TOF position -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
406  }
407  #endif
408 
409  // Simpler way now, hopefully it works
410  FLTNB* event1Float = ap_ProjectionLine->GetPosition1();
411  FLTNB* event2Float = ap_ProjectionLine->GetPosition2();
412  double event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
413  double event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
414 
415  // **************************************
416  // STEP 1: LOR length calculation
417  // **************************************
418  double length_LOR = ap_ProjectionLine->GetLength();
419 
420  // **************************************
421  // STEP 2: Compute entrance voxel indexes
422  // **************************************
423  double alphaFirst[] = { 0.0, 0.0, 0.0 };
424  double alphaLast[] = { 0.0, 0.0, 0.0 };
425 
426  double alphaMin = 0.0f, alphaMax = 1.0f;
427  double delta_pos[] = {
428  event2[ 0 ] - event1[ 0 ],
429  event2[ 1 ] - event1[ 1 ],
430  event2[ 2 ] - event1[ 2 ]
431  };
432 
433  // Get TOF info
434  double tof_resolution = ap_ProjectionLine->GetTOFResolution();
435  double tof_measurement = ap_ProjectionLine->GetTOFMeasurement();
436 
437  // TOF standard deviation and truncation
438  double tof_sigma = tof_resolution * SPEED_OF_LIGHT * 0.5 / TWO_SQRT_TWO_LN_2;
439  double tof_half_span = tof_sigma * m_TOFnbSigmas;
440 
441  // convert delta time into delta length
442  double tof_deltaL = tof_measurement * SPEED_OF_LIGHT * 0.5;
443 
444  // special case for aberrant TOF measurements which belong to scattered or random counts
445  // return an empty line because we know that the count does not depict the emitting object that we want to reconstruct
446  if ( fabs(tof_deltaL) > length_LOR * 0.5)
447  {
448  return 0;
449  }
450 
451  // distance between the first event1 and the center of the Gaussian distribution along the LOR
452  double lor_tof_center = length_LOR * 0.5 + tof_deltaL;
453 
454  // coefficient for conversion from erf use to integral of actual TOF function (Gaussian with maximum=1)
455  double tof_norm_coef = tof_sigma * sqrt(2*M_PI);
456 
457  // index along each axis of the first voxel falling inside the truncated Gaussian distribution
458  double tof_edge_low[] = {0,0,0};
459  // index along each axis of the last voxel falling inside the truncated Gaussian distribution
460  double tof_edge_high[] = {0,0,0};
461  double tof_center;
462  INTNB tof_index;
463 
464  // low/high voxel edges (in absolute coordinates) for truncated TOF
465  for (int ax=0;ax<3;ax++)
466  {
467  // absolute coordinate along each axis of the center of the TOF distribution
468  tof_center = event1[ax] + lor_tof_center * delta_pos[ax] / length_LOR;
469 
470  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
471  tof_edge_low[ax] = tof_center - tof_half_span * fabs(delta_pos[ax]) / length_LOR;
472  tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), 0);
473  // if low TOF edge above the highest FOV edge, return empty line
474  if (tof_index>mp_nbVox[ax]-1) return 0;
475  tof_edge_low[ax] = (double)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
476 
477  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
478  tof_edge_high[ax] = tof_center + tof_half_span * fabs(delta_pos[ax]) / length_LOR;
479  tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
480  // if high TOF edge below the lowest FOV edge, return empty line
481  if (tof_index<0) return 0;
482  tof_edge_high[ax] = (double)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
483  }
484 
485  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
486  for( int i = 0; i < 3; ++i )
487  {
488  if( delta_pos[ i ] != 0.0 )
489  {
490  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
491  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
492  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
493  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
494  }
495  }
496 
497  // if alphaMax is less than or equal to alphaMin no intersection
498  // and return an empty buffer
499  if( alphaMax <= alphaMin ) return 0;
500 
501  // Now we have to find the indices of the particular plane
502  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
503  int iMin = 0, iMax = 0;
504  int jMin = 0, jMax = 0;
505  int kMin = 0, kMax = 0;
506 
507  // For the X-axis
508  if( delta_pos[ 0 ] > 0.0f )
509  {
510  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
511  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
512  }
513  else if( delta_pos[ 0 ] < 0.0f )
514  {
515  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
516  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
517  }
518  if( delta_pos[ 0 ] == 0 )
519  {
520  iMin = 1, iMax = 0;
521  }
522 
523  // For the Y-axis
524  if( delta_pos[ 1 ] > 0 )
525  {
526  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
527  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
528  }
529  else if( delta_pos[ 1 ] < 0 )
530  {
531  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
532  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
533  }
534  else if( delta_pos[ 1 ] == 0 )
535  {
536  jMin = 1, jMax = 0;
537  }
538 
539  // For the Z-axis
540  if( delta_pos[ 2 ] > 0 )
541  {
542  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
543  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
544  }
545  else if( delta_pos[ 2 ] < 0 )
546  {
547  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
548  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
549  }
550  else if( delta_pos[ 2 ] == 0 )
551  {
552  kMin = 1, kMax = 0;
553  }
554 
555  // Computing the last term n number of intersection
556  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
557  + ( kMax - kMin + 1 );
558 
559  // Array storing the first alpha in X, Y and Z
560  double alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
561 
562  // Computing the first alpha in X
563  if( delta_pos[ 0 ] > 0 )
564  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
565  else if( delta_pos[ 0 ] < 0 )
566  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
567 
568  // Computing the first alpha in Y
569  if( delta_pos[ 1 ] > 0 )
570  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
571  else if( delta_pos[ 1 ] < 0 )
572  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
573 
574  // Computing the first alpha in Z
575  if( delta_pos[ 2 ] > 0 )
576  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
577  else if( delta_pos[ 2 ] < 0 )
578  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
579 
580  // Computing the alpha updating
581  double alpha_u[ 3 ] = {
582  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
583  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
584  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
585  };
586 
587  // Computing the index updating
588  INTNB index_u[ 3 ] = {
589  (delta_pos[ 0 ] < 0) ? -1 : 1,
590  (delta_pos[ 1 ] < 0) ? -1 : 1,
591  (delta_pos[ 2 ] < 0) ? -1 : 1
592  };
593 
594  // Check which alpha is the min/max and increment
595  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
596  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
597  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
598 
599  // Computing the minimum value in the alpha_XYZ buffer
600  double const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ], (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
601 
602  // Computing the first index of intersection
603  double const alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
604  INTNB index_ijk[ 3 ] = {
605  1 + (INTNB)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
606  1 + (INTNB)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
607  1 + (INTNB)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
608  };
609 
610  INTNB const w = mp_nbVox[ 0 ];
611  INTNB const wh = w * mp_nbVox[ 1 ];
612 
613  // tof : erf of first voxel edge - Gaussian center
614  double previous_edge_erf = erf( (length_LOR * alphaMin - lor_tof_center)/ (sqrt(2.)*tof_sigma) );
615  double next_edge_erf;
616 
617  // Loop over the number of plane to cross
618  FLTNB coeff = 0.0f;
619  INTNB numVox = 0;
620  for( int nP = 0; nP < n - 1; ++nP )
621  {
622  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
623  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
624  {
625  // Storing values
626  if( ( alpha_XYZ[ 0 ] >= alphaMin )
627  && ( index_ijk[ 0 ] - 1 >= 0 )
628  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
629  && ( index_ijk[ 1 ] - 1 >= 0 )
630  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
631  && ( index_ijk[ 2 ] - 1 >= 0 )
632  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
633  {
634  // tof : erf of next voxel edge - Gaussian center
635  next_edge_erf = erf( (length_LOR * alpha_XYZ[ 0 ] - lor_tof_center) / (sqrt(2.)*tof_sigma) );
636  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
637  coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) * tof_norm_coef ;
638  // keeping record of the previous edge, so as to save 1 erf computation
639  previous_edge_erf = next_edge_erf;
640 
641  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
642  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, numVox, coeff);
643  }
644 
645  // Increment values
646  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
647  index_ijk[ 0 ] += index_u[ 0 ];
648  }
649  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
650  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
651  {
652  // Storing values
653  if( ( alpha_XYZ[ 1 ] >= alphaMin )
654  && ( index_ijk[ 0 ] - 1 >= 0 )
655  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
656  && ( index_ijk[ 1 ] - 1 >= 0 )
657  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
658  && ( index_ijk[ 2 ] - 1 >= 0 )
659  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
660  {
661  // tof : erf of next voxel edge - Gaussian center
662  next_edge_erf = erf( (length_LOR * alpha_XYZ[ 1 ] - lor_tof_center) / (sqrt(2.)*tof_sigma) );
663  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
664  coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) * tof_norm_coef ;
665  // keeping record of the previous edge, so as to save 1 erf computation
666  previous_edge_erf = next_edge_erf;
667 
668  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
669  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, numVox, coeff);
670  }
671 
672  // Increment values
673  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
674  index_ijk[ 1 ] += index_u[ 1 ];
675  }
676  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
677  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
678  {
679  // Storing values
680  if( ( alpha_XYZ[ 2 ] >= alphaMin )
681  && ( index_ijk[ 0 ] - 1 >= 0 )
682  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
683  && ( index_ijk[ 1 ] - 1 >= 0 )
684  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
685  && ( index_ijk[ 2 ] - 1 >= 0 )
686  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
687  {
688  // tof : erf of next voxel edge - Gaussian center
689  next_edge_erf = erf( (length_LOR * alpha_XYZ[ 2 ] - lor_tof_center) / (sqrt(2.)*tof_sigma) );
690  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
691  coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) * tof_norm_coef ;
692  // keeping record of the previous edge, so as to save 1 erf computation
693  previous_edge_erf = next_edge_erf;
694 
695  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
696  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, numVox, coeff);
697  }
698 
699  // Increment values
700  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
701  index_ijk[ 2 ] += index_u[ 2 ];
702  }
703  }
704 
705  return 0;
706 }
707 
708 // =====================================================================
709 // ---------------------------------------------------------------------
710 // ---------------------------------------------------------------------
711 // =====================================================================
712 
713 int iProjectorIncrementalSiddon::ProjectWithTOFBin(int a_direction, oProjectionLine* ap_ProjectionLine)
714 {
715  Cerr("***** iProjectorIncrementalSiddon::ProjectWithTOFBin() -> Not yet implemented !" << endl);
716  return 1;
717 }
718 
719 // =====================================================================
720 // ---------------------------------------------------------------------
721 // ---------------------------------------------------------------------
722 // =====================================================================
bool m_compatibleWithSPECTAttenuationCorrection
Definition: vProjector.hh:317
FLTNB mp_sizeVox[3]
Definition: vProjector.hh:301
~iProjectorIncrementalSiddon()
The destructor of iProjectorIncrementalSiddon.
INTNB mp_nbVox[3]
Definition: vProjector.hh:302
#define FLTNB
Definition: gVariables.hh:55
#define TWO_SQRT_TWO_LN_2
Definition: gVariables.hh:72
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Definition: vProjector.hh:307
void ShowHelpSpecific()
A function used to show help about the child module.
This class is designed to generically described any on-the-fly projector.
Definition: vProjector.hh:54
FLTNB GetTOFMeasurement()
This function is used to get the TOF measurement.
Declaration of class iProjectorIncrementalSiddon.
FLTNB GetLength()
This function is used to get the length of the line.
void Exit(int code)
FLTNB GetTOFResolution()
This function is used to get the TOF resolution.
#define Cerr(MESSAGE)
FLTNB * GetPosition1()
This function is used to get the pointer to the mp_position1 (3-values tab).
bool m_initialized
Definition: vProjector.hh:323
void AddVoxel(int a_direction, INTNB a_voxelIndice, FLTNB a_voxelWeight)
This function is used to add a voxel contribution to the line, assuming TOF bin 0 (i...
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
int ProjectWithTOFPos(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF continuous information.
iProjectorIncrementalSiddon()
The constructor of iProjectorIncrementalSiddon.
INTNB EstimateMaxNumberOfVoxelsPerLine()
This function is used to compute and provide an estimate of the maximum number of voxels that could c...
int InitializeSpecific()
This function is used to initialize specific stuff to the child projector.
FLTNB m_TOFnbSigmas
Definition: vProjector.hh:312
#define INTNB
Definition: gVariables.hh:64
This class is designed to manage and store system matrix elements associated to a vEvent...
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project without TOF.
Declaration of class sOutputManager.
int ProjectWithTOFBin(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF binned information.
FLTNB mp_halfFOV[3]
Definition: vProjector.hh:304
#define EXIT_DEBUG
Definition: gVariables.hh:69
#define SPEED_OF_LIGHT
Definition: gVariables.hh:75
FLTNB * GetPosition2()
This function is used to get the pointer to the mp_position2 (3-values tab).
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
#define FORWARD
#define Cout(MESSAGE)
void AddVoxelInTOFBin(int a_direction, int a_TOFBin, INTNB a_voxelIndice, FLTNB a_voxelWeight)
This function is used to add a voxel contribution to the line and provided TOF bin.
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child projector.
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.