CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
code/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 &&
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
592  HPFLTNB *ptof_sigma = new HPFLTNB[ GetNbTOFResolutions() ];
593  HPFLTNB *ptof_sigma_sqrt2 = new HPFLTNB[ GetNbTOFResolutions() ];
594  HPFLTNB *ptof_half_span = new HPFLTNB[ GetNbTOFResolutions() ];
595 
596  for (int r=0 ; r<GetNbTOFResolutions() ; r++)
597  {
598 
599  ptof_sigma[ r ] = mp_TOFResolutionInMm[ r ] / TWO_SQRT_TWO_LN_2;
600  ptof_sigma_sqrt2[ r ] = sqrt(2.)*ptof_sigma[ r ];
601  ptof_half_span[ r ] = 0.;
602 
603  if (m_TOFWeightingFcnPrecomputedFlag) ptof_half_span[ r ] = ((HPFLTNB)m_TOFWeightingFcnNbSamples)/(2.*m_TOFPrecomputedSamplingFactor);
604  else if (m_TOFBinProperProcessingFlag) ptof_half_span[ r ] = ptof_sigma[ r ] * m_TOFNbSigmas + 0.5 * m_TOFBinSizeInMm;
605  else ptof_half_span[ r ] = ptof_sigma[ r ] * m_TOFNbSigmas;
606 
607  // Recover largest tof half span
608  HPFLTNB tof_half_span = ptof_half_span[ 0 ];
609 
610  for (int r=1 ; r<GetNbTOFResolutions() ; r++) tof_half_span = tof_half_span > ptof_half_span[ r ] ? tof_half_span : ptof_half_span[ r ] ;
611  }
612  */
613 
614  // TOF standard deviation and truncation
616  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
617  HPFLTNB tof_half_span = 0.;
618 
620  else if (m_TOFBinProperProcessingFlag) tof_half_span = tof_sigma * m_TOFNbSigmas + 0.5 * m_TOFBinSizeInMm;
621  else tof_half_span = tof_sigma * m_TOFNbSigmas;
622 
623  // distance between the first event1 and the TOF measurement
624  HPFLTNB lor_tof_center = lor_length * 0.5 + tof_delta;
625 
626  // coordinates of the lower edge of the first voxel falling inside the truncated TOF distribution centered at the TOF measurement
627  HPFLTNB tof_edge_low[] = {0,0,0};
628  // coordinate of the upper edge of the last voxel falling inside the truncated TOF distribution centered at the TOF measurement
629  HPFLTNB tof_edge_high[] = {0,0,0};
630  HPFLTNB tof_center;
631  INTNB tof_index;
632 
633 
634  // low/high voxel edges (in absolute coordinates) for truncated TOF
635  for (int ax=0;ax<3;ax++)
636  {
637  // absolute coordinate along each axis of the center of the TOF distribution
638  tof_center = event1[ax] + lor_tof_center * r[ax] / lor_length;
639 
640  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
641  tof_edge_low[ax] = tof_center - tof_half_span * fabs(r[ax]) / lor_length;
642  tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
643  // if low TOF edge above the highest FOV edge, return empty line
644  if (tof_index>mp_nbVox[ax]-1) return 0;
645  tof_edge_low[ax] = (HPFLTNB)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
646 
647  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
648  tof_edge_high[ax] = tof_center + tof_half_span * fabs(r[ax]) / lor_length;
649  tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
650  // if high TOF edge below the lowest FOV edge, return empty line
651  if (tof_index<0) return 0;
652  tof_edge_high[ax] = (HPFLTNB)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
653  }
654 
655  // Find the first and last intersecting plane in X using the parametric
656  // values alphaMin and alphaMax
657  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
658 
659  // Variables for Joseph
660  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
661  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
662  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
663  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
664  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
665  int32_t finalIdx = 0; // final index
666  int8_t limitX1 = 1; int8_t limitX2 = 1;
667  int8_t limitY1 = 1; int8_t limitY2 = 1;
668  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
669 
670  // Condition on the largest component of r
671  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
672  {
673  // Computing the parametric values
674  // For the X-axis
675  // We stay in the limit of the image space
676  HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - event1[ 0 ] ) / r[ 0 ];
677  HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] - event1[ 0 ] ) / r[ 0 ];
678  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
679  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
680 
681  // For the Y-axis
682  // We introduce 1 voxel size around Y-axis
683  // Make a shift on first and last plane (like an offset)
684  if( r[ 1 ] != 0.0 )
685  {
686  HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
687  / r[ 1 ];
688  HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
689  / r[ 1 ];
690  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
691  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
692  }
693 
694  // For the Z-axis
695  // We introduce 1 voxel size around Z-axis
696  // Make a shift on first and last plane (like an offset)
697  if( r[ 2 ] != 0.0 )
698  {
699  HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
700  / r[ 2 ];
701  HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
702  / r[ 2 ];
703  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
704  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
705  }
706 
707  if( alphaMax <= alphaMin ) return 0;
708 
709  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
710  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
711 
712  // Computing weight of normalization
713  HPFLTNB const factor( ::fabs( r[ 0 ] ) / lor_length );
714  HPFLTNB const factor_for_tof( lor_length / r[ 0 ]) ;
715  HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
716 
717  // Computing the increment
718  HPFLTNB const ri[ 3 ] = {
719  1.0, // r[ 0 ] / r[ 0 ]
720  r[ 1 ] / r[ 0 ],
721  r[ 2 ] / r[ 0 ]
722  };
723 
724  // Computing the first and the last plane
725  int iMin = 0, iMax = 0;
726  if( r[ 0 ] > 0.0 )
727  {
728  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
729  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
730  }
731  else if( r[ 0 ] < 0.0 )
732  {
733  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
734  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
735  }
736 
737  // Computing an offset to get the correct position in plane
738  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
739 
741  //HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 0 ] + iMin * mp_sizeVox[ 0 ] - event1[ 0 ] ) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
742  //HPFLTNB next_edge_erf = 0.;
743  HPFLTNB tof_weight = 0.;
744 
745  // Loop over all the crossing planes
746  for( int i = iMin; i < iMax; ++i )
747  {
748  // Computing position on crossed plane in term of grid spacing
749  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
750  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
751  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
752 
753  // Find the index in the image
754  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
755  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
756 
757  // Storing values and indices
758  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
759 
760  // Check Y bounds
761  if( index[ 0 ] <= 0 ) limitY1 = 0;
762  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
763 
764  // Check Z bounds
765  if( index[ 1 ] <= 0 ) limitZ1 = 0;
766  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
767 
768  // If the main voxel and all the neighbours are masked and inside the FOV, skip
769  // Try to avoid as much computations as possible and be on the safe side
770  // (better to include some masked voxels than to skip some relevant voxels)
771  if ( limitY1 && limitZ1 && limitY2 && limitZ2 &&
775  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ]) ) continue;
776 
777  // Bilinear interpolation component (using floor)
778  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
779  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
780  wfl[ 0 ] /= mp_sizeVox[ 1 ];
781  wfl[ 1 ] /= mp_sizeVox[ 2 ];
782 
783  // Bilinear interpolation component (using ceil)
784  wcl[ 0 ] = 1.0 - wfl[ 0 ];
785  wcl[ 1 ] = 1.0 - wfl[ 1 ];
786 
787  // Final coefficients
788  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
789  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
790  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
791  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
792 
793  tof_weight = 0.;
794 
796  {
797  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
798  // nearest to the projection of the voxel center onto the LOR
799 
800  /* // Implementation including offset
801  for(int r=0 ; r<m_nbTOFResolutions ; r++)
802  {
803  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center + mp_TOFOffsetInMm[ r ]) * m_TOFPrecomputedSamplingFactor );
804  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight += m2p_TOFWeightingFcn[r][temp];
805  }
806  */
807  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center ) * m_TOFPrecomputedSamplingFactor );
808  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight = m2p_TOFWeightingFcn[0][temp];
809  }
810  else
811  {
813  //next_edge_erf = erf( ( ( step + mp_sizeVox[ 0 ] * 0.5 ) * factor_for_tof - lor_tof_center ) / tof_sigma_sqrt2 );
815  //tof_weight = 0.5 * ::fabs(previous_edge_erf - next_edge_erf) ;
817  //previous_edge_erf = next_edge_erf;
818 
819 
820  HPFLTNB sum_tof_gaussian_norm_coef = 0.;
821 
822  for (int r=0 ; r<GetNbTOFResolutions() ; r++)
823  {
825  {
826  // TODO TM : implement multi-tof kernel for proper TOF processing (integration method)
827  Cerr("***** iProjectorJoseph::ProjectTOFListmode()() -> TOF proper processing not implemented for joseph. Check code !" << endl);
828  return 1;
829  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
830  // of the Gaussian centered at the projection of the voxel center onto the LOR
831  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
832  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof + mp_TOFOffsetInMm[ r ]));
834  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
835  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof + mp_TOFOffsetInMm[ r ]));
836  //HPFLTNB new_erf = erf(temp_erf/ptof_sigma_sqrt2[ r ]);
837  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
838  tof_weight += 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
839  }
840  else
841  {
842  // value of the normalized Gaussian (centered at the TOF measurement)
843  // at the projection of the voxel center onto the LOR
844  //HPFLTNB temp = ( step * factor_for_tof - lor_tof_center + mp_TOFOffsetInMm[ r ] ) / ptof_sigma[ r ];
845  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center + mp_TOFOffsetInMm[ r ] ) / tof_sigma;
846  tof_weight += mp_TOFProbabilities[ r ] * exp(- 0.5 * temp * temp ) ;
847  sum_tof_gaussian_norm_coef += mp_TOFProbabilities[ r ]/mp_TOFGaussianNormCoef[ r ];
848  }
849  }
850  tof_weight /= sum_tof_gaussian_norm_coef;
851  }
852 
853  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * tof_weight * limitY1 * limitZ1);
854  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * tof_weight * limitY1 * limitZ2);
855  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * tof_weight * limitY2 * limitZ2);
856  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * tof_weight * limitY2 * limitZ1);
857 
858  limitY1 = 1.0; limitY2 = 1.0;
859  limitZ1 = 1.0; limitZ2 = 1.0;
860  }
861  }
862  else
863  {
864  // Computing the parametric values
865  // For the X-axis
866  // We introduce 1 voxel size around Y-axis
867  if( r[ 0 ] != 0.0 )
868  {
869  HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
870  / r[ 0 ];
871  HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
872  / r[ 0 ];
873  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
874  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
875  }
876 
877  // For the Y-axis
878  // Make a shift on first and last plane (like an offset)
879  // We stay in the limit of the image space
880  HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - event1[ 1 ] ) / r[ 1 ];
881  HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] - event1[ 1 ] ) / r[ 1 ];
882  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
883  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
884 
885  // For the Z-axis
886  // We introduce 1 voxel size around Z-axis
887  // Make a shift on first and last plane (like an offset)
888  if( r[ 2 ] != 0.0 )
889  {
890  HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
891  / r[ 2 ];
892  HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
893  / r[ 2 ];
894  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
895  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
896  }
897 
898  if( alphaMax <= alphaMin ) return 0;
899 
900  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
901  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
902 
903  // Computing weight of normalization
904  HPFLTNB const factor( ::fabs( r[ 1 ] ) / lor_length );
905  HPFLTNB const factor_for_tof( lor_length / r[ 1 ] );
906  HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
907 
908  // Computing the increment
909  HPFLTNB const ri[ 3 ] = {
910  r[ 0 ] / r[ 1 ],
911  1.0, // r[ 1 ] / r[ 1 ]
912  r[ 2 ] / r[ 1 ]
913  };
914 
915  // Computing the first and the last plane
916  int jMin = 0, jMax = 0;
917  if( r[ 1 ] > 0.0 )
918  {
919  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
920  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
921  }
922  else if( r[ 1 ] < 0.0 )
923  {
924  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
925  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
926  }
927 
929  //HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 1 ] + jMin * mp_sizeVox[ 1 ] - event1[ 1 ]) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
930  //HPFLTNB next_edge_erf = 0.;
931  HPFLTNB tof_weight = 0.;
932 
933  // Computing an offset to get the correct position in plane
934  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
935 
936  // Loop over all the crossing planes
937  for( int j = jMin; j < jMax; ++j )
938  {
939  // Computing position on crossed plane in term of grid spacing
940  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
941  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
942  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
943 
944  // Find the index in the image
945  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
946  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
947 
948  // Storing values and indices
949  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
950 
951  // Check X bounds
952  if( index[ 0 ] <= 0 ) limitX1 = 0;
953  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
954 
955  // Check Z bounds
956  if( index[ 1 ] <= 0 ) limitZ1 = 0;
957  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
958 
959  // If the main voxel and all the neighbours are masked and inside the FOV, skip
960  // Try to avoid as much computations as possible and be on the safe side
961  // (better to include some masked voxels than to skip some relevant voxels)
962  if ( limitX1 && limitZ1 && limitX2 && limitZ2 &&
966  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + 1) ) continue;
967 
968  // Bilinear interpolation component (using floor)
969  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
970  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
971  wfl[ 0 ] /= mp_sizeVox[ 0 ];
972  wfl[ 1 ] /= mp_sizeVox[ 2 ];
973 
974  // Bilinear interpolation component (using ceil)
975  wcl[ 0 ] = 1.0 - wfl[ 0 ];
976  wcl[ 1 ] = 1.0 - wfl[ 1 ];
977 
978  // Final coefficients
979  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
980  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
981  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
982  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
983 
984  tof_weight = 0.;
985 
987  {
988  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
989  // nearest to the projection of the voxel center onto the LOR
990 
991  /* // Implementation including offset
992  for(int r=0 ; r<m_nbTOFResolutions ; r++)
993  {
994  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center + mp_TOFOffsetInMm[ r ]) * m_TOFPrecomputedSamplingFactor );
995  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight += m2p_TOFWeightingFcn[r][temp];
996  }
997  */
998  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( step * factor_for_tof - lor_tof_center ) * m_TOFPrecomputedSamplingFactor );
999  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weight = m2p_TOFWeightingFcn[0][temp];
1000  }
1001  else
1002  {
1004  //next_edge_erf = erf( ( ( step + mp_sizeVox[ 1 ] * 0.5 ) * factor_for_tof - lor_tof_center ) / tof_sigma_sqrt2 );
1006  //tof_weight = 0.5 * ::fabs(previous_edge_erf - next_edge_erf) ;
1008  //previous_edge_erf = next_edge_erf;
1009 
1010  HPFLTNB sum_tof_gaussian_norm_coef = 0.;
1011 
1012  for (int r=0 ; r<GetNbTOFResolutions() ; r++)
1013  {
1015  {
1016  // TODO TM : implement multi-tof kernel for proper TOF processing (integration method)
1017  Cerr("***** iProjectorJoseph::ProjectTOFListmode()() -> TOF proper processing not implemented for joseph. Check code !" << endl);
1018  return 1;
1019  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
1020  // of the Gaussian centered at the projection of the voxel center onto the LOR
1021  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
1022  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof + mp_TOFOffsetInMm[ r ]));
1024  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
1025  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof + mp_TOFOffsetInMm[ r ]));
1027  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
1028  tof_weight = 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
1029  }
1030  else
1031  {
1032  // value of the normalized Gaussian (centered at the TOF measurement)
1033  // at the projection of the voxel center onto the LOR
1035  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center + mp_TOFOffsetInMm[ r ] ) / tof_sigma;
1036  tof_weight += mp_TOFProbabilities[ r ] * exp(- 0.5 * temp * temp ) ;
1037  sum_tof_gaussian_norm_coef += mp_TOFProbabilities[ r ]/mp_TOFGaussianNormCoef[ r ];
1038  }
1039  }
1040  tof_weight /= sum_tof_gaussian_norm_coef;
1041  }
1042 
1043  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * tof_weight * limitX1 * limitZ1);
1044  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * tof_weight * limitX1 * limitZ2);
1045  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * tof_weight * limitX2 * limitZ2);
1046  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * tof_weight * limitX2 * limitZ1);
1047 
1048  limitX1 = 1; limitX2 = 1;
1049  limitZ1 = 1; limitZ2 = 1;
1050  }
1051  }
1052 
1053  //delete[] ptof_half_span;
1054  //delete[] ptof_sigma;
1055  //delete[] ptof_sigma_sqrt2;
1056 
1057  return 0;
1058 }
1059 
1060 // =====================================================================
1061 // ---------------------------------------------------------------------
1062 // ---------------------------------------------------------------------
1063 // =====================================================================
1064 
1065 int iProjectorJoseph::ProjectTOFHistogram(int a_direction, oProjectionLine* ap_ProjectionLine)
1066 {
1067  #ifdef CASTOR_DEBUG
1068  if (!m_initialized)
1069  {
1070  Cerr("***** iProjectorJoseph::ProjectTOFHistogram() -> Called while not initialized !" << endl);
1071  Exit(EXIT_DEBUG);
1072  }
1073  #endif
1074 
1075  #ifdef CASTOR_VERBOSE
1076  if (m_verbose>=10)
1077  {
1078  string direction = "";
1079  if (a_direction==FORWARD) direction = "forward";
1080  else direction = "backward";
1081  Cout("iProjectorJoseph::Project with TOF bins -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
1082  }
1083  #endif
1084 
1085  // Get event positions
1086  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
1087  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
1088 
1089  // Distance between point event1 and event2
1090  HPFLTNB const r[ 3 ] = {
1091  event2[ 0 ] - event1[ 0 ],
1092  event2[ 1 ] - event1[ 1 ],
1093  event2[ 2 ] - event1[ 2 ]
1094  };
1095 
1096  // LOR length
1097  HPFLTNB lor_length = ap_ProjectionLine->GetLength();
1098  HPFLTNB lor_length_half = lor_length * 0.5;
1099 
1100  // Get TOF info
1101  INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
1102  INTNB tof_half_nb_bins = tof_nb_bins/2;
1103 
1104  // TOF Gaussian standard deviation and truncation
1105  HPFLTNB tof_sigma = mp_TOFResolutionInMm[ 0 ] / TWO_SQRT_TWO_LN_2;
1106  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
1107  HPFLTNB tof_half_span = 0.;
1109  else tof_half_span = tof_sigma * m_TOFNbSigmas;
1110 
1111  // if integration of the Gaussian over the TOF bin, need for help variables to save calls to erf
1112  HPFLTNB prev_erf = 0., new_erf = 0.;
1113 
1114  // minimum and maximum TOF bins, the whole span
1115  INTNB tof_bin_last = tof_half_nb_bins;
1116  INTNB tof_bin_first = -tof_half_nb_bins;
1117 
1118  // distance between the first event1 and the center of a TOF bin along the LOR
1119  HPFLTNB lor_tof_center = 0.;
1120  // the sum of all TOF bin weights for a voxel
1121  //HPFLTNB tof_norm_coef = 0.;
1122 
1123  // the first and the last relevant TOF bins for a voxel
1124  INTNB tof_bin_first_for_voxel = 0, tof_bin_last_for_voxel = 0;
1125 
1126 /*
1127  // Square of r
1128  HPFLTNB const r2[ 3 ] = {
1129  r[ 0 ] * r[ 0 ],
1130  r[ 1 ] * r[ 1 ],
1131  r[ 2 ] * r[ 2 ]
1132  };
1133 */
1134 
1135  // Find the first and last intersecting plane in X using the parametric
1136  // values alphaMin and alphaMax
1137  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
1138 
1139  // Variables for Joseph
1140  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
1141  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
1142  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
1143  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
1144  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
1145  int32_t finalIdx = 0; // final index
1146  int8_t limitX1 = 1; int8_t limitX2 = 1;
1147  int8_t limitY1 = 1; int8_t limitY2 = 1;
1148  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
1149 
1150  // Condition on the largest component of r
1151  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
1152  {
1153  // Computing the parametric values
1154  // For the X-axis
1155  // We stay in the limit of the image space
1156  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
1157  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
1158  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
1159  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
1160 
1161  // For the Y-axis
1162  // We introduce 1 voxel size around Y-axis
1163  // Make a shift on first and last plane (like an offset)
1164  if( r[ 1 ] != 0.0 )
1165  {
1166  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
1167  / r[ 1 ];
1168  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
1169  / r[ 1 ];
1170  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
1171  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
1172  }
1173 
1174  // For the Z-axis
1175  // We introduce 1 voxel size around Z-axis
1176  // Make a shift on first and last plane (like an offset)
1177  if( r[ 2 ] != 0.0 )
1178  {
1179  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1180  / r[ 2 ];
1181  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1182  / r[ 2 ];
1183  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
1184  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
1185  }
1186 
1187  if( alphaMax <= alphaMin ) return 0;
1188 
1189  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
1190  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
1191 
1192  // temporary storage for TOF bin weights
1193  // allocation after potential returns
1194  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
1195  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
1196 
1197  // Computing weight of normalization
1198  HPFLTNB const factor( ::fabs( r[ 0 ] ) / lor_length );
1199  HPFLTNB const factor_for_tof( lor_length / r[ 0 ] );
1200  HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
1201 
1202  // Computing the increment
1203  HPFLTNB const ri[ 3 ] = {
1204  1.0, // r[ 0 ] / r[ 0 ]
1205  r[ 1 ] / r[ 0 ],
1206  r[ 2 ] / r[ 0 ]
1207  };
1208 
1209  // Computing the first and the last plane
1210  int iMin = 0, iMax = 0;
1211  if( r[ 0 ] > 0.0 )
1212  {
1213  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
1214  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1215  }
1216  else if( r[ 0 ] < 0.0 )
1217  {
1218  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
1219  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1220  }
1221 
1222  // Computing an offset to get the correct position in plane
1223  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
1224 
1225  // Loop over all the crossing planes
1226  for( int i = iMin; i < iMax; ++i )
1227  {
1228  // Computing position on crossed plane in term of grid spacing
1229  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
1230  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
1231  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
1232 
1233  // Find the index in the image
1234  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
1235  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
1236 
1237  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
1238 
1239  // Check Y bounds
1240  if( index[ 0 ] <= 0 ) limitY1 = 0;
1241  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
1242 
1243  // Check Z bounds
1244  if( index[ 1 ] <= 0 ) limitZ1 = 0;
1245  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
1246 
1247  // If the main voxel and all the neighbours are masked and inside the FOV, skip
1248  // Try to avoid as much computations as possible and be on the safe side
1249  // (better to include some masked voxels than to skip some relevant voxels)
1250  if ( limitY1 && limitZ1 && limitY2 && limitZ2 &&
1254  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + mp_nbVox[ 0 ]) ) continue;
1255 
1256  // Compute the first and the last relevant TOF bin for this voxel
1258  {
1259  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1260  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 )));
1261  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 )));
1262  }
1263  else
1264  {
1265  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1266  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - lor_length_half ) / m_TOFBinSizeInMm )));
1267  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - lor_length_half) / m_TOFBinSizeInMm )));
1268  }
1269 
1270  lor_tof_center = lor_length_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1271 
1272  // shift tof bin indices from -/+ to 0:nbBins range
1273  tof_bin_first_for_voxel += tof_half_nb_bins;
1274  tof_bin_last_for_voxel += tof_half_nb_bins;
1275 
1276  // initialization of help variables for reducing calls to erf
1278  {
1279  // bound the integration to the Gaussian truncation
1280  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
1281  prev_erf = erf(temp/tof_sigma_sqrt2);
1282  }
1283 
1284  // compute the TOF bin weights for the current voxel for all relevant TOF bins
1285  //tof_norm_coef = 0.;
1286  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1287  {
1289  {
1290  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1291  // nearest to the projection of the voxel center onto the LOR
1292  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor);
1293  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = m2p_TOFWeightingFcn[0][temp];
1294  // add the weight to the sum
1295  //tof_norm_coef += tof_weights_temp[tof_bin];
1296  // update TOF bin center along the LOR for the next TOF bin
1297  lor_tof_center += m_TOFBinSizeInMm;
1298  }
1299  else
1300  {
1302  {
1303  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1304  // reuse of integration from the previous bin to save calls to erf
1305  // bound the integration to the Gaussian truncation
1306  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
1307  new_erf = erf(temp/tof_sigma_sqrt2);
1308  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1309  // add the weight to the sum
1310  //tof_norm_coef += tof_weights_temp[tof_bin];
1311  prev_erf = new_erf;
1312  // update TOF bin center along the LOR for the next TOF bin
1313  lor_tof_center += m_TOFBinSizeInMm;
1314  }
1315  else
1316  {
1317  // 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
1318  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center) / tof_sigma;
1319  // save the weight temporarily
1320  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * mp_TOFGaussianNormCoef[ 0 ] * m_TOFBinSizeInMm;
1321  // add the weight to the sum
1322  //tof_norm_coef += tof_weights_temp[tof_bin];
1323  // update TOF bin center along the LOR for the next TOF bin
1324  lor_tof_center += m_TOFBinSizeInMm;
1325  }
1326  }
1327  }
1328 
1329  // Bilinear interpolation component (using floor)
1330  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
1331  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
1332  wfl[ 0 ] /= mp_sizeVox[ 1 ];
1333  wfl[ 1 ] /= mp_sizeVox[ 2 ];
1334 
1335  // Bilinear interpolation component (using ceil)
1336  wcl[ 0 ] = 1.0 - wfl[ 0 ];
1337  wcl[ 1 ] = 1.0 - wfl[ 1 ];
1338 
1339  // Final coefficients
1340  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
1341  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
1342  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
1343  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
1344 /*
1345  if (tof_norm_coef>0.)
1346  {
1347  // first normalize TOF bin weights so that they sum to 1
1348  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;
1349  }
1350 */
1351  // compute and write the final TOF bin projection coefficients for current voxels
1352  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);
1353  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);
1354  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);
1355  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);
1356 
1357 
1358  limitY1 = 1.0; limitY2 = 1.0;
1359  limitZ1 = 1.0; limitZ2 = 1.0;
1360  }
1361  delete[] tof_weights_temp;
1362  }
1363  else
1364  {
1365  // Computing the parametric values
1366  // For the X-axis
1367  // We introduce 1 voxel size around Y-axis
1368  if( r[ 0 ] != 0.0 )
1369  {
1370  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
1371  / r[ 0 ];
1372  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
1373  / r[ 0 ];
1374  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
1375  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
1376  }
1377 
1378  // For the Y-axis
1379  // Make a shift on first and last plane (like an offset)
1380  // We stay in the limit of the image space
1381  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
1382  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
1383  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
1384  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
1385 
1386  // For the Z-axis
1387  // We introduce 1 voxel size around Z-axis
1388  // Make a shift on first and last plane (like an offset)
1389  if( r[ 2 ] != 0.0 )
1390  {
1391  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1392  / r[ 2 ];
1393  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1394  / r[ 2 ];
1395  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
1396  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
1397  }
1398 
1399  if( alphaMax <= alphaMin ) return 0;
1400 
1401  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
1402  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
1403 
1404  // temporary storage for TOF bins Gaussian integrals over the current voxel, allocation after potential returns
1405  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
1406  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
1407 
1408  // Computing weight of normalization
1409  HPFLTNB const factor( ::fabs( r[ 1 ] ) / lor_length );
1410  HPFLTNB const factor_for_tof( lor_length / r[ 1 ] );
1411  HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
1412 
1413  // Computing the increment
1414  HPFLTNB const ri[ 3 ] = {
1415  r[ 0 ] / r[ 1 ],
1416  1.0, // r[ 1 ] / r[ 1 ]
1417  r[ 2 ] / r[ 1 ]
1418  };
1419 
1420  // Computing the first and the last plane
1421  int jMin = 0, jMax = 0;
1422  if( r[ 1 ] > 0.0 )
1423  {
1424  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
1425  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1426  }
1427  else if( r[ 1 ] < 0.0 )
1428  {
1429  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
1430  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1431  }
1432 
1433  // Computing an offset to get the correct position in plane
1434  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
1435 
1436  // Loop over all the crossing planes
1437  for( int j = jMin; j < jMax; ++j )
1438  {
1439  // Computing position on crossed plane in term of grid spacing
1440  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
1441  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
1442  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
1443 
1444  // Find the index in the image
1445  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
1446  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
1447 
1448  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
1449 
1450  // Check X bounds
1451  if( index[ 0 ] <= 0 ) limitX1 = 0;
1452  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
1453 
1454  // Check Z bounds
1455  if( index[ 1 ] <= 0 ) limitZ1 = 0;
1456  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
1457 
1458  // If the main voxel and all the neighbours are masked and inside the FOV, skip
1459  // Try to avoid as much computations as possible and be on the safe side
1460  // (better to include some masked voxels than to skip some relevant voxels)
1461  if ( limitX1 && limitZ1 && limitX2 && limitZ2 &&
1465  mp_ImageDimensionsAndQuantification->IsVoxelMasked(finalIdx + 1) ) continue;
1466 
1467  // Compute the first and the last relevant TOF bin for this voxel
1469  {
1470  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1471  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 )));
1472  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 )));
1473  }
1474  else
1475  {
1476  // taking the TOF bins whose TOF uncertainty function (centered at the bin center) reaches the voxel center projection
1477  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (step * factor_for_tof - tof_half_span - lor_length_half ) / m_TOFBinSizeInMm )));
1478  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (step * factor_for_tof + tof_half_span - lor_length_half) / m_TOFBinSizeInMm )));
1479  }
1480 
1481  lor_tof_center = lor_length_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1482 
1483  // if min/max TOF bins for this voxel do not fall into the total available TOF bins range, skip
1484  if(tof_bin_first_for_voxel>tof_bin_last || tof_bin_last_for_voxel<tof_bin_first) continue;
1485 
1486  // shift tof bin indices from -/+ to 0:nbBins range
1487  tof_bin_first_for_voxel += tof_half_nb_bins;
1488  tof_bin_last_for_voxel += tof_half_nb_bins;
1489 
1490  // initialization of help variables for reducing calls to erf
1492  {
1493  // bound the integration to the Gaussian truncation
1494  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - step * factor_for_tof));
1495  prev_erf = erf(temp/tof_sigma_sqrt2);
1496  }
1497 
1498  // compute TOF bin weights for the current voxel for all relevant TOF bins
1499  //tof_norm_coef = 0.;
1500  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1501  {
1503  {
1504  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1505  // nearest to the projection of the voxel center onto the LOR
1506  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (step * factor_for_tof - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1507  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = m2p_TOFWeightingFcn[0][temp];
1508  // add the weight to the sum for normalization
1509  //tof_norm_coef += tof_weights_temp[tof_bin];
1510  // update TOF center along the LOR for the next TOF bin
1511  lor_tof_center += m_TOFBinSizeInMm;
1512  }
1513  else
1514  {
1516  {
1517  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1518  // reuse of integration from the previous bin to save calls to erf
1519  // bound the integration to the Gaussian truncation
1520  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - step * factor_for_tof));
1521  new_erf = erf(temp/tof_sigma_sqrt2);
1522  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1523  // add the weight to the sum for normalization
1524  //tof_norm_coef += tof_weights_temp[tof_bin];
1525  prev_erf = new_erf;
1526  // update TOF center along the LOR for the next TOF bin
1527  lor_tof_center += m_TOFBinSizeInMm;
1528  }
1529  else
1530  {
1531  // 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
1532  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center) / tof_sigma;
1533  // save the weight temporarily
1534  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * mp_TOFGaussianNormCoef[ 0 ] * m_TOFBinSizeInMm;
1535  // add the weight to the sum
1536  //tof_norm_coef += tof_weights_temp[tof_bin];
1537  // update TOF bin center along the LOR for the next TOF bin
1538  lor_tof_center += m_TOFBinSizeInMm;
1539  }
1540  }
1541  }
1542 
1543  // Bilinear interpolation component (using floor)
1544  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
1545  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
1546  wfl[ 0 ] /= mp_sizeVox[ 0 ];
1547  wfl[ 1 ] /= mp_sizeVox[ 2 ];
1548 
1549  // Bilinear interpolation component (using ceil)
1550  wcl[ 0 ] = 1.0 - wfl[ 0 ];
1551  wcl[ 1 ] = 1.0 - wfl[ 1 ];
1552 
1553  // Final coefficients
1554  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
1555  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
1556  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
1557  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
1558 
1559  // Storing values and indices
1560 /*
1561  if (tof_norm_coef>0.)
1562  {
1563  // first normalize TOF bin weights so that they sum to 1
1564  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;
1565  }
1566 */
1567  // compute and write the final TOF bin projection coefficients for current voxels
1568  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);
1569  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);
1570  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);
1571  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);
1572 
1573  limitX1 = 1; limitX2 = 1;
1574  limitZ1 = 1; limitZ2 = 1;
1575  }
1576  delete[] tof_weights_temp;
1577  }
1578 
1579  return 0;
1580 }
1581 
1582 // =====================================================================
1583 // ---------------------------------------------------------------------
1584 // ---------------------------------------------------------------------
1585 // =====================================================================
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.