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