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