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