CASToR  2.0
Tomographic Reconstruction (PET/SPECT/CT)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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-2018 all CASToR contributors listed below:
18 
19  --> current contributors: Thibaut MERLIN, Simon STUTE, Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Mael MILLARDET
20  --> past contributors: Valentin VIELZEUF
21 
22 This is CASToR version 2.0.
23 */
24 
31 #include "iProjectorJoseph.hh"
32 #include "sOutputManager.hh"
33 
34 #include <cmath>
35 #ifdef _WIN32
36 // Avoid compilation errors due to mix up between std::min()/max() and
37 // min max macros
38 #undef min
39 #undef max
40 #endif
41 
42 // =====================================================================
43 // ---------------------------------------------------------------------
44 // ---------------------------------------------------------------------
45 // =====================================================================
46 
48 {
49  // This projector is not compatible with SPECT attenuation correction because
50  // the voxels contributing to the line are not strictly ordered with respect to
51  // their distance to point2 (due to interpolations at each plane crossed)
53  // This projector is compatible with compression as it works only with the
54  // cartesian coordinates of 2 end points of a line
56  // Default pointers and parameters
57  mp_boundariesX = nullptr;
58  mp_boundariesY = nullptr;
59  mp_boundariesZ = nullptr;
60  mp_maskPad = nullptr;
61  m_toleranceX = 0.;
62  m_toleranceY = 0.;
63  m_toleranceZ = 0.;
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  // No options for joseph
119  ;
120  // Normal end
121  return 0;
122 }
123 
124 // =====================================================================
125 // ---------------------------------------------------------------------
126 // ---------------------------------------------------------------------
127 // =====================================================================
128 
130 {
131  cout << "This projector is a line projector that uses linear interpolation between pixels." << endl;
132  cout << "It is implemented from the following published paper:" << endl;
133  cout << "P. M. Joseph, \"An improved algorithm for reprojecting rays through pixel images\", IEEE Trans. Med. Imaging, vol. 1, pp. 192-6, 1982." << endl;
134 }
135 
136 // =====================================================================
137 // ---------------------------------------------------------------------
138 // ---------------------------------------------------------------------
139 // =====================================================================
140 
142 {
143  // Nothing to check for this projector
144  ;
145  // Normal end
146  return 0;
147 }
148 
149 // =====================================================================
150 // ---------------------------------------------------------------------
151 // ---------------------------------------------------------------------
152 // =====================================================================
153 
155 {
156  // Verbose
157  if (m_verbose>=2) Cout("iProjectorJoseph::InitializeSpecific() -> Use Joseph projector" << endl);
158 
159  // Allocate and compute boundaries (grid through voxel centers)
160  mp_boundariesX = new HPFLTNB[ mp_nbVox[ 0 ] + 2 ];
161  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 );
162  mp_boundariesY = new HPFLTNB[ mp_nbVox[ 1 ] + 2 ];
163  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 );
164  mp_boundariesZ = new HPFLTNB[ mp_nbVox[ 2 ] + 2 ];
165  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 );
166 
167  // Allocating the mask buffer for the padded image space
168  INTNB nElts = ( mp_nbVox[ 0 ] + 2 ) * ( mp_nbVox[ 1 ] + 2 ) * ( mp_nbVox[ 2 ] + 2 );
169  mp_maskPad = new uint8_t[ nElts ];
170  ::memset( mp_maskPad, 0, sizeof( uint8_t ) * nElts );
171  for( INTNB k = 1; k < mp_nbVox[ 2 ] + 1; ++k )
172  {
173  for( INTNB j = 1; j < mp_nbVox[ 1 ] + 1; ++j )
174  {
175  for( INTNB i = 1; i < mp_nbVox[ 0 ] + 1; ++i )
176  {
177  mp_maskPad[ i + j * ( ( mp_nbVox[ 0 ] + 2 ) ) + k * ( mp_nbVox[ 0 ] + 2 ) * ( mp_nbVox[ 1 ] + 2 ) ] = 1;
178  }
179  }
180  }
181 
182  // Set the tolerance with respect to voxel sizes in each dimensions
183  HPFLTNB tolerance_factor = 1.e-4;
184  m_toleranceX = ((HPFLTNB)(mp_sizeVox[0])) * tolerance_factor;
185  m_toleranceY = ((HPFLTNB)(mp_sizeVox[1])) * tolerance_factor;
186  m_toleranceZ = ((HPFLTNB)(mp_sizeVox[2])) * tolerance_factor;
187 
188  // Setting the bounds
189  m_boundX = (-mp_halfFOV[ 0 ]) - mp_sizeVox[ 0 ] * 0.5;
190  m_boundY = (-mp_halfFOV[ 1 ]) - mp_sizeVox[ 1 ] * 0.5;
191  m_boundZ = (-mp_halfFOV[ 2 ]) - mp_sizeVox[ 2 ] * 0.5;
192 
193  // Normal end
194  return 0;
195 }
196 
197 // =====================================================================
198 // ---------------------------------------------------------------------
199 // ---------------------------------------------------------------------
200 // =====================================================================
201 
203 {
204  // Find the maximum number of voxels along a given dimension
205  INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
206  if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
207  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
208  // We should have at most 4 voxels in a given plane, so multiply by 4
209  max_nb_voxels_in_dimension *= 4;
210  // Return the value
211  return max_nb_voxels_in_dimension;
212 }
213 
214 // =====================================================================
215 // ---------------------------------------------------------------------
216 // ---------------------------------------------------------------------
217 // =====================================================================
218 
219 int iProjectorJoseph::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
220 {
221  #ifdef CASTOR_DEBUG
222  if (!m_initialized)
223  {
224  Cerr("***** iProjectorJoseph::ProjectWithoutTOF() -> Called while not initialized !" << endl);
225  Exit(EXIT_DEBUG);
226  }
227  #endif
228 
229  #ifdef CASTOR_VERBOSE
230  if (m_verbose>=10)
231  {
232  string direction = "";
233  if (a_direction==FORWARD) direction = "forward";
234  else direction = "backward";
235  Cout("iProjectorJoseph::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
236  }
237  #endif
238 
239  // Get event positions
240  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
241  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
242 
243  // Distance between point event1 and event2
244  HPFLTNB const r[ 3 ] = {
245  event2[ 0 ] - event1[ 0 ],
246  event2[ 1 ] - event1[ 1 ],
247  event2[ 2 ] - event1[ 2 ]
248  };
249 
250  // Square of r
251  HPFLTNB const r2[ 3 ] = {
252  r[ 0 ] * r[ 0 ],
253  r[ 1 ] * r[ 1 ],
254  r[ 2 ] * r[ 2 ]
255  };
256 
257  // Find the first and last intersecting plane in X using the parametric
258  // values alphaMin and alphaMax
259  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
260 
261  // Variables for Joseph
262  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
263  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
264  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
265  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
266  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
267  int32_t finalIdx = 0; // final index
268  int8_t limitX1 = 1; int8_t limitX2 = 1;
269  int8_t limitY1 = 1; int8_t limitY2 = 1;
270  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
271 
272  // Condition on the largest component of r
273  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
274  {
275  // Computing the parametric values
276  // For the X-axis
277  // We stay in the limit of the image space
278  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
279  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
280  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
281  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
282 
283  // For the Y-axis
284  // We introduce 1 voxel size around Y-axis
285  // Make a shift on first and last plane (like an offset)
286  if( r[ 1 ] != 0.0 )
287  {
288  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
289  / r[ 1 ];
290  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
291  / r[ 1 ];
292  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
293  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
294  }
295 
296  // For the Z-axis
297  // We introduce 1 voxel size around Z-axis
298  // Make a shift on first and last plane (like an offset)
299  if( r[ 2 ] != 0.0 )
300  {
301  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
302  / r[ 2 ];
303  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
304  / r[ 2 ];
305  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
306  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
307  }
308 
309  if( alphaMax <= alphaMin ) return 0;
310 
311  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
312  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
313 
314  // Computing weight of normalization
315  HPFLTNB const factor( ::fabs( r[ 0 ] )
316  / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
317  HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
318 
319  // Computing the increment
320  HPFLTNB const ri[ 3 ] = {
321  1.0, // r[ 0 ] / r[ 0 ]
322  r[ 1 ] / r[ 0 ],
323  r[ 2 ] / r[ 0 ]
324  };
325 
326  // Computing the first and the last plane
327  int iMin = 0, iMax = 0;
328  if( r[ 0 ] > 0.0 )
329  {
330  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
331  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
332  }
333  else if( r[ 0 ] < 0.0 )
334  {
335  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
336  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
337  }
338 
339  // Computing an offset to get the correct position in plane
340  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
341 
342  // Loop over all the crossing planes
343  for( int i = iMin; i < iMax; ++i )
344  {
345  // Computing position on crossed plane in term of grid spacing
346  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
347  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
348  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
349 
350  // Find the index in the image
351  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
352  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
353 
354  // Storing values and indices
355  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
356 
357  // Bilinear interpolation component (using floor)
358  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
359  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
360  wfl[ 0 ] /= mp_sizeVox[ 1 ];
361  wfl[ 1 ] /= mp_sizeVox[ 2 ];
362 
363  // Bilinear interpolation component (using ceil)
364  wcl[ 0 ] = 1.0 - wfl[ 0 ];
365  wcl[ 1 ] = 1.0 - wfl[ 1 ];
366 
367  // Final coefficients
368  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
369  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
370  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
371  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
372 
373  // Check Y bounds
374  if( index[ 0 ] <= 0 ) limitY1 = 0;
375  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
376 
377  // Check Z bounds
378  if( index[ 1 ] <= 0 ) limitZ1 = 0;
379  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
380 
381  if (!m_hasMask || (limitY1 && limitZ1 && index[ 0 ] <= mp_nbVox[ 1 ] && index[ 1 ] <= mp_nbVox[ 2 ] && mp_mask[finalIdx]))
382  {
383  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitY1 * limitZ1, w[ 0 ] * weight * limitY1 * limitZ1);
384  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * weight * limitY1 * limitZ2);
385  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * weight * limitY2 * limitZ2);
386  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * weight * limitY2 * limitZ1);
387  }
388  limitY1 = 1.0; limitY2 = 1.0;
389  limitZ1 = 1.0; limitZ2 = 1.0;
390  }
391  }
392  else
393  {
394  // Computing the parametric values
395  // For the X-axis
396  // We introduce 1 voxel size around Y-axis
397  if( r[ 0 ] != 0.0 )
398  {
399  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
400  / r[ 0 ];
401  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
402  / r[ 0 ];
403  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
404  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
405  }
406 
407  // For the Y-axis
408  // Make a shift on first and last plane (like an offset)
409  // We stay in the limit of the image space
410  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
411  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
412  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
413  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
414 
415  // For the Z-axis
416  // We introduce 1 voxel size around Z-axis
417  // Make a shift on first and last plane (like an offset)
418  if( r[ 2 ] != 0.0 )
419  {
420  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
421  / r[ 2 ];
422  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
423  / r[ 2 ];
424  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
425  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
426  }
427 
428  if( alphaMax <= alphaMin ) return 0;
429 
430  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
431  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
432 
433  // Computing weight of normalization
434  HPFLTNB const factor( ::fabs( r[ 1 ] )
435  / ::sqrt( r2[ 0 ] + r2[ 1 ] + r2[ 2 ] ) );
436  HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
437 
438  // Computing the increment
439  HPFLTNB const ri[ 3 ] = {
440  r[ 0 ] / r[ 1 ],
441  1.0, // r[ 1 ] / r[ 1 ]
442  r[ 2 ] / r[ 1 ]
443  };
444 
445  // Computing the first and the last plane
446  int jMin = 0, jMax = 0;
447  if( r[ 1 ] > 0.0 )
448  {
449  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
450  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
451  }
452  else if( r[ 1 ] < 0.0 )
453  {
454  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
455  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
456  }
457 
458  // Computing an offset to get the correct position in plane
459  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
460 
461  // Loop over all the crossing planes
462  for( int j = jMin; j < jMax; ++j )
463  {
464  // Computing position on crossed plane in term of grid spacing
465  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
466  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
467  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
468 
469  // Find the index in the image
470  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
471  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
472 
473  // Storing values and indices
474  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
475 
476  // Bilinear interpolation component (using floor)
477  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
478  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
479  wfl[ 0 ] /= mp_sizeVox[ 0 ];
480  wfl[ 1 ] /= mp_sizeVox[ 2 ];
481 
482  // Bilinear interpolation component (using ceil)
483  wcl[ 0 ] = 1.0 - wfl[ 0 ];
484  wcl[ 1 ] = 1.0 - wfl[ 1 ];
485 
486  // Final coefficients
487  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
488  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
489  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
490  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
491 
492  // Check X bounds
493  if( index[ 0 ] <= 0 ) limitX1 = 0;
494  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
495 
496  // Check Z bounds
497  if( index[ 1 ] <= 0 ) limitZ1 = 0;
498  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
499 
500  if (!m_hasMask || (limitX1 && limitZ1 && index[ 0 ] <= mp_nbVox[ 0 ] && index[ 1 ] <= mp_nbVox[ 2 ] && mp_mask[finalIdx]))
501  {
502  ap_ProjectionLine->AddVoxel(a_direction, finalIdx * limitX1 * limitZ1, w[ 0 ] * weight * limitX1 * limitZ1);
503  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * weight * limitX1 * limitZ2);
504  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * weight * limitX2 * limitZ2);
505  ap_ProjectionLine->AddVoxel(a_direction, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * weight * limitX2 * limitZ1);
506  }
507 
508  limitX1 = 1; limitX2 = 1;
509  limitZ1 = 1; limitZ2 = 1;
510  }
511  }
512 
513  return 0;
514 }
515 
516 // =====================================================================
517 // ---------------------------------------------------------------------
518 // ---------------------------------------------------------------------
519 // =====================================================================
520 
521 int iProjectorJoseph::ProjectWithTOFPos(int a_direction, oProjectionLine* ap_ProjectionLine)
522 {
523 
524  #ifdef CASTOR_DEBUG
525  if (!m_initialized)
526  {
527  Cerr("***** iProjectorJoseph::ProjectWithTOFPos() -> Called while not initialized !" << endl);
528  Exit(EXIT_DEBUG);
529  }
530  #endif
531 
532  #ifdef CASTOR_VERBOSE
533  if (m_verbose>=10)
534  {
535  string direction = "";
536  if (a_direction==FORWARD) direction = "forward";
537  else direction = "backward";
538  Cout("iProjectorJoseph::Project with TOF measurement -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
539  }
540  #endif
541 
542  // Get event positions
543  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
544  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
545 
546  // Distance between point event1 and event2
547  HPFLTNB const r[ 3 ] = {
548  event2[ 0 ] - event1[ 0 ],
549  event2[ 1 ] - event1[ 1 ],
550  event2[ 2 ] - event1[ 2 ]
551  };
552 
553 /*
554  // Square of r
555  HPFLTNB const r2[ 3 ] = {
556  r[ 0 ] * r[ 0 ],
557  r[ 1 ] * r[ 1 ],
558  r[ 2 ] * r[ 2 ]
559  };
560 */
561 
562  // LOR length
563  HPFLTNB lor_length = ap_ProjectionLine->GetLength();
564 
565  // Get TOF info
566  FLTNB tof_resolutionT = ap_ProjectionLine->GetTOFResolution();
567  FLTNB tof_measurement = ap_ProjectionLine->GetTOFMeasurement();
568 
569  // TOF standard deviation and truncation
570  HPFLTNB tof_sigma = tof_resolutionT * SPEED_OF_LIGHT * 0.5 / TWO_SQRT_TWO_LN_2;
571  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
572  HPFLTNB tof_half_span = tof_sigma * m_TOFnbSigmas;
573 
574  // convert delta time into delta length
575  HPFLTNB tof_delta = tof_measurement * SPEED_OF_LIGHT * 0.5;
576 
577  // distance between the first event and the center of the Gaussian distribution along the LOR
578  HPFLTNB lor_tof_center = lor_length * 0.5 + tof_delta;
579 
580  // coordinates of the lower edge of the first voxel falling inside the truncated Gaussian distribution
581  HPFLTNB tof_edge_low[] = {0,0,0};
582  // coordinate of the upper edge of the last voxel falling inside the truncated Gaussian distribution
583  HPFLTNB tof_edge_high[] = {0,0,0};
584  HPFLTNB tof_center;
585  INTNB tof_index;
586 
587  // low/high voxel edges (in absolute coordinates) for truncated TOF
588  for (int ax=0;ax<3;ax++)
589  {
590  // absolute coordinate along each axis of the center of the TOF distribution
591  tof_center = event1[ax] + lor_tof_center * r[ax] / lor_length;
592 
593  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
594  tof_edge_low[ax] = tof_center - tof_half_span * fabs(r[ax]) / lor_length;
595  tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
596  // if low TOF edge above the highest FOV edge, return empty line
597  if (tof_index>mp_nbVox[ax]-1) return 0;
598  tof_edge_low[ax] = (HPFLTNB)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
599 
600  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
601  tof_edge_high[ax] = tof_center + tof_half_span * fabs(r[ax]) / lor_length;
602  tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
603  // if high TOF edge below the lowest FOV edge, return empty line
604  if (tof_index<0) return 0;
605  tof_edge_high[ax] = (HPFLTNB)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
606  }
607 
608  // Find the first and last intersecting plane in X using the parametric
609  // values alphaMin and alphaMax
610  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
611 
612  // Variables for Joseph
613  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
614  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
615  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
616  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
617  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
618  int32_t finalIdx = 0; // final index
619  int8_t limitX1 = 1; int8_t limitX2 = 1;
620  int8_t limitY1 = 1; int8_t limitY2 = 1;
621  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
622 
623  // Condition on the largest component of r
624  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
625  {
626  // Computing the parametric values
627  // For the X-axis
628  // We stay in the limit of the image space
629  HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - event1[ 0 ] ) / r[ 0 ];
630  HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] - event1[ 0 ] ) / r[ 0 ];
631  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
632  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
633 
634  // For the Y-axis
635  // We introduce 1 voxel size around Y-axis
636  // Make a shift on first and last plane (like an offset)
637  if( r[ 1 ] != 0.0 )
638  {
639  HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
640  / r[ 1 ];
641  HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
642  / r[ 1 ];
643  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
644  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
645  }
646 
647  // For the Z-axis
648  // We introduce 1 voxel size around Z-axis
649  // Make a shift on first and last plane (like an offset)
650  if( r[ 2 ] != 0.0 )
651  {
652  HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
653  / r[ 2 ];
654  HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
655  / r[ 2 ];
656  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
657  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
658  }
659 
660  if( alphaMax <= alphaMin ) return 0;
661 
662  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
663  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
664 
665  // Computing weight of normalization
666  //HPFLTNB const factor( ::fabs( r[ 0 ] ) / lor_length );
667  HPFLTNB const factor_for_tof( lor_length / r[ 0 ]) ;
668  //HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
669 
670  // Computing the increment
671  HPFLTNB const ri[ 3 ] = {
672  1.0, // r[ 0 ] / r[ 0 ]
673  r[ 1 ] / r[ 0 ],
674  r[ 2 ] / r[ 0 ]
675  };
676 
677  // Computing the first and the last plane
678  int iMin = 0, iMax = 0;
679  if( r[ 0 ] > 0.0 )
680  {
681  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
682  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
683  }
684  else if( r[ 0 ] < 0.0 )
685  {
686  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
687  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
688  }
689 
690  // Computing an offset to get the correct position in plane
691  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
692 
693  // tof : erf of previous voxel edge - Gaussian center
694  HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 0 ] + iMin * mp_sizeVox[ 0 ] - event1[ 0 ] ) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
695  HPFLTNB next_edge_erf, tof_weight;
696 
697  // Loop over all the crossing planes
698  for( int i = iMin; i < iMax; ++i )
699  {
700  // Computing position on crossed plane in term of grid spacing
701  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
702  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
703  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
704 
705  // Find the index in the image
706  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
707  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
708 
709  // Bilinear interpolation component (using floor)
710  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
711  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
712  wfl[ 0 ] /= mp_sizeVox[ 1 ];
713  wfl[ 1 ] /= mp_sizeVox[ 2 ];
714 
715  // Bilinear interpolation component (using ceil)
716  wcl[ 0 ] = 1.0 - wfl[ 0 ];
717  wcl[ 1 ] = 1.0 - wfl[ 1 ];
718 
719  // Final coefficients
720  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
721  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
722  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
723  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
724 
725  // Check Y bounds
726  if( index[ 0 ] <= 0 ) limitY1 = 0;
727  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
728 
729  // Check Z bounds
730  if( index[ 1 ] <= 0 ) limitZ1 = 0;
731  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
732 
733  // Storing values and indices
734  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
735 
736  // tof : erf of next voxel edge - Gaussian center
737  next_edge_erf = erf( ( ( step + mp_sizeVox[ 0 ] * 0.5 ) * factor_for_tof - lor_tof_center ) / tof_sigma_sqrt2 );
738  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
739  tof_weight = 0.5 * ::fabs(previous_edge_erf - next_edge_erf) ;
740  // keeping record of the previous edge, so as to save 1 erf computation
741  previous_edge_erf = next_edge_erf;
742 
743  // check if the main voxel is relevant
744  if (!m_hasMask || (limitY1 && limitZ1 && index[ 0 ] <= mp_nbVox[ 1 ] && index[ 1 ] <= mp_nbVox[ 2 ] && mp_mask[finalIdx]))
745  {
746  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, finalIdx * limitY1 * limitZ1, w[ 0 ] * tof_weight * limitY1 * limitZ1);
747  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, ( finalIdx + m_nbVoxXY ) * limitY1 * limitZ2, w[ 1 ] * tof_weight * limitY1 * limitZ2);
748  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, ( finalIdx + mp_nbVox[ 0 ] + m_nbVoxXY ) * limitY2 * limitZ2, w[ 2 ] * tof_weight * limitY2 * limitZ2);
749  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, ( finalIdx + mp_nbVox[ 0 ] ) * limitY2 * limitZ1, w[ 3 ] * tof_weight * limitY2 * limitZ1);
750  }
751 
752  limitY1 = 1.0; limitY2 = 1.0;
753  limitZ1 = 1.0; limitZ2 = 1.0;
754  }
755  }
756  else
757  {
758  // Computing the parametric values
759  // For the X-axis
760  // We introduce 1 voxel size around Y-axis
761  if( r[ 0 ] != 0.0 )
762  {
763  HPFLTNB const alphaX_0 = ( tof_edge_low[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
764  / r[ 0 ];
765  HPFLTNB const alphaX_1 = ( tof_edge_high[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
766  / r[ 0 ];
767  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
768  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
769  }
770 
771  // For the Y-axis
772  // Make a shift on first and last plane (like an offset)
773  // We stay in the limit of the image space
774  HPFLTNB const alphaY_0 = ( tof_edge_low[ 1 ] - event1[ 1 ] ) / r[ 1 ];
775  HPFLTNB const alphaY_1 = ( tof_edge_high[ 1 ] - event1[ 1 ] ) / r[ 1 ];
776  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
777  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
778 
779  // For the Z-axis
780  // We introduce 1 voxel size around Z-axis
781  // Make a shift on first and last plane (like an offset)
782  if( r[ 2 ] != 0.0 )
783  {
784  HPFLTNB const alphaZ_0 = ( tof_edge_low[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
785  / r[ 2 ];
786  HPFLTNB const alphaZ_1 = ( tof_edge_high[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
787  / r[ 2 ];
788  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
789  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
790  }
791 
792  if( alphaMax <= alphaMin ) return 0;
793 
794  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
795  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
796 
797  // Computing weight of normalization
798  //HPFLTNB const factor( ::fabs( r[ 1 ] ) / lor_length );
799  HPFLTNB const factor_for_tof( lor_length / r[ 1 ] );
800  //HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
801 
802  // Computing the increment
803  HPFLTNB const ri[ 3 ] = {
804  r[ 0 ] / r[ 1 ],
805  1.0, // r[ 1 ] / r[ 1 ]
806  r[ 2 ] / r[ 1 ]
807  };
808 
809  // Computing the first and the last plane
810  int jMin = 0, jMax = 0;
811  if( r[ 1 ] > 0.0 )
812  {
813  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
814  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
815  }
816  else if( r[ 1 ] < 0.0 )
817  {
818  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
819  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
820  }
821 
822  // tof : erf of previous voxel edge - Gaussian center
823  HPFLTNB previous_edge_erf = erf( ( ( -mp_halfFOV[ 1 ] + jMin * mp_sizeVox[ 1 ] - event1[ 1 ]) * factor_for_tof - lor_tof_center) / tof_sigma_sqrt2 );
824  HPFLTNB next_edge_erf, tof_weight;
825 
826  // Computing an offset to get the correct position in plane
827  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
828 
829  // Loop over all the crossing planes
830  for( int j = jMin; j < jMax; ++j )
831  {
832  // Computing position on crossed plane in term of grid spacing
833  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
834  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
835  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
836 
837  // Find the index in the image
838  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
839  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
840 
841  // Bilinear interpolation component (using floor)
842  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
843  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
844  wfl[ 0 ] /= mp_sizeVox[ 0 ];
845  wfl[ 1 ] /= mp_sizeVox[ 2 ];
846 
847  // Bilinear interpolation component (using ceil)
848  wcl[ 0 ] = 1.0 - wfl[ 0 ];
849  wcl[ 1 ] = 1.0 - wfl[ 1 ];
850 
851  // Final coefficients
852  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
853  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
854  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
855  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
856 
857  // Check X bounds
858  if( index[ 0 ] <= 0 ) limitX1 = 0;
859  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
860 
861  // Check Z bounds
862  if( index[ 1 ] <= 0 ) limitZ1 = 0;
863  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
864 
865  // Storing values and indices
866  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
867 
868  // tof : erf of next voxel edge - Gaussian center
869  next_edge_erf = erf( ( ( step + mp_sizeVox[ 1 ] * 0.5 ) * factor_for_tof - lor_tof_center ) / tof_sigma_sqrt2 );
870  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
871  tof_weight = 0.5 * ::fabs(previous_edge_erf - next_edge_erf) ;
872  // keeping record of the previous edge, so as to save 1 erf computation
873  previous_edge_erf = next_edge_erf;
874 
875  // check if the main voxel is relevant
876  if (!m_hasMask || (limitX1 && limitZ1 && index[ 0 ] <= mp_nbVox[ 0 ] && index[ 1 ] <= mp_nbVox[ 2 ] && mp_mask[finalIdx]))
877  {
878  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, finalIdx * limitX1 * limitZ1, w[ 0 ] * tof_weight * limitX1 * limitZ1);
879  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, ( finalIdx + m_nbVoxXY ) * limitX1 * limitZ2, w[ 1 ] * tof_weight * limitX1 * limitZ2);
880  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, ( finalIdx + 1 + m_nbVoxXY ) * limitX2 * limitZ2, w[ 2 ] * tof_weight * limitX2 * limitZ2);
881  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, ( finalIdx + 1 ) * limitX2 * limitZ1, w[ 3 ] * tof_weight * limitX2 * limitZ1);
882  }
883 
884  limitX1 = 1; limitX2 = 1;
885  limitZ1 = 1; limitZ2 = 1;
886  }
887  }
888  return 0;
889 }
890 
891 // =====================================================================
892 // ---------------------------------------------------------------------
893 // ---------------------------------------------------------------------
894 // =====================================================================
895 
896 int iProjectorJoseph::ProjectWithTOFBin(int a_direction, oProjectionLine* ap_ProjectionLine)
897 {
898  #ifdef CASTOR_DEBUG
899  if (!m_initialized)
900  {
901  Cerr("***** iProjectorJoseph::ProjectWithTOFBin() -> Called while not initialized !" << endl);
902  Exit(EXIT_DEBUG);
903  }
904  #endif
905 
906  #ifdef CASTOR_VERBOSE
907  if (m_verbose>=10)
908  {
909  string direction = "";
910  if (a_direction==FORWARD) direction = "forward";
911  else direction = "backward";
912  Cout("iProjectorJoseph::Project with TOF bins -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
913  }
914  #endif
915 
916  // Get event positions
917  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
918  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
919 
920  // Distance between point event1 and event2
921  HPFLTNB const r[ 3 ] = {
922  event2[ 0 ] - event1[ 0 ],
923  event2[ 1 ] - event1[ 1 ],
924  event2[ 2 ] - event1[ 2 ]
925  };
926 
927  // LOR length
928  HPFLTNB lor_length = ap_ProjectionLine->GetLength();
929  HPFLTNB lor_length_half = lor_length * 0.5;
930 
931  // Get TOF info
932  // TOF temporal resolution (FWHM)
933  FLTNB tof_resolutionT = ap_ProjectionLine->GetTOFResolution();
934  // TOF bin temporal size
935  HPFLTNB tof_bin_sizeT = ap_ProjectionLine->GetTOFBinSize();
936  INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
937  INTNB tof_half_nb_bins = tof_nb_bins/2;
938 
939  // TOF spatial standard deviation (simple TOF Gaussian, not convolved with the TOF bin width, maybe TODO)
940  HPFLTNB tof_sigma = tof_resolutionT * SPEED_OF_LIGHT * 0.5 / TWO_SQRT_TWO_LN_2;
941  //HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
942  HPFLTNB tof_half_span = tof_sigma * m_TOFnbSigmas;
943 
944  // normalized Gaussian (integral=1) : precomputation of the coefficient that multiplies the exponential
945  HPFLTNB gaussian_norm_coef = INV_SQRT_2_PI * 1./tof_sigma;
946 
947  // spatial size of the TOF bin in mm
948  HPFLTNB tof_bin_size = tof_bin_sizeT * SPEED_OF_LIGHT * 0.5;
949 
950  // minimum and maximum TOF bins, the whole span
951  INTNB tof_bin_last = tof_half_nb_bins;
952  INTNB tof_bin_first = -tof_half_nb_bins;
953 
954  // distance between the first event1 and the center of the Gaussian distribution of the first TOF bin along the LOR
955  //HPFLTNB tof_bin_center_first = lor_length * 0.5 + (tof_bin_first) * tof_bin_size;
956  // distance between the first event1 and the center of the Gaussian distribution of the current TOF bin along the LOR
957  HPFLTNB lor_tof_center = 0.;
958  // normalization coefficient used for making the sum of TOF bin coefficients for a voxel equal the nonTOF voxel coefficient
959  HPFLTNB tof_norm_coef = 0.;
960 
961  // tof help variables
962  HPFLTNB tof_weight;
963 
964 /*
965  // Square of r
966  HPFLTNB const r2[ 3 ] = {
967  r[ 0 ] * r[ 0 ],
968  r[ 1 ] * r[ 1 ],
969  r[ 2 ] * r[ 2 ]
970  };
971 */
972 
973  // Find the first and last intersecting plane in X using the parametric
974  // values alphaMin and alphaMax
975  HPFLTNB alphaMin = 0.0, alphaMax = 1.0;
976 
977  // Variables for Joseph
978  HPFLTNB pos[ 3 ] = { 0.0, 0.0, 0.0 }; // Position of point in image space
979  HPFLTNB wfl[ 2 ] = { 0.0, 0.0 }; // Weight floor
980  HPFLTNB wcl[ 2 ] = { 0.0, 0.0 }; // Weight ceil
981  HPFLTNB w[ 4 ] = { 0.0, 0.0, 0.0, 0.0 }; // Interpolation weight
982  int16_t index[ 2 ] = { 0, 0 }; // index in the image space
983  int32_t finalIdx = 0; // final index
984  int8_t limitX1 = 1; int8_t limitX2 = 1;
985  int8_t limitY1 = 1; int8_t limitY2 = 1;
986  int8_t limitZ1 = 1; int8_t limitZ2 = 1;
987 
988  // Condition on the largest component of r
989  if( ::fabs( r[ 0 ] ) > ::fabs( r[ 1 ] ) )
990  {
991  // Computing the parametric values
992  // For the X-axis
993  // We stay in the limit of the image space
994  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
995  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] - event1[ 0 ] ) / r[ 0 ];
996  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
997  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
998 
999  // For the Y-axis
1000  // We introduce 1 voxel size around Y-axis
1001  // Make a shift on first and last plane (like an offset)
1002  if( r[ 1 ] != 0.0 )
1003  {
1004  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
1005  / r[ 1 ];
1006  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] + mp_sizeVox[ 1 ] * 0.5 - event1[ 1 ] )
1007  / r[ 1 ];
1008  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
1009  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
1010  }
1011 
1012  // For the Z-axis
1013  // We introduce 1 voxel size around Z-axis
1014  // Make a shift on first and last plane (like an offset)
1015  if( r[ 2 ] != 0.0 )
1016  {
1017  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1018  / r[ 2 ];
1019  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1020  / r[ 2 ];
1021  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
1022  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
1023  }
1024 
1025  if( alphaMax <= alphaMin ) return 0;
1026 
1027  if( r[ 1 ] == 0.0 ) if( event1[ 1 ] < -mp_halfFOV[ 1 ] || event1[ 1 ] > mp_halfFOV[ 1 ] ) return 0;
1028  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
1029 
1030  // temporary storage for TOF bins Gaussian integrals over the current voxel
1031  // allocation after potential returns
1032  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
1033  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
1034 
1035  // Computing weight of normalization
1036  HPFLTNB const factor( ::fabs( r[ 0 ] ) / lor_length );
1037  HPFLTNB const factor_for_tof( lor_length / r[ 0 ] );
1038  HPFLTNB const weight( mp_sizeVox[ 0 ] / factor );
1039 
1040  // Computing the increment
1041  HPFLTNB const ri[ 3 ] = {
1042  1.0, // r[ 0 ] / r[ 0 ]
1043  r[ 1 ] / r[ 0 ],
1044  r[ 2 ] / r[ 0 ]
1045  };
1046 
1047  // Computing the first and the last plane
1048  int iMin = 0, iMax = 0;
1049  if( r[ 0 ] > 0.0 )
1050  {
1051  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMin * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
1052  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMax * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1053  }
1054  else if( r[ 0 ] < 0.0 )
1055  {
1056  iMin = ::ceil( mp_nbVox[ 0 ] - m_toleranceX - ( mp_halfFOV[ 0 ] - alphaMax * r[ 0 ] - event1[ 0 ] ) / mp_sizeVox[ 0 ] );
1057  iMax = ::floor( m_toleranceX + ( event1[ 0 ] + alphaMin * r[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[ 0 ] );
1058  }
1059 
1060  // Computing an offset to get the correct position in plane
1061  HPFLTNB const offset = -mp_halfFOV[ 0 ] + ( mp_sizeVox[ 0 ] * 0.5 ) - event1[ 0 ];
1062 
1063  // Loop over all the crossing planes
1064  for( int i = iMin; i < iMax; ++i )
1065  {
1066  // Computing position on crossed plane in term of grid spacing
1067  HPFLTNB const step = offset + i * mp_sizeVox[ 0 ];
1068  pos[ 1 ] = event1[ 1 ] + step * ri[ 1 ];
1069  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
1070 
1071  // Find the index in the image
1072  index[ 0 ] = ::floor( m_toleranceY + ( pos[ 1 ] - m_boundY ) / mp_sizeVox[ 1 ] );
1073  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
1074 
1075  finalIdx = i + ( index[ 0 ] - 1 ) * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
1076 
1077  // check if the main voxel is relevant
1078  if (m_hasMask && index[ 0 ] > 0 && index[ 1 ] > 0 && index[ 0 ] <= mp_nbVox[ 1 ] && index[ 1 ] <= mp_nbVox[ 2 ] && !mp_mask[finalIdx]) continue;
1079 
1080  // Compute the first and the last relevant TOF bin for this voxel (currently using voxel center and simple truncation)
1081  INTNB tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(trunc( (step * factor_for_tof - tof_half_span - lor_length_half ) / tof_bin_size )));
1082  INTNB tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(trunc( (step * factor_for_tof + tof_half_span - lor_length_half ) / tof_bin_size )));
1083  lor_tof_center = lor_length_half + tof_bin_first_for_voxel * tof_bin_size;
1084 
1085  // if min/max TOF bins for this voxel do not fall into the total available TOF bins range, skip
1086  if(tof_bin_first_for_voxel>tof_bin_last || tof_bin_last_for_voxel<tof_bin_first) continue;
1087 
1088  // shift tof bin indices from -/+ to 0:nbBins range
1089  tof_bin_first_for_voxel += tof_half_nb_bins;
1090  tof_bin_last_for_voxel += tof_half_nb_bins;
1091 
1092  // first compute the normalization for TOF bin coefficients for the current voxel (simple sum)
1093  tof_norm_coef = 0.;
1094  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1095  {
1096  // approximation of integration of the Gaussian over the voxel
1097  // TOF coefficient = value of the Gaussian at voxel center (projected onto the LOR) * voxel size (projected onto the LOR)
1098  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center) / tof_sigma;
1099  tof_weight = exp(- 0.5 * temp * temp ) * gaussian_norm_coef * weight;
1100  // add the weight to the sum for normalization
1101  tof_norm_coef += tof_weight;
1102  // save the weight temporarily
1103  tof_weights_temp[tof_bin] = tof_weight;
1104  // update TOF center along the LOR for the next TOF bin
1105  lor_tof_center += tof_bin_size;
1106  }
1107 
1108  // Bilinear interpolation component (using floor)
1109  wfl[ 0 ] = pos[ 1 ] - ( m_boundY + index[ 0 ] * mp_sizeVox[ 1 ] );
1110  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
1111  wfl[ 0 ] /= mp_sizeVox[ 1 ];
1112  wfl[ 1 ] /= mp_sizeVox[ 2 ];
1113 
1114  // Bilinear interpolation component (using ceil)
1115  wcl[ 0 ] = 1.0 - wfl[ 0 ];
1116  wcl[ 1 ] = 1.0 - wfl[ 1 ];
1117 
1118  // Final coefficients
1119  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
1120  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
1121  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
1122  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
1123 
1124  // Check Y bounds
1125  if( index[ 0 ] <= 0 ) limitY1 = 0;
1126  if( index[ 0 ] >= mp_nbVox[ 1 ] ) limitY2 = 0;
1127 
1128  // Check Z bounds
1129  if( index[ 1 ] <= 0 ) limitZ1 = 0;
1130  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
1131 
1132  // compute and write the final TOF bin coefficients for current voxels
1133  if (tof_norm_coef>0.)
1134  {
1135  // first normalize TOF bin coefficients
1136  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1137  {
1138  tof_weights_temp[tof_bin] /= tof_norm_coef;
1139  }
1140  // then write all TOF bins for each voxel
1141  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);
1142  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);
1143  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);
1144  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);
1145  }
1146 
1147  limitY1 = 1.0; limitY2 = 1.0;
1148  limitZ1 = 1.0; limitZ2 = 1.0;
1149  }
1150  delete[] tof_weights_temp;
1151  }
1152  else
1153  {
1154  // Computing the parametric values
1155  // For the X-axis
1156  // We introduce 1 voxel size around Y-axis
1157  if( r[ 0 ] != 0.0 )
1158  {
1159  HPFLTNB const alphaX_0 = ( -mp_halfFOV[ 0 ] - mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
1160  / r[ 0 ];
1161  HPFLTNB const alphaX_1 = ( mp_halfFOV[ 0 ] + mp_sizeVox[ 0 ] * 0.5 - event1[ 0 ] )
1162  / r[ 0 ];
1163  alphaMin = std::max( alphaMin, std::min( alphaX_0, alphaX_1 ) );
1164  alphaMax = std::min( alphaMax, std::max( alphaX_0, alphaX_1 ) );
1165  }
1166 
1167  // For the Y-axis
1168  // Make a shift on first and last plane (like an offset)
1169  // We stay in the limit of the image space
1170  HPFLTNB const alphaY_0 = ( -mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
1171  HPFLTNB const alphaY_1 = ( mp_halfFOV[ 1 ] - event1[ 1 ] ) / r[ 1 ];
1172  alphaMin = std::max( alphaMin, std::min( alphaY_0, alphaY_1 ) );
1173  alphaMax = std::min( alphaMax, std::max( alphaY_0, alphaY_1 ) );
1174 
1175  // For the Z-axis
1176  // We introduce 1 voxel size around Z-axis
1177  // Make a shift on first and last plane (like an offset)
1178  if( r[ 2 ] != 0.0 )
1179  {
1180  HPFLTNB const alphaZ_0 = ( -mp_halfFOV[ 2 ] - mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1181  / r[ 2 ];
1182  HPFLTNB const alphaZ_1 = ( mp_halfFOV[ 2 ] + mp_sizeVox[ 2 ] * 0.5 - event1[ 2 ] )
1183  / r[ 2 ];
1184  alphaMin = std::max( alphaMin, std::min( alphaZ_0, alphaZ_1 ) );
1185  alphaMax = std::min( alphaMax, std::max( alphaZ_0, alphaZ_1 ) );
1186  }
1187 
1188  if( alphaMax <= alphaMin ) return 0;
1189 
1190  if( r[ 0 ] == 0.0 ) if( event1[ 0 ] < -mp_halfFOV[ 0 ] || event1[ 0 ] > mp_halfFOV[ 0 ] ) return 0;
1191  if( r[ 2 ] == 0.0 ) if( event1[ 2 ] < -mp_halfFOV[ 2 ] || event1[ 2 ] > mp_halfFOV[ 2 ] ) return 0;
1192 
1193  // temporary storage for TOF bins Gaussian integrals over the current voxel, 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[ 1 ] ) / lor_length );
1199  HPFLTNB const factor_for_tof( lor_length / r[ 1 ] );
1200  HPFLTNB const weight( mp_sizeVox[ 1 ] / factor );
1201 
1202  // Computing the increment
1203  HPFLTNB const ri[ 3 ] = {
1204  r[ 0 ] / r[ 1 ],
1205  1.0, // r[ 1 ] / r[ 1 ]
1206  r[ 2 ] / r[ 1 ]
1207  };
1208 
1209  // Computing the first and the last plane
1210  int jMin = 0, jMax = 0;
1211  if( r[ 1 ] > 0.0 )
1212  {
1213  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMin * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
1214  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMax * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1215  }
1216  else if( r[ 1 ] < 0.0 )
1217  {
1218  jMin = ::ceil( mp_nbVox[ 1 ] - m_toleranceY - ( mp_halfFOV[ 1 ] - alphaMax * r[ 1 ] - event1[ 1 ] ) / mp_sizeVox[ 1 ] );
1219  jMax = ::floor( m_toleranceY + ( event1[ 1 ] + alphaMin * r[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[ 1 ] );
1220  }
1221 
1222  // Computing an offset to get the correct position in plane
1223  HPFLTNB const offset = -mp_halfFOV[ 1 ] + ( mp_sizeVox[ 1 ] * 0.5 ) - event1[ 1 ];
1224 
1225  // Loop over all the crossing planes
1226  for( int j = jMin; j < jMax; ++j )
1227  {
1228  // Computing position on crossed plane in term of grid spacing
1229  HPFLTNB const step = offset + j * mp_sizeVox[ 1 ];
1230  pos[ 0 ] = event1[ 0 ] + step * ri[ 0 ];
1231  pos[ 2 ] = event1[ 2 ] + step * ri[ 2 ];
1232 
1233  // Find the index in the image
1234  index[ 0 ] = ::floor( m_toleranceX + ( pos[ 0 ] - m_boundX ) / mp_sizeVox[ 0 ] );
1235  index[ 1 ] = ::floor( m_toleranceZ + ( pos[ 2 ] - m_boundZ ) / mp_sizeVox[ 2 ] );
1236 
1237  finalIdx = ( index[ 0 ] - 1 ) + j * mp_nbVox[ 0 ] + ( index[ 1 ] - 1 ) * m_nbVoxXY;
1238 
1239  // check if the main voxel is relevant
1240  if (m_hasMask && (index[ 0 ] > 0 && index[ 1 ] > 0 && index[ 0 ] <= mp_nbVox[ 0 ] && index[ 1 ] <= mp_nbVox[ 2 ]) && !mp_mask[finalIdx]) continue;
1241 
1242  // Compute the first and the last relevant TOF bin for this voxel (currently using voxel center and simple truncation)
1243  INTNB tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(trunc( (step * factor_for_tof - tof_half_span - lor_length_half ) / tof_bin_size )));
1244  INTNB tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(trunc( (step * factor_for_tof + tof_half_span - lor_length_half ) / tof_bin_size )));
1245  lor_tof_center = lor_length_half + tof_bin_first_for_voxel * tof_bin_size;
1246 
1247  // if min/max TOF bins for this voxel do not fall into the total available TOF bins range, skip
1248  if(tof_bin_first_for_voxel>tof_bin_last || tof_bin_last_for_voxel<tof_bin_first) continue;
1249 
1250  // shift tof bin indices from -/+ to 0:nbBins range
1251  tof_bin_first_for_voxel += tof_half_nb_bins;
1252  tof_bin_last_for_voxel += tof_half_nb_bins;
1253 
1254  // first compute the normalization for TOF bin coefficients for the current voxel (simple sum)
1255  tof_norm_coef = 0.;
1256  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1257  {
1258  // approximation of integration of the Gaussian over the voxel
1259  // TOF coefficient = value of the Gaussian at voxel center (projected onto the LOR) * voxel size (projected onto the LOR)
1260  HPFLTNB temp = ( step * factor_for_tof - lor_tof_center) / tof_sigma;
1261  tof_weight = exp(- 0.5 * temp * temp ) * gaussian_norm_coef * weight;
1262  // add the weight to the sum for normalization
1263  tof_norm_coef += tof_weight;
1264  // save the weight temporarily
1265  tof_weights_temp[tof_bin] = tof_weight;
1266  // update TOF center along the LOR for the next TOF bin
1267  lor_tof_center += tof_bin_size;
1268  }
1269 
1270  // Bilinear interpolation component (using floor)
1271  wfl[ 0 ] = pos[ 0 ] - ( m_boundX + index[ 0 ] * mp_sizeVox[ 0 ] );
1272  wfl[ 1 ] = pos[ 2 ] - ( m_boundZ + index[ 1 ] * mp_sizeVox[ 2 ] );
1273  wfl[ 0 ] /= mp_sizeVox[ 0 ];
1274  wfl[ 1 ] /= mp_sizeVox[ 2 ];
1275 
1276  // Bilinear interpolation component (using ceil)
1277  wcl[ 0 ] = 1.0 - wfl[ 0 ];
1278  wcl[ 1 ] = 1.0 - wfl[ 1 ];
1279 
1280  // Final coefficients
1281  w[ 0 ] = wcl[ 0 ] * wcl[ 1 ];
1282  w[ 1 ] = wcl[ 0 ] * wfl[ 1 ];
1283  w[ 2 ] = wfl[ 0 ] * wfl[ 1 ];
1284  w[ 3 ] = wfl[ 0 ] * wcl[ 1 ];
1285 
1286  // Check X bounds
1287  if( index[ 0 ] <= 0 ) limitX1 = 0;
1288  if( index[ 0 ] >= mp_nbVox[ 0 ] ) limitX2 = 0;
1289 
1290  // Check Z bounds
1291  if( index[ 1 ] <= 0 ) limitZ1 = 0;
1292  if( index[ 1 ] >= mp_nbVox[ 2 ] ) limitZ2 = 0;
1293 
1294  // Storing values and indices
1295 
1296  // compute and write the final TOF bin coefficients for current voxels
1297  if (tof_norm_coef>0.)
1298  {
1299  // first normalize TOF bin coefficients
1300  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1301  {
1302  tof_weights_temp[tof_bin] /= tof_norm_coef;
1303  }
1304  // then write all TOF bins for each voxel
1305  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);
1306  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);
1307  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);
1308  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);
1309  }
1310 
1311  limitX1 = 1; limitX2 = 1;
1312  limitZ1 = 1; limitZ2 = 1;
1313  }
1314  delete[] tof_weights_temp;
1315  }
1316 
1317  return 0;
1318 }
1319 
1320 // =====================================================================
1321 // ---------------------------------------------------------------------
1322 // ---------------------------------------------------------------------
1323 // =====================================================================
bool m_compatibleWithSPECTAttenuationCorrection
Definition: vProjector.hh:355
FLTNB mp_sizeVox[3]
Definition: vProjector.hh:338
INTNB m_nbVoxXY
Definition: vProjector.hh:340
INTNB mp_nbVox[3]
Definition: vProjector.hh:339
#define FLTNB
Definition: gVariables.hh:81
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.
int ProjectWithTOFPos(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF continuous information.
#define TWO_SQRT_TWO_LN_2
Definition: gVariables.hh:100
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Definition: vProjector.hh:344
#define HPFLTNB
Definition: gVariables.hh:83
This class is designed to generically described any on-the-fly projector.
Definition: vProjector.hh:76
int ProjectWithTOFBin(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF binned information.
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
FLTNB GetTOFMeasurement()
This function is used to get the TOF measurement.
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.
FLTNB GetTOFResolution()
This function is used to get the TOF resolution.
#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:364
bool * mp_mask
Definition: vProjector.hh:365
void AddVoxel(int a_direction, INTNB a_voxelIndice, FLTNB a_voxelWeight)
This function is used to add a voxel contribution to the line, assuming TOF bin 0 (i...
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:366
Declaration of class iProjectorJoseph.
FLTNB m_TOFnbSigmas
Definition: vProjector.hh:349
#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.
Declaration of class sOutputManager.
int GetNbTOFBins()
This function is used to get the number of TOF bins in use.
FLTNB mp_halfFOV[3]
Definition: vProjector.hh:341
#define EXIT_DEBUG
Definition: gVariables.hh:97
#define SPEED_OF_LIGHT
Definition: gVariables.hh:106
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.
FLTNB GetTOFBinSize()
This function is used to get the size in ps of a TOF bin.
#define INV_SQRT_2_PI
Definition: gVariables.hh:103
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:358
void AddVoxelInTOFBin(int a_direction, int a_TOFBin, INTNB a_voxelIndice, FLTNB a_voxelWeight)
This function is used to add a voxel contribution to the line and provided TOF bin.
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.