CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/projector/iProjectorJoseph.cc
Go to the documentation of this file.
1 
8 #include "iProjectorJoseph.hh"
9 #include "sOutputManager.hh"
10 
11 #include <cmath>
12 #ifdef _WIN32
13 // Avoid compilation errors due to mix up between std::min()/max() and
14 // min max macros
15 #undef min
16 #undef max
17 #endif
18 
19 // =====================================================================
20 // ---------------------------------------------------------------------
21 // ---------------------------------------------------------------------
22 // =====================================================================
23 
25 {
26  // This projector is not compatible with SPECT attenuation correction because
27  // the voxels contributing to the line are not strictly ordered with respect to
28  // their distance to point2 (due to interpolations at each plane crossed)
30  // This projector is compatible with compression as it works only with the
31  // cartesian coordinates of 2 end points of a line
33  // Default pointers and parameters
34  mp_boundariesX = nullptr;
35  mp_boundariesY = nullptr;
36  mp_boundariesZ = nullptr;
37  mp_maskPad = nullptr;
38  m_toleranceX = 0.;
39  m_toleranceY = 0.;
40  m_toleranceZ = 0.;
41  m_tolerance_fctr = 1.e-6;
42 }
43 
44 // =====================================================================
45 // ---------------------------------------------------------------------
46 // ---------------------------------------------------------------------
47 // =====================================================================
48 
50 {
51  if ( mp_boundariesX )
52  {
53  delete[] mp_boundariesX;
54  mp_boundariesX = nullptr;
55  }
56 
57  if ( mp_boundariesY )
58  {
59  delete[] mp_boundariesY;
60  mp_boundariesY = nullptr;
61  }
62 
63  if ( mp_boundariesZ )
64  {
65  delete[] mp_boundariesZ;
66  mp_boundariesZ = nullptr;
67  }
68 
69  if ( mp_maskPad )
70  {
71  delete[] mp_maskPad;
72  mp_maskPad = nullptr;
73  }
74 }
75 
76 // =====================================================================
77 // ---------------------------------------------------------------------
78 // ---------------------------------------------------------------------
79 // =====================================================================
80 
81 int iProjectorJoseph::ReadConfigurationFile(const string& a_configurationFile)
82 {
83  // No options for joseph
84  ;
85  // Normal end
86  return 0;
87 }
88 
89 // =====================================================================
90 // ---------------------------------------------------------------------
91 // ---------------------------------------------------------------------
92 // =====================================================================
93 
94 int iProjectorJoseph::ReadOptionsList(const string& a_optionsList)
95 {
96  // Currently only one option: Tolerance with respect to voxel sizes in each dimensions
97  uint16_t option[1];
98 
99  // Check tolerance value
100  if (ReadStringOption(a_optionsList, option, 1, ",", "Tolerance factor for plane index computation "))
101  {
102  Cerr("***** iProjectorJoseph::ReadConfigurationFile() -> Failed to correctly read the list of options !" << endl);
103  return 1;
104  }
105 
106  // Check tolerance factor
107  if (option[0] < 2.)
108  {
109  Cerr("***** iProjectorJoseph::ReadOptionsList() -> Tolerance factor parameter must be >2 (tolerance factor < 10^-2)" << endl);
110  Cerr(" -> Provided parameter: "<< option[0] << endl);
111  return 1;
112  }
113 
114  m_tolerance_fctr = pow(10 , -(HPFLTNB)option[0]);
115 
116  // Normal end
117  return 0;
118 }
119 
120 // =====================================================================
121 // ---------------------------------------------------------------------
122 // ---------------------------------------------------------------------
123 // =====================================================================
124 
126 {
127  cout << "This projector is a line projector that uses linear interpolation between pixels." << endl;
128  cout << "It is implemented from the following published paper:" << endl;
129  cout << "P. M. Joseph, \"An improved algorithm for reprojecting rays through pixel images\", IEEE Trans. Med. Imaging, vol. 1, pp. 192-6, 1982." << endl;
130  cout << "There is 1 optional parameter for this projector:" << endl;
131  cout << " tolerance_factor: x. -> 'x' must be an unsigned integer, and represent the tolerance precision factor for plane index computation (10^(-x))." << endl;
132  cout << " -> Default: 'x' = 6" << endl;
133 }
134 
135 
136 
137 // =====================================================================
138 // ---------------------------------------------------------------------
139 // ---------------------------------------------------------------------
140 // =====================================================================
141 
143 {
144  if ( m_tolerance_fctr > 0.01
145  || m_tolerance_fctr <= 0 )
146  {
147  Cerr("***** iProjectorJoseph::CheckSpecificParameters() -> Tolerance factor must be defined in the specific interval : ]0 ; 1.e-2]" << endl);
148  Cerr("***** -> Default value is 1.e-6" << endl);
149  Cerr("***** -> Current value is " << m_tolerance_fctr << endl);
150  return 1;
151  }
152 
153  // Normal end
154  return 0;
155 }
156 
157 // =====================================================================
158 // ---------------------------------------------------------------------
159 // ---------------------------------------------------------------------
160 // =====================================================================
161 
163 {
164  // Verbose
165  if (m_verbose>=2) Cout("iProjectorJoseph::InitializeSpecific() -> Use Joseph projector" << endl);
166 
167  // Allocate and compute boundaries (grid through voxel centers)
168  mp_boundariesX = new HPFLTNB[ mp_nbVox[ 0 ] + 2 ];
169  for( INTNB i = 0; i < mp_nbVox[ 0 ] + 2; ++i ) mp_boundariesX[ i ] = -((HPFLTNB)(mp_halfFOV[ 0 ])) + ((HPFLTNB)(mp_sizeVox[ 0 ])) * ( ((HPFLTNB)i) - 0.5 );
170  mp_boundariesY = new HPFLTNB[ mp_nbVox[ 1 ] + 2 ];
171  for( INTNB i = 0; i < mp_nbVox[ 1 ] + 2; ++i ) mp_boundariesY[ i ] = -((HPFLTNB)(mp_halfFOV[ 1 ])) + ((HPFLTNB)(mp_sizeVox[ 1 ])) * ( ((HPFLTNB)i) - 0.5 );
172  mp_boundariesZ = new HPFLTNB[ mp_nbVox[ 2 ] + 2 ];
173  for( INTNB i = 0; i < mp_nbVox[ 2 ] + 2; ++i ) mp_boundariesZ[ i ] = -((HPFLTNB)(mp_halfFOV[ 2 ])) + ((HPFLTNB)(mp_sizeVox[ 2 ])) * ( ((HPFLTNB)i) - 0.5 );
174 
175  // Allocating the mask buffer for the padded image space
176  INTNB nElts = ( mp_nbVox[ 0 ] + 2 ) * ( mp_nbVox[ 1 ] + 2 ) * ( mp_nbVox[ 2 ] + 2 );
177  mp_maskPad = new uint8_t[ nElts ];
178  ::memset( mp_maskPad, 0, sizeof( uint8_t ) * nElts );
179  for( INTNB k = 1; k < mp_nbVox[ 2 ] + 1; ++k )
180  {
181  for( INTNB j = 1; j < mp_nbVox[ 1 ] + 1; ++j )
182  {
183  for( INTNB i = 1; i < mp_nbVox[ 0 ] + 1; ++i )
184  {
185  mp_maskPad[ i + j * ( ( mp_nbVox[ 0 ] + 2 ) ) + k * ( mp_nbVox[ 0 ] + 2 ) * ( mp_nbVox[ 1 ] + 2 ) ] = 1;
186  }
187  }
188  }
189 
190  // Set the tolerance with respect to voxel sizes in each dimensions
194 
195  // Setting the bounds
196  m_boundX = (-mp_halfFOV[ 0 ]) - mp_sizeVox[ 0 ] * 0.5;
197  m_boundY = (-mp_halfFOV[ 1 ]) - mp_sizeVox[ 1 ] * 0.5;
198  m_boundZ = (-mp_halfFOV[ 2 ]) - mp_sizeVox[ 2 ] * 0.5;
199 
200  // Normal end
201  return 0;
202 }
203 
204 // =====================================================================
205 // ---------------------------------------------------------------------
206 // ---------------------------------------------------------------------
207 // =====================================================================
208 
210 {
211  // Find the maximum number of voxels along a given dimension
212  INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
213  if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
214  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
215  // We should have at most 4 voxels in a given plane, so multiply by 4
216  max_nb_voxels_in_dimension *= 4;
217  // Return the value
218  return max_nb_voxels_in_dimension;
219 }
220 
221 // =====================================================================
222 // ---------------------------------------------------------------------
223 // ---------------------------------------------------------------------
224 // =====================================================================
225 
226 int iProjectorJoseph::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
227 {
228  #ifdef CASTOR_DEBUG
229  if (!m_initialized)
230  {
231  Cerr("***** iProjectorJoseph::ProjectWithoutTOF() -> Called while not initialized !" << endl);
232  Exit(EXIT_DEBUG);
233  }
234  #endif
235 
236  #ifdef CASTOR_VERBOSE
237  if (m_verbose>=10)
238  {
239  string direction = "";
240  if (a_direction==FORWARD) direction = "forward";
241  else direction = "backward";
242  Cout("iProjectorJoseph::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
243  }
244  #endif
245 
246  // Get event positions
247  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
248  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
249 
250  // Distance between point event1 and event2
251  HPFLTNB const r[ 3 ] = {
252  event2[ 0 ] - event1[ 0 ],
253  event2[ 1 ] - event1[ 1 ],
254  event2[ 2 ] - event1[ 2 ]
255  };
256 
257  // Square of r
258  HPFLTNB const r2[ 3 ] = {
259  r[ 0 ] * r[ 0 ],
260  r[ 1 ] * r[ 1 ],
261  r[ 2 ] * r[ 2 ]
262  };
263 
264  // Find the first and last intersecting plane in X using the parametric
265  // values alphaMin and alphaMax
266  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
267 
268  // Variables for Joseph
269  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
270  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
271  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
272  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
273  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
274  int32_t finalIdx = 0; // final index
275  int8_t limitX1 = 1; int8_t limitX2 = 1;
276  int8_t limitY1 = 1; int8_t limitY2 = 1;
277  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
278 
279  // Condition on the largest component of r
280  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
281  {
282  // Computing the parametric values
283  // For the X-axis
284  // We stay in the limit of the image space
285  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
286  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
287  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
288  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
289 
290  // For the Y-axis
291  // We introduce 1 voxel size around Y-axis
292  // Make a shift on first and last plane (like an offset)
293  if( r[ 1 ] != 0.0 )
294  {
295  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
296  / r[ 1 ];
297  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
298  / r[ 1 ];
299  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
300  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
301  }
302 
303  // For the Z-axis
304  // We introduce 1 voxel size around Z-axis
305  // Make a shift on first and last plane (like an offset)
306  if( r[ 2 ] != 0.0 )
307  {
308  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
309  / r[ 2 ];
310  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
311  / r[ 2 ];
312  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
313  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
314  }
315 
316  if( alphaMax <= alphaMin ) return 0;
317 
318  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
319  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
320 
321  // Computing weight of normalization
322  HPFLTNB const factor( ::fabs( r[ 0 ] )
323  / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
324  HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
325 
326  // Computing the increment
327  HPFLTNB const ri[ 3 ] = {
328  1.0, // r[ 0 ] / r[ 0 ]
329  r[ 1 ] / r[ 0 ],
330  r[ 2 ] / r[ 0 ]
331  };
332 
333  // Computing the first and the last plane
334  int iMin = 0, iMax = 0;
335  if( r[ 0 ] > 0.0 )
336  {
337  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
338  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
339  }
340  else if( r[ 0 ] < 0.0 )
341  {
342  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
343  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
344  }
345 
346  // Computing an offset to get the correct position in plane
347  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
348 
349  // Loop over all the crossing planes
350  for( int i = iMin; i < iMax; ++i )
351  {
352  // Computing position on crossed plane in term of grid spacing
353  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
354  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
355  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
356 
357  // Find the index in the image
358  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
359  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
360 
361  // Check Y bounds
362  if( index[ 0 ] <= 0 ) limitY1 = 0;
363  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
364 
365  // Check Z bounds
366  if( index[ 1 ] <= 0 ) limitZ1 = 0;
367  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
368 
369  // Storing values and indices
370  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
371 
372  // If the main voxel and all the neighbours are masked and inside the FOV, skip
373  // Try to avoid as much computations as possible and be on the safe side
374  // (better to include some masked voxels than to skip some relevant voxels)
375  if ( limitY1 && limitZ1 && limitY2 && limitZ2 &&
378  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY) &&
379  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ]) ) continue;
380 
381  // Bilinear interpolation component (using floor)
382  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
383  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
384  wfl[ 0 ] /= mp_sizeVox[ 1 ];
385  wfl[ 1 ] /= mp_sizeVox[ 2 ];
386 
387  // Bilinear interpolation component (using ceil)
388  wcl[ 0 ] = 1.0 - wfl[ 0 ];
389  wcl[ 1 ] = 1.0 - wfl[ 1 ];
390 
391  // Final coefficients
392  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
393  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
394  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
395  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
396 
397  // Add voxels and their weights to the projection line
398  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * limitY1 * limitZ1);
399  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * limitY1 * limitZ2);
400  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * limitY2 * limitZ2);
401  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * limitY2 * limitZ1);
402 
403  limitY1 = 1; limitY2 = 1;
404  limitZ1 = 1; limitZ2 = 1;
405  }
406  }
407  else
408  {
409  // Computing the parametric values
410  // For the X-axis
411  // We introduce 1 voxel size around Y-axis
412  if( r[ 0 ] != 0.0 )
413  {
414  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
415  / r[ 0 ];
416  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
417  / r[ 0 ];
418  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
419  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
420  }
421 
422  // For the Y-axis
423  // Make a shift on first and last plane (like an offset)
424  // We stay in the limit of the image space
425  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
426  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
427  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
428  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
429 
430  // For the Z-axis
431  // We introduce 1 voxel size around Z-axis
432  // Make a shift on first and last plane (like an offset)
433  if( r[ 2 ] != 0.0 )
434  {
435  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
436  / r[ 2 ];
437  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
438  / r[ 2 ];
439  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
440  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
441  }
442 
443  if( alphaMax <= alphaMin ) return 0;
444 
445  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
446  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
447 
448  // Computing weight of normalization
449  HPFLTNB const factor( ::fabs( r[ 1 ] ) / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
450  HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
451 
452  // Computing the increment
453  HPFLTNB const ri[ 3 ] = {
454  r[ 0 ] / r[ 1 ],
455  1.0, // r[ 1 ] / r[ 1 ]
456  r[ 2 ] / r[ 1 ]
457  };
458 
459  // Computing the first and the last plane
460  int jMin = 0, jMax = 0;
461  if( r[ 1 ] > 0.0 )
462  {
463  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
464  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
465  }
466  else if( r[ 1 ] < 0.0 )
467  {
468  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
469  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
470  }
471 
472  // Computing an offset to get the correct position in plane
473  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
474 
475  // Loop over all the crossing planes
476  for( int j = jMin; j < jMax; ++j )
477  {
478  // Computing position on crossed plane in term of grid spacing
479  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
480  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
481  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
482 
483  // Find the index in the image
484  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
485  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
486 
487  // Storing values and indices
488  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
489 
490  // Check X bounds
491  if( index[ 0 ] <= 0 ) limitX1 = 0;
492  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
493 
494  // Check Z bounds
495  if( index[ 1 ] <= 0 ) limitZ1 = 0;
496  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
497 
498  // If the main voxel and all the neighbours are masked and inside the FOV, skip
499  // Try to avoid as much computations as possible and be on the safe side
500  // (better to include some masked voxels than to skip some relevant voxels)
501  if ( limitX1 && limitZ1 && limitX2 && limitZ2 &&
505  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + 1) ) continue;
506 
507  // Bilinear interpolation component (using floor)
508  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
509  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
510  wfl[ 0 ] /= mp_sizeVox[ 0 ];
511  wfl[ 1 ] /= mp_sizeVox[ 2 ];
512 
513  // Bilinear interpolation component (using ceil)
514  wcl[ 0 ] = 1.0 - wfl[ 0 ];
515  wcl[ 1 ] = 1.0 - wfl[ 1 ];
516 
517  // Final coefficients
518  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
519  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
520  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
521  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
522 
523  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * limitX1 * limitZ1);
524  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * limitX1 * limitZ2);
525  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * limitX2 * limitZ2);
526  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * limitX2 * limitZ1);
527 
528  limitX1 = 1; limitX2 = 1;
529  limitZ1 = 1; limitZ2 = 1;
530  }
531  }
532 
533  return 0;
534 }
535 
536 // =====================================================================
537 // ---------------------------------------------------------------------
538 // ---------------------------------------------------------------------
539 // =====================================================================
540 
541 int iProjectorJoseph::ProjectTOFListmode(int a_direction, oProjectionLine* ap_ProjectionLine)
542 {
543 
544  #ifdef CASTOR_DEBUG
545  if (!m_initialized)
546  {
547  Cerr("***** iProjectorJoseph::ProjectTOFListmode() -> Called while not initialized !" << endl);
548  Exit(EXIT_DEBUG);
549  }
550  #endif
551 
552  #ifdef CASTOR_VERBOSE
553  if (m_verbose>=10)
554  {
555  string direction = "";
556  if (a_direction==FORWARD) direction = "forward";
557  else direction = "backward";
558  Cout("iProjectorJoseph::Project with TOF measurement -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
559  }
560  #endif
561 
562  // Get event positions
563  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
564  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
565 
566  // Distance between point event1 and event2
567  HPFLTNB const r[ 3 ] = {
568  event2[ 0 ] - event1[ 0 ],
569  event2[ 1 ] - event1[ 1 ],
570  event2[ 2 ] - event1[ 2 ]
571  };
572 
573 /*
574  // Square of r
575  HPFLTNB const r2[ 3 ] = {
576  r[ 0 ] * r[ 0 ],
577  r[ 1 ] * r[ 1 ],
578  r[ 2 ] * r[ 2 ]
579  };
580 */
581 
582  // LOR length
583  HPFLTNB lor_length = ap_ProjectionLine->GetLength();
584 
585  // Get TOF info
586  FLTNB tof_measurement = ap_ProjectionLine->GetTOFMeasurementInPs();
587 
588  // convert delta time into delta length
589  HPFLTNB tof_delta = tof_measurement * SPEED_OF_LIGHT_IN_MM_PER_PS * 0.5;
590 
591  // TOF standard deviation and truncation
593  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
594  HPFLTNB tof_half_span = 0.;
595 
597  else if (m_TOFBinProperProcessingFlag) tof_half_span = tof_sigma * m_TOFNbSigmas + 0.5 * m_TOFBinSizeInMm;
598  else tof_half_span = tof_sigma * m_TOFNbSigmas;
599 
600  // distance between the first event1 and the TOF measurement
601  HPFLTNB lor_tof_center = lor_length * 0.5 + tof_delta;
602 
603  // coordinates of the lower edge of the first voxel falling inside the truncated TOF distribution centered at the TOF measurement
604  HPFLTNB tof_edge_low[] = {0,0,0};
605  // coordinate of the upper edge of the last voxel falling inside the truncated TOF distribution centered at the TOF measurement
606  HPFLTNB tof_edge_high[] = {0,0,0};
607  HPFLTNB tof_center;
608  INTNB tof_index;
609 
610  // low/high voxel edges (in absolute coordinates) for truncated TOF
611  for (int ax=0;ax<3;ax++)
612  {
613  // absolute coordinate along each axis of the center of the TOF distribution
614  tof_center = event1[ax] + lor_tof_center * r[ax] / lor_length;
615 
616  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
617  tof_edge_low[ax] = tof_center - tof_half_span * fabs(r[ax]) / lor_length;
618  tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
619  // if low TOF edge above the highest FOV edge, return empty line
620  if (tof_index>mp_nbVox[ax]-1) return 0;
621  tof_edge_low[ax] = (HPFLTNB)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
622 
623  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
624  tof_edge_high[ax] = tof_center + tof_half_span * fabs(r[ax]) / lor_length;
625  tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
626  // if high TOF edge below the lowest FOV edge, return empty line
627  if (tof_index<0) return 0;
628  tof_edge_high[ax] = (HPFLTNB)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
629  }
630 
631  // Find the first and last intersecting plane in X using the parametric
632  // values alphaMin and alphaMax
633  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
634 
635  // Variables for Joseph
636  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
637  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
638  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
639  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
640  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
641  int32_t finalIdx = 0; // final index
642  int8_t limitX1 = 1; int8_t limitX2 = 1;
643  int8_t limitY1 = 1; int8_t limitY2 = 1;
644  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
645 
646  // Condition on the largest component of r
647  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
648  {
649  // Computing the parametric values
650  // For the X-axis
651  // We stay in the limit of the image space
652  HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - event1[ 0 ] ) / r[ 0 ];
653  HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] - event1[ 0 ] ) / r[ 0 ];
654  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
655  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
656 
657  // For the Y-axis
658  // We introduce 1 voxel size around Y-axis
659  // Make a shift on first and last plane (like an offset)
660  if( r[ 1 ] != 0.0 )
661  {
662  HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
663  / r[ 1 ];
664  HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
665  / r[ 1 ];
666  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
667  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
668  }
669 
670  // For the Z-axis
671  // We introduce 1 voxel size around Z-axis
672  // Make a shift on first and last plane (like an offset)
673  if( r[ 2 ] != 0.0 )
674  {
675  HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
676  / r[ 2 ];
677  HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
678  / r[ 2 ];
679  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
680  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
681  }
682 
683  if( alphaMax <= alphaMin ) return 0;
684 
685  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
686  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
687 
688  // Computing weight of normalization
689  HPFLTNB const factor( ::fabs( r[ 0 ] ) / lor_length );
690  HPFLTNB const factor_for_tof( lor_length / r[ 0 ]) ;
691  HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
692 
693  // Computing the increment
694  HPFLTNB const ri[ 3 ] = {
695  1.0, // r[ 0 ] / r[ 0 ]
696  r[ 1 ] / r[ 0 ],
697  r[ 2 ] / r[ 0 ]
698  };
699 
700  // Computing the first and the last plane
701  int iMin = 0, iMax = 0;
702  if( r[ 0 ] > 0.0 )
703  {
704  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
705  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
706  }
707  else if( r[ 0 ] < 0.0 )
708  {
709  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
710  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
711  }
712 
713  // Computing an offset to get the correct position in plane
714  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
715 
717  //HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 0 ] + iMin * mp_sizeVox[ 0 ] - event1[ 0 ] ) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
718  //HPFLTNB next_edge_erf = 0.;
719  HPFLTNB tof_weight = 0.;
720 
721  // Loop over all the crossing planes
722  for( int i = iMin; i < iMax; ++i )
723  {
724  // Computing position on crossed plane in term of grid spacing
725  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
726  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
727  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
728 
729  // Find the index in the image
730  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
731  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
732 
733  // Storing values and indices
734  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
735 
736  // Check Y bounds
737  if( index[ 0 ] <= 0 ) limitY1 = 0;
738  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
739 
740  // Check Z bounds
741  if( index[ 1 ] <= 0 ) limitZ1 = 0;
742  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
743 
744  // If the main voxel and all the neighbours are masked and inside the FOV, skip
745  // Try to avoid as much computations as possible and be on the safe side
746  // (better to include some masked voxels than to skip some relevant voxels)
747  if ( limitY1 && limitZ1 && limitY2 && limitZ2 &&
750  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY) &&
751  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ]) ) continue;
752 
753  // Bilinear interpolation component (using floor)
754  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
755  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
756  wfl[ 0 ] /= mp_sizeVox[ 1 ];
757  wfl[ 1 ] /= mp_sizeVox[ 2 ];
758 
759  // Bilinear interpolation component (using ceil)
760  wcl[ 0 ] = 1.0 - wfl[ 0 ];
761  wcl[ 1 ] = 1.0 - wfl[ 1 ];
762 
763  // Final coefficients
764  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
765  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
766  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
767  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
768 
769  tof_weight = 0.;
771  {
772  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
773  // nearest to the projection of the voxel center onto the LOR
774  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center ) * m_TOFPrecomputedSamplingFactor );
775  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight = mp_TOFWeightingFcn[temp];
776  }
777  else
778  {
780  //next_edge_erf = erf( ( ( step + mp_sizeVox[ 0 ] * 0.5 ) * factor_for_tof - lor_tof_center ) / tof_sigma_sqrt2 );
782  //tof_weight = 0.5 * ::fabs(previous_edge_erf - next_edge_erf) ;
784  //previous_edge_erf = next_edge_erf;
785 
787  {
788  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
789  // of the Gaussian centered at the projection of the voxel center onto the LOR
790  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
791  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
792  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
793  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
794  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
795  tof_weight = 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
796  }
797  else
798  {
799  // value of the normalized Gaussian (centered at the TOF measurement)
800  // at the projection of the voxel center onto the LOR
801  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center ) / tof_sigma;
802  tof_weight = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
803  }
804  }
805 
806  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * tof_weight * limitY1 * limitZ1);
807  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * tof_weight * limitY1 * limitZ2);
808  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * tof_weight * limitY2 * limitZ2);
809  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * tof_weight * limitY2 * limitZ1);
810 
811  limitY1 = 1.0; limitY2 = 1.0;
812  limitZ1 = 1.0; limitZ2 = 1.0;
813  }
814  }
815  else
816  {
817  // Computing the parametric values
818  // For the X-axis
819  // We introduce 1 voxel size around Y-axis
820  if( r[ 0 ] != 0.0 )
821  {
822  HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
823  / r[ 0 ];
824  HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
825  / r[ 0 ];
826  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
827  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
828  }
829 
830  // For the Y-axis
831  // Make a shift on first and last plane (like an offset)
832  // We stay in the limit of the image space
833  HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - event1[ 1 ] ) / r[ 1 ];
834  HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] - event1[ 1 ] ) / r[ 1 ];
835  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
836  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
837 
838  // For the Z-axis
839  // We introduce 1 voxel size around Z-axis
840  // Make a shift on first and last plane (like an offset)
841  if( r[ 2 ] != 0.0 )
842  {
843  HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
844  / r[ 2 ];
845  HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
846  / r[ 2 ];
847  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
848  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
849  }
850 
851  if( alphaMax <= alphaMin ) return 0;
852 
853  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
854  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
855 
856  // Computing weight of normalization
857  HPFLTNB const factor( ::fabs( r[ 1 ] ) / lor_length );
858  HPFLTNB const factor_for_tof( lor_length / r[ 1 ] );
859  HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
860 
861  // Computing the increment
862  HPFLTNB const ri[ 3 ] = {
863  r[ 0 ] / r[ 1 ],
864  1.0, // r[ 1 ] / r[ 1 ]
865  r[ 2 ] / r[ 1 ]
866  };
867 
868  // Computing the first and the last plane
869  int jMin = 0, jMax = 0;
870  if( r[ 1 ] > 0.0 )
871  {
872  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
873  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
874  }
875  else if( r[ 1 ] < 0.0 )
876  {
877  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
878  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
879  }
880 
882  //HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 1 ] + jMin * mp_sizeVox[ 1 ] - event1[ 1 ]) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
883  //HPFLTNB next_edge_erf = 0.;
884  HPFLTNB tof_weight = 0.;
885 
886  // Computing an offset to get the correct position in plane
887  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
888 
889  // Loop over all the crossing planes
890  for( int j = jMin; j < jMax; ++j )
891  {
892  // Computing position on crossed plane in term of grid spacing
893  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
894  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
895  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
896 
897  // Find the index in the image
898  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
899  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
900 
901  // Storing values and indices
902  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
903 
904  // Check X bounds
905  if( index[ 0 ] <= 0 ) limitX1 = 0;
906  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
907 
908  // Check Z bounds
909  if( index[ 1 ] <= 0 ) limitZ1 = 0;
910  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
911 
912  // If the main voxel and all the neighbours are masked and inside the FOV, skip
913  // Try to avoid as much computations as possible and be on the safe side
914  // (better to include some masked voxels than to skip some relevant voxels)
915  if ( limitX1 && limitZ1 && limitX2 && limitZ2 &&
919  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + 1) ) continue;
920 
921  // Bilinear interpolation component (using floor)
922  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
923  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
924  wfl[ 0 ] /= mp_sizeVox[ 0 ];
925  wfl[ 1 ] /= mp_sizeVox[ 2 ];
926 
927  // Bilinear interpolation component (using ceil)
928  wcl[ 0 ] = 1.0 - wfl[ 0 ];
929  wcl[ 1 ] = 1.0 - wfl[ 1 ];
930 
931  // Final coefficients
932  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
933  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
934  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
935  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
936 
937  tof_weight = 0.;
939  {
940  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
941  // nearest to the projection of the voxel center onto the LOR
942  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
943  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight = mp_TOFWeightingFcn[temp];
944  }
945  else
946  {
948  //next_edge_erf = erf( ( ( step + mp_sizeVox[ 1 ] * 0.5 ) * factor_for_tof - lor_tof_center ) / tof_sigma_sqrt2 );
950  //tof_weight = 0.5 * ::fabs(previous_edge_erf - next_edge_erf) ;
952  //previous_edge_erf = next_edge_erf;
953 
955  {
956  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
957  // of the Gaussian centered at the projection of the voxel center onto the LOR
958  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
959  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
960  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
961  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
962  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
963  tof_weight = 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
964  }
965  else
966  {
967  // value of the normalized Gaussian (centered at the TOF measurement)
968  // at the projection of the voxel center onto the LOR
969  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center ) / tof_sigma;
970  tof_weight = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
971  }
972  }
973 
974  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * tof_weight * limitX1 * limitZ1);
975  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * tof_weight * limitX1 * limitZ2);
976  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * tof_weight * limitX2 * limitZ2);
977  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * tof_weight * limitX2 * limitZ1);
978 
979  limitX1 = 1; limitX2 = 1;
980  limitZ1 = 1; limitZ2 = 1;
981  }
982  }
983  return 0;
984 }
985 
986 // =====================================================================
987 // ---------------------------------------------------------------------
988 // ---------------------------------------------------------------------
989 // =====================================================================
990 
991 int iProjectorJoseph::ProjectTOFHistogram(int a_direction, oProjectionLine* ap_ProjectionLine)
992 {
993  #ifdef CASTOR_DEBUG
994  if (!m_initialized)
995  {
996  Cerr("***** iProjectorJoseph::ProjectTOFHistogram() -> Called while not initialized !" << endl);
997  Exit(EXIT_DEBUG);
998  }
999  #endif
1000 
1001  #ifdef CASTOR_VERBOSE
1002  if (m_verbose>=10)
1003  {
1004  string direction = "";
1005  if (a_direction==FORWARD) direction = "forward";
1006  else direction = "backward";
1007  Cout("iProjectorJoseph::Project with TOF bins -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
1008  }
1009  #endif
1010 
1011  // Get event positions
1012  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
1013  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
1014 
1015  // Distance between point event1 and event2
1016  HPFLTNB const r[ 3 ] = {
1017  event2[ 0 ] - event1[ 0 ],
1018  event2[ 1 ] - event1[ 1 ],
1019  event2[ 2 ] - event1[ 2 ]
1020  };
1021 
1022  // LOR length
1023  HPFLTNB lor_length = ap_ProjectionLine->GetLength();
1024  HPFLTNB lor_length_half = lor_length * 0.5;
1025 
1026  // Get TOF info
1027  INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
1028  INTNB tof_half_nb_bins = tof_nb_bins/2;
1029 
1030  // TOF Gaussian standard deviation and truncation
1032  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
1033  HPFLTNB tof_half_span = 0.;
1035  else tof_half_span = tof_sigma * m_TOFNbSigmas;
1036 
1037  // if integration of the Gaussian over the TOF bin, need for help variables to save calls to erf
1038  HPFLTNB prev_erf = 0., new_erf = 0.;
1039 
1040  // minimum and maximum TOF bins, the whole span
1041  INTNB tof_bin_last = tof_half_nb_bins;
1042  INTNB tof_bin_first = -tof_half_nb_bins;
1043 
1044  // distance between the first event1 and the center of a TOF bin along the LOR
1045  HPFLTNB lor_tof_center = 0.;
1046  // the sum of all TOF bin weights for a voxel
1047  //HPFLTNB tof_norm_coef = 0.;
1048 
1049  // the first and the last relevant TOF bins for a voxel
1050  INTNB tof_bin_first_for_voxel = 0, tof_bin_last_for_voxel = 0;
1051 
1052 /*
1053  // Square of r
1054  HPFLTNB const r2[ 3 ] = {
1055  r[ 0 ] * r[ 0 ],
1056  r[ 1 ] * r[ 1 ],
1057  r[ 2 ] * r[ 2 ]
1058  };
1059 */
1060 
1061  // Find the first and last intersecting plane in X using the parametric
1062  // values alphaMin and alphaMax
1063  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
1064 
1065  // Variables for Joseph
1066  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
1067  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
1068  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
1069  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
1070  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
1071  int32_t finalIdx = 0; // final index
1072  int8_t limitX1 = 1; int8_t limitX2 = 1;
1073  int8_t limitY1 = 1; int8_t limitY2 = 1;
1074  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
1075 
1076  // Condition on the largest component of r
1077  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
1078  {
1079  // Computing the parametric values
1080  // For the X-axis
1081  // We stay in the limit of the image space
1082  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
1083  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
1084  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
1085  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
1086 
1087  // For the Y-axis
1088  // We introduce 1 voxel size around Y-axis
1089  // Make a shift on first and last plane (like an offset)
1090  if( r[ 1 ] != 0.0 )
1091  {
1092  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
1093  / r[ 1 ];
1094  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
1095  / r[ 1 ];
1096  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
1097  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
1098  }
1099 
1100  // For the Z-axis
1101  // We introduce 1 voxel size around Z-axis
1102  // Make a shift on first and last plane (like an offset)
1103  if( r[ 2 ] != 0.0 )
1104  {
1105  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1106  / r[ 2 ];
1107  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1108  / r[ 2 ];
1109  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
1110  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
1111  }
1112 
1113  if( alphaMax <= alphaMin ) return 0;
1114 
1115  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
1116  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
1117 
1118  // temporary storage for TOF bin weights
1119  // allocation after potential returns
1120  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
1121  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
1122 
1123  // Computing weight of normalization
1124  HPFLTNB const factor( ::fabs( r[ 0 ] ) / lor_length );
1125  HPFLTNB const factor_for_tof( lor_length / r[ 0 ] );
1126  HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
1127 
1128  // Computing the increment
1129  HPFLTNB const ri[ 3 ] = {
1130  1.0, // r[ 0 ] / r[ 0 ]
1131  r[ 1 ] / r[ 0 ],
1132  r[ 2 ] / r[ 0 ]
1133  };
1134 
1135  // Computing the first and the last plane
1136  int iMin = 0, iMax = 0;
1137  if( r[ 0 ] > 0.0 )
1138  {
1139  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
1140  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1141  }
1142  else if( r[ 0 ] < 0.0 )
1143  {
1144  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
1145  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1146  }
1147 
1148  // Computing an offset to get the correct position in plane
1149  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
1150 
1151  // Loop over all the crossing planes
1152  for( int i = iMin; i < iMax; ++i )
1153  {
1154  // Computing position on crossed plane in term of grid spacing
1155  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
1156  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
1157  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
1158 
1159  // Find the index in the image
1160  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
1161  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
1162 
1163  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
1164 
1165  // Check Y bounds
1166  if( index[ 0 ] <= 0 ) limitY1 = 0;
1167  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
1168 
1169  // Check Z bounds
1170  if( index[ 1 ] <= 0 ) limitZ1 = 0;
1171  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
1172 
1173  // If the main voxel and all the neighbours are masked and inside the FOV, skip
1174  // Try to avoid as much computations as possible and be on the safe side
1175  // (better to include some masked voxels than to skip some relevant voxels)
1176  if ( limitY1 && limitZ1 && limitY2 && limitZ2 &&
1179  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY) &&
1180  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ]) ) continue;
1181 
1182  // Compute the first and the last relevant TOF bin for this voxel
1184  {
1185  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1186  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (step * factor_for_tof - tof_half_span - lor_length_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1187  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (step * factor_for_tof + tof_half_span - lor_length_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1188  }
1189  else
1190  {
1191  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1192  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - lor_length_half ) / m_TOFBinSizeInMm )));
1193  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - lor_length_half) / m_TOFBinSizeInMm )));
1194  }
1195 
1196  lor_tof_center = lor_length_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1197 
1198  // shift tof bin indices from -/+ to 0:nbBins range
1199  tof_bin_first_for_voxel += tof_half_nb_bins;
1200  tof_bin_last_for_voxel += tof_half_nb_bins;
1201 
1202  // initialization of help variables for reducing calls to erf
1204  {
1205  // bound the integration to the Gaussian truncation
1206  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
1207  prev_erf = erf(temp/tof_sigma_sqrt2);
1208  }
1209 
1210  // compute the TOF bin weights for the current voxel for all relevant TOF bins
1211  //tof_norm_coef = 0.;
1212  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1213  {
1215  {
1216  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1217  // nearest to the projection of the voxel center onto the LOR
1218  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor);
1219  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1220  // add the weight to the sum
1221  //tof_norm_coef += tof_weights_temp[tof_bin];
1222  // update TOF bin center along the LOR for the next TOF bin
1223  lor_tof_center += m_TOFBinSizeInMm;
1224  }
1225  else
1226  {
1228  {
1229  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1230  // reuse of integration from the previous bin to save calls to erf
1231  // bound the integration to the Gaussian truncation
1232  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
1233  new_erf = erf(temp/tof_sigma_sqrt2);
1234  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1235  // add the weight to the sum
1236  //tof_norm_coef += tof_weights_temp[tof_bin];
1237  prev_erf = new_erf;
1238  // update TOF bin center along the LOR for the next TOF bin
1239  lor_tof_center += m_TOFBinSizeInMm;
1240  }
1241  else
1242  {
1243  // 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
1244  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center) / tof_sigma;
1245  // save the weight temporarily
1246  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1247  // add the weight to the sum
1248  //tof_norm_coef += tof_weights_temp[tof_bin];
1249  // update TOF bin center along the LOR for the next TOF bin
1250  lor_tof_center += m_TOFBinSizeInMm;
1251  }
1252  }
1253  }
1254 
1255  // Bilinear interpolation component (using floor)
1256  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
1257  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
1258  wfl[ 0 ] /= mp_sizeVox[ 1 ];
1259  wfl[ 1 ] /= mp_sizeVox[ 2 ];
1260 
1261  // Bilinear interpolation component (using ceil)
1262  wcl[ 0 ] = 1.0 - wfl[ 0 ];
1263  wcl[ 1 ] = 1.0 - wfl[ 1 ];
1264 
1265  // Final coefficients
1266  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
1267  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
1268  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
1269  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
1270 /*
1271  if (tof_norm_coef>0.)
1272  {
1273  // first normalize TOF bin weights so that they sum to 1
1274  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;
1275  }
1276 */
1277  // compute and write the final TOF bin projection coefficients for current voxels
1278  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * limitY1 * limitZ1, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1279  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * limitY1 * limitZ2 , tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1280  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * limitY2 * limitZ2 , tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1281  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * limitY2 * limitZ1 , tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1282 
1283 
1284  limitY1 = 1.0; limitY2 = 1.0;
1285  limitZ1 = 1.0; limitZ2 = 1.0;
1286  }
1287  delete[] tof_weights_temp;
1288  }
1289  else
1290  {
1291  // Computing the parametric values
1292  // For the X-axis
1293  // We introduce 1 voxel size around Y-axis
1294  if( r[ 0 ] != 0.0 )
1295  {
1296  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
1297  / r[ 0 ];
1298  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
1299  / r[ 0 ];
1300  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
1301  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
1302  }
1303 
1304  // For the Y-axis
1305  // Make a shift on first and last plane (like an offset)
1306  // We stay in the limit of the image space
1307  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
1308  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
1309  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
1310  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
1311 
1312  // For the Z-axis
1313  // We introduce 1 voxel size around Z-axis
1314  // Make a shift on first and last plane (like an offset)
1315  if( r[ 2 ] != 0.0 )
1316  {
1317  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1318  / r[ 2 ];
1319  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1320  / r[ 2 ];
1321  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
1322  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
1323  }
1324 
1325  if( alphaMax <= alphaMin ) return 0;
1326 
1327  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
1328  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
1329 
1330  // temporary storage for TOF bins Gaussian integrals over the current voxel, allocation after potential returns
1331  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
1332  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
1333 
1334  // Computing weight of normalization
1335  HPFLTNB const factor( ::fabs( r[ 1 ] ) / lor_length );
1336  HPFLTNB const factor_for_tof( lor_length / r[ 1 ] );
1337  HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
1338 
1339  // Computing the increment
1340  HPFLTNB const ri[ 3 ] = {
1341  r[ 0 ] / r[ 1 ],
1342  1.0, // r[ 1 ] / r[ 1 ]
1343  r[ 2 ] / r[ 1 ]
1344  };
1345 
1346  // Computing the first and the last plane
1347  int jMin = 0, jMax = 0;
1348  if( r[ 1 ] > 0.0 )
1349  {
1350  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
1351  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1352  }
1353  else if( r[ 1 ] < 0.0 )
1354  {
1355  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
1356  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1357  }
1358 
1359  // Computing an offset to get the correct position in plane
1360  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
1361 
1362  // Loop over all the crossing planes
1363  for( int j = jMin; j < jMax; ++j )
1364  {
1365  // Computing position on crossed plane in term of grid spacing
1366  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
1367  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
1368  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
1369 
1370  // Find the index in the image
1371  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
1372  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
1373 
1374  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
1375 
1376  // Check X bounds
1377  if( index[ 0 ] <= 0 ) limitX1 = 0;
1378  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
1379 
1380  // Check Z bounds
1381  if( index[ 1 ] <= 0 ) limitZ1 = 0;
1382  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
1383 
1384  // If the main voxel and all the neighbours are masked and inside the FOV, skip
1385  // Try to avoid as much computations as possible and be on the safe side
1386  // (better to include some masked voxels than to skip some relevant voxels)
1387  if ( limitX1 && limitZ1 && limitX2 && limitZ2 &&
1391  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + 1) ) continue;
1392 
1393  // Compute the first and the last relevant TOF bin for this voxel
1395  {
1396  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1397  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (step * factor_for_tof - tof_half_span - lor_length_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1398  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (step * factor_for_tof + tof_half_span - lor_length_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1399  }
1400  else
1401  {
1402  // taking the TOF bins whose TOF uncertainty function (centered at the bin center) reaches the voxel center projection
1403  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - lor_length_half ) / m_TOFBinSizeInMm )));
1404  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - lor_length_half) / m_TOFBinSizeInMm )));
1405  }
1406 
1407  lor_tof_center = lor_length_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1408 
1409  // if min/max TOF bins for this voxel do not fall into the total available TOF bins range, skip
1410  if(tof_bin_first_for_voxel>tof_bin_last || tof_bin_last_for_voxel<tof_bin_first) continue;
1411 
1412  // shift tof bin indices from -/+ to 0:nbBins range
1413  tof_bin_first_for_voxel += tof_half_nb_bins;
1414  tof_bin_last_for_voxel += tof_half_nb_bins;
1415 
1416  // initialization of help variables for reducing calls to erf
1418  {
1419  // bound the integration to the Gaussian truncation
1420  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
1421  prev_erf = erf(temp/tof_sigma_sqrt2);
1422  }
1423 
1424  // compute TOF bin weights for the current voxel for all relevant TOF bins
1425  //tof_norm_coef = 0.;
1426  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1427  {
1429  {
1430  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1431  // nearest to the projection of the voxel center onto the LOR
1432  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1433  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1434  // add the weight to the sum for normalization
1435  //tof_norm_coef += tof_weights_temp[tof_bin];
1436  // update TOF center along the LOR for the next TOF bin
1437  lor_tof_center += m_TOFBinSizeInMm;
1438  }
1439  else
1440  {
1442  {
1443  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1444  // reuse of integration from the previous bin to save calls to erf
1445  // bound the integration to the Gaussian truncation
1446  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
1447  new_erf = erf(temp/tof_sigma_sqrt2);
1448  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1449  // add the weight to the sum for normalization
1450  //tof_norm_coef += tof_weights_temp[tof_bin];
1451  prev_erf = new_erf;
1452  // update TOF center along the LOR for the next TOF bin
1453  lor_tof_center += m_TOFBinSizeInMm;
1454  }
1455  else
1456  {
1457  // 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
1458  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center) / tof_sigma;
1459  // save the weight temporarily
1460  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1461  // add the weight to the sum
1462  //tof_norm_coef += tof_weights_temp[tof_bin];
1463  // update TOF bin center along the LOR for the next TOF bin
1464  lor_tof_center += m_TOFBinSizeInMm;
1465  }
1466  }
1467  }
1468 
1469  // Bilinear interpolation component (using floor)
1470  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
1471  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
1472  wfl[ 0 ] /= mp_sizeVox[ 0 ];
1473  wfl[ 1 ] /= mp_sizeVox[ 2 ];
1474 
1475  // Bilinear interpolation component (using ceil)
1476  wcl[ 0 ] = 1.0 - wfl[ 0 ];
1477  wcl[ 1 ] = 1.0 - wfl[ 1 ];
1478 
1479  // Final coefficients
1480  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
1481  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
1482  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
1483  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
1484 
1485  // Storing values and indices
1486 /*
1487  if (tof_norm_coef>0.)
1488  {
1489  // first normalize TOF bin weights so that they sum to 1
1490  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;
1491  }
1492 */
1493  // compute and write the final TOF bin projection coefficients for current voxels
1494  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * limitX1 * limitZ1, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1495  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * limitX1 * limitZ2, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1496  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * limitX2 * limitZ2, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1497  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * limitX2 * limitZ1, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1498 
1499  limitX1 = 1; limitX2 = 1;
1500  limitZ1 = 1; limitZ2 = 1;
1501  }
1502  delete[] tof_weights_temp;
1503  }
1504 
1505  return 0;
1506 }
1507 
1508 // =====================================================================
1509 // ---------------------------------------------------------------------
1510 // ---------------------------------------------------------------------
1511 // =====================================================================
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the &#39;a_input&#39; string corresponding to the &#39;a_option&#39; into &#39;a_nbElts&#39; elements, using the &#39;sep&#39; separator. The results are returned in the templated &#39;ap_return&#39; dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
#define Cerr(MESSAGE)
int InitializeSpecific()
This function is used to initialize specific stuff to the child projector.
void ShowHelpSpecific()
A function used to show help about the child module.
void AddVoxelAllTOFBins(int a_direction, INTNB a_voxelIndex, FLTNB a_voxelWeight, HPFLTNB *a_tofWeights, INTNB a_tofBinFirst, INTNB a_tofBinLast)
FLTNB GetTOFMeasurementInPs()
This function is used to get the TOF measurement in ps.
This class is designed to generically described any on-the-fly projector.
#define SPEED_OF_LIGHT_IN_MM_PER_PS
int ReadConfigurationFile(const string &a_configurationFile)
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
FLTNB GetLength()
This function is used to get the length of the line.
int ReadOptionsList(const string &a_optionsList)
void Exit(int code)
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child projector.
~iProjectorJoseph()
The destructor of iProjectorJoseph.
int ProjectTOFListmode(int a_direction, oProjectionLine *ap_ProjectionLine)
FLTNB * GetPosition1()
This function is used to get the pointer to the mp_position1 (3-values tab).
void AddVoxel(int a_direction, INTNB a_voxelIndice, FLTNB a_voxelWeight)
INTNB EstimateMaxNumberOfVoxelsPerLine()
This function is used to compute and provide an estimate of the maximum number of voxels that could c...
This class is designed to manage and store system matrix elements associated to a vEvent...
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
int ProjectTOFHistogram(int a_direction, oProjectionLine *ap_ProjectionLine)
int GetNbTOFBins()
This function is used to get the number of TOF bins in use.
FLTNB * GetPosition2()
This function is used to get the pointer to the mp_position2 (3-values tab).
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
iProjectorJoseph()
The constructor of iProjectorJoseph.
#define Cout(MESSAGE)
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.