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