CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/projector/iProjectorIncrementalSiddon.cc
Go to the documentation of this file.
1 
8 #include "iProjectorIncrementalSiddon.hh"
9 #include "sOutputManager.hh"
10 #ifdef _WIN32
11 // Avoid compilation errors due to mix up between std::min()/max() and
12 // min max macros
13 #undef min
14 #undef max
15 #endif
16 
17 // =====================================================================
18 // ---------------------------------------------------------------------
19 // ---------------------------------------------------------------------
20 // =====================================================================
21 
23 {
24  // This projector is compatible with SPECT attenuation correction because
25  // all voxels contributing to a given line are ordered from point 1 (focal)
26  // to point 2 (scanner). Note that there can be some aliasing problem with
27  // this incremental method, but which will depend on the voxel size and number.
28  // However, these problems are quite negligible and pretty uncommon. So we still
29  // say that this projector is compatible.
31  // This projector is compatible with compression as it works only with the
32  // cartesian coordinates of 2 end points of a line
34 }
35 
36 // =====================================================================
37 // ---------------------------------------------------------------------
38 // ---------------------------------------------------------------------
39 // =====================================================================
40 
42 {
43 }
44 
45 // =====================================================================
46 // ---------------------------------------------------------------------
47 // ---------------------------------------------------------------------
48 // =====================================================================
49 
50 int iProjectorIncrementalSiddon::ReadConfigurationFile(const string& a_configurationFile)
51 {
52  // No options for incremental siddon
53  ;
54  // Normal end
55  return 0;
56 }
57 
58 // =====================================================================
59 // ---------------------------------------------------------------------
60 // ---------------------------------------------------------------------
61 // =====================================================================
62 
63 int iProjectorIncrementalSiddon::ReadOptionsList(const string& a_optionsList)
64 {
65  // No options for incremental siddon
66  ;
67  // Normal end
68  return 0;
69 }
70 
71 // =====================================================================
72 // ---------------------------------------------------------------------
73 // ---------------------------------------------------------------------
74 // =====================================================================
75 
77 {
78  cout << "This projector is a simple line projector that computes the exact path length of a line through the voxels." << endl;
79  cout << "It is basically an incremental version of the original algorithm proposed by R. L. Siddon (see the classicSiddon projector)." << endl;
80  cout << "It is implemented from the following published paper:" << endl;
81  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;
82 }
83 
84 // =====================================================================
85 // ---------------------------------------------------------------------
86 // ---------------------------------------------------------------------
87 // =====================================================================
88 
90 {
91  // Nothing to check for this projector
92  ;
93  // Normal end
94  return 0;
95 }
96 
97 // =====================================================================
98 // ---------------------------------------------------------------------
99 // ---------------------------------------------------------------------
100 // =====================================================================
101 
103 {
104  // Verbose
105  if (m_verbose>=2) Cout("iProjectorIncrementalSiddon::InitializeSpecific() -> Use incremental Siddon projector" << endl);
106  // Normal end
107  return 0;
108 }
109 
110 // =====================================================================
111 // ---------------------------------------------------------------------
112 // ---------------------------------------------------------------------
113 // =====================================================================
114 
116 {
117  // Find the maximum number of voxels along a given dimension
118  INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
119  if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
120  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
121  // We should have at most 4 voxels in a given plane, so multiply by 4
122  // (note: this is not true however it ensures no overflow and is already quite optimized for RAM usage !)
123  max_nb_voxels_in_dimension *= 4;
124  // Return the value
125  return max_nb_voxels_in_dimension;
126 }
127 
128 // =====================================================================
129 // ---------------------------------------------------------------------
130 // ---------------------------------------------------------------------
131 // =====================================================================
132 
133 int iProjectorIncrementalSiddon::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
134 {
135  #ifdef CASTOR_DEBUG
136  if (!m_initialized)
137  {
138  Cerr("***** iProjectorIncrementalSiddon::ProjectWithoutTOF() -> Called while not initialized !" << endl);
139  Exit(EXIT_DEBUG);
140  }
141  #endif
142 
143  #ifdef CASTOR_VERBOSE
144  if (m_verbose>=10)
145  {
146  string direction = "";
147  if (a_direction==FORWARD) direction = "forward";
148  else direction = "backward";
149  Cout("iProjectorIncrementalSiddon::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
150  }
151  #endif
152 
153  // Get event positions
154  FLTNB* event1Float = ap_ProjectionLine->GetPosition1();
155  FLTNB* event2Float = ap_ProjectionLine->GetPosition2();
156  HPFLTNB event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
157  HPFLTNB event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
158 
159  // **************************************
160  // STEP 1: LOR length calculation
161  // **************************************
162  HPFLTNB length_LOR = ap_ProjectionLine->GetLength();
163 
164  // **************************************
165  // STEP 2: Compute entrance voxel indexes
166  // **************************************
167  HPFLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
168  HPFLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
169 
170  HPFLTNB alphaMin = 0.0f, alphaMax = 1.0f;
171  HPFLTNB delta_pos[] = {
172  event2[ 0 ] - event1[ 0 ],
173  event2[ 1 ] - event1[ 1 ],
174  event2[ 2 ] - event1[ 2 ]
175  };
176 
177  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
178  for( int i = 0; i < 3; ++i )
179  {
180  if( delta_pos[ i ] != 0.0 )
181  {
182  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
183  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
184  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
185  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
186  }
187  }
188 
189  // if alphaMax is less than or equal to alphaMin no intersection
190  // and return an empty buffer
191  if( alphaMax <= alphaMin ) return 0;
192 
193  // Now we have to find the indices of the particular plane
194  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
195  int iMin = 0, iMax = 0;
196  int jMin = 0, jMax = 0;
197  int kMin = 0, kMax = 0;
198 
199  // For the X-axis
200  if( delta_pos[ 0 ] > 0.0f )
201  {
202  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
203  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
204  }
205  else if( delta_pos[ 0 ] < 0.0f )
206  {
207  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
208  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
209  }
210  if( delta_pos[ 0 ] == 0 )
211  {
212  iMin = 1, iMax = 0;
213  }
214 
215  // For the Y-axis
216  if( delta_pos[ 1 ] > 0 )
217  {
218  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
219  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
220  }
221  else if( delta_pos[ 1 ] < 0 )
222  {
223  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
224  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
225  }
226  else if( delta_pos[ 1 ] == 0 )
227  {
228  jMin = 1, jMax = 0;
229  }
230 
231  // For the Z-axis
232  if( delta_pos[ 2 ] > 0 )
233  {
234  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
235  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
236  }
237  else if( delta_pos[ 2 ] < 0 )
238  {
239  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
240  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
241  }
242  else if( delta_pos[ 2 ] == 0 )
243  {
244  kMin = 1, kMax = 0;
245  }
246 
247  // Computing the last term n number of intersection
248  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
249  + ( kMax - kMin + 1 );
250 
251  // Array storing the first alpha in X, Y and Z
252  HPFLTNB alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
253 
254  // Computing the first alpha in X
255  if( delta_pos[ 0 ] > 0 )
256  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
257  else if( delta_pos[ 0 ] < 0 )
258  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
259 
260  // Computing the first alpha in Y
261  if( delta_pos[ 1 ] > 0 )
262  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
263  else if( delta_pos[ 1 ] < 0 )
264  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
265 
266  // Computing the first alpha in Z
267  if( delta_pos[ 2 ] > 0 )
268  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
269  else if( delta_pos[ 2 ] < 0 )
270  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
271 
272  // Computing the alpha updating
273  HPFLTNB alpha_u[ 3 ] = {
274  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
275  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
276  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
277  };
278 
279  // Computing the index updating
280  INTNB index_u[ 3 ] = {
281  (delta_pos[ 0 ] < 0) ? -1 : 1,
282  (delta_pos[ 1 ] < 0) ? -1 : 1,
283  (delta_pos[ 2 ] < 0) ? -1 : 1
284  };
285 
286  // Check which alpha is the min/max and increment
287  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
288  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
289  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
290 
291  // Computing the minimum value in the alpha_XYZ buffer
292  HPFLTNB const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ],
293  (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
294 
295  // Computing the first index of intersection
296  HPFLTNB alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
297  INTNB index_ijk[ 3 ] = {
298  1 + (int)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
299  1 + (int)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
300  1 + (int)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
301  };
302 
303  INTNB const w = mp_nbVox[ 0 ];
304  INTNB const wh = w * mp_nbVox[ 1 ];
305 
306  // Loop over the number of plane to cross
307  HPFLTNB alpha_c = alphaMin;
308  FLTNB coeff = 0.0f;
309  INTNB numVox = 0;
310 
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  // if this voxel is masked, do nothing
328  if (!mp_ImageDimensionsAndQuantification->IsVoxelMasked(numVox)) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
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  // if this voxel is masked, do nothing
350  if (!mp_ImageDimensionsAndQuantification->IsVoxelMasked(numVox)) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
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  // if this voxel is masked, do nothing
372  if (!mp_ImageDimensionsAndQuantification->IsVoxelMasked(numVox)) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
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::ProjectTOFListmode(int a_direction, oProjectionLine* ap_ProjectionLine)
390 {
391  #ifdef CASTOR_DEBUG
392  if (!m_initialized)
393  {
394  Cerr("***** iProjectorIncrementalSiddon::ProjectTOFListmode() -> 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  HPFLTNB event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
413  HPFLTNB event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
414 
415  // **************************************
416  // STEP 1: LOR length calculation
417  // **************************************
418  HPFLTNB length_LOR = ap_ProjectionLine->GetLength();
419 
420  // **************************************
421  // STEP 2: Compute entrance voxel indexes
422  // **************************************
423  HPFLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
424  HPFLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
425 
426  HPFLTNB alphaMin = 0.0f, alphaMax = 1.0f;
427  HPFLTNB delta_pos[] = {
428  event2[ 0 ] - event1[ 0 ],
429  event2[ 1 ] - event1[ 1 ],
430  event2[ 2 ] - event1[ 2 ]
431  };
432 
433  // Get TOF info
434  HPFLTNB tof_measurement = ap_ProjectionLine->GetTOFMeasurementInPs();
435 
436  // convert delta time into delta length
437  HPFLTNB tof_delta = tof_measurement * SPEED_OF_LIGHT_IN_MM_PER_PS * 0.5;
438 
439  // TOF Gaussian standard deviation and truncation
441  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
442  HPFLTNB tof_half_span = 0.;
443 
445  else if (m_TOFBinProperProcessingFlag) tof_half_span = tof_sigma * m_TOFNbSigmas + 0.5 * m_TOFBinSizeInMm;
446  else tof_half_span = tof_sigma * m_TOFNbSigmas;
447 
448  // distance between the first event1 and the TOF measurement
449  HPFLTNB lor_tof_center = length_LOR * 0.5 + tof_delta;
450 
451  // index along each axis of the first voxel falling inside the truncated TOF distribution centered at the TOF measurement
452  HPFLTNB tof_edge_low[] = {0,0,0};
453  // index along each axis of the last voxel falling inside the truncated TOF distribution centered at the TOF measurement
454  HPFLTNB tof_edge_high[] = {0,0,0};
455  HPFLTNB tof_center;
456  INTNB tof_index_low[] = {0,0,0};
457  INTNB tof_index_high[] = {0,0,0};
458 
459  // low/high voxel edges (in absolute coordinates) for truncated TOF
460  for (int ax=0;ax<3;ax++)
461  {
462  // absolute coordinate along each axis of the center of the TOF distribution
463  tof_center = event1[ax] + lor_tof_center * delta_pos[ax] / length_LOR;
464 
465  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
466  tof_edge_low[ax] = tof_center - tof_half_span * fabs(delta_pos[ax]) / length_LOR;
467  tof_index_low[ax] = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
468  // if low TOF edge above the highest FOV edge, return empty line
469  if (tof_index_low[ax]>mp_nbVox[ax]-1) return 0;
470  tof_edge_low[ax] = (HPFLTNB)tof_index_low[ax] * mp_sizeVox[ax] - mp_halfFOV[ax];
471 
472  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
473  tof_edge_high[ax] = tof_center + tof_half_span * fabs(delta_pos[ax]) / length_LOR;
474  tof_index_high[ax] = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
475  // if high TOF edge below the lowest FOV edge, return empty line
476  if (tof_index_high[ax]<0) return 0;
477  tof_edge_high[ax] = (HPFLTNB)(tof_index_high[ax]+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
478  }
479 
480  // Computation of alphaMin et alphaMax values entrance and exit point of the ray at voxel grid (edges),
481  // with respect to the truncated TOF distribution centered at the TOF measurement,
482  // limited by the FOV, absolute normalized distance from event1
483  for( int i = 0; i < 3; ++i )
484  {
485  if( delta_pos[ i ] != 0.0 )
486  {
487  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
488  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
489  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
490  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
491  }
492  }
493 
494  // if alphaMax is less than or equal to alphaMin no intersection
495  // and return an empty buffer
496  if( alphaMax <= alphaMin ) return 0;
497 
498  // Now we have to find the indices of the particular plane
499  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
500  int iMin = 0, iMax = 0;
501  int jMin = 0, jMax = 0;
502  int kMin = 0, kMax = 0;
503 
504  // For the X-axis
505  if( delta_pos[ 0 ] > 0.0f )
506  {
507  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
508  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
509  }
510  else if( delta_pos[ 0 ] < 0.0f )
511  {
512  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
513  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
514  }
515  if( delta_pos[ 0 ] == 0 )
516  {
517  iMin = 1, iMax = 0;
518  }
519 
520  // For the Y-axis
521  if( delta_pos[ 1 ] > 0 )
522  {
523  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
524  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
525  }
526  else if( delta_pos[ 1 ] < 0 )
527  {
528  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
529  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
530  }
531  else if( delta_pos[ 1 ] == 0 )
532  {
533  jMin = 1, jMax = 0;
534  }
535 
536  // For the Z-axis
537  if( delta_pos[ 2 ] > 0 )
538  {
539  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
540  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
541  }
542  else if( delta_pos[ 2 ] < 0 )
543  {
544  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
545  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
546  }
547  else if( delta_pos[ 2 ] == 0 )
548  {
549  kMin = 1, kMax = 0;
550  }
551 
552  // Computing the last term n number of intersection
553  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
554  + ( kMax - kMin + 1 );
555 
556  // Array storing the first alpha in X, Y and Z
557  HPFLTNB alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
558 
559  // Computing the first alpha in X
560  if( delta_pos[ 0 ] > 0 )
561  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
562  else if( delta_pos[ 0 ] < 0 )
563  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
564 
565  // Computing the first alpha in Y
566  if( delta_pos[ 1 ] > 0 )
567  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
568  else if( delta_pos[ 1 ] < 0 )
569  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
570 
571  // Computing the first alpha in Z
572  if( delta_pos[ 2 ] > 0 )
573  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
574  else if( delta_pos[ 2 ] < 0 )
575  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
576 
577  // Computing the alpha updating
578  HPFLTNB alpha_u[ 3 ] = {
579  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
580  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
581  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
582  };
583 
584  // Computing the index updating
585  INTNB index_u[ 3 ] = {
586  (delta_pos[ 0 ] < 0) ? -1 : 1,
587  (delta_pos[ 1 ] < 0) ? -1 : 1,
588  (delta_pos[ 2 ] < 0) ? -1 : 1
589  };
590 
591  // Check which alpha is the min/max and increment
592  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
593  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
594  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
595 
596  // Computing the minimum value in the alpha_XYZ buffer
597  HPFLTNB const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ], (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
598 
599  // Computing the first index of intersection
600  HPFLTNB alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
601  INTNB index_ijk[ 3 ] = {
602  1 + (INTNB)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
603  1 + (INTNB)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
604  1 + (INTNB)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
605  };
606 
607  INTNB const w = mp_nbVox[ 0 ];
608  INTNB const wh = w * mp_nbVox[ 1 ];
609 
611  //HPFLTNB previous_edge_erf = 0.;
612  //HPFLTNB next_edge_erf = 0.;
613  //bool previous_edge_erf_initialized = false;
614 
615  // Loop over the number of plane to cross
616  HPFLTNB alpha_c = alphaMin;
617  FLTNB coeff = 0.0f;
618  INTNB numVox = 0;
619  for( int nP = 0; nP < n - 1; ++nP )
620  {
621  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
622  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
623  {
624  // Storing values
625  if( ( alpha_XYZ[ 0 ] >= alphaMin )
626  && ( index_ijk[ 0 ] - 1 >= tof_index_low[0] )
627  && ( index_ijk[ 0 ] - 1 <= tof_index_high[0] )
628  && ( index_ijk[ 1 ] - 1 >= tof_index_low[1] )
629  && ( index_ijk[ 1 ] - 1 <= tof_index_high[1] )
630  && ( index_ijk[ 2 ] - 1 >= tof_index_low[2] )
631  && ( index_ijk[ 2 ] - 1 <= tof_index_high[2] ) )
632  {
634  //if (!previous_edge_erf_initialized)
635  //{
636  //previous_edge_erf = erf( (length_LOR *alpha_c - lor_tof_center) / tof_sigma_sqrt2 );
637  //previous_edge_erf_initialized = true;
638  //}
639 
640  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
641 
642  // voxel center
643  alphaMid = ( alpha_XYZ[ 0 ] + alpha_c ) * 0.5;
644 
645  // non TOF projection coefficient
646  coeff = ( alpha_XYZ[ 0 ] - alpha_c ) * length_LOR;
647 
649  {
650  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
651  // nearest to the projection of the voxel center onto the LOR
652  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
653  // multiply the non TOF projection coefficient with the TOF weight
654  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) coeff *= mp_TOFWeightingFcn[temp];
655  else coeff = 0.;
656  }
657  else
658  {
660  {
661  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
662  // of the Gaussian centered at the projection of the voxel center onto the LOR
663  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
664  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
665  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
666  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
667  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
668  // multiply the non TOF projection coefficient with the TOF weight
669  coeff *= 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
670  }
671  else
672  {
674  //next_edge_erf = erf( (length_LOR * alpha_XYZ[ 0 ] - lor_tof_center) / tof_sigma_sqrt2 );
676  //coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) ;
678  //previous_edge_erf = next_edge_erf;
679 
680  // TOF weight = value of the normalized Gaussian (centered at the TOF measurement)
681  // at the projection of the voxel center onto the LOR
682  HPFLTNB temp = (alphaMid * length_LOR - lor_tof_center) / tof_sigma;
683  // multiply the non TOF projection coefficient with the TOF weight
684  coeff *= exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
685  }
686  }
687 
688  if (!mp_ImageDimensionsAndQuantification->IsVoxelMasked(numVox)) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
689  }
690 
691  // Increment values
692  alpha_c = alpha_XYZ[ 0 ];
693  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
694  index_ijk[ 0 ] += index_u[ 0 ];
695  }
696  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
697  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
698  {
699  // Storing values
700  if( ( alpha_XYZ[ 1 ] >= alphaMin )
701  && ( index_ijk[ 0 ] - 1 >= tof_index_low[0] )
702  && ( index_ijk[ 0 ] - 1 <= tof_index_high[0] )
703  && ( index_ijk[ 1 ] - 1 >= tof_index_low[1] )
704  && ( index_ijk[ 1 ] - 1 <= tof_index_high[1] )
705  && ( index_ijk[ 2 ] - 1 >= tof_index_low[2] )
706  && ( index_ijk[ 2 ] - 1 <= tof_index_high[2] ) )
707  {
709  //if (!previous_edge_erf_initialized)
710  //{
711  //previous_edge_erf = erf( (length_LOR *alpha_c - lor_tof_center) / tof_sigma_sqrt2 );
712  //previous_edge_erf_initialized = true;
713  //}
714 
715  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
716 
717  alphaMid = ( alpha_XYZ[ 1 ] + alpha_c ) / 2.;
718 
719  // non TOF projection coefficient
720  coeff = ( alpha_XYZ[ 1 ] - alpha_c ) * length_LOR;
721 
723  {
724  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
725  // nearest to the projection of the voxel center onto the LOR
726  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
727  // multiply the non TOF projection coefficient with the TOF weight
728  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) coeff *= mp_TOFWeightingFcn[temp];
729  else coeff=0.;
730  }
731  else
732  {
734  {
735  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
736  // of the Gaussian centered at the projection of the voxel center onto the LOR
737  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
738  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
739  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
740  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
741  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
742  // multiply the non TOF projection coefficient with the TOF weight
743  coeff *= 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
744  }
745  else
746  {
748  //next_edge_erf = erf( (length_LOR * alpha_XYZ[ 1 ] - lor_tof_center) / tof_sigma_sqrt2 );
750  //coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) ;
752  //previous_edge_erf = next_edge_erf;
753 
754  // TOF weight = value of the normalized Gaussian (centered at the TOF measurement)
755  // at the projection of the voxel center onto the LOR
756  HPFLTNB temp = (alphaMid * length_LOR - lor_tof_center) / tof_sigma;
757  // multiply the non TOF projection coefficient with the TOF weight
758  coeff *= exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
759  }
760  }
761 
762  // if the voxel is not masked
763  if (!mp_ImageDimensionsAndQuantification->IsVoxelMasked(numVox)) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
764  }
765 
766  // Increment values
767  alpha_c = alpha_XYZ[ 1 ];
768  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
769  index_ijk[ 1 ] += index_u[ 1 ];
770  }
771  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
772  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
773  {
774  // Storing values
775  if( ( alpha_XYZ[ 2 ] >= alphaMin )
776  && ( index_ijk[ 0 ] - 1 >= tof_index_low[0] )
777  && ( index_ijk[ 0 ] - 1 <= tof_index_high[0] )
778  && ( index_ijk[ 1 ] - 1 >= tof_index_low[1] )
779  && ( index_ijk[ 1 ] - 1 <= tof_index_high[1] )
780  && ( index_ijk[ 2 ] - 1 >= tof_index_low[2] )
781  && ( index_ijk[ 2 ] - 1 <= tof_index_high[2] ) )
782  {
784  //if (!previous_edge_erf_initialized)
785  //{
786  //previous_edge_erf = erf( (length_LOR *alpha_c - lor_tof_center) / tof_sigma_sqrt2 );
787  //previous_edge_erf_initialized = true;
788  //}
789 
790  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
791 
792  alphaMid = ( alpha_XYZ[ 2 ] + alpha_c ) / 2.;
793 
794  // non TOF projection coefficient
795  coeff = ( alpha_XYZ[ 2 ] - alpha_c ) * length_LOR;
796 
798  {
799  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
800  // nearest to the projection of the voxel center onto the LOR
801  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
802  // multiply the non TOF projection coefficient with the TOF weight
803  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) coeff *= mp_TOFWeightingFcn[temp];
804  else coeff = 0.;
805  }
806  else
807  {
809  {
810  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
811  // of the Gaussian centered at the projection of the voxel center onto the LOR
812  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
813  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
814  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
815  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
816  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
817  // multiply the non TOF projection coefficient with the TOF weight
818  coeff *= 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
819  }
820  else
821  {
823  //next_edge_erf = erf( (length_LOR * alpha_XYZ[ 2 ] - lor_tof_center) / tof_sigma_sqrt2 );
825  //coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) ;
827  //previous_edge_erf = next_edge_erf;
828 
829  // TOF weight = value of the normalized Gaussian (centered at the TOF measurement)
830  // at the projection of the voxel center onto the LOR
831  HPFLTNB temp = (alphaMid * length_LOR - lor_tof_center) / tof_sigma;
832  // multiply the non TOF projection coefficient with the TOF weight
833  coeff *= exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
834  }
835  }
836 
837  // if the voxel is not masked
838  if (!mp_ImageDimensionsAndQuantification->IsVoxelMasked(numVox)) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
839  }
840 
841  // Increment values
842  alpha_c = alpha_XYZ[ 2 ];
843  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
844  index_ijk[ 2 ] += index_u[ 2 ];
845  }
846  }
847 
848  return 0;
849 }
850 
851 // =====================================================================
852 // ---------------------------------------------------------------------
853 // ---------------------------------------------------------------------
854 // =====================================================================
855 
856 int iProjectorIncrementalSiddon::ProjectTOFHistogram(int a_direction, oProjectionLine* ap_ProjectionLine)
857 {
858  #ifdef CASTOR_DEBUG
859  if (!m_initialized)
860  {
861  Cerr("***** iProjectorIncrementalSiddon::ProjectTOFHistogram() -> Called while not initialized !" << endl);
862  Exit(EXIT_DEBUG);
863  }
864  #endif
865 
866  #ifdef CASTOR_VERBOSE
867  if (m_verbose>=10)
868  {
869  string direction = "";
870  if (a_direction==FORWARD) direction = "forward";
871  else direction = "backward";
872  Cout("iProjectorIncrementalSiddon::Project with TOF bins -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
873  }
874  #endif
875 
876  // Simpler way now, hopefully it works
877  FLTNB* event1Float = ap_ProjectionLine->GetPosition1();
878  FLTNB* event2Float = ap_ProjectionLine->GetPosition2();
879  HPFLTNB event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
880  HPFLTNB event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
881 
882  // **************************************
883  // STEP 1: LOR length calculation
884  // **************************************
885  HPFLTNB length_LOR = ap_ProjectionLine->GetLength();
886  HPFLTNB length_LOR_half = length_LOR * 0.5;
887 
888  // **************************************
889  // STEP 2: Compute entrance voxel indexes
890  // **************************************
891  HPFLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
892  HPFLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
893 
894  HPFLTNB alphaMin = 0.0f, alphaMax = 1.0f;
895  HPFLTNB delta_pos[] = {
896  event2[ 0 ] - event1[ 0 ],
897  event2[ 1 ] - event1[ 1 ],
898  event2[ 2 ] - event1[ 2 ]
899  };
900 
901  // Get TOF info
902  INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
903  INTNB tof_half_nb_bins = tof_nb_bins/2;
904 
905  // TOF Gaussian standard deviation and truncation
907  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
908  HPFLTNB tof_half_span = 0.;
910  else tof_half_span = tof_sigma * m_TOFNbSigmas;
911 
912  // if integration of the Gaussian over the TOF bin, need for help variables to save calls to erf
913  HPFLTNB prev_erf = 0., new_erf = 0.;
914 
915  // minimum and maximum TOF bins
916  INTNB tof_bin_last = tof_half_nb_bins;
917  INTNB tof_bin_first = -tof_half_nb_bins;
918 
919  // distance between the first event1 and the center of a TOF bin along the LOR
920  HPFLTNB lor_tof_center = 0.;
921  // the sum of all TOF bin weights for a voxel
922  //HPFLTNB tof_norm_coef = 0.;
923 
924  // the first and the last relevant TOF bins for a voxel
925  INTNB tof_bin_first_for_voxel = 0, tof_bin_last_for_voxel = 0;
926 
927  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
928  for( int i = 0; i < 3; ++i )
929  {
930  if( delta_pos[ i ] != 0.0 )
931  {
932  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
933  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
934  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
935  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
936  }
937  }
938 
939  // if alphaMax is less than or equal to alphaMin no intersection
940  // and return an empty buffer
941  if( alphaMax <= alphaMin ) return 0;
942 
943  // temporary storage for TOF bin weights
944  // allocation after potential returns
945  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
946  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
947 
948  // Now we have to find the indices of the particular plane
949  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
950  int iMin = 0, iMax = 0;
951  int jMin = 0, jMax = 0;
952  int kMin = 0, kMax = 0;
953 
954  // For the X-axis
955  if( delta_pos[ 0 ] > 0.0f )
956  {
957  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
958  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
959  }
960  else if( delta_pos[ 0 ] < 0.0f )
961  {
962  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
963  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
964  }
965  if( delta_pos[ 0 ] == 0 )
966  {
967  iMin = 1, iMax = 0;
968  }
969 
970  // For the Y-axis
971  if( delta_pos[ 1 ] > 0 )
972  {
973  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
974  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
975  }
976  else if( delta_pos[ 1 ] < 0 )
977  {
978  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
979  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
980  }
981  else if( delta_pos[ 1 ] == 0 )
982  {
983  jMin = 1, jMax = 0;
984  }
985 
986  // For the Z-axis
987  if( delta_pos[ 2 ] > 0 )
988  {
989  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
990  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
991  }
992  else if( delta_pos[ 2 ] < 0 )
993  {
994  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
995  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
996  }
997  else if( delta_pos[ 2 ] == 0 )
998  {
999  kMin = 1, kMax = 0;
1000  }
1001 
1002  // Computing the last term n number of intersection
1003  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
1004  + ( kMax - kMin + 1 );
1005 
1006  // Array storing the first alpha in X, Y and Z
1007  HPFLTNB alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
1008 
1009  // Computing the first alpha in X
1010  if( delta_pos[ 0 ] > 0 )
1011  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
1012  else if( delta_pos[ 0 ] < 0 )
1013  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
1014 
1015  // Computing the first alpha in Y
1016  if( delta_pos[ 1 ] > 0 )
1017  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
1018  else if( delta_pos[ 1 ] < 0 )
1019  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
1020 
1021  // Computing the first alpha in Z
1022  if( delta_pos[ 2 ] > 0 )
1023  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
1024  else if( delta_pos[ 2 ] < 0 )
1025  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
1026 
1027  // Computing the alpha updating
1028  HPFLTNB alpha_u[ 3 ] = {
1029  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
1030  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
1031  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
1032  };
1033 
1034  // Computing the index updating
1035  INTNB index_u[ 3 ] = {
1036  (delta_pos[ 0 ] < 0) ? -1 : 1,
1037  (delta_pos[ 1 ] < 0) ? -1 : 1,
1038  (delta_pos[ 2 ] < 0) ? -1 : 1
1039  };
1040 
1041  // Check which alpha is the min/max and increment
1042  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
1043  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
1044  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
1045 
1046  // Computing the minimum value in the alpha_XYZ buffer
1047  HPFLTNB const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ], (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
1048 
1049  // Computing the first index of intersection
1050  HPFLTNB alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
1051  INTNB index_ijk[ 3 ] = {
1052  1 + (INTNB)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
1053  1 + (INTNB)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
1054  1 + (INTNB)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
1055  };
1056 
1057  INTNB const w = mp_nbVox[ 0 ];
1058  INTNB const wh = w * mp_nbVox[ 1 ];
1059 
1060  // Loop over the number of plane to cross
1061  HPFLTNB alpha_c = alphaMin;
1062  FLTNB coeff = 0.0f;
1063  INTNB numVox = 0;
1064 
1065  for( int nP = 0; nP < n - 1; ++nP )
1066  {
1067  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
1068  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
1069  {
1070  // Storing values
1071  if( ( alpha_XYZ[ 0 ] >= alphaMin )
1072  && ( index_ijk[ 0 ] - 1 >= 0 )
1073  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
1074  && ( index_ijk[ 1 ] - 1 >= 0 )
1075  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
1076  && ( index_ijk[ 2 ] - 1 >= 0 )
1077  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
1078  {
1079  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
1080 
1081  // if this voxel is masked, do nothing
1083  {
1084  coeff = ( alpha_XYZ[ 0 ] - alpha_c ) * length_LOR;
1085 
1086  alphaMid = ( alpha_XYZ[ 0 ] + alpha_c ) * 0.5;
1087 
1088  // compute the first and the last relevant TOF bin for this voxel
1090  {
1091  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1092  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (alphaMid * length_LOR - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1093  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (alphaMid * length_LOR + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1094  }
1095  else
1096  {
1097  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1098  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (alphaMid * length_LOR - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
1099  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (alphaMid * length_LOR + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
1100  }
1101 
1102  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1103 
1104  // shift tof bin indices from -/+ to 0:nbBins range
1105  tof_bin_first_for_voxel += tof_half_nb_bins;
1106  tof_bin_last_for_voxel += tof_half_nb_bins;
1107 
1108  // initialization of help variables for reducing calls to erf
1110  {
1111  // bound the integration to the Gaussian truncation
1112  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1113  prev_erf = erf(temp/tof_sigma_sqrt2);
1114  }
1115 
1116  // compute TOF bin weights for the current voxel for all relevant TOF bins
1117  //tof_norm_coef = 0.;
1118  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1119  {
1121  {
1122  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1123  // nearest to the projection of the voxel center onto the LOR
1124  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor);
1125  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1126  // add the weight to the sum for normalization
1127  //tof_norm_coef += tof_weights_temp[tof_bin];
1128  // update TOF bin center along the LOR for the next TOF bin
1129  lor_tof_center += m_TOFBinSizeInMm;
1130  }
1131  else
1132  {
1134  {
1135  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1136  // reuse of integration from the previous bin to save calls to erf
1137  // bound the integration to the Gaussian truncation
1138  HPFLTNB temp = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1139  new_erf = erf(temp/tof_sigma_sqrt2);
1140  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1141  // add the weight to the sum
1142  // tof_norm_coef += tof_weights_temp[tof_bin];
1143  prev_erf = new_erf;
1144  // update TOF bin center along the LOR for the next TOF bin
1145  lor_tof_center += m_TOFBinSizeInMm;
1146  }
1147  else
1148  {
1149  // TOF weight = TOF bin size * value of the normalized Gaussian (centered at the TOF bin center) at the projection of the voxel center onto the LOR
1150  HPFLTNB temp = ( alphaMid * length_LOR - lor_tof_center) / tof_sigma;
1151  // save the weight temporarily
1152  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1153  // add the weight to the sum
1154  //tof_norm_coef += tof_weights_temp[tof_bin];
1155  // update TOF center along the LOR for the next TOF bin
1156  lor_tof_center += m_TOFBinSizeInMm;
1157  }
1158  }
1159  }
1160 /*
1161  if (tof_norm_coef>0.)
1162  {
1163  // first normalize TOF bin weights so that they sum to 1
1164  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
1165  }
1166 */
1167  // write the final TOF bin weighted projection coefficients for the current voxel
1168  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, numVox, coeff, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1169 
1170  }
1171  }
1172  // Increment values
1173  alpha_c = alpha_XYZ[ 0 ];
1174  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
1175  index_ijk[ 0 ] += index_u[ 0 ];
1176  }
1177  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
1178  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
1179  {
1180  // Storing values
1181  if( ( alpha_XYZ[ 1 ] >= alphaMin )
1182  && ( index_ijk[ 0 ] - 1 >= 0 )
1183  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
1184  && ( index_ijk[ 1 ] - 1 >= 0 )
1185  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
1186  && ( index_ijk[ 2 ] - 1 >= 0 )
1187  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
1188  {
1189  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
1190 
1191  // if this voxel is masked, do nothing
1193  {
1194  coeff = ( alpha_XYZ[ 1 ] - alpha_c ) * length_LOR;
1195 
1196  alphaMid = ( alpha_XYZ[ 1 ] + alpha_c ) * 0.5;
1197 
1198  // compute the first and the last relevant TOF bin for this voxel
1200  {
1201  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1202  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (alphaMid * length_LOR - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1203  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (alphaMid * length_LOR + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1204  }
1205  else
1206  {
1207  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1208  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (alphaMid * length_LOR - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
1209  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (alphaMid * length_LOR + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
1210  }
1211 
1212  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1213 
1214  // shift tof bin indices from -/+ to 0:nbBins range
1215  tof_bin_first_for_voxel += tof_half_nb_bins;
1216  tof_bin_last_for_voxel += tof_half_nb_bins;
1217 
1218  // initialization of help variables for reducing calls to erf
1220  {
1221  // bound the integration to the Gaussian truncation
1222  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1223  prev_erf = erf(temp/tof_sigma_sqrt2);
1224  }
1225 
1226  // compute TOF bin weights for the current voxel for all relevant TOF bins
1227  //tof_norm_coef = 0.;
1228  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1229  {
1231  {
1232  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1233  // nearest to the projection of the voxel center onto the LOR
1234  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1235  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1236  // add the weight to the sum
1237  //tof_norm_coef += tof_weights_temp[tof_bin];
1238  // update TOF center along the LOR for the next TOF bin
1239  lor_tof_center += m_TOFBinSizeInMm;
1240  }
1241  else
1242  {
1244  {
1245  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1246  // reuse of integration from the previous bin to save calls to erf
1247  // bound the integration to the Gaussian truncation
1248  HPFLTNB temp = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1249  new_erf = erf(temp/tof_sigma_sqrt2);
1250  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1251  // add the weight to the sum for normalization
1252  //tof_norm_coef += tof_weights_temp[tof_bin];
1253  prev_erf = new_erf;
1254  // update TOF center along the LOR for the next TOF bin
1255  lor_tof_center += m_TOFBinSizeInMm;
1256  }
1257  else
1258  {
1259  // TOF weight = TOF bin size * value of the normalized Gaussian (centered at the TOF bin center) at the projection of the voxel center onto the LOR
1260  HPFLTNB temp = ( alphaMid * length_LOR - lor_tof_center) / tof_sigma;
1261  // save the weight temporarily
1262  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1263  // add the weight to the sum for normalization
1264  //tof_norm_coef += tof_weights_temp[tof_bin];
1265  // update TOF center along the LOR for the next TOF bin
1266  lor_tof_center += m_TOFBinSizeInMm;
1267  }
1268  }
1269  }
1270 /*
1271  if (tof_norm_coef>0.)
1272  {
1273  // first normalize TOF bin weights so that they sum to 1
1274  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
1275  }
1276 */
1277  // write the final TOF bin weighted projection coefficients for the current voxel
1278  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, numVox, coeff, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1279  }
1280  }
1281 
1282  // Increment values
1283  alpha_c = alpha_XYZ[ 1 ];
1284  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
1285  index_ijk[ 1 ] += index_u[ 1 ];
1286  }
1287  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
1288  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
1289  {
1290  // Storing values
1291  if( ( alpha_XYZ[ 2 ] >= alphaMin )
1292  && ( index_ijk[ 0 ] - 1 >= 0 )
1293  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
1294  && ( index_ijk[ 1 ] - 1 >= 0 )
1295  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
1296  && ( index_ijk[ 2 ] - 1 >= 0 )
1297  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
1298  {
1299  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
1300 
1301  // if this voxel is masked, do nothing
1303  {
1304  coeff = ( alpha_XYZ[ 2 ] - alpha_c ) * length_LOR;
1305 
1306  alphaMid = ( alpha_XYZ[ 2 ] + alpha_c ) * 0.5;
1307 
1308  // compute the first and the last relevant TOF bin for this voxel
1310  {
1311  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1312  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (alphaMid * length_LOR - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1313  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (alphaMid * length_LOR + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1314  }
1315  else
1316  {
1317  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1318  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (alphaMid * length_LOR - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
1319  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (alphaMid * length_LOR + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
1320  }
1321 
1322  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1323 
1324  // shift tof bin indices from -/+ to 0:nbBins range
1325  tof_bin_first_for_voxel += tof_half_nb_bins;
1326  tof_bin_last_for_voxel += tof_half_nb_bins;
1327 
1328 
1329  // initialization of help variables for reducing calls to erf
1331  {
1332  // bound the integration to the Gaussian truncation
1333  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1334  prev_erf = erf(temp/tof_sigma_sqrt2);
1335  }
1336 
1337  // compute TOF bin weights for the current voxel for all relevant TOF bins
1338  //tof_norm_coef = 0.;
1339  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1340  {
1342  {
1343  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1344  // nearest to the projection of the voxel center onto the LOR
1345  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1346  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1347  // add the weight to the sum
1348  //tof_norm_coef += tof_weights_temp[tof_bin];
1349  // update TOF bin center along the LOR for the next TOF bin
1350  lor_tof_center += m_TOFBinSizeInMm;
1351  }
1352  else
1353  {
1355  {
1356  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1357  // reuse of integration from the previous bin to save calls to erf
1358  // bound the integration to the Gaussian truncation
1359  HPFLTNB temp = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1360  new_erf = erf(temp/tof_sigma_sqrt2);
1361  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1362  // add the weight to the sum
1363  //tof_norm_coef += tof_weights_temp[tof_bin];
1364  prev_erf = new_erf;
1365  // update TOF center along the LOR for the next TOF bin
1366  lor_tof_center += m_TOFBinSizeInMm;
1367  }
1368  else
1369  {
1370  // TOF weight = TOF bin size * value of the normalized Gaussian (centered at the TOF bin center) at the projection of the voxel center onto the LOR
1371  HPFLTNB temp = ( alphaMid * length_LOR - lor_tof_center) / tof_sigma;
1372  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1373  // add the weight to the sum
1374  //tof_norm_coef += tof_weights_temp[tof_bin];
1375  // update TOF center along the LOR for the next TOF bin
1376  lor_tof_center += m_TOFBinSizeInMm;
1377  }
1378  }
1379  }
1380 /*
1381  if (tof_norm_coef>0.)
1382  {
1383  // first normalize TOF bin weights so that they sum to 1
1384  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
1385  }
1386 */
1387  // write the final TOF bin weighted projection coefficients for the current voxel
1388  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, numVox, coeff, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1389  }
1390  }
1391 
1392  // Increment values
1393  alpha_c = alpha_XYZ[ 2 ];
1394  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
1395  index_ijk[ 2 ] += index_u[ 2 ];
1396  }
1397  }
1398 
1399  delete[] tof_weights_temp;
1400 
1401  return 0;
1402 }
1403 
1404 // =====================================================================
1405 // ---------------------------------------------------------------------
1406 // ---------------------------------------------------------------------
1407 // =====================================================================
int ProjectTOFListmode(int a_direction, oProjectionLine *ap_ProjectionLine)
~iProjectorIncrementalSiddon()
The destructor of iProjectorIncrementalSiddon.
#define Cerr(MESSAGE)
void AddVoxelAllTOFBins(int a_direction, INTNB a_voxelIndex, FLTNB a_voxelWeight, HPFLTNB *a_tofWeights, INTNB a_tofBinFirst, INTNB a_tofBinLast)
void ShowHelpSpecific()
A function used to show help about the child module.
FLTNB GetTOFMeasurementInPs()
This function is used to get the TOF measurement in ps.
This class is designed to generically described any on-the-fly projector.
#define SPEED_OF_LIGHT_IN_MM_PER_PS
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
FLTNB GetLength()
This function is used to get the length of the line.
void Exit(int code)
FLTNB * GetPosition1()
This function is used to get the pointer to the mp_position1 (3-values tab).
void AddVoxel(int a_direction, INTNB a_voxelIndice, FLTNB a_voxelWeight)
int ReadConfigurationFile(const string &a_configurationFile)
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.
This class is designed to manage and store system matrix elements associated to a vEvent...
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
int ProjectTOFHistogram(int a_direction, oProjectionLine *ap_ProjectionLine)
int GetNbTOFBins()
This function is used to get the number of TOF bins in use.
FLTNB * GetPosition2()
This function is used to get the pointer to the mp_position2 (3-values tab).
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child projector.
#define Cout(MESSAGE)
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.