CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/projector/iProjectorDistanceDriven.cc
Go to the documentation of this file.
1 
8 #include "iProjectorDistanceDriven.hh"
9 #include "sOutputManager.hh"
10 
11 #include <cmath>
12 #include <algorithm>
13 #ifdef _WIN32
14 // Avoid compilation errors due to mix up between std::min()/max() and
15 // min max macros
16 #undef min
17 #undef max
18 #endif
19 
20 // =====================================================================
21 // ---------------------------------------------------------------------
22 // ---------------------------------------------------------------------
23 // =====================================================================
24 
26 {
27  // This projector is not compatible with SPECT attenuation correction because
28  // the voxels contributing to the line are not strictly ordered with respect to
29  // their distance to point2 (due to interpolations at each plane crossed)
31  // This projector is not compatible with compression as it works only with the
32  // detection element indices
34  // Default pointers and parameters
35  m_toleranceX = 0.;
36  m_toleranceY = 0.;
37  m_toleranceZ = 0.;
38  m_tolerance_fctr = 1.e-6;
39 }
40 
41 // =====================================================================
42 // ---------------------------------------------------------------------
43 // ---------------------------------------------------------------------
44 // =====================================================================
45 
47 {
48  ;
49 }
50 
51 // =====================================================================
52 // ---------------------------------------------------------------------
53 // ---------------------------------------------------------------------
54 // =====================================================================
55 
56 int iProjectorDistanceDriven::ReadConfigurationFile(const string& a_configurationFile)
57 {
58  // No options for distance driven
59  ;
60  // Normal end
61  return 0;
62 }
63 
64 // =====================================================================
65 // ---------------------------------------------------------------------
66 // ---------------------------------------------------------------------
67 // =====================================================================
68 
69 int iProjectorDistanceDriven::ReadOptionsList(const string& a_optionsList)
70 {
71  // Currently only one option: Tolerance with respect to voxel sizes in each dimensions
72  uint16_t option[1];
73 
74  // Check tolerance value
75  if (ReadStringOption(a_optionsList, option, 1, ",", "Tolerance factor for plane index computation "))
76  {
77  Cerr("***** iProjectorDistanceDriven::ReadConfigurationFile() -> Failed to correctly read the list of options !" << endl);
78  return 1;
79  }
80 
81  // Check tolerance factor
82  if (option[0] < 2.)
83  {
84  Cerr("***** iProjectorDistanceDriven::ReadOptionsList() -> Tolerance factor parameter must be >2 (tolerance factor < 10^-2)" << endl);
85  Cerr(" -> Provided parameter: "<< option[0] << endl);
86  return 1;
87  }
88 
89  m_tolerance_fctr = pow(10 , -(HPFLTNB)option[0]);
90 
91  // Normal end
92  return 0;
93 }
94 
95 // =====================================================================
96 // ---------------------------------------------------------------------
97 // ---------------------------------------------------------------------
98 // =====================================================================
99 
101 {
102  cout << "This projector is a line projector based on computations of overlap between a pair of detection elements and voxels." << endl;
103  cout << "It is implemented from the following published paper:" << endl;
104  cout << "B. De Man and S. Basu, \"Distance-driven projection and backprojection in three dimensions\", Phys. Med. Biol., vol. 49, pp. 2463-75, 2004." << endl;
105  cout << "There is 1 optional parameter for this projector:" << endl;
106  cout << " tolerance_factor: x. -> 'x' must be an unsigned integer, and represent the tolerance precision factor for plane index computation (10^(-x))." << endl;
107  cout << " -> Default: 'x' = 6" << endl;
108 }
109 
110 // =====================================================================
111 // ---------------------------------------------------------------------
112 // ---------------------------------------------------------------------
113 // =====================================================================
114 
116 {
117  if ( m_tolerance_fctr > 0.01
118  || m_tolerance_fctr <= 0 )
119  {
120  Cerr("***** iProjectorDistanceDriven::CheckSpecificParameters() -> Tolerance factor must be defined in the specific interval : ]0 ; 1.e-2]" << endl);
121  Cerr("***** -> Default value is 1.e-6" << endl);
122  Cerr("***** -> Current value is " << m_tolerance_fctr << endl);
123  return 1;
124  }
125 
126  // Normal end
127  return 0;
128 }
129 
130 // =====================================================================
131 // ---------------------------------------------------------------------
132 // ---------------------------------------------------------------------
133 // =====================================================================
134 
136 {
137  // Verbose
138  if (m_verbose>=2) Cout("iProjectorDistanceDriven::InitializeSpecific() -> Use Distance Driven projector" << endl);
139 
140  // Set the tolerance with respect to voxel sizes in each dimensions
144 
145  // Normal end
146  return 0;
147 }
148 
149 // =====================================================================
150 // ---------------------------------------------------------------------
151 // ---------------------------------------------------------------------
152 // =====================================================================
153 
155 {
156  // For this projector, we estimated that the number of voxels in a slice will be always higher than the number of contributing voxels for any line of response
158 }
159 
160 // =====================================================================
161 // ---------------------------------------------------------------------
162 // ---------------------------------------------------------------------
163 // =====================================================================
164 
165 int iProjectorDistanceDriven::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
166 {
167  #ifdef CASTOR_DEBUG
168  if (!m_initialized)
169  {
170  Cerr("***** iProjectorDistanceDriven::ProjectWithoutTOF() -> Called while not initialized !" << endl);
171  Exit(EXIT_DEBUG);
172  }
173  #endif
174 
175  #ifdef CASTOR_VERBOSE
176  if (m_verbose>=10)
177  {
178  string direction = "";
179  if (a_direction==FORWARD) direction = "forward";
180  else direction = "backward";
181  Cout("iProjectorDistanceDriven::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
182  }
183  #endif
184 
185  // Computing the coordinates of the points on the bound of the source/crystal
186  // 1. The first point is on transaxial plan and the last point is on axial
187  // plan.
188  // Source spot size (CT) / Crystal 1 (PET):
189  //
190  // ------- P13 -------
191  // | |
192  // | |
193  // P12 Center P11
194  // | 0 |
195  // | |
196  // ------- P14 -------
197  // Y
198  // <---------. X
199  // |
200  // |
201  // |
202  // \/ Z
203  // --------------
204 
205  // Computing the position of the point2.
206  // In Distance Driven the point p21 relies the point P11, p22 -> p12,
207  // p23 -> p13 and p24 -> p14
208  // Pixel (CT) / Crystal 2 (PET):
209  //
210  // ------- P23 -------
211  // | |
212  // | |
213  // P22 Center P21
214  // | 0 |
215  // | |
216  // ------- P24 -------
217  // Y
218  // <---------. X
219  // |
220  // |
221  // |
222  // \/ Z
223  // --------------
224 
225  // buffers storing point 1 and 2
226  FLTNB p1_x[ 5 ];
227  FLTNB p1_y[ 5 ];
228  FLTNB p1_z[ 5 ];
229  FLTNB p2_x[ 5 ];
230  FLTNB p2_y[ 5 ];
231  FLTNB p2_z[ 5 ];
232 
233  // Get the positions of the centers of the edges of crystal elements or source position
235  ap_ProjectionLine->GetIndex1(), // Index 1
236  ap_ProjectionLine->GetIndex2(), // Index 2
237  ap_ProjectionLine->GetPosition1(), // Line position for point 1
238  ap_ProjectionLine->GetPosition2(), // Line position for point 2
239  &p1_x[1], &p1_y[1], &p1_z[1], // Edges for point 1
240  &p2_x[1], &p2_y[1], &p2_z[1] // Edges for point 2
241  ))
242  {
243  Cerr("***** iProjectorDistanceDriven::ProjectWithoutTOF() -> A problem occurred while getting the edges' center positions !" << endl);
244  return 1;
245  }
246 
247  // Point 1 focal (CT) or crystal (PET) position
248  FLTNB* p1 = ap_ProjectionLine->GetPosition1();
249 
250  // Point 2 pixel (CT) or crystal (PET) position
251  FLTNB* p2 = ap_ProjectionLine->GetPosition2();
252 
253  // Center position p1
254  p1_x[ 0 ] = p1[ 0 ]; p1_y[ 0 ] = p1[ 1 ]; p1_z[ 0 ] = p1[ 2 ];
255 
256  // Center position p2
257  p2_x[ 0 ] = p2[ 0 ]; p2_y[ 0 ] = p2[ 1 ]; p2_z[ 0 ] = p2[ 2 ];
258 
259  // storing the ray relying p1 and p2
260  HPFLTNB r[ 5 ][ 3 ];
261 
262  // buffer storing the intersection
263  HPFLTNB ri[ 4 ][ 3 ];
264 
265  // Take the position of ray relying p1 and p2 for all the 5 points
266  for( int p = 0; p < 5; ++p )
267  {
268  r[ p ][ 0 ] = p2_x[ p ] - p1_x[ p ];
269  r[ p ][ 1 ] = p2_y[ p ] - p1_y[ p ];
270  r[ p ][ 2 ] = p2_z[ p ] - p1_z[ p ];
271  }
272 
273  // Computing the square of distance for the center
274  HPFLTNB const r2[ 3 ] = {
275  r[ 0 ][ 0 ] * r[ 0 ][ 0 ],
276  r[ 0 ][ 1 ] * r[ 0 ][ 1 ],
277  r[ 0 ][ 2 ] * r[ 0 ][ 2 ]
278  };
279 
280  // Find the first and last intersecting plane using the parametric
281  // values alpha_min and alpha_max
282  HPFLTNB alpha_min = 0.0, alpha_max = 1.0;
283 
284  // Buffer storing the alpha on each axis
285  // 2 different buffers to get the min and the max
286  HPFLTNB alpha_x_min[ 4 ], alpha_x_max[ 4 ];
287  HPFLTNB alpha_y_min[ 4 ], alpha_y_max[ 4 ];
288  HPFLTNB alpha_z_min[ 4 ], alpha_z_max[ 4 ];
289 
290  // Position of the projected points in plane
291  // If the main direction is X:
292  // pos[0] is the position of point1 in Y
293  // pos[1] is the position of point2 in Y
294  // pos[2] is the position of point3 in Z
295  // pos[3] is the position of point4 in Z
296  HPFLTNB pos[ 4 ];
297 
298  // Width of intersection
299  // w1 normalizes the overlap 1
300  // w2 normalizes the overlap 2
301  HPFLTNB w1, w2;
302 
303  // Buffer storing the min. and max. indices bounding the overlap
304  int index[ 4 ];
305 
306  // Buffer storing the distances
307  // 50 HPFLTNB is largely enough!!!
308  HPFLTNB distance_x[ 50 ];
309  HPFLTNB distance_y[ 50 ];
310  HPFLTNB distance_z[ 50 ];
311 
312  // Computing the angle of line vs. horizontal
313  float const angle = std::acos( ( r[ 0 ][ 0 ] ) / ( std::sqrt( r2[ 0 ] + r2[ 1 ] ) ) ) * 180.0f / M_PI;
314 
315  // Condition on the largest component of r, taking the center
316  if( ( angle >= 0.0 && angle <= 45.0 ) || ( angle >= 135.0 && angle <= 180.0 ) )
317  {
318  // Computing the parametric values alpha_min and alpha_max
319  // Because the main direction is X, we use only the center
320  alpha_x_min[ 0 ] = ( (-mp_halfFOV[ 0 ]) - p1_x[ 0 ] ) / r[ 0 ][ 0 ];
321  alpha_x_min[ 1 ] = ( mp_halfFOV[ 0 ] - p1_x[ 0 ] ) / r[ 0 ][ 0 ];
322  alpha_x_max[ 0 ] = alpha_x_min[ 0 ];
323  alpha_x_max[ 1 ] = alpha_x_min[ 1 ];
324  alpha_min = std::max( alpha_min, std::min( alpha_x_min[ 0 ], alpha_x_min[ 1 ] ) );
325  alpha_max = std::min( alpha_max, std::max( alpha_x_max[ 0 ], alpha_x_max[ 1 ] ) );
326 
327  // Compute the parametric value for each line
328  // For Y, we use only the point 1 and 2
329  if( r[ 1 ][ 1 ] != 0.0 ) // point1
330  {
331  alpha_y_min[ 0 ] = ( (-mp_halfFOV[ 1 ]) - p1_y[ 1 ] ) / r[ 1 ][ 1 ];
332  alpha_y_min[ 1 ] = ( mp_halfFOV[ 1 ] - p1_y[ 1 ] ) / r[ 1 ][ 1 ];
333  alpha_y_max[ 0 ] = alpha_y_min[ 0 ];
334  alpha_y_max[ 1 ] = alpha_y_min[ 1 ];
335  }
336  else
337  {
338  alpha_y_min[ 0 ] = 0.0;
339  alpha_y_min[ 1 ] = 0.0;
340  alpha_y_max[ 0 ] = 1.0;
341  alpha_y_max[ 1 ] = 1.0;
342  }
343 
344  if( r[ 2 ][ 1 ] != 0.0 ) // point2
345  {
346  alpha_y_min[ 2 ] = ( (-mp_halfFOV[ 1 ]) - p1_y[ 2 ] ) / r[ 2 ][ 1 ];
347  alpha_y_min[ 3 ] = ( mp_halfFOV[ 1 ] - p1_y[ 2 ] ) / r[ 2 ][ 1 ];
348  alpha_y_max[ 2 ] = alpha_y_min[ 2 ];
349  alpha_y_max[ 3 ] = alpha_y_min[ 3 ];
350  }
351  else
352  {
353  alpha_y_min[ 2 ] = 0.0;
354  alpha_y_min[ 3 ] = 0.0;
355  alpha_y_max[ 2 ] = 1.0;
356  alpha_y_max[ 3 ] = 1.0;
357  }
358 
359  // Getting the minimum of alpha_y_min
360  alpha_min = std::max( alpha_min,
361  alpha_y_min[ std::minmax_element( alpha_y_min, alpha_y_min + 4 ).first - alpha_y_min ] );
362  // Getting the maximum of alpha_y_max
363  alpha_max = std::min( alpha_max,
364  alpha_y_max[ std::minmax_element( alpha_y_max, alpha_y_max + 4 ).second - alpha_y_max ] );
365 
366  // For Z, we use only the point 3 and 4
367  if( r[ 3 ][ 2 ] != 0.0 ) // point3
368  {
369  alpha_z_min[ 0 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
370  alpha_z_min[ 1 ] = ( mp_halfFOV[ 2 ] - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
371  alpha_z_max[ 0 ] = alpha_z_min[ 0 ];
372  alpha_z_max[ 1 ] = alpha_z_min[ 1 ];
373  }
374  else
375  {
376  alpha_z_min[ 0 ] = 0.0;
377  alpha_z_min[ 1 ] = 0.0;
378  alpha_z_max[ 0 ] = 1.0;
379  alpha_z_max[ 1 ] = 1.0;
380  }
381 
382  if( r[ 4 ][ 2 ] != 0.0 ) // point4
383  {
384  alpha_z_min[ 2 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
385  alpha_z_min[ 3 ] = ( mp_halfFOV[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
386  alpha_z_max[ 2 ] = alpha_z_min[ 2 ];
387  alpha_z_max[ 3 ] = alpha_z_min[ 3 ];
388  }
389  else
390  {
391  alpha_z_min[ 2 ] = 0.0;
392  alpha_z_min[ 3 ] = 0.0;
393  alpha_z_max[ 2 ] = 1.0;
394  alpha_z_max[ 3 ] = 1.0;
395  }
396 
397  // Getting the minimum of alpha_z_min
398  alpha_min = std::max( alpha_min,
399  alpha_z_min[ std::minmax_element( alpha_z_min, alpha_z_min + 4 ).first
400  - alpha_z_min ] );
401  // Getting the maximum of alpha_z_max
402  alpha_max = std::min( alpha_max,
403  alpha_z_max[ std::minmax_element( alpha_z_max, alpha_z_max + 4 ).second
404  - alpha_z_max ] );
405 
406  if( alpha_max <= alpha_min ) return 0;
407 
408  // Computing the first and the last plane
409  int16_t i_min = 0, i_max = 0;
410  if( r[ 0 ][ 0 ] > 0.0 )
411  {
412  i_min = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alpha_min * r[ 0 ][ 0 ] - p1_x[ 0 ] ) / mp_sizeVox[ 0 ] );
413  i_max = ::floor( m_toleranceX + ( p1_x[ 0 ] + alpha_max * r[ 0 ][ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
414  }
415  else if( r[ 0 ][ 0 ] < 0.0 )
416  {
417  i_min = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alpha_max * r[ 0 ][ 0 ] - p1_x[ 0 ] ) / mp_sizeVox[ 0 ] );
418  i_max = ::floor( m_toleranceX + ( p1_x[ 0 ] + alpha_min * r[ 0 ][ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
419  }
420 
421  // Computing weight of normalization
422  // Using the center
423  HPFLTNB const factor( ::fabs( r[ 0 ][ 0 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
424  HPFLTNB weight( mp_sizeVox[ 0 ] / factor );
425 
426  // Computing the increment for each 4 lines to project
427  // (the center is not projected)
428  for( uint8_t p = 0; p < 4; ++p )
429  {
430  ri[ p ][ 0 ] = 1.0; //r[ p + 1 ][ 0 ] / r[ p + 1 ][ 0 ]
431  ri[ p ][ 1 ] = r[ p + 1 ][ 1 ] / r[ p + 1 ][ 0 ];
432  ri[ p ][ 2 ] = r[ p + 1 ][ 2 ] / r[ p + 1 ][ 0 ];
433  }
434 
435  // Increment orientation
436  int8_t incr_orient_trans = 0;
437  int8_t idx_orient_trans = 0;
438 
439  //int8_t const incr_orient_trans = p2_y[ 1 ] < p2_y[ 2 ] ? 1 : -1;
440  int8_t const incr_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : -1;
441 
442  // Index orientation
443  //int8_t const idx_orient_trans = p2_y[ 1 ] < p2_y[ 2 ] ? 1 : 0;
444  int8_t const idx_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : 0;
445 
446  // Computing an offset to get the correct position in plane
447  HPFLTNB const offset = (-mp_halfFOV[ 0 ]) + ( mp_sizeVox[ 0 ] * 0.5 ) - p1_x[ 0 ];
448 
449  // Loop over the crossed X planes
450  for( int16_t i = i_min; i < i_max; ++i )
451  {
452  // Computing the coordinates of crossed plane
453  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
454  // in Y (point 1 and 2)
455  pos[ 0 ] = p1_y[ 1 ] + step * ri[ 0 ][ 1 ];
456  pos[ 1 ] = p1_y[ 2 ] + step * ri[ 1 ][ 1 ];
457  // in Z (point 3 and 4)
458  pos[ 2 ] = p1_z[ 3 ] + step * ri[ 2 ][ 2 ];
459  pos[ 3 ] = p1_z[ 4 ] + step * ri[ 3 ][ 2 ];
460 
461  // Computing the factor w1 and w2 normalizing the overlaps
462  w1 = ::fabs( pos[ 0 ] - pos[ 1 ] );
463  w2 = ::fabs( pos[ 2 ] - pos[ 3 ] );
464  HPFLTNB final_weight = 0.0;
465  if( ( w1 * w2 ) != 0 ) final_weight = weight / ( w1 * w2 );
466 
467  // Computing the index min and max on each axis
468  // In Y
469  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 0 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
470  index[ 1 ] = ::floor( m_toleranceY + ( pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
471  // In Z
472  index[ 2 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
473  index[ 3 ] = ::floor( m_toleranceZ + ( pos[ 3 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
474 
475  // apply mask if required
476  int test_index1 = i + index[ 0 ] * mp_nbVox[ 0 ] + index[ 2 ] * m_nbVoxXY;
477  int test_index2 = i + index[ 1 ] * mp_nbVox[ 0 ] + index[ 3 ] * m_nbVoxXY;
478  bool test_index = (index[ 0 ] >= 0 && index[ 2 ] >= 0 && index[ 1 ] >= 0 && index[ 3 ] >= 0
479  && index[ 0 ] < mp_nbVox[ 1 ] && index[ 1 ] < mp_nbVox[ 1 ] && index[ 2 ] < mp_nbVox[ 2 ] && index[ 3 ] < mp_nbVox[ 2 ]);
480  if (test_index && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index1) && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index2)) continue;
481 
482  if( index[ 0 ] < index[ 1 ] )
483  {
484  incr_orient_trans = 1;
485  idx_orient_trans = 1;
486  }
487  else if( index[ 0 ] > index[ 1 ] )
488  {
489  incr_orient_trans = -1;
490  idx_orient_trans = 0;
491  }
492 
493  // Getting the number of distance in Y
494  int16_t const n_distance_y = ::abs( index[ 0 ] - index[ 1 ] ) + 2;
495  // Computing the distances in Y
496  distance_y[ 0 ] = pos[ 0 ];
497  distance_y[ n_distance_y - 1 ] = pos[ 1 ];
498  // Computing the rest if necessary
499  if( n_distance_y > 2 )
500  {
501  for( int16_t d = 0; d < n_distance_y - 2; ++d )
502  distance_y[ d + 1 ] = (-mp_halfFOV[ 1 ]) + ( index[ 0 ] + idx_orient_trans + ( incr_orient_trans * d ) ) * mp_sizeVox[ 1 ];
503  }
504 
505  // Computing the final distance in Y
506  for( int16_t d = 0; d < n_distance_y - 1; ++d )
507  distance_y[ d ] = ::fabs( distance_y[ d + 1 ] - distance_y[ d ] );
508 
509  // Getting the number of distance in Z
510  int16_t const n_distance_z = ::abs( index[ 2 ] - index[ 3 ] ) + 2;
511  // Storing the positions in Y
512  distance_z[ 0 ] = pos[ 2 ];
513  distance_z[ n_distance_z - 1 ] = pos[ 3 ];
514  // Storing the rest if necessary
515  if( n_distance_z > 2 )
516  {
517  for( int16_t d = 0; d < n_distance_z - 2; ++d )
518  distance_z[ d + 1 ] = (-mp_halfFOV[ 2 ]) + ( index[ 2 ] + idx_orient_axial + ( incr_orient_axial * d ) ) * mp_sizeVox[ 2 ];
519  }
520 
521  // Computing the final distance in Z
522  for( int16_t d = 0; d < n_distance_z - 1; ++d )
523  distance_z[ d ] = ::fabs( distance_z[ d + 1 ] - distance_z[ d ] );
524 
525  // Loop over the overlap and store the elements
526  int16_t index_y, index_z;
527  for( int16_t jj = 0; jj < n_distance_z - 1; ++jj )
528  {
529  index_z = index[ 2 ] + jj * incr_orient_axial;
530  if( index_z < 0 || index_z > mp_nbVox[ 2 ] - 1 ) continue;
531 
532  for( int16_t ii = 0; ii < n_distance_y - 1; ++ii )
533  {
534  index_y = index[ 0 ] + ii * incr_orient_trans;
535  if( index_y < 0 || index_y > mp_nbVox[ 1 ] - 1 ) continue;
536 
537  ap_ProjectionLine->AddVoxel(a_direction, i + index_y * mp_nbVox[ 0 ] + index_z * m_nbVoxXY, final_weight * distance_z[ jj ] * distance_y[ ii ]);
538  }
539  }
540  }
541  }
542  else
543  {
544  // Compute the parametric value for each line
545  // For X, we use only the point 1 and 2
546  if( r[ 1 ][ 0 ] != 0.0 ) // point1
547  {
548  alpha_x_min[ 0 ] = ( (-mp_halfFOV[ 0 ]) - p1_x[ 1 ] ) / r[ 1 ][ 0 ];
549  alpha_x_min[ 1 ] = ( mp_halfFOV[ 0 ] - p1_x[ 1 ] ) / r[ 1 ][ 0 ];
550  alpha_x_max[ 0 ] = alpha_x_min[ 0 ];
551  alpha_x_max[ 1 ] = alpha_x_min[ 1 ];
552  }
553  else
554  {
555  alpha_x_min[ 0 ] = 0.0;
556  alpha_x_min[ 1 ] = 0.0;
557  alpha_x_max[ 0 ] = 1.0;
558  alpha_x_max[ 1 ] = 1.0;
559  }
560 
561  if( r[ 2 ][ 0 ] != 0.0 ) // point2
562  {
563  alpha_x_min[ 2 ] = ( (-mp_halfFOV[ 0 ]) - p1_x[ 2 ] ) / r[ 2 ][ 0 ];
564  alpha_x_min[ 3 ] = ( mp_halfFOV[ 0 ] - p1_x[ 2 ] ) / r[ 2 ][ 0 ];
565  alpha_x_max[ 2 ] = alpha_x_min[ 2 ];
566  alpha_x_max[ 3 ] = alpha_x_min[ 3 ];
567  }
568  else
569  {
570  alpha_x_min[ 2 ] = 0.0;
571  alpha_x_min[ 3 ] = 0.0;
572  alpha_x_max[ 2 ] = 1.0;
573  alpha_x_max[ 3 ] = 1.0;
574  }
575 
576  // Getting the minimum of alpha_x_min
577  alpha_min = std::max( alpha_min,
578  alpha_x_min[ std::minmax_element( alpha_x_min, alpha_x_min + 4 ).first - alpha_x_min ] );
579  // Getting the maximum of alpha_y_max
580  alpha_max = std::min( alpha_max,
581  alpha_x_max[ std::minmax_element( alpha_x_max, alpha_x_max + 4 ).second - alpha_x_max ] );
582 
583  // Computing the parametric values alpha_min and alpha_max
584  // Because the main direction is Y, we use only the center
585  alpha_y_min[ 0 ] = ( (-mp_halfFOV[ 1 ]) - p1_y[ 0 ] ) / r[ 0 ][ 1 ];
586  alpha_y_min[ 1 ] = ( mp_halfFOV[ 1 ] - p1_y[ 0 ] ) / r[ 0 ][ 1 ];
587  alpha_y_max[ 0 ] = alpha_y_min[ 0 ];
588  alpha_y_max[ 1 ] = alpha_y_min[ 1 ];
589  alpha_min = std::max( alpha_min,
590  std::min( alpha_y_min[ 0 ], alpha_y_min[ 1 ] ) );
591  alpha_max = std::min( alpha_max,
592  std::max( alpha_y_max[ 0 ], alpha_y_max[ 1 ] ) );
593 
594  // For Z, we use only the point 3 and 4
595  if( r[ 3 ][ 2 ] != 0.0 ) // point3
596  {
597  alpha_z_min[ 0 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
598  alpha_z_min[ 1 ] = ( mp_halfFOV[ 2 ] - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
599  alpha_z_max[ 0 ] = alpha_z_min[ 0 ];
600  alpha_z_max[ 1 ] = alpha_z_min[ 1 ];
601  }
602  else
603  {
604  alpha_z_min[ 0 ] = 0.0;
605  alpha_z_min[ 1 ] = 0.0;
606  alpha_z_max[ 0 ] = 1.0;
607  alpha_z_max[ 1 ] = 1.0;
608  }
609 
610  if( r[ 4 ][ 2 ] != 0.0 ) // point4
611  {
612  alpha_z_min[ 2 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
613  alpha_z_min[ 3 ] = ( mp_halfFOV[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
614  alpha_z_max[ 2 ] = alpha_z_min[ 2 ];
615  alpha_z_max[ 3 ] = alpha_z_min[ 3 ];
616  }
617  else
618  {
619  alpha_z_min[ 2 ] = 0.0;
620  alpha_z_min[ 3 ] = 0.0;
621  alpha_z_max[ 2 ] = 1.0;
622  alpha_z_max[ 3 ] = 1.0;
623  }
624 
625  // Getting the minimum of alpha_z_min
626  alpha_min = std::max( alpha_min, alpha_z_min[ std::minmax_element( alpha_z_min, alpha_z_min + 4 ).first - alpha_z_min ] );
627  // Getting the maximum of alpha_z_max
628  alpha_max = std::min( alpha_max, alpha_z_max[ std::minmax_element( alpha_z_max, alpha_z_max + 4 ).second - alpha_z_max ] );
629 
630  if( alpha_max <= alpha_min ) return 0;
631 
632  // Computing the first and the last plane
633  int16_t j_min = 0, j_max = 0;
634  if( r[ 0 ][ 1 ] > 0.0 )
635  {
636  j_min = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alpha_min * r[ 0 ][ 1 ] - p1_y[ 0 ] ) / mp_sizeVox[ 1 ] );
637  j_max = ::floor( m_toleranceY + ( p1_y[ 0 ] + alpha_max * r[ 0 ][ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
638  }
639  else if( r[ 0 ][ 1 ] < 0.0 )
640  {
641  j_min = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alpha_max * r[ 0 ][ 1 ] - p1_y[ 0 ] ) / mp_sizeVox[ 1 ] );
642  j_max = ::floor( m_toleranceY + ( p1_y[ 0 ] + alpha_min * r[ 0 ][ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
643  }
644 
645  // Computing weight of normalization
646  // Using the center
647  HPFLTNB const factor( ::fabs( r[ 0 ][ 1 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
648  HPFLTNB weight( mp_sizeVox[ 1 ] / factor );
649 
650  // Computing the increment for each 4 lines to project
651  // (the center is not projected)
652  for( uint8_t p = 0; p < 4; ++p )
653  {
654  ri[ p ][ 0 ] = r[ p + 1 ][ 0 ] / r[ p + 1 ][ 1 ];
655  ri[ p ][ 1 ] = 1.0;
656  ri[ p ][ 2 ] = r[ p + 1 ][ 2 ] / r[ p + 1 ][ 1 ];
657  }
658 
659  // Increment orientation and Index orientation
660  int8_t incr_orient_trans = 0;
661  int8_t idx_orient_trans = 0;
662 
663  //int8_t const incr_orient_trans = p2_x[ 1 ] < p2_x[ 2 ] ? 1 : -1;
664  int8_t const incr_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : -1;
665  //int8_t const idx_orient_trans = p2_x[ 1 ] < p2_x[ 2 ] ? 1 : 0;
666  int8_t const idx_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : 0;
667 
668  // Computing an offset to get the correct position in plane
669  HPFLTNB const offset = (-mp_halfFOV[ 1 ]) + ( mp_sizeVox[ 1 ] * 0.5 ) - p1_y[ 0 ];
670 
671  // Loop over the crossed Y planes
672  for( int16_t j = j_min; j < j_max; ++j )
673  {
674  // Computing the coordinates of crossed plane
675  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
676  // in X (point 1 and 2)
677  pos[ 0 ] = p1_x[ 1 ] + step * ri[ 0 ][ 0 ];
678  pos[ 1 ] = p1_x[ 2 ] + step * ri[ 1 ][ 0 ];
679  // in Z (point 3 and 4)
680  pos[ 2 ] = p1_z[ 3 ] + step * ri[ 2 ][ 2 ];
681  pos[ 3 ] = p1_z[ 4 ] + step * ri[ 3 ][ 2 ];
682 
683  // Computing the factor w1 and w2 normalizing the overlaps
684  w1 = ::fabs( pos[ 0 ] - pos[ 1 ] );
685  w2 = ::fabs( pos[ 2 ] - pos[ 3 ] );
686  HPFLTNB final_weight = 0.0;
687  if( ( w1 * w2 ) != 0 ) final_weight = weight / ( w1 * w2 );
688 
689  // Computing the index min and max on each axis
690  // In X
691  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
692  index[ 1 ] = ::floor( m_toleranceX + ( pos[ 1 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
693  // In Z
694  index[ 2 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
695  index[ 3 ] = ::floor( m_toleranceZ + ( pos[ 3 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
696 
697  // if voxel masked, skip
698  int test_index1 = index[ 0 ] + j* mp_nbVox[ 0 ] + index[ 2 ] * m_nbVoxXY;
699  int test_index2 = index[ 1 ] + j* mp_nbVox[ 0 ] + index[ 3 ] * m_nbVoxXY;
700  bool test_index = (index[ 0 ] >= 0 && index[ 2 ] >= 0 && index[ 1 ] >= 0 && index[ 3 ] >= 0
701  && index[ 0 ] < mp_nbVox[ 0 ] && index[ 1 ] < mp_nbVox[ 0 ] && index[ 2 ] < mp_nbVox[ 2 ] && index[ 3 ] < mp_nbVox[ 2 ]);
702  if (test_index && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index1) && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index2)) continue;
703 
704  if( index[ 0 ] < index[ 1 ] )
705  {
706  incr_orient_trans = 1;
707  idx_orient_trans = 1;
708  }
709  else if( index[ 0 ] > index[ 1 ] )
710  {
711  incr_orient_trans = -1;
712  idx_orient_trans = 0;
713  }
714 
715  // Getting the number of distance in X
716  int16_t const n_distance_x = ::abs( index[ 0 ] - index[ 1 ] ) + 2;
717  // Computing the distances in X
718  distance_x[ 0 ] = pos[ 0 ];
719  distance_x[ n_distance_x - 1 ] = pos[ 1 ];
720  // Computing the rest if necessary
721  if( n_distance_x > 2 )
722  {
723  for( int16_t d = 0; d < n_distance_x - 2; ++d )
724  distance_x[ d + 1 ] = (-mp_halfFOV[ 0 ]) + ( index[ 0 ] + idx_orient_trans + ( incr_orient_trans * d ) ) * mp_sizeVox[ 0 ];
725  }
726 
727  // Computing the final distance in X
728  for( int16_t d = 0; d < n_distance_x - 1; ++d )
729  distance_x[ d ] = ::fabs( distance_x[ d + 1 ] - distance_x[ d ] );
730 
731  // Getting the number of distance in Z
732  int16_t const n_distance_z = ::abs( index[ 2 ] - index[ 3 ] ) + 2;
733  // Storing the positions in Y
734  distance_z[ 0 ] = pos[ 2 ];
735  distance_z[ n_distance_z - 1 ] = pos[ 3 ];
736  // Storing the rest if necessary
737  if( n_distance_z > 2 )
738  {
739  for( int16_t d = 0; d < n_distance_z - 2; ++d )
740  distance_z[ d + 1 ] = (-mp_halfFOV[ 2 ]) + ( index[ 2 ] + idx_orient_axial + ( incr_orient_axial * d ) ) * mp_sizeVox[ 2 ];
741  }
742 
743  // Computing the final distance in Z
744  for( int16_t d = 0; d < n_distance_z - 1; ++d )
745  distance_z[ d ] = ::fabs( distance_z[ d + 1 ] - distance_z[ d ] );
746 
747  // Loop over the overlap and store the elements
748  int16_t index_x, index_z;
749  for( int16_t jj = 0; jj < n_distance_z - 1; ++jj )
750  {
751  index_z = index[ 2 ] + jj * incr_orient_axial;
752  if( index_z < 0 || index_z > mp_nbVox[ 2 ] - 1 ) continue;
753 
754  for( int16_t ii = 0; ii < n_distance_x - 1; ++ii )
755  {
756  index_x = index[ 0 ] + ii * incr_orient_trans;
757  if( index_x < 0 || index_x > mp_nbVox[ 0 ] - 1 ) continue;
758 
759  ap_ProjectionLine->AddVoxel(a_direction, index_x + j * mp_nbVox[ 0 ] + index_z * m_nbVoxXY, final_weight * distance_z[ jj ] * distance_x[ ii ]);
760  }
761  }
762  }
763  }
764 
765  return 0;
766 }
767 
768 // =====================================================================
769 // ---------------------------------------------------------------------
770 // ---------------------------------------------------------------------
771 // =====================================================================
772 
773 int iProjectorDistanceDriven::ProjectTOFListmode(int a_direction, oProjectionLine* ap_ProjectionLine)
774 {
775  #ifdef CASTOR_DEBUG
776  if (!m_initialized)
777  {
778  Cerr("***** iProjectorDistanceDriven::ProjectTOFListmode() -> Called while not initialized !" << endl);
779  Exit(EXIT_DEBUG);
780  }
781  #endif
782 
783  #ifdef CASTOR_VERBOSE
784  if (m_verbose>=10)
785  {
786  string direction = "";
787  if (a_direction==FORWARD) direction = "forward";
788  else direction = "backward";
789  Cout("iProjectorDistanceDriven::Project with TOF measurement -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
790  }
791  #endif
792 
793  // Computing the coordinates of the points on the bound of the source/crystal
794  // 1. The first point is on transaxial plan and the last point is on axial
795  // plan.
796  // Source spot size (CT) / Crystal 1 (PET):
797  //
798  // ------- P13 -------
799  // | |
800  // | |
801  // P12 Center P11
802  // | 0 |
803  // | |
804  // ------- P14 -------
805  // Y
806  // <---------. X
807  // |
808  // |
809  // |
810  // \/ Z
811  // --------------
812 
813  // Computing the position of the point2.
814  // In Distance Driven the point p21 relies the point P11, p22 -> p12,
815  // p23 -> p13 and p24 -> p14
816  // Pixel (CT) / Crystal 2 (PET):
817  //
818  // ------- P23 -------
819  // | |
820  // | |
821  // P22 Center P21
822  // | 0 |
823  // | |
824  // ------- P24 -------
825  // Y
826  // <---------. X
827  // |
828  // |
829  // |
830  // \/ Z
831  // --------------
832 
833  // buffers storing point 1 and 2
834  FLTNB p1_x[ 5 ];
835  FLTNB p1_y[ 5 ];
836  FLTNB p1_z[ 5 ];
837  FLTNB p2_x[ 5 ];
838  FLTNB p2_y[ 5 ];
839  FLTNB p2_z[ 5 ];
840 
842  ap_ProjectionLine->GetIndex1(), // Index 1
843  ap_ProjectionLine->GetIndex2(), // Index 2
844  ap_ProjectionLine->GetPosition1(), // Line position for point 1
845  ap_ProjectionLine->GetPosition2(), // Line position for point 2
846  &p1_x[1], &p1_y[1], &p1_z[1], // Edges for point 1
847  &p2_x[1], &p2_y[1], &p2_z[1] // Edges for point 2
848  ))
849  {
850  Cerr("***** iProjectorDistanceDriven::ProjectWithoutTOF() -> A problem occurred while getting the edges' center positions !" << endl);
851  return 1;
852  }
853 
854  // Point 1 focal (CT) or crystal (PET) position
855  FLTNB* p1 = ap_ProjectionLine->GetPosition1();
856 
857  // Point 2 pixel (CT) or crystal (PET) position
858  FLTNB* p2 = ap_ProjectionLine->GetPosition2();
859 
860  // Center position p1
861  p1_x[ 0 ] = p1[ 0 ]; p1_y[ 0 ] = p1[ 1 ]; p1_z[ 0 ] = p1[ 2 ];
862 
863  // Center position p2
864  p2_x[ 0 ] = p2[ 0 ]; p2_y[ 0 ] = p2[ 1 ]; p2_z[ 0 ] = p2[ 2 ];
865 
866  // storing the ray relying p1 and p2
867  HPFLTNB r[ 5 ][ 3 ];
868 
869  // buffer storing the intersection
870  HPFLTNB ri[ 4 ][ 3 ];
871 
872  // Take the position of ray relying p1 and p2 for all the 5 points
873  for( int p = 0; p < 5; ++p )
874  {
875  r[ p ][ 0 ] = p2_x[ p ] - p1_x[ p ];
876  r[ p ][ 1 ] = p2_y[ p ] - p1_y[ p ];
877  r[ p ][ 2 ] = p2_z[ p ] - p1_z[ p ];
878  }
879 
880  // Computing the square of distance for the center
881  HPFLTNB const r2[ 3 ] = {
882  r[ 0 ][ 0 ] * r[ 0 ][ 0 ],
883  r[ 0 ][ 1 ] * r[ 0 ][ 1 ],
884  r[ 0 ][ 2 ] * r[ 0 ][ 2 ]
885  };
886 
887  // Find the first and last intersecting plane using the parametric
888  // values alpha_min and alpha_max
889  HPFLTNB alpha_min = 0.0, alpha_max = 1.0;
890 
891  // Buffer storing the alpha on each axis
892  // 2 different buffers to get the min and the max
893  HPFLTNB alpha_x_min[ 4 ], alpha_x_max[ 4 ];
894  HPFLTNB alpha_y_min[ 4 ], alpha_y_max[ 4 ];
895  HPFLTNB alpha_z_min[ 4 ], alpha_z_max[ 4 ];
896 
897  // Position of the projected points in plane
898  // If the main direction is X:
899  // pos[0] is the position of point1 in Y
900  // pos[1] is the position of point2 in Y
901  // pos[2] is the position of point3 in Z
902  // pos[3] is the position of point4 in Z
903  HPFLTNB pos[ 4 ];
904 
905  // Width of intersection
906  // w1 normalizes the overlap 1
907  // w2 normalizes the overlap 2
908  HPFLTNB w1, w2;
909 
910  // Buffer storing the min. and max. indices bounding the overlap
911  int index[ 4 ];
912 
913  // Buffer storing the distances
914  // 50 HPFLTNB is largely enough!!!
915  HPFLTNB distance_x[ 50 ];
916  HPFLTNB distance_y[ 50 ];
917  HPFLTNB distance_z[ 50 ];
918 
919  FLTNB length_LOR = ap_ProjectionLine->GetLength();
920 
921  // Get TOF info
922  HPFLTNB tof_measurement = ap_ProjectionLine->GetTOFMeasurementInPs();
923 
924  // convert delta time into delta length
925  HPFLTNB tof_delta = tof_measurement * SPEED_OF_LIGHT_IN_MM_PER_PS * 0.5;
926 
927  // TOF standard deviation and truncation
929  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
930  HPFLTNB tof_half_span = 0.;
931 
933  else if (m_TOFBinProperProcessingFlag) tof_half_span = tof_sigma * m_TOFNbSigmas + 0.5 * m_TOFBinSizeInMm;
934  else tof_half_span = tof_sigma * m_TOFNbSigmas;
935 
936  // distance between the first event1 and the TOF measurement
937  HPFLTNB lor_tof_center = length_LOR * 0.5 + tof_delta;
938 
939  // index along each axis of the first voxel falling inside the truncated TOF distribution centered at the TOF measurement
940  HPFLTNB tof_edge_low[] = {0,0,0};
941  // index along each axis of the last voxel falling inside the truncated TOF distribution centered at the TOF measurement
942  HPFLTNB tof_edge_high[] = {0,0,0};
943  HPFLTNB tof_center[] = {0,0,0};
944  INTNB tof_index;
945 
946  // low/high voxel edges (in absolute coordinates) for truncated TOF
947  for (int ax=0;ax<3;ax++)
948  {
949  // absolute coordinate along each axis of the center of the TOF distribution
950  tof_center[ax] = p1[ax] + lor_tof_center * r[0][ax] / length_LOR;
951 
952  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
953  tof_edge_low[ax] = tof_center[ax] - tof_half_span * fabs(r[0][ax]) / length_LOR;
954  tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
955  // if low TOF edge above the highest FOV edge, return empty line
956  if (tof_index>mp_nbVox[ax]-1) return 0;
957  tof_edge_low[ax] = (HPFLTNB)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
958 
959  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
960  tof_edge_high[ax] = tof_center[ax] + tof_half_span * fabs(r[0][ax]) / length_LOR;
961  tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
962  // if high TOF edge below the lowest FOV edge, return empty line
963  if (tof_index<0) return 0;
964  tof_edge_high[ax] = (HPFLTNB)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
965  }
966 
967  // Computing the angle of line vs. horizontal
968  float const angle = std::acos( ( r[ 0 ][ 0 ] ) / ( std::sqrt( r2[ 0 ] + r2[ 1 ] ) ) ) * 180.0f / M_PI;
969 
970  // Condition on the largest component of r, taking the center
971  if( ( angle >= 0.0 && angle <= 45.0 ) || ( angle >= 135.0 && angle <= 180.0 ) )
972  {
973  // Computing the parametric values alpha_min and alpha_max
974  // taking into account TOF truncation
975  // Because the main direction is X, we use only the center
976  alpha_x_min[ 0 ] = ( tof_edge_low[ 0 ] - p1_x[ 0 ] ) / r[ 0 ][ 0 ];
977  alpha_x_min[ 1 ] = ( tof_edge_high[ 0 ] - p1_x[ 0 ] ) / r[ 0 ][ 0 ];
978  alpha_x_max[ 0 ] = alpha_x_min[ 0 ];
979  alpha_x_max[ 1 ] = alpha_x_min[ 1 ];
980  alpha_min = std::max( alpha_min, std::min( alpha_x_min[ 0 ], alpha_x_min[ 1 ] ) );
981  alpha_max = std::min( alpha_max, std::max( alpha_x_max[ 0 ], alpha_x_max[ 1 ] ) );
982 
983  // Compute the parametric value for each line
984  // For Y, we use only the point 1 and 2
985  if( r[ 1 ][ 1 ] != 0 ) // point1
986  {
987  alpha_y_min[ 0 ] = ( tof_edge_low[ 1 ] - p1_y[ 1 ] ) / r[ 1 ][ 1 ];
988  alpha_y_min[ 1 ] = ( tof_edge_high[ 1 ] - p1_y[ 1 ] ) / r[ 1 ][ 1 ];
989  alpha_y_max[ 0 ] = alpha_y_min[ 0 ];
990  alpha_y_max[ 1 ] = alpha_y_min[ 1 ];
991  }
992  else
993  {
994  alpha_y_min[ 0 ] = 0.0;
995  alpha_y_min[ 1 ] = 0.0;
996  alpha_y_max[ 0 ] = 1.0;
997  alpha_y_max[ 1 ] = 1.0;
998  }
999 
1000  if( r[ 2 ][ 1 ] != 0 ) // point2
1001  {
1002  alpha_y_min[ 2 ] = ( tof_edge_low[ 1 ] - p1_y[ 2 ] ) / r[ 2 ][ 1 ];
1003  alpha_y_min[ 3 ] = ( tof_edge_high[ 1 ] - p1_y[ 2 ] ) / r[ 2 ][ 1 ];
1004  alpha_y_max[ 2 ] = alpha_y_min[ 2 ];
1005  alpha_y_max[ 3 ] = alpha_y_min[ 3 ];
1006  }
1007  else
1008  {
1009  alpha_y_min[ 2 ] = 0.0;
1010  alpha_y_min[ 3 ] = 0.0;
1011  alpha_y_max[ 2 ] = 1.0;
1012  alpha_y_max[ 3 ] = 1.0;
1013  }
1014 
1015  // Getting the minimum of alpha_y_min
1016  alpha_min = std::max( alpha_min,
1017  alpha_y_min[ std::minmax_element( alpha_y_min, alpha_y_min + 4 ).first - alpha_y_min ] );
1018  // Getting the maximum of alpha_y_max
1019  alpha_max = std::min( alpha_max,
1020  alpha_y_max[ std::minmax_element( alpha_y_max, alpha_y_max + 4 ).second - alpha_y_max ] );
1021 
1022  // For Z, we use only the point 3 and 4
1023  if( r[ 3 ][ 2 ] != 0 ) // point3
1024  {
1025  alpha_z_min[ 0 ] = ( tof_edge_low[ 2 ] - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
1026  alpha_z_min[ 1 ] = ( tof_edge_high[ 2 ] - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
1027  alpha_z_max[ 0 ] = alpha_z_min[ 0 ];
1028  alpha_z_max[ 1 ] = alpha_z_min[ 1 ];
1029  }
1030  else
1031  {
1032  alpha_z_min[ 0 ] = 0.0;
1033  alpha_z_min[ 1 ] = 0.0;
1034  alpha_z_max[ 0 ] = 1.0;
1035  alpha_z_max[ 1 ] = 1.0;
1036  }
1037 
1038  if( r[ 4 ][ 2 ] != 0 ) // point4
1039  {
1040  alpha_z_min[ 2 ] = ( tof_edge_low[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
1041  alpha_z_min[ 3 ] = ( tof_edge_high[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
1042  alpha_z_max[ 2 ] = alpha_z_min[ 2 ];
1043  alpha_z_max[ 3 ] = alpha_z_min[ 3 ];
1044  }
1045  else
1046  {
1047  alpha_z_min[ 2 ] = 0.0;
1048  alpha_z_min[ 3 ] = 0.0;
1049  alpha_z_max[ 2 ] = 1.0;
1050  alpha_z_max[ 3 ] = 1.0;
1051  }
1052 
1053  // Getting the minimum of alpha_z_min
1054  alpha_min = std::max( alpha_min,
1055  alpha_z_min[ std::minmax_element( alpha_z_min, alpha_z_min + 4 ).first
1056  - alpha_z_min ] );
1057  // Getting the maximum of alpha_z_max
1058  alpha_max = std::min( alpha_max,
1059  alpha_z_max[ std::minmax_element( alpha_z_max, alpha_z_max + 4 ).second
1060  - alpha_z_max ] );
1061 
1062  if( alpha_max <= alpha_min ) return 0;
1063 
1064  // Computing the first and the last plane
1065  int16_t i_min = 0, i_max = 0;
1066  if( r[ 0 ][ 0 ] > 0.0 )
1067  {
1068  i_min = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alpha_min * r[ 0 ][ 0 ] - p1_x[ 0 ] ) / mp_sizeVox[ 0 ] );
1069  i_max = ::floor( m_toleranceX + ( p1_x[ 0 ] + alpha_max * r[ 0 ][ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1070  }
1071  else if( r[ 0 ][ 0 ] < 0.0 )
1072  {
1073  i_min = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alpha_max * r[ 0 ][ 0 ] - p1_x[ 0 ] ) / mp_sizeVox[ 0 ] );
1074  i_max = ::floor( m_toleranceX + ( p1_x[ 0 ] + alpha_min * r[ 0 ][ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1075  }
1076 
1077  // Computing weight of normalization
1078  // Using the center
1079  HPFLTNB const factor( ::fabs( r[ 0 ][ 0 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
1080  HPFLTNB weight( mp_sizeVox[ 0 ] / factor );
1081  HPFLTNB const factor_for_tof( length_LOR / r[ 0 ][ 0 ] );
1082 
1083  // Computing the increment for each 4 lines to project
1084  // (the center is not projected)
1085  for( uint8_t p = 0; p < 4; ++p )
1086  {
1087  ri[ p ][ 0 ] = 1.0; //r[ p + 1 ][ 0 ] / r[ p + 1 ][ 0 ]
1088  ri[ p ][ 1 ] = r[ p + 1 ][ 1 ] / r[ p + 1 ][ 0 ];
1089  ri[ p ][ 2 ] = r[ p + 1 ][ 2 ] / r[ p + 1 ][ 0 ];
1090  }
1091 
1092  // Increment orientation
1093  int8_t incr_orient_trans = 0;
1094  int8_t idx_orient_trans = 0;
1095 
1096  //int8_t const incr_orient_trans = p2_y[ 1 ] < p2_y[ 2 ] ? 1 : -1;
1097  int8_t const incr_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : -1;
1098 
1099  // Index orientation
1100  //int8_t const idx_orient_trans = p2_y[ 1 ] < p2_y[ 2 ] ? 1 : 0;
1101  int8_t const idx_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : 0;
1102 
1103  // Computing an offset to get the correct position in plane
1104  HPFLTNB const offset = (-mp_halfFOV[ 0 ]) + ( mp_sizeVox[ 0 ] * 0.5 ) - p1_x[ 0 ];
1105 
1106  // Loop over the crossed X planes
1107  for( int16_t i = i_min; i < i_max; ++i )
1108  {
1109  // Computing the coordinates of crossed plane
1110  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
1111  // in Y (point 1 and 2)
1112  pos[ 0 ] = p1_y[ 1 ] + step * ri[ 0 ][ 1 ];
1113  pos[ 1 ] = p1_y[ 2 ] + step * ri[ 1 ][ 1 ];
1114  // in Z (point 3 and 4)
1115  pos[ 2 ] = p1_z[ 3 ] + step * ri[ 2 ][ 2 ];
1116  pos[ 3 ] = p1_z[ 4 ] + step * ri[ 3 ][ 2 ];
1117 
1118  // Computing the factor w1 and w2 normalizing the overlaps
1119  w1 = ::fabs( pos[ 0 ] - pos[ 1 ] );
1120  w2 = ::fabs( pos[ 2 ] - pos[ 3 ] );
1121  HPFLTNB final_weight = 0.0;
1122  if( ( w1 * w2 ) != 0 ) final_weight = weight / ( w1 * w2 );
1123 
1124  // Computing the index min and max on each axis
1125  // In Y
1126  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 0 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1127  index[ 1 ] = ::floor( m_toleranceY + ( pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1128  // In Z
1129  index[ 2 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
1130  index[ 3 ] = ::floor( m_toleranceZ + ( pos[ 3 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
1131 
1132  // apply mask if required
1133  int test_index1 = i + index[ 0 ] * mp_nbVox[ 0 ] + index[ 2 ] * m_nbVoxXY;
1134  int test_index2 = i + index[ 1 ] * mp_nbVox[ 0 ] + index[ 3 ] * m_nbVoxXY;
1135  bool test_index = (index[ 0 ] >= 0 && index[ 2 ] >= 0 && index[ 1 ] >= 0 && index[ 3 ] >= 0
1136  && index[ 0 ] < mp_nbVox[ 1 ] && index[ 1 ] < mp_nbVox[ 1 ] && index[ 2 ] < mp_nbVox[ 2 ] && index[ 3 ] < mp_nbVox[ 2 ]);
1137  if (test_index && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index1) && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index2)) continue;
1138 
1139  if( index[ 0 ] < index[ 1 ] )
1140  {
1141  incr_orient_trans = 1;
1142  idx_orient_trans = 1;
1143  }
1144  else if( index[ 0 ] > index[ 1 ] )
1145  {
1146  incr_orient_trans = -1;
1147  idx_orient_trans = 0;
1148  }
1149 
1150  // Getting the number of distance in Y
1151  int16_t const n_distance_y = ::abs( index[ 0 ] - index[ 1 ] ) + 2;
1152  // Computing the distances in Y
1153  distance_y[ 0 ] = pos[ 0 ];
1154  distance_y[ n_distance_y - 1 ] = pos[ 1 ];
1155  // Computing the rest if necessary
1156  if( n_distance_y > 2 )
1157  {
1158  for( int16_t d = 0; d < n_distance_y - 2; ++d )
1159  distance_y[ d + 1 ] = (-mp_halfFOV[ 1 ]) + ( index[ 0 ] + idx_orient_trans + ( incr_orient_trans * d ) ) * mp_sizeVox[ 1 ];
1160  }
1161 
1162  // Computing the final distance in Y
1163  for( int16_t d = 0; d < n_distance_y - 1; ++d )
1164  distance_y[ d ] = ::fabs( distance_y[ d + 1 ] - distance_y[ d ] );
1165 
1166  // Getting the number of distance in Z
1167  int16_t const n_distance_z = ::abs( index[ 2 ] - index[ 3 ] ) + 2;
1168  // Storing the positions in Y
1169  distance_z[ 0 ] = pos[ 2 ];
1170  distance_z[ n_distance_z - 1 ] = pos[ 3 ];
1171  // Storing the rest if necessary
1172  if( n_distance_z > 2 )
1173  {
1174  for( int16_t d = 0; d < n_distance_z - 2; ++d )
1175  distance_z[ d + 1 ] = (-mp_halfFOV[ 2 ]) + ( index[ 2 ] + idx_orient_axial + ( incr_orient_axial * d ) ) * mp_sizeVox[ 2 ];
1176  }
1177 
1178  // Computing the final distance in Z
1179  for( int16_t d = 0; d < n_distance_z - 1; ++d )
1180  distance_z[ d ] = ::fabs( distance_z[ d + 1 ] - distance_z[ d ] );
1181 
1182  HPFLTNB tof_weight = 0.;
1184  {
1185  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
1186  // nearest to the projection of the voxel center onto the LOR
1187  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1188  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight = mp_TOFWeightingFcn[temp];
1189  }
1190  else
1191  {
1193  {
1194  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
1195  // of the Gaussian centered at the projection of the voxel center onto the LOR
1196  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
1197  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
1198  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
1199  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
1200  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
1201  tof_weight = 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
1202  }
1203  else
1204  {
1205  // value of the normalized Gaussian (centered at the TOF measurement)
1206  // at the projection of the voxel center onto the LOR
1207  HPFLTNB temp = (step * factor_for_tof - lor_tof_center) / tof_sigma;
1208  tof_weight = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
1209  }
1210  }
1211 
1212  // Loop over the overlap and store the elements
1213  int16_t index_y, index_z;
1214  for( int16_t jj = 0; jj < n_distance_z - 1; ++jj )
1215  {
1216  index_z = index[ 2 ] + jj * incr_orient_axial;
1217  if( index_z < 0 || index_z > mp_nbVox[ 2 ] - 1 ) continue;
1218 
1219  for( int16_t ii = 0; ii < n_distance_y - 1; ++ii )
1220  {
1221  index_y = index[ 0 ] + ii * incr_orient_trans;
1222  if( index_y < 0 || index_y > mp_nbVox[ 1 ] - 1 ) continue;
1223 
1224  ap_ProjectionLine->AddVoxel(a_direction, i + index_y * mp_nbVox[ 0 ] + index_z * m_nbVoxXY, final_weight * distance_z[ jj ] * distance_y[ ii ] * tof_weight);
1225  }
1226  }
1227  }
1228  }
1229  else
1230  {
1231  // Compute the parametric value for each line
1232  // taking into account TOF truncation
1233  // For X, we use only the point 1 and 2
1234  if( r[ 1 ][ 0 ] != 0.0 ) // point1
1235  {
1236  alpha_x_min[ 0 ] = ( tof_edge_low[ 0 ] - p1_x[ 1 ] ) / r[ 1 ][ 0 ];
1237  alpha_x_min[ 1 ] = ( tof_edge_high[ 0 ] - p1_x[ 1 ] ) / r[ 1 ][ 0 ];
1238  alpha_x_max[ 0 ] = alpha_x_min[ 0 ];
1239  alpha_x_max[ 1 ] = alpha_x_min[ 1 ];
1240  }
1241  else
1242  {
1243  alpha_x_min[ 0 ] = 0.0;
1244  alpha_x_min[ 1 ] = 0.0;
1245  alpha_x_max[ 0 ] = 1.0;
1246  alpha_x_max[ 1 ] = 1.0;
1247  }
1248 
1249  if( r[ 2 ][ 0 ] != 0 ) // point2
1250  {
1251  alpha_x_min[ 2 ] = ( tof_edge_low[ 0 ] - p1_x[ 2 ] ) / r[ 2 ][ 0 ];
1252  alpha_x_min[ 3 ] = ( tof_edge_high[ 0 ] - p1_x[ 2 ] ) / r[ 2 ][ 0 ];
1253  alpha_x_max[ 2 ] = alpha_x_min[ 2 ];
1254  alpha_x_max[ 3 ] = alpha_x_min[ 3 ];
1255  }
1256  else
1257  {
1258  alpha_x_min[ 2 ] = 0.0;
1259  alpha_x_min[ 3 ] = 0.0;
1260  alpha_x_max[ 2 ] = 1.0;
1261  alpha_x_max[ 3 ] = 1.0;
1262  }
1263 
1264  // Getting the minimum of alpha_x_min
1265  alpha_min = std::max( alpha_min,
1266  alpha_x_min[ std::minmax_element( alpha_x_min, alpha_x_min + 4 ).first - alpha_x_min ] );
1267  // Getting the maximum of alpha_y_max
1268  alpha_max = std::min( alpha_max,
1269  alpha_x_max[ std::minmax_element( alpha_x_max, alpha_x_max + 4 ).second - alpha_x_max ] );
1270 
1271  // Computing the parametric values alpha_min and alpha_max
1272  // Because the main direction is Y, we use only the center
1273  alpha_y_min[ 0 ] = ( tof_edge_low[1] - p1_y[ 0 ] ) / r[ 0 ][ 1 ];
1274  alpha_y_min[ 1 ] = ( tof_edge_high[1] - p1_y[ 0 ] ) / r[ 0 ][ 1 ];
1275  alpha_y_max[ 0 ] = alpha_y_min[ 0 ];
1276  alpha_y_max[ 1 ] = alpha_y_min[ 1 ];
1277  alpha_min = std::max( alpha_min,
1278  std::min( alpha_y_min[ 0 ], alpha_y_min[ 1 ] ) );
1279  alpha_max = std::min( alpha_max,
1280  std::max( alpha_y_max[ 0 ], alpha_y_max[ 1 ] ) );
1281 
1282  // For Z, we use only the point 3 and 4
1283  if( r[ 3 ][ 2 ] != 0 ) // point3
1284  {
1285  alpha_z_min[ 0 ] = ( tof_edge_low[ 2 ] - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
1286  alpha_z_min[ 1 ] = ( tof_edge_high[ 2 ]- p1_z[ 3 ] ) / r[ 3 ][ 2 ];
1287  alpha_z_max[ 0 ] = alpha_z_min[ 0 ];
1288  alpha_z_max[ 1 ] = alpha_z_min[ 1 ];
1289  }
1290  else
1291  {
1292  alpha_z_min[ 0 ] = 0.0;
1293  alpha_z_min[ 1 ] = 0.0;
1294  alpha_z_max[ 0 ] = 1.0;
1295  alpha_z_max[ 1 ] = 1.0;
1296  }
1297 
1298  if( r[ 4 ][ 2 ] != 0 ) // point4
1299  {
1300  alpha_z_min[ 2 ] = ( tof_edge_low[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
1301  alpha_z_min[ 3 ] = ( tof_edge_high[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
1302  alpha_z_max[ 2 ] = alpha_z_min[ 2 ];
1303  alpha_z_max[ 3 ] = alpha_z_min[ 3 ];
1304  }
1305  else
1306  {
1307  alpha_z_min[ 2 ] = 0.0;
1308  alpha_z_min[ 3 ] = 0.0;
1309  alpha_z_max[ 2 ] = 1.0;
1310  alpha_z_max[ 3 ] = 1.0;
1311  }
1312 
1313  // Getting the minimum of alpha_z_min
1314  alpha_min = std::max( alpha_min, alpha_z_min[ std::minmax_element( alpha_z_min, alpha_z_min + 4 ).first - alpha_z_min ] );
1315  // Getting the maximum of alpha_z_max
1316  alpha_max = std::min( alpha_max, alpha_z_max[ std::minmax_element( alpha_z_max, alpha_z_max + 4 ).second - alpha_z_max ] );
1317 
1318  if( alpha_max <= alpha_min ) return 0;
1319 
1320  // Computing the first and the last plane
1321  int16_t j_min = 0, j_max = 0;
1322  if( r[ 0 ][ 1 ] > 0.0 )
1323  {
1324  j_min = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alpha_min * r[ 0 ][ 1 ] - p1_y[ 0 ] ) / mp_sizeVox[ 1 ] );
1325  j_max = ::floor( m_toleranceY + ( p1_y[ 0 ] + alpha_max * r[ 0 ][ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1326  }
1327  else if( r[ 0 ][ 1 ] < 0.0 )
1328  {
1329  j_min = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alpha_max * r[ 0 ][ 1 ] - p1_y[ 0 ] ) / mp_sizeVox[ 1 ] );
1330  j_max = ::floor( m_toleranceY + ( p1_y[ 0 ] + alpha_min * r[ 0 ][ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1331  }
1332 
1333  // Computing weight of normalization
1334  // Using the center
1335  HPFLTNB const factor( ::fabs( r[ 0 ][ 1 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
1336  HPFLTNB weight( mp_sizeVox[ 1 ] / factor );
1337  HPFLTNB const factor_for_tof( length_LOR / r[ 0 ][ 1 ] );
1338 
1339  // Computing the increment for each 4 lines to project
1340  // (the center is not projected)
1341  for( uint8_t p = 0; p < 4; ++p )
1342  {
1343  ri[ p ][ 0 ] = r[ p + 1 ][ 0 ] / r[ p + 1 ][ 1 ];
1344  ri[ p ][ 1 ] = 1.0;
1345  ri[ p ][ 2 ] = r[ p + 1 ][ 2 ] / r[ p + 1 ][ 1 ];
1346  }
1347 
1348  // Increment orientation and Index orientation
1349  int8_t incr_orient_trans = 0;
1350  int8_t idx_orient_trans = 0;
1351 
1352  //int8_t const incr_orient_trans = p2_x[ 1 ] < p2_x[ 2 ] ? 1 : -1;
1353  int8_t const incr_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : -1;
1354  //int8_t const idx_orient_trans = p2_x[ 1 ] < p2_x[ 2 ] ? 1 : 0;
1355  int8_t const idx_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : 0;
1356 
1357  // Computing an offset to get the correct position in plane
1358  HPFLTNB const offset = (-mp_halfFOV[ 1 ]) + ( mp_sizeVox[ 1 ] * 0.5 ) - p1_y[ 0 ];
1359 
1360  // Loop over the crossed Y planes
1361  for( int16_t j = j_min; j < j_max; ++j )
1362  {
1363  // Computing the coordinates of crossed plane
1364  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
1365  // in Y (point 1 and 2)
1366  pos[ 0 ] = p1_x[ 1 ] + step * ri[ 0 ][ 0 ];
1367  pos[ 1 ] = p1_x[ 2 ] + step * ri[ 1 ][ 0 ];
1368  // in Z (point 3 and 4)
1369  pos[ 2 ] = p1_z[ 3 ] + step * ri[ 2 ][ 2 ];
1370  pos[ 3 ] = p1_z[ 4 ] + step * ri[ 3 ][ 2 ];
1371 
1372  // Computing the factor w1 and w2 normalizing the overlaps
1373  w1 = ::fabs( pos[ 0 ] - pos[ 1 ] );
1374  w2 = ::fabs( pos[ 2 ] - pos[ 3 ] );
1375  HPFLTNB final_weight = 0.0;
1376  if( ( w1 * w2 ) != 0 ) final_weight = weight / ( w1 * w2 );
1377 
1378  // Computing the index min and max on each axis
1379  // In Y
1380  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1381  index[ 1 ] = ::floor( m_toleranceX + ( pos[ 1 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1382  // In Z
1383  index[ 2 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
1384  index[ 3 ] = ::floor( m_toleranceZ + ( pos[ 3 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
1385 
1386  // if voxel masked, skip
1387  int test_index1 = index[ 0 ] + j* mp_nbVox[ 0 ] + index[ 2 ] * m_nbVoxXY;
1388  int test_index2 = index[ 1 ] + j* mp_nbVox[ 0 ] + index[ 3 ] * m_nbVoxXY;
1389  bool test_index = (index[ 0 ] >= 0 && index[ 2 ] >= 0 && index[ 1 ] >= 0 && index[ 3 ] >= 0
1390  && index[ 0 ] < mp_nbVox[ 0 ] && index[ 1 ] < mp_nbVox[ 0 ] && index[ 2 ] < mp_nbVox[ 2 ] && index[ 3 ] < mp_nbVox[ 2 ]);
1391  if (test_index && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index1) && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index2)) continue;
1392 
1393  if( index[ 0 ] < index[ 1 ] )
1394  {
1395  incr_orient_trans = 1;
1396  idx_orient_trans = 1;
1397  }
1398  else if( index[ 0 ] > index[ 1 ] )
1399  {
1400  incr_orient_trans = -1;
1401  idx_orient_trans = 0;
1402  }
1403 
1404  // Getting the number of distance in X
1405  int16_t const n_distance_x = ::abs( index[ 0 ] - index[ 1 ] ) + 2;
1406  // Computing the distances in X
1407  distance_x[ 0 ] = pos[ 0 ];
1408  distance_x[ n_distance_x - 1 ] = pos[ 1 ];
1409  // Computing the rest if necessary
1410  if( n_distance_x > 2 )
1411  {
1412  for( int16_t d = 0; d < n_distance_x - 2; ++d )
1413  distance_x[ d + 1 ] = (-mp_halfFOV[ 0 ]) + ( index[ 0 ] + idx_orient_trans + ( incr_orient_trans * d ) ) * mp_sizeVox[ 0 ];
1414  }
1415 
1416  // Computing the final distance in X
1417  for( int16_t d = 0; d < n_distance_x - 1; ++d )
1418  distance_x[ d ] = ::fabs( distance_x[ d + 1 ] - distance_x[ d ] );
1419 
1420  // Getting the number of distance in Z
1421  int16_t const n_distance_z = ::abs( index[ 2 ] - index[ 3 ] ) + 2;
1422  // Storing the positions in Y
1423  distance_z[ 0 ] = pos[ 2 ];
1424  distance_z[ n_distance_z - 1 ] = pos[ 3 ];
1425  // Storing the rest if necessary
1426  if( n_distance_z > 2 )
1427  {
1428  for( int16_t d = 0; d < n_distance_z - 2; ++d )
1429  distance_z[ d + 1 ] = (-mp_halfFOV[ 2 ]) + ( index[ 2 ] + idx_orient_axial + ( incr_orient_axial * d ) ) * mp_sizeVox[ 2 ];
1430  }
1431 
1432  // Computing the final distance in Z
1433  for( int16_t d = 0; d < n_distance_z - 1; ++d )
1434  distance_z[ d ] = ::fabs( distance_z[ d + 1 ] - distance_z[ d ] );
1435 
1436  HPFLTNB tof_weight = 0.;
1438  {
1439  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
1440  // nearest to the projection of the voxel center onto the LOR
1441  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1442  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight = mp_TOFWeightingFcn[temp];
1443  }
1444  else
1445  {
1447  {
1448  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
1449  // of the Gaussian centered at the projection of the voxel center onto the LOR
1450  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
1451  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
1452  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
1453  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
1454  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
1455  tof_weight = 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
1456  }
1457  else
1458  {
1459  // value of the normalized Gaussian (centered at the TOF measurement)
1460  // at the projection of the voxel center onto the LOR
1461  HPFLTNB temp = (step * factor_for_tof - lor_tof_center) / tof_sigma;
1462  tof_weight = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
1463  }
1464  }
1465 
1466  // Loop over the overlap and store the elements
1467  int16_t index_x, index_z;
1468  for( int16_t jj = 0; jj < n_distance_z - 1; ++jj )
1469  {
1470  index_z = index[ 2 ] + jj * incr_orient_axial;
1471  if( index_z < 0 || index_z > mp_nbVox[ 2 ] - 1 ) continue;
1472 
1473  for( int16_t ii = 0; ii < n_distance_x - 1; ++ii )
1474  {
1475  index_x = index[ 0 ] + ii * incr_orient_trans;
1476  if( index_x < 0 || index_x > mp_nbVox[ 0 ] - 1 ) continue;
1477 
1478  ap_ProjectionLine->AddVoxel(a_direction, index_x + j * mp_nbVox[ 0 ] + index_z * m_nbVoxXY, final_weight * distance_z[ jj ] * distance_x[ ii ] * tof_weight);
1479  }
1480  }
1481  }
1482  }
1483 
1484  return 0;
1485 }
1486 
1487 // =====================================================================
1488 // ---------------------------------------------------------------------
1489 // ---------------------------------------------------------------------
1490 // =====================================================================
1491 
1492 int iProjectorDistanceDriven::ProjectTOFHistogram(int a_direction, oProjectionLine* ap_ProjectionLine)
1493 {
1494  #ifdef CASTOR_DEBUG
1495  if (!m_initialized)
1496  {
1497  Cerr("***** iProjectorDistanceDriven::ProjectTOFHistogram() -> Called while not initialized !" << endl);
1498  Exit(EXIT_DEBUG);
1499  }
1500  #endif
1501 
1502  #ifdef CASTOR_VERBOSE
1503  if (m_verbose>=10)
1504  {
1505  string direction = "";
1506  if (a_direction==FORWARD) direction = "forward";
1507  else direction = "backward";
1508  Cout("iProjectorDistanceDriven::Project with TOF bins -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
1509  }
1510  #endif
1511 
1512  // Computing the coordinates of the points on the bound of the source/crystal
1513  // 1. The first point is on transaxial plan and the last point is on axial
1514  // plan.
1515  // Source spot size (CT) / Crystal 1 (PET):
1516  //
1517  // ------- P13 -------
1518  // | |
1519  // | |
1520  // P12 Center P11
1521  // | 0 |
1522  // | |
1523  // ------- P14 -------
1524  // Y
1525  // <---------. X
1526  // |
1527  // |
1528  // |
1529  // \/ Z
1530  // --------------
1531 
1532  // Computing the position of the point2.
1533  // In Distance Driven the point p21 relies the point P11, p22 -> p12,
1534  // p23 -> p13 and p24 -> p14
1535  // Pixel (CT) / Crystal 2 (PET):
1536  //
1537  // ------- P23 -------
1538  // | |
1539  // | |
1540  // P22 Center P21
1541  // | 0 |
1542  // | |
1543  // ------- P24 -------
1544  // Y
1545  // <---------. X
1546  // |
1547  // |
1548  // |
1549  // \/ Z
1550  // --------------
1551 
1552  // length LOR, connecting central crystal points
1553  HPFLTNB length_LOR = ap_ProjectionLine->GetLength();
1554  HPFLTNB length_LOR_half = length_LOR * 0.5;
1555 
1556  // Get TOF info
1557  INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
1558  INTNB tof_half_nb_bins = tof_nb_bins/2;
1559 
1560  // TOF Gaussian standard deviation and truncation
1562  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
1563  HPFLTNB tof_half_span = 0.;
1565  else tof_half_span = tof_sigma * m_TOFNbSigmas;
1566 
1567  // if integration of the Gaussian over the TOF bin, need for help variables to save calls to erf
1568  HPFLTNB prev_erf = 0., new_erf = 0.;
1569 
1570  // minimum and maximum TOF bins
1571  INTNB tof_bin_last = tof_half_nb_bins;
1572  INTNB tof_bin_first = -tof_half_nb_bins;
1573 
1574  // distance between the first event1 and the center of a TOF bin along the LOR
1575  HPFLTNB lor_tof_center = 0.;
1576  // the sum of all TOF bin weights for a voxel
1577  //HPFLTNB tof_norm_coef = 0.;
1578 
1579  // the first and the last relevant TOF bins for a voxel
1580  INTNB tof_bin_first_for_voxel = 0, tof_bin_last_for_voxel = 0;
1581 
1582  // buffers storing point 1 and 2
1583  FLTNB p1_x[ 5 ];
1584  FLTNB p1_y[ 5 ];
1585  FLTNB p1_z[ 5 ];
1586  FLTNB p2_x[ 5 ];
1587  FLTNB p2_y[ 5 ];
1588  FLTNB p2_z[ 5 ];
1589 
1590  // Get the positions of the centers of the edges of crystal elements or source position
1592  ap_ProjectionLine->GetIndex1(), // Index 1
1593  ap_ProjectionLine->GetIndex2(), // Index 2
1594  ap_ProjectionLine->GetPosition1(), // Line position for point 1
1595  ap_ProjectionLine->GetPosition2(), // Line position for point 2
1596  &p1_x[1], &p1_y[1], &p1_z[1], // Edges for point 1
1597  &p2_x[1], &p2_y[1], &p2_z[1] // Edges for point 2
1598  ))
1599  {
1600  Cerr("***** iProjectorDistanceDriven::ProjectWithoutTOF() -> A problem occurred while getting the edges' center positions !" << endl);
1601  return 1;
1602  }
1603 
1604  // Point 1 focal (CT) or crystal (PET) position
1605  FLTNB* p1 = ap_ProjectionLine->GetPosition1();
1606 
1607  // Point 2 pixel (CT) or crystal (PET) position
1608  FLTNB* p2 = ap_ProjectionLine->GetPosition2();
1609 
1610  // Center position p1
1611  p1_x[ 0 ] = p1[ 0 ]; p1_y[ 0 ] = p1[ 1 ]; p1_z[ 0 ] = p1[ 2 ];
1612 
1613  // Center position p2
1614  p2_x[ 0 ] = p2[ 0 ]; p2_y[ 0 ] = p2[ 1 ]; p2_z[ 0 ] = p2[ 2 ];
1615 
1616  // storing the ray connecting p1 and p2
1617  HPFLTNB r[ 5 ][ 3 ];
1618 
1619  // buffer storing the intersection
1620  HPFLTNB ri[ 4 ][ 3 ];
1621 
1622  // Take the position of ray connecting p1 and p2 for all the 5 points
1623  for( int p = 0; p < 5; ++p )
1624  {
1625  r[ p ][ 0 ] = p2_x[ p ] - p1_x[ p ];
1626  r[ p ][ 1 ] = p2_y[ p ] - p1_y[ p ];
1627  r[ p ][ 2 ] = p2_z[ p ] - p1_z[ p ];
1628  }
1629 
1630  // Computing the square of distance for the center
1631  HPFLTNB const r2[ 3 ] = {
1632  r[ 0 ][ 0 ] * r[ 0 ][ 0 ],
1633  r[ 0 ][ 1 ] * r[ 0 ][ 1 ],
1634  r[ 0 ][ 2 ] * r[ 0 ][ 2 ]
1635  };
1636 
1637  // Find the first and last intersecting plane using the parametric
1638  // values alpha_min and alpha_max
1639  HPFLTNB alpha_min = 0.0, alpha_max = 1.0;
1640 
1641  // Buffer storing the alpha on each axis
1642  // 2 different buffers to get the min and the max
1643  HPFLTNB alpha_x_min[ 4 ], alpha_x_max[ 4 ];
1644  HPFLTNB alpha_y_min[ 4 ], alpha_y_max[ 4 ];
1645  HPFLTNB alpha_z_min[ 4 ], alpha_z_max[ 4 ];
1646 
1647  // Position of the projected points in plane
1648  // If the main direction is X:
1649  // pos[0] is the position of point1 in Y
1650  // pos[1] is the position of point2 in Y
1651  // pos[2] is the position of point3 in Z
1652  // pos[3] is the position of point4 in Z
1653  HPFLTNB pos[ 4 ];
1654 
1655  // Width of intersection
1656  // w1 normalizes the overlap 1
1657  // w2 normalizes the overlap 2
1658  HPFLTNB w1, w2;
1659 
1660  // Buffer storing the min. and max. indices bounding the overlap
1661  int index[ 4 ];
1662 
1663  // Buffer storing the distances
1664  // 50 HPFLTNB is largely enough!!!
1665  HPFLTNB distance_x[ 50 ];
1666  HPFLTNB distance_y[ 50 ];
1667  HPFLTNB distance_z[ 50 ];
1668 
1669  // Computing the angle of line vs. horizontal
1670  float const angle = std::acos( ( r[ 0 ][ 0 ] ) / ( std::sqrt( r2[ 0 ] + r2[ 1 ] ) ) ) * 180.0f / M_PI;
1671 
1672  // Condition on the largest component of r, taking the center
1673  if( ( angle >= 0.0 && angle <= 45.0 ) || ( angle >= 135.0 && angle <= 180.0 ) )
1674  {
1675  // Computing the parametric values alpha_min and alpha_max
1676  // Because the main direction is X, we use only the center
1677  alpha_x_min[ 0 ] = ( (-mp_halfFOV[ 0 ]) - p1_x[ 0 ] ) / r[ 0 ][ 0 ];
1678  alpha_x_min[ 1 ] = ( mp_halfFOV[ 0 ] - p1_x[ 0 ] ) / r[ 0 ][ 0 ];
1679  alpha_x_max[ 0 ] = alpha_x_min[ 0 ];
1680  alpha_x_max[ 1 ] = alpha_x_min[ 1 ];
1681  alpha_min = std::max( alpha_min, std::min( alpha_x_min[ 0 ], alpha_x_min[ 1 ] ) );
1682  alpha_max = std::min( alpha_max, std::max( alpha_x_max[ 0 ], alpha_x_max[ 1 ] ) );
1683 
1684  // Compute the parametric value for each line
1685  // For Y, we use only the point 1 and 2
1686  if( r[ 1 ][ 1 ] != 0.0 ) // point1
1687  {
1688  alpha_y_min[ 0 ] = ( (-mp_halfFOV[ 1 ]) - p1_y[ 1 ] ) / r[ 1 ][ 1 ];
1689  alpha_y_min[ 1 ] = ( mp_halfFOV[ 1 ] - p1_y[ 1 ] ) / r[ 1 ][ 1 ];
1690  alpha_y_max[ 0 ] = alpha_y_min[ 0 ];
1691  alpha_y_max[ 1 ] = alpha_y_min[ 1 ];
1692  }
1693  else
1694  {
1695  alpha_y_min[ 0 ] = 0.0;
1696  alpha_y_min[ 1 ] = 0.0;
1697  alpha_y_max[ 0 ] = 1.0;
1698  alpha_y_max[ 1 ] = 1.0;
1699  }
1700 
1701  if( r[ 2 ][ 1 ] != 0.0 ) // point2
1702  {
1703  alpha_y_min[ 2 ] = ( (-mp_halfFOV[ 1 ]) - p1_y[ 2 ] ) / r[ 2 ][ 1 ];
1704  alpha_y_min[ 3 ] = ( mp_halfFOV[ 1 ] - p1_y[ 2 ] ) / r[ 2 ][ 1 ];
1705  alpha_y_max[ 2 ] = alpha_y_min[ 2 ];
1706  alpha_y_max[ 3 ] = alpha_y_min[ 3 ];
1707  }
1708  else
1709  {
1710  alpha_y_min[ 2 ] = 0.0;
1711  alpha_y_min[ 3 ] = 0.0;
1712  alpha_y_max[ 2 ] = 1.0;
1713  alpha_y_max[ 3 ] = 1.0;
1714  }
1715 
1716  // Getting the minimum of alpha_y_min
1717  alpha_min = std::max( alpha_min,
1718  alpha_y_min[ std::minmax_element( alpha_y_min, alpha_y_min + 4 ).first - alpha_y_min ] );
1719  // Getting the maximum of alpha_y_max
1720  alpha_max = std::min( alpha_max,
1721  alpha_y_max[ std::minmax_element( alpha_y_max, alpha_y_max + 4 ).second - alpha_y_max ] );
1722 
1723  // For Z, we use only the point 3 and 4
1724  if( r[ 3 ][ 2 ] != 0.0 ) // point3
1725  {
1726  alpha_z_min[ 0 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
1727  alpha_z_min[ 1 ] = ( mp_halfFOV[ 2 ] - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
1728  alpha_z_max[ 0 ] = alpha_z_min[ 0 ];
1729  alpha_z_max[ 1 ] = alpha_z_min[ 1 ];
1730  }
1731  else
1732  {
1733  alpha_z_min[ 0 ] = 0.0;
1734  alpha_z_min[ 1 ] = 0.0;
1735  alpha_z_max[ 0 ] = 1.0;
1736  alpha_z_max[ 1 ] = 1.0;
1737  }
1738 
1739  if( r[ 4 ][ 2 ] != 0.0 ) // point4
1740  {
1741  alpha_z_min[ 2 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
1742  alpha_z_min[ 3 ] = ( mp_halfFOV[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
1743  alpha_z_max[ 2 ] = alpha_z_min[ 2 ];
1744  alpha_z_max[ 3 ] = alpha_z_min[ 3 ];
1745  }
1746  else
1747  {
1748  alpha_z_min[ 2 ] = 0.0;
1749  alpha_z_min[ 3 ] = 0.0;
1750  alpha_z_max[ 2 ] = 1.0;
1751  alpha_z_max[ 3 ] = 1.0;
1752  }
1753 
1754  // Getting the minimum of alpha_z_min
1755  alpha_min = std::max( alpha_min,
1756  alpha_z_min[ std::minmax_element( alpha_z_min, alpha_z_min + 4 ).first
1757  - alpha_z_min ] );
1758  // Getting the maximum of alpha_z_max
1759  alpha_max = std::min( alpha_max,
1760  alpha_z_max[ std::minmax_element( alpha_z_max, alpha_z_max + 4 ).second
1761  - alpha_z_max ] );
1762 
1763  if( alpha_max <= alpha_min ) return 0;
1764 
1765  // temporary storage for TOF bin weights
1766  // allocation after potential returns
1767  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
1768  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
1769 
1770  // Computing the first and the last plane
1771  int16_t i_min = 0, i_max = 0;
1772  if( r[ 0 ][ 0 ] > 0.0 )
1773  {
1774  i_min = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alpha_min * r[ 0 ][ 0 ] - p1_x[ 0 ] ) / mp_sizeVox[ 0 ] );
1775  i_max = ::floor( m_toleranceX + ( p1_x[ 0 ] + alpha_max * r[ 0 ][ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1776  }
1777  else if( r[ 0 ][ 0 ] < 0.0 )
1778  {
1779  i_min = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alpha_max * r[ 0 ][ 0 ] - p1_x[ 0 ] ) / mp_sizeVox[ 0 ] );
1780  i_max = ::floor( m_toleranceX + ( p1_x[ 0 ] + alpha_min * r[ 0 ][ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1781  }
1782 
1783  // Computing weight of normalization
1784  // Using the center
1785  HPFLTNB const factor( ::fabs( r[ 0 ][ 0 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
1786  HPFLTNB const factor_for_tof( length_LOR / r[ 0 ][ 0 ] );
1787  HPFLTNB weight( mp_sizeVox[ 0 ] / factor );
1788 
1789  // Computing the increment for each 4 lines to project
1790  // (the center is not projected)
1791  for( uint8_t p = 0; p < 4; ++p )
1792  {
1793  ri[ p ][ 0 ] = 1.0; //r[ p + 1 ][ 0 ] / r[ p + 1 ][ 0 ]
1794  ri[ p ][ 1 ] = r[ p + 1 ][ 1 ] / r[ p + 1 ][ 0 ];
1795  ri[ p ][ 2 ] = r[ p + 1 ][ 2 ] / r[ p + 1 ][ 0 ];
1796  }
1797 
1798  // Increment orientation
1799  int8_t incr_orient_trans = 0;
1800  int8_t idx_orient_trans = 0;
1801 
1802  //int8_t const incr_orient_trans = p2_y[ 1 ] < p2_y[ 2 ] ? 1 : -1;
1803  int8_t const incr_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : -1;
1804 
1805  // Index orientation
1806  //int8_t const idx_orient_trans = p2_y[ 1 ] < p2_y[ 2 ] ? 1 : 0;
1807  int8_t const idx_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : 0;
1808 
1809  // Computing an offset to get the correct position in plane
1810  HPFLTNB const offset = (-mp_halfFOV[ 0 ]) + ( mp_sizeVox[ 0 ] * 0.5 ) - p1_x[ 0 ];
1811 
1812  // Loop over the crossed X planes
1813  for( int16_t i = i_min; i < i_max; ++i )
1814  {
1815  // Computing the coordinates of crossed plane
1816  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
1817  // in Y (point 1 and 2)
1818  pos[ 0 ] = p1_y[ 1 ] + step * ri[ 0 ][ 1 ];
1819  pos[ 1 ] = p1_y[ 2 ] + step * ri[ 1 ][ 1 ];
1820  // in Z (point 3 and 4)
1821  pos[ 2 ] = p1_z[ 3 ] + step * ri[ 2 ][ 2 ];
1822  pos[ 3 ] = p1_z[ 4 ] + step * ri[ 3 ][ 2 ];
1823 
1824  // Computing the factor w1 and w2 normalizing the overlaps
1825  w1 = ::fabs( pos[ 0 ] - pos[ 1 ] );
1826  w2 = ::fabs( pos[ 2 ] - pos[ 3 ] );
1827  HPFLTNB final_weight = 0.0;
1828  if( ( w1 * w2 ) != 0 ) final_weight = weight / ( w1 * w2 );
1829 
1830  // Computing the index min and max on each axis
1831  // In Y
1832  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 0 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1833  index[ 1 ] = ::floor( m_toleranceY + ( pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1834  // In Z
1835  index[ 2 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
1836  index[ 3 ] = ::floor( m_toleranceZ + ( pos[ 3 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
1837 
1838  // apply mask if required
1839  int test_index1 = i + index[ 0 ] * mp_nbVox[ 0 ] + index[ 2 ] * m_nbVoxXY;
1840  int test_index2 = i + index[ 1 ] * mp_nbVox[ 0 ] + index[ 3 ] * m_nbVoxXY;
1841  bool test_index = (index[ 0 ] >= 0 && index[ 2 ] >= 0 && index[ 1 ] >= 0 && index[ 3 ] >= 0
1842  && index[ 0 ] < mp_nbVox[ 1 ] && index[ 1 ] < mp_nbVox[ 1 ] && index[ 2 ] < mp_nbVox[ 2 ] && index[ 3 ] < mp_nbVox[ 2 ]);
1843  if (test_index && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index1) && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index2)) continue;
1844 
1845  if( index[ 0 ] < index[ 1 ] )
1846  {
1847  incr_orient_trans = 1;
1848  idx_orient_trans = 1;
1849  }
1850  else if( index[ 0 ] > index[ 1 ] )
1851  {
1852  incr_orient_trans = -1;
1853  idx_orient_trans = 0;
1854  }
1855 
1856  // Getting the number of distance in Y
1857  int16_t const n_distance_y = ::abs( index[ 0 ] - index[ 1 ] ) + 2;
1858  // Computing the distances in Y
1859  distance_y[ 0 ] = pos[ 0 ];
1860  distance_y[ n_distance_y - 1 ] = pos[ 1 ];
1861  // Computing the rest if necessary
1862  if( n_distance_y > 2 )
1863  {
1864  for( int16_t d = 0; d < n_distance_y - 2; ++d )
1865  distance_y[ d + 1 ] = (-mp_halfFOV[ 1 ]) + ( index[ 0 ] + idx_orient_trans + ( incr_orient_trans * d ) ) * mp_sizeVox[ 1 ];
1866  }
1867 
1868  // Computing the final distance in Y
1869  for( int16_t d = 0; d < n_distance_y - 1; ++d )
1870  distance_y[ d ] = ::fabs( distance_y[ d + 1 ] - distance_y[ d ] );
1871 
1872  // Getting the number of distance in Z
1873  int16_t const n_distance_z = ::abs( index[ 2 ] - index[ 3 ] ) + 2;
1874  // Storing the positions in Y
1875  distance_z[ 0 ] = pos[ 2 ];
1876  distance_z[ n_distance_z - 1 ] = pos[ 3 ];
1877  // Storing the rest if necessary
1878  if( n_distance_z > 2 )
1879  {
1880  for( int16_t d = 0; d < n_distance_z - 2; ++d )
1881  distance_z[ d + 1 ] = (-mp_halfFOV[ 2 ]) + ( index[ 2 ] + idx_orient_axial + ( incr_orient_axial * d ) ) * mp_sizeVox[ 2 ];
1882  }
1883 
1884  // Computing the final distance in Z
1885  for( int16_t d = 0; d < n_distance_z - 1; ++d )
1886  distance_z[ d ] = ::fabs( distance_z[ d + 1 ] - distance_z[ d ] );
1887 
1888  // Compute the first and the last relevant TOF bin for this voxel
1890  {
1891  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1892  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (step * factor_for_tof - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1893  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (step * factor_for_tof + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1894  }
1895  else
1896  {
1897  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1898  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
1899  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
1900  }
1901 
1902  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1903 
1904  // shift tof bin indices from -/+ to 0:nbBins range
1905  tof_bin_first_for_voxel += tof_half_nb_bins;
1906  tof_bin_last_for_voxel += tof_half_nb_bins;
1907 
1908  // initialization of help variables for reducing calls to erf
1910  {
1911  // bound the integration to the Gaussian truncation
1912  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
1913  prev_erf = erf(temp/tof_sigma_sqrt2);
1914  }
1915 
1916  // compute TOF bin weights for the current voxel for all relevant TOF bins
1917  //tof_norm_coef = 0.;
1918  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1919  {
1921  {
1922  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1923  // nearest to the projection of the voxel center onto the LOR
1924  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1925  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1926  // add the weight to the sum
1927  //tof_norm_coef += tof_weights_temp[tof_bin];
1928  // update TOF center along the LOR for the next TOF bin
1929  lor_tof_center += m_TOFBinSizeInMm;
1930  }
1931  else
1932  {
1934  {
1935  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1936  // reuse of integration from the previous bin to save calls to erf
1937  // bound the integration to the Gaussian truncation
1938  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
1939  new_erf = erf(temp/tof_sigma_sqrt2);
1940  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1941  // add the weight to the sum
1942  //tof_norm_coef += tof_weights_temp[tof_bin];
1943  prev_erf = new_erf;
1944  // update TOF center along the LOR for the next TOF bin
1945  lor_tof_center += m_TOFBinSizeInMm;
1946  }
1947  else
1948  {
1949  // 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
1950  HPFLTNB temp = (step * factor_for_tof - lor_tof_center) / tof_sigma;
1951  // save the weight temporarily
1952  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1953  // add the weight to the sum
1954  //tof_norm_coef += tof_weights_temp[tof_bin];
1955  // update TOF bin center along the LOR for the next TOF bin
1956  lor_tof_center += m_TOFBinSizeInMm;
1957  }
1958  }
1959  }
1960 /*
1961  // compute and write the final TOF bin projection coefficients for the current voxels
1962  if (tof_norm_coef>0.)
1963  {
1964  // first normalize TOF bin weights so that they sum to 1
1965  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;
1966  }
1967 */
1968  // Loop over the overlap and store the elements
1969  int16_t index_y, index_z;
1970  for( int16_t jj = 0; jj < n_distance_z - 1; ++jj )
1971  {
1972  index_z = index[ 2 ] + jj * incr_orient_axial;
1973  if( index_z < 0 || index_z > mp_nbVox[ 2 ] - 1 ) continue;
1974 
1975  for( int16_t ii = 0; ii < n_distance_y - 1; ++ii )
1976  {
1977  index_y = index[ 0 ] + ii * incr_orient_trans;
1978  if( index_y < 0 || index_y > mp_nbVox[ 1 ] - 1 ) continue;
1979 
1980  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, i + index_y * mp_nbVox[ 0 ] + index_z * m_nbVoxXY, final_weight * distance_z[ jj ] * distance_y[ ii ], tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1981  }
1982  }
1983  }
1984  delete[] tof_weights_temp;
1985  }
1986  else
1987  {
1988  // Compute the parametric value for each line
1989  // For X, we use only the point 1 and 2
1990  if( r[ 1 ][ 0 ] != 0.0 ) // point1
1991  {
1992  alpha_x_min[ 0 ] = ( (-mp_halfFOV[ 0 ]) - p1_x[ 1 ] ) / r[ 1 ][ 0 ];
1993  alpha_x_min[ 1 ] = ( mp_halfFOV[ 0 ] - p1_x[ 1 ] ) / r[ 1 ][ 0 ];
1994  alpha_x_max[ 0 ] = alpha_x_min[ 0 ];
1995  alpha_x_max[ 1 ] = alpha_x_min[ 1 ];
1996  }
1997  else
1998  {
1999  alpha_x_min[ 0 ] = 0.0;
2000  alpha_x_min[ 1 ] = 0.0;
2001  alpha_x_max[ 0 ] = 1.0;
2002  alpha_x_max[ 1 ] = 1.0;
2003  }
2004 
2005  if( r[ 2 ][ 0 ] != 0.0 ) // point2
2006  {
2007  alpha_x_min[ 2 ] = ( (-mp_halfFOV[ 0 ]) - p1_x[ 2 ] ) / r[ 2 ][ 0 ];
2008  alpha_x_min[ 3 ] = ( mp_halfFOV[ 0 ] - p1_x[ 2 ] ) / r[ 2 ][ 0 ];
2009  alpha_x_max[ 2 ] = alpha_x_min[ 2 ];
2010  alpha_x_max[ 3 ] = alpha_x_min[ 3 ];
2011  }
2012  else
2013  {
2014  alpha_x_min[ 2 ] = 0.0;
2015  alpha_x_min[ 3 ] = 0.0;
2016  alpha_x_max[ 2 ] = 1.0;
2017  alpha_x_max[ 3 ] = 1.0;
2018  }
2019 
2020  // Getting the minimum of alpha_x_min
2021  alpha_min = std::max( alpha_min,
2022  alpha_x_min[ std::minmax_element( alpha_x_min, alpha_x_min + 4 ).first - alpha_x_min ] );
2023  // Getting the maximum of alpha_y_max
2024  alpha_max = std::min( alpha_max,
2025  alpha_x_max[ std::minmax_element( alpha_x_max, alpha_x_max + 4 ).second - alpha_x_max ] );
2026 
2027  // Computing the parametric values alpha_min and alpha_max
2028  // Because the main direction is Y, we use only the center
2029  alpha_y_min[ 0 ] = ( (-mp_halfFOV[ 1 ]) - p1_y[ 0 ] ) / r[ 0 ][ 1 ];
2030  alpha_y_min[ 1 ] = ( mp_halfFOV[ 1 ] - p1_y[ 0 ] ) / r[ 0 ][ 1 ];
2031  alpha_y_max[ 0 ] = alpha_y_min[ 0 ];
2032  alpha_y_max[ 1 ] = alpha_y_min[ 1 ];
2033  alpha_min = std::max( alpha_min,
2034  std::min( alpha_y_min[ 0 ], alpha_y_min[ 1 ] ) );
2035  alpha_max = std::min( alpha_max,
2036  std::max( alpha_y_max[ 0 ], alpha_y_max[ 1 ] ) );
2037 
2038  // For Z, we use only the point 3 and 4
2039  if( r[ 3 ][ 2 ] != 0.0 ) // point3
2040  {
2041  alpha_z_min[ 0 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
2042  alpha_z_min[ 1 ] = ( mp_halfFOV[ 2 ] - p1_z[ 3 ] ) / r[ 3 ][ 2 ];
2043  alpha_z_max[ 0 ] = alpha_z_min[ 0 ];
2044  alpha_z_max[ 1 ] = alpha_z_min[ 1 ];
2045  }
2046  else
2047  {
2048  alpha_z_min[ 0 ] = 0.0;
2049  alpha_z_min[ 1 ] = 0.0;
2050  alpha_z_max[ 0 ] = 1.0;
2051  alpha_z_max[ 1 ] = 1.0;
2052  }
2053 
2054  if( r[ 4 ][ 2 ] != 0.0 ) // point4
2055  {
2056  alpha_z_min[ 2 ] = ( (-mp_halfFOV[ 2 ]) - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
2057  alpha_z_min[ 3 ] = ( mp_halfFOV[ 2 ] - p1_z[ 4 ] ) / r[ 4 ][ 2 ];
2058  alpha_z_max[ 2 ] = alpha_z_min[ 2 ];
2059  alpha_z_max[ 3 ] = alpha_z_min[ 3 ];
2060  }
2061  else
2062  {
2063  alpha_z_min[ 2 ] = 0.0;
2064  alpha_z_min[ 3 ] = 0.0;
2065  alpha_z_max[ 2 ] = 1.0;
2066  alpha_z_max[ 3 ] = 1.0;
2067  }
2068 
2069  // Getting the minimum of alpha_z_min
2070  alpha_min = std::max( alpha_min, alpha_z_min[ std::minmax_element( alpha_z_min, alpha_z_min + 4 ).first - alpha_z_min ] );
2071  // Getting the maximum of alpha_z_max
2072  alpha_max = std::min( alpha_max, alpha_z_max[ std::minmax_element( alpha_z_max, alpha_z_max + 4 ).second - alpha_z_max ] );
2073 
2074  if( alpha_max <= alpha_min ) return 0;
2075 
2076  // temporary storage for TOF bins Gaussian integrals over the current voxel
2077  // allocation after potential returns
2078  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
2079  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
2080 
2081  // Computing the first and the last plane
2082  int16_t j_min = 0, j_max = 0;
2083  if( r[ 0 ][ 1 ] > 0.0 )
2084  {
2085  j_min = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alpha_min * r[ 0 ][ 1 ] - p1_y[ 0 ] ) / mp_sizeVox[ 1 ] );
2086  j_max = ::floor( m_toleranceY + ( p1_y[ 0 ] + alpha_max * r[ 0 ][ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
2087  }
2088  else if( r[ 0 ][ 1 ] < 0.0 )
2089  {
2090  j_min = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alpha_max * r[ 0 ][ 1 ] - p1_y[ 0 ] ) / mp_sizeVox[ 1 ] );
2091  j_max = ::floor( m_toleranceY + ( p1_y[ 0 ] + alpha_min * r[ 0 ][ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
2092  }
2093 
2094  // Computing weight of normalization
2095  // Using the center
2096  HPFLTNB const factor( ::fabs( r[ 0 ][ 1 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
2097  HPFLTNB const factor_for_tof( length_LOR / r[ 0 ][ 1 ] );
2098  HPFLTNB weight( mp_sizeVox[ 1 ] / factor );
2099 
2100  // Computing the increment for each 4 lines to project
2101  // (the center is not projected)
2102  for( uint8_t p = 0; p < 4; ++p )
2103  {
2104  ri[ p ][ 0 ] = r[ p + 1 ][ 0 ] / r[ p + 1 ][ 1 ];
2105  ri[ p ][ 1 ] = 1.0;
2106  ri[ p ][ 2 ] = r[ p + 1 ][ 2 ] / r[ p + 1 ][ 1 ];
2107  }
2108 
2109  // Increment orientation and Index orientation
2110  int8_t incr_orient_trans = 0;
2111  int8_t idx_orient_trans = 0;
2112 
2113  //int8_t const incr_orient_trans = p2_x[ 1 ] < p2_x[ 2 ] ? 1 : -1;
2114  int8_t const incr_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : -1;
2115  //int8_t const idx_orient_trans = p2_x[ 1 ] < p2_x[ 2 ] ? 1 : 0;
2116  int8_t const idx_orient_axial = p2_z[ 3 ] < p2_z[ 4 ] ? 1 : 0;
2117 
2118  // Computing an offset to get the correct position in plane
2119  HPFLTNB const offset = (-mp_halfFOV[ 1 ]) + ( mp_sizeVox[ 1 ] * 0.5 ) - p1_y[ 0 ];
2120 
2121  // Loop over the crossed Y planes
2122  for( int16_t j = j_min; j < j_max; ++j )
2123  {
2124  // Computing the coordinates of crossed plane
2125  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
2126  // in Y (point 1 and 2)
2127  pos[ 0 ] = p1_x[ 1 ] + step * ri[ 0 ][ 0 ];
2128  pos[ 1 ] = p1_x[ 2 ] + step * ri[ 1 ][ 0 ];
2129  // in Z (point 3 and 4)
2130  pos[ 2 ] = p1_z[ 3 ] + step * ri[ 2 ][ 2 ];
2131  pos[ 3 ] = p1_z[ 4 ] + step * ri[ 3 ][ 2 ];
2132 
2133  // Computing the factor w1 and w2 normalizing the overlaps
2134  w1 = ::fabs( pos[ 0 ] - pos[ 1 ] );
2135  w2 = ::fabs( pos[ 2 ] - pos[ 3 ] );
2136  HPFLTNB final_weight = 0.0;
2137  if( ( w1 * w2 ) != 0 ) final_weight = weight / ( w1 * w2 );
2138 
2139  // Computing the index min and max on each axis
2140  // In X
2141  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
2142  index[ 1 ] = ::floor( m_toleranceX + ( pos[ 1 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
2143  // In Z
2144  index[ 2 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
2145  index[ 3 ] = ::floor( m_toleranceZ + ( pos[ 3 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[ 2 ] );
2146 
2147  int test_index1 = index[ 0 ] + j* mp_nbVox[ 0 ] + index[ 2 ] * m_nbVoxXY;
2148  int test_index2 = index[ 1 ] + j* mp_nbVox[ 0 ] + index[ 3 ] * m_nbVoxXY;
2149  bool test_index = (index[ 0 ] >= 0 && index[ 2 ] >= 0 && index[ 1 ] >= 0 && index[ 3 ] >= 0
2150  && index[ 0 ] < mp_nbVox[ 0 ] && index[ 1 ] < mp_nbVox[ 0 ] && index[ 2 ] < mp_nbVox[ 2 ] && index[ 3 ] < mp_nbVox[ 2 ]);
2151  if (test_index && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index1) && mp_ImageDimensionsAndQuantification->IsVoxelMasked(test_index2)) continue;
2152 
2153  if( index[ 0 ] < index[ 1 ] )
2154  {
2155  incr_orient_trans = 1;
2156  idx_orient_trans = 1;
2157  }
2158  else if( index[ 0 ] > index[ 1 ] )
2159  {
2160  incr_orient_trans = -1;
2161  idx_orient_trans = 0;
2162  }
2163 
2164  // Getting the number of distance in X
2165  int16_t const n_distance_x = ::abs( index[ 0 ] - index[ 1 ] ) + 2;
2166  // Computing the distances in X
2167  distance_x[ 0 ] = pos[ 0 ];
2168  distance_x[ n_distance_x - 1 ] = pos[ 1 ];
2169  // Computing the rest if necessary
2170  if( n_distance_x > 2 )
2171  {
2172  for( int16_t d = 0; d < n_distance_x - 2; ++d )
2173  distance_x[ d + 1 ] = (-mp_halfFOV[ 0 ]) + ( index[ 0 ] + idx_orient_trans + ( incr_orient_trans * d ) ) * mp_sizeVox[ 0 ];
2174  }
2175 
2176  // Computing the final distance in X
2177  for( int16_t d = 0; d < n_distance_x - 1; ++d )
2178  distance_x[ d ] = ::fabs( distance_x[ d + 1 ] - distance_x[ d ] );
2179 
2180  // Getting the number of distance in Z
2181  int16_t const n_distance_z = ::abs( index[ 2 ] - index[ 3 ] ) + 2;
2182  // Storing the positions in Y
2183  distance_z[ 0 ] = pos[ 2 ];
2184  distance_z[ n_distance_z - 1 ] = pos[ 3 ];
2185  // Storing the rest if necessary
2186  if( n_distance_z > 2 )
2187  {
2188  for( int16_t d = 0; d < n_distance_z - 2; ++d )
2189  distance_z[ d + 1 ] = (-mp_halfFOV[ 2 ]) + ( index[ 2 ] + idx_orient_axial + ( incr_orient_axial * d ) ) * mp_sizeVox[ 2 ];
2190  }
2191 
2192  // Computing the final distance in Z
2193  for( int16_t d = 0; d < n_distance_z - 1; ++d )
2194  distance_z[ d ] = ::fabs( distance_z[ d + 1 ] - distance_z[ d ] );
2195 
2196  // Compute the first and the last relevant TOF bin for this voxel
2198  {
2199  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
2200  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (step * factor_for_tof - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
2201  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (step * factor_for_tof + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
2202  }
2203  else
2204  {
2205  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
2206  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
2207  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
2208  }
2209 
2210  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
2211 
2212  // shift tof bin indices from -/+ to 0:nbBins range
2213  tof_bin_first_for_voxel += tof_half_nb_bins;
2214  tof_bin_last_for_voxel += tof_half_nb_bins;
2215 
2216  // initialization of help variables for reducing calls to erf
2218  {
2219  // bound the integration to the Gaussian truncation<
2220  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
2221  prev_erf = erf(temp/tof_sigma_sqrt2);
2222  }
2223 
2224  // compute TOF bin weights for the current voxel for all relevant TOF bins
2225  //tof_norm_coef = 0.;
2226  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
2227  {
2229  {
2230  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
2231  // nearest to the projection of the voxel center onto the LOR
2232  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor);
2233  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
2234  // add the weight to the sum
2235  //tof_norm_coef += tof_weights_temp[tof_bin];
2236  // update TOF center along the LOR for the next TOF bin
2237  lor_tof_center += m_TOFBinSizeInMm;
2238  }
2239  else
2240  {
2242  {
2243  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
2244  // reuse of integration from the previous bin to save calls to erf
2245  // bound the integration to the Gaussian truncation
2246  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
2247  new_erf = erf(temp/tof_sigma_sqrt2);
2248  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
2249  // add the weight to the sum
2250  //tof_norm_coef += tof_weights_temp[tof_bin];
2251  prev_erf = new_erf;
2252  // update TOF center along the LOR for the next TOF bin
2253  lor_tof_center += m_TOFBinSizeInMm;
2254  }
2255  else
2256  {
2257  // 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
2258  HPFLTNB temp = (step * factor_for_tof - lor_tof_center) / tof_sigma;
2259  // save the weight temporarily
2260  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
2261  // add the weight to the sum
2262  //tof_norm_coef += tof_weights_temp[tof_bin];
2263  // update TOF bin center along the LOR for the next TOF bin
2264  lor_tof_center += m_TOFBinSizeInMm;
2265  }
2266  }
2267  }
2268 /*
2269  // compute and write the final TOF bin projection coefficients for current voxels
2270  if (tof_norm_coef>0.)
2271  {
2272  // first normalize TOF bin weights so that they sum to 1
2273  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;
2274  }
2275 */
2276  // Loop over the overlap and store the elements
2277  int16_t index_x, index_z;
2278  for( int16_t jj = 0; jj < n_distance_z - 1; ++jj )
2279  {
2280  index_z = index[ 2 ] + jj * incr_orient_axial;
2281  if( index_z < 0 || index_z > mp_nbVox[ 2 ] - 1 ) continue;
2282 
2283  for( int16_t ii = 0; ii < n_distance_x - 1; ++ii )
2284  {
2285  index_x = index[ 0 ] + ii * incr_orient_trans;
2286  if( index_x < 0 || index_x > mp_nbVox[ 0 ] - 1 ) continue;
2287 
2288  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, index_x + j * mp_nbVox[ 0 ] + index_z * m_nbVoxXY, final_weight * distance_z[ jj ] * distance_x[ ii ], tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
2289  }
2290  }
2291 
2292  }
2293  delete[] tof_weights_temp;
2294  }
2295 
2296  return 0;
2297 
2298 }
2299 
2300 // =====================================================================
2301 // ---------------------------------------------------------------------
2302 // ---------------------------------------------------------------------
2303 // =====================================================================
virtual int GetEdgesCenterPositions(int a_index1, int a_index2, FLTNB ap_pos_line_point1[3], FLTNB ap_pos_line_point2[3], FLTNB ap_pos_point1_x[4], FLTNB ap_pos_point1_y[4], FLTNB ap_pos_point1_z[4], FLTNB ap_pos_point2_x[4], FLTNB ap_pos_point2_y[4], FLTNB ap_pos_point2_z[4])=0
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the &#39;a_input&#39; string corresponding to the &#39;a_option&#39; into &#39;a_nbElts&#39; elements, using the &#39;sep&#39; separator. The results are returned in the templated &#39;ap_return&#39; dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
#define Cerr(MESSAGE)
iProjectorDistanceDriven()
The constructor of iProjectorDistanceDriven.
void AddVoxelAllTOFBins(int a_direction, INTNB a_voxelIndex, FLTNB a_voxelWeight, HPFLTNB *a_tofWeights, INTNB a_tofBinFirst, INTNB a_tofBinLast)
INTNB EstimateMaxNumberOfVoxelsPerLine()
This function is used to compute and provide an estimate of the maximum number of voxels that could c...
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
int ReadOptionsList(const string &a_optionsList)
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
FLTNB GetLength()
This function is used to get the length of the line.
void Exit(int code)
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child projector.
int GetIndex2()
This function is used to get the index associated to point 2.
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)
~iProjectorDistanceDriven()
The destructor of iProjectorDistanceDriven.
int ReadConfigurationFile(const string &a_configurationFile)
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 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).
int GetIndex1()
This function is used to get the index associated to point 1.
#define Cout(MESSAGE)
int ProjectTOFListmode(int a_direction, oProjectionLine *ap_ProjectionLine)
void ShowHelpSpecific()
A function used to show help about the child projector.