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