CASToR  2.0
Tomographic Reconstruction (PET/SPECT/CT)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
iProjectorClassicSiddon.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 <algorithm>
32 #include <cstring>
33 #include <cassert>
34 
36 #include "sOutputManager.hh"
37 
38 // =====================================================================
39 // ---------------------------------------------------------------------
40 // ---------------------------------------------------------------------
41 // =====================================================================
42 
44 {
45  // This projector is compatible with SPECT attenuation correction because
46  // all voxels contributing to a given line are ordered from point 1 (focal)
47  // to point 2 (scanner)
49  // This projector is compatible with compression as it works only with the
50  // cartesian coordinates of 2 end points of a line
52 }
53 
54 // =====================================================================
55 // ---------------------------------------------------------------------
56 // ---------------------------------------------------------------------
57 // =====================================================================
58 
60 {
61 }
62 
63 // =====================================================================
64 // ---------------------------------------------------------------------
65 // ---------------------------------------------------------------------
66 // =====================================================================
67 
68 int iProjectorClassicSiddon::ReadConfigurationFile(const string& a_configurationFile)
69 {
70  // No options for classic siddon
71  ;
72  // Normal end
73  return 0;
74 }
75 
76 // =====================================================================
77 // ---------------------------------------------------------------------
78 // ---------------------------------------------------------------------
79 // =====================================================================
80 
81 int iProjectorClassicSiddon::ReadOptionsList(const string& a_optionsList)
82 {
83  // No options for classic siddon
84  ;
85  // Normal end
86  return 0;
87 }
88 
89 // =====================================================================
90 // ---------------------------------------------------------------------
91 // ---------------------------------------------------------------------
92 // =====================================================================
93 
95 {
96  cout << "This projector is a simple line projector that computes the exact path length of a line through the voxels." << endl;
97  cout << "It is implemented from the following published paper:" << endl;
98  cout << "R. L. Siddon, \"Fast calculation of the exact radiological path for a three-dimensional CT array\", Med. Phys., vol. 12, pp. 252-5, 1985." << endl;
99  cout << "All voxels are correctly ordered in the line, so this projector can be used with SPECT attenuation correction." << endl;
100 }
101 
102 // =====================================================================
103 // ---------------------------------------------------------------------
104 // ---------------------------------------------------------------------
105 // =====================================================================
106 
108 {
109  // Nothing to check for this projector
110  ;
111  // Normal end
112  return 0;
113 }
114 
115 // =====================================================================
116 // ---------------------------------------------------------------------
117 // ---------------------------------------------------------------------
118 // =====================================================================
119 
121 {
122  // Verbose
123  if (m_verbose>=2) Cout("iProjectorClassicSiddon::InitializeSpecific() -> Use classic Siddon projector" << endl);
124  // Normal end
125  return 0;
126 }
127 
128 // =====================================================================
129 // ---------------------------------------------------------------------
130 // ---------------------------------------------------------------------
131 // =====================================================================
132 
134 {
135  // Find the maximum number of voxels along a given dimension
136  INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
137  if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
138  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
139  // We should have at most 4 voxels in a given plane, so multiply by 4
140  // (note: this is not true however it ensures no overflow and is already quite optimized for RAM usage !)
141  max_nb_voxels_in_dimension *= 4;
142  // Return the value
143  return max_nb_voxels_in_dimension;
144 }
145 
146 // =====================================================================
147 // ---------------------------------------------------------------------
148 // ---------------------------------------------------------------------
149 // =====================================================================
150 
151 int iProjectorClassicSiddon::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
152 {
153  #ifdef CASTOR_DEBUG
154  if (!m_initialized)
155  {
156  Cerr("***** iProjectorClassicSiddon::ProjectWithoutTOF() -> Called while not initialized !" << endl);
157  Exit(EXIT_DEBUG);
158  }
159  #endif
160 
161  #ifdef CASTOR_VERBOSE
162  if (m_verbose>=10)
163  {
164  string direction = "";
165  if (a_direction==FORWARD) direction = "forward";
166  else direction = "backward";
167  Cout("iProjectorClassicSiddon::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
168  }
169  #endif
170 
171  // Get event positions
172  FLTNB* event1_position = ap_ProjectionLine->GetPosition1();
173  FLTNB* event2_position = ap_ProjectionLine->GetPosition2();
174 
175  FLTNB event1[3] = { event1_position[0], event1_position[1], event1_position[2] };
176  FLTNB event2[3] = { event2_position[0], event2_position[1], event2_position[2] };
177 
178  // **************************************
179  // STEP 1: LOR length calculation
180  // **************************************
181  FLTNB length_LOR = ap_ProjectionLine->GetLength();
182 
183  // **************************************
184  // STEP 2: Compute entrance voxel indexes
185  // **************************************
186  FLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
187  FLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
188 
189  FLTNB alphaMin = 0.0, alphaMax = 1.0;
190  FLTNB delta_pos[] = {
191  event2[ 0 ] - event1[ 0 ],
192  event2[ 1 ] - event1[ 1 ],
193  event2[ 2 ] - event1[ 2 ]
194  };
195 
196  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
197  for( int i = 0; i < 3; ++i )
198  {
199  if( delta_pos[ i ] != 0.0 )
200  {
201  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
202  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
203  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
204  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
205  }
206  }
207 
208  // if alphaMax is less than or equal to alphaMin no intersection
209  // and return an empty buffer
210  if( alphaMax <= alphaMin ) return 0;
211 
212  // Now we have to find the indices of the particular plane
213  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
214  int iMin = 0, iMax = 0;
215  int jMin = 0, jMax = 0;
216  int kMin = 0, kMax = 0;
217 
218  // For the X-axis
219  if( delta_pos[ 0 ] > 0.0 )
220  {
221  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
222  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
223  }
224  else if( delta_pos[ 0 ] < 0.0 )
225  {
226  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
227  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
228  }
229  if( delta_pos[ 0 ] == 0. )
230  {
231  iMin = 1, iMax = 0;
232  }
233 
234  // For the Y-axis
235  if( delta_pos[ 1 ] > 0. )
236  {
237  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
238  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
239  }
240  else if( delta_pos[ 1 ] < 0. )
241  {
242  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
243  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
244  }
245  else if( delta_pos[ 1 ] == 0. )
246  {
247  jMin = 1, jMax = 0;
248  }
249 
250  // For the Z-axis
251  if( delta_pos[ 2 ] > 0. )
252  {
253  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
254  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
255  }
256  else if( delta_pos[ 2 ] < 0. )
257  {
258  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
259  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
260  }
261  else if( delta_pos[ 2 ] == 0. )
262  {
263  kMin = 1, kMax = 0;
264  }
265 
266  // Computing the last term n number of intersection
267  int n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
268  + ( kMax - kMin + 1 );
269 
270  // We create a buffer storing the merging data
271  // We merge alphaMin, alphaMax, alphaX, alphaY and alphaZ
272  FLTNB *alpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
273  FLTNB *tmpAlpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
274  FLTNB *alphaX = new FLTNB[ ( mp_nbVox[ 0 ] + 1 ) ];
275  FLTNB *alphaY = new FLTNB[ ( mp_nbVox[ 1 ] + 1 ) ];
276  FLTNB *alphaZ = new FLTNB[ ( mp_nbVox[ 2 ] + 1 ) ];
277 
278  INTNB iElement = iMax - iMin + 1;
279  if( iElement > 0 )
280  {
281  FLTNB *idx = alphaX;
282  if( delta_pos[ 0 ] > 0. )
283  {
284  for( int i = iMin; i <= iMax; ++i )
285  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
286  }
287  else if( delta_pos[ 0 ] < 0. )
288  {
289  for( int i = iMax; i >= iMin; --i )
290  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
291  }
292  }
293 
294  // For alphaY
295  INTNB jElement = jMax - jMin + 1;
296  if( jElement > 0 )
297  {
298  FLTNB *idx = alphaY;
299  if( delta_pos[ 1 ] > 0. )
300  {
301  for( int j = jMin; j <= jMax; ++j )
302  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
303  }
304  else if( delta_pos[ 1 ] < 0. )
305  {
306  for( int j = jMax; j >= jMin; --j )
307  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
308  }
309  }
310 
311  // For alphaZ
312  INTNB kElement = kMax - kMin + 1;
313  if( kElement > 0 )
314  {
315  FLTNB *idx = alphaZ;
316  if( delta_pos[ 2 ] > 0. )
317  {
318  for( int k = kMin; k <= kMax; ++k )
319  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
320  }
321  else if( delta_pos[ 2 ] < 0. )
322  {
323  for( int k = kMax; k >= kMin; --k )
324  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
325  }
326  }
327 
328  std::merge(
329  alphaX, alphaX + iElement,
330  tmpAlpha, tmpAlpha,
331  alpha );
332 
333  ::memcpy( tmpAlpha, alpha, ( iElement ) * sizeof( FLTNB ) );
334 
335  std::merge(
336  alphaY, alphaY + jElement,
337  tmpAlpha, tmpAlpha + iElement,
338  alpha );
339 
340  ::memcpy( tmpAlpha, alpha, ( iElement + jElement ) * sizeof( FLTNB ) );
341 
342  std::merge(
343  alphaZ, alphaZ + kElement,
344  tmpAlpha, tmpAlpha + iElement + jElement,
345  alpha );
346 
347  // Computing some constants
348  FLTNB const i1 = ( event1[ 0 ] - (-mp_halfFOV[ 0 ]) + mp_sizeVox[0] ) / mp_sizeVox[0];
349  FLTNB const i2 = delta_pos[ 0 ] / mp_sizeVox[0];
350  FLTNB const j1 = ( event1[ 1 ] - (-mp_halfFOV[ 1 ]) + mp_sizeVox[1] ) / mp_sizeVox[1];
351  FLTNB const j2 = delta_pos[ 1 ] / mp_sizeVox[1];
352  FLTNB const k1 = ( event1[ 2 ] - (-mp_halfFOV[ 2 ]) + mp_sizeVox[2] ) / mp_sizeVox[2];
353  FLTNB const k2 = delta_pos[ 2 ] / mp_sizeVox[2];
354 
355  // Computing the index of the voxels
356  FLTNB alphaMid = 0.0;
357  INTNB i = 0, j = 0, k = 0; // indices of the voxel
358  FLTNB *pAlpha = &alpha[ 1 ];
359  FLTNB *pAlphaPrevious = &alpha[ 0 ];
360  FLTNB coeff = 0.0;
361  INTNB numVox = 0;
362  // Loop over the number of crossed planes
363  for( int nP = 0; nP < n - 1; ++nP, ++pAlpha, ++pAlphaPrevious )
364  {
365  alphaMid = ( *pAlpha + *pAlphaPrevious ) * 0.5;
366 
367  i = alphaMid * i2 + i1;
368  if( i < 1 || i > mp_nbVox[ 0 ] ) continue;
369 
370  j = alphaMid * j2 + j1;
371  if( j < 1 || j > mp_nbVox[ 1 ] ) continue;
372 
373  k = alphaMid * k2 + k1;
374  if( k < 1 || k > mp_nbVox[ 2 ] ) continue;
375 
376  numVox = ( i - 1 ) + ( j - 1 ) * mp_nbVox[0] + ( k - 1 ) * mp_nbVox[0] * mp_nbVox[1];
377 
378  // if this voxel is masked, skip
379  if (m_hasMask && !mp_mask[numVox]) continue;
380 
381  coeff = length_LOR * ( *pAlpha - *pAlphaPrevious );
382 
383  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
384  }
385 
386  delete[] alpha;
387  delete[] tmpAlpha;
388  delete[] alphaX;
389  delete[] alphaY;
390  delete[] alphaZ;
391 
392  #ifdef CASTOR_VERBOSE
393  if (m_verbose>=10)
394  {
395  Cout("iProjectorClassicSiddon::Project without TOF -> Exit function" << endl);
396  }
397  #endif
398 
399  return 0;
400 }
401 
402 // =====================================================================
403 // ---------------------------------------------------------------------
404 // ---------------------------------------------------------------------
405 // =====================================================================
406 
407 int iProjectorClassicSiddon::ProjectWithTOFPos(int a_direction, oProjectionLine* ap_ProjectionLine)
408 {
409  #ifdef CASTOR_DEBUG
410  if (!m_initialized)
411  {
412  Cerr("***** iProjectorClassicSiddon::ProjectWithTOFPos() -> Called while not initialized !" << endl);
413  Exit(EXIT_DEBUG);
414  }
415  #endif
416 
417  #ifdef CASTOR_VERBOSE
418  if (m_verbose>=10)
419  {
420  string direction = "";
421  if (a_direction==FORWARD) direction = "forward";
422  else direction = "backward";
423  Cout("iProjectorClassicSiddon::Project with TOF measurement -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
424  }
425  #endif
426 
427  // Simpler way now, hopefully it works
428  FLTNB* event1_position = ap_ProjectionLine->GetPosition1();
429  FLTNB* event2_position = ap_ProjectionLine->GetPosition2();
430 
431  FLTNB event1[3] = { event1_position[0], event1_position[1], event1_position[2] };
432  FLTNB event2[3] = { event2_position[0], event2_position[1], event2_position[2] };
433 
434  // **************************************
435  // STEP 1: LOR length calculation
436  // **************************************
437  FLTNB length_LOR = ap_ProjectionLine->GetLength();
438 
439  // **************************************
440  // STEP 2: Compute entrance voxel indexes
441  // **************************************
442  FLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
443  FLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
444 
445  FLTNB alphaMin = 0.0, alphaMax = 1.0;
446  FLTNB delta_pos[] = {
447  event2[ 0 ] - event1[ 0 ],
448  event2[ 1 ] - event1[ 1 ],
449  event2[ 2 ] - event1[ 2 ]
450  };
451 
452  // Get TOF info
453  HPFLTNB tof_resolutionT = ap_ProjectionLine->GetTOFResolution();
454  HPFLTNB tof_measurement = ap_ProjectionLine->GetTOFMeasurement();
455 
456  // TOF standard deviation and truncation
457  HPFLTNB tof_sigma = tof_resolutionT * SPEED_OF_LIGHT * 0.5 / TWO_SQRT_TWO_LN_2;
458  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
459  HPFLTNB tof_half_span = tof_sigma * m_TOFnbSigmas;
460 
461  // convert delta time into delta length
462  HPFLTNB tof_delta = tof_measurement * SPEED_OF_LIGHT * 0.5;
463 
464  // distance between the first event1 and the center of the Gaussian distribution along the LOR
465  HPFLTNB lor_tof_center = length_LOR * 0.5 + tof_delta;
466 
467  // index along each axis of the first voxel falling inside the truncated Gaussian distribution
468  HPFLTNB tof_edge_low[] = {0,0,0};
469  // index along each axis of the last voxel falling inside the truncated Gaussian distribution
470  HPFLTNB tof_edge_high[] = {0,0,0};
471  HPFLTNB tof_center;
472  INTNB tof_index;
473 
474  // low/high voxel edges (in absolute coordinates) for truncated TOF
475  for (int ax=0;ax<3;ax++)
476  {
477  // absolute coordinate along each axis of the center of the TOF distribution
478  tof_center = event1[ax] + lor_tof_center * delta_pos[ax] / length_LOR;
479 
480  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
481  tof_edge_low[ax] = tof_center - tof_half_span * fabs(delta_pos[ax]) / length_LOR;
482  tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
483  // if low TOF edge above the highest FOV edge, return empty line
484  if (tof_index>mp_nbVox[ax]-1) return 0;
485  tof_edge_low[ax] = (HPFLTNB)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
486 
487  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
488  tof_edge_high[ax] = tof_center + tof_half_span * fabs(delta_pos[ax]) / length_LOR;
489  tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
490  // if high TOF edge below the lowest FOV edge, return empty line
491  if (tof_index<0) return 0;
492  tof_edge_high[ax] = (HPFLTNB)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
493  }
494 
495  // Computation of alphaMin et alphaMax values
496  // entrance and exit point of the ray at voxel grid (edges), with respect to the truncated TOF distribution, limited by the FOV,
497  // absolute normalized distance from event1
498  for( int i = 0; i < 3; ++i )
499  {
500  if( delta_pos[ i ] != 0.0 )
501  {
502  alphaFirst[i] = (tof_edge_low[i] - event1[i]) / (delta_pos[ i ]);
503  alphaLast[i] = (tof_edge_high[i] - event1[i]) / (delta_pos[ i ]);
504  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
505  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
506  }
507  }
508 
509  // if alphaMax is less than or equal to alphaMin no intersection
510  // and return an empty buffer
511  if( alphaMax <= alphaMin ) return 0;
512 
513  // Min/max indices of voxels intersected by the LOR along each axis, 0-N indices (same order as absolute coordinates) match the FOV
514  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
515  int iMin = 0, iMax = 0;
516  int jMin = 0, jMax = 0;
517  int kMin = 0, kMax = 0;
518 
519  // For the X-axis
520  if( delta_pos[ 0 ] > 0.0 )
521  {
522  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
523  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
524  }
525  else if( delta_pos[ 0 ] < 0.0 )
526  {
527  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
528  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
529  }
530  if( delta_pos[ 0 ] == 0. )
531  {
532  iMin = 1, iMax = 0;
533  }
534 
535  // For the Y-axis
536  if( delta_pos[ 1 ] > 0. )
537  {
538  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
539  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
540  }
541  else if( delta_pos[ 1 ] < 0. )
542  {
543  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
544  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
545  }
546  else if( delta_pos[ 1 ] == 0. )
547  {
548  jMin = 1, jMax = 0;
549  }
550 
551  // For the Z-axis
552  if( delta_pos[ 2 ] > 0. )
553  {
554  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
555  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
556  }
557  else if( delta_pos[ 2 ] < 0. )
558  {
559  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
560  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
561  }
562  else if( delta_pos[ 2 ] == 0. )
563  {
564  kMin = 1, kMax = 0;
565  }
566 
567  // Computing the last term n number of intersection
568  int n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
569  + ( kMax - kMin + 1 );
570 
571  // We create a buffer storing the merging data
572  // We merge alphaMin, alphaMax, alphaX, alphaY and alphaZ
573  FLTNB *alpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
574  FLTNB *tmpAlpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
575  FLTNB *alphaX = new FLTNB[ ( mp_nbVox[ 0 ] + 1 ) ];
576  FLTNB *alphaY = new FLTNB[ ( mp_nbVox[ 1 ] + 1 ) ];
577  FLTNB *alphaZ = new FLTNB[ ( mp_nbVox[ 2 ] + 1 ) ];
578 
579  INTNB iElement = iMax - iMin + 1;
580  if( iElement > 0 )
581  {
582  FLTNB *idx = alphaX;
583  if( delta_pos[ 0 ] > 0. )
584  {
585  for( int i = iMin; i <= iMax; ++i )
586  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
587  }
588  else if( delta_pos[ 0 ] < 0. )
589  {
590  for( int i = iMax; i >= iMin; --i )
591  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
592  }
593  }
594 
595  // For alphaY
596  INTNB jElement = jMax - jMin + 1;
597  if( jElement > 0 )
598  {
599  FLTNB *idx = alphaY;
600  if( delta_pos[ 1 ] > 0. )
601  {
602  for( int j = jMin; j <= jMax; ++j )
603  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
604  }
605  else if( delta_pos[ 1 ] < 0. )
606  {
607  for( int j = jMax; j >= jMin; --j )
608  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
609  }
610  }
611 
612  // For alphaZ
613  INTNB kElement = kMax - kMin + 1;
614  if( kElement > 0 )
615  {
616  FLTNB *idx = alphaZ;
617  if( delta_pos[ 2 ] > 0. )
618  {
619  for( int k = kMin; k <= kMax; ++k )
620  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
621  }
622  else if( delta_pos[ 2 ] < 0. )
623  {
624  for( int k = kMax; k >= kMin; --k )
625  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
626  }
627  }
628 
629  std::merge(
630  alphaX, alphaX + iElement,
631  tmpAlpha, tmpAlpha,
632  alpha );
633 
634  ::memcpy( tmpAlpha, alpha, ( iElement ) * sizeof( FLTNB ) );
635 
636  std::merge(
637  alphaY, alphaY + jElement,
638  tmpAlpha, tmpAlpha + iElement,
639  alpha );
640 
641  ::memcpy( tmpAlpha, alpha, ( iElement + jElement ) * sizeof( FLTNB ) );
642 
643  std::merge(
644  alphaZ, alphaZ + kElement,
645  tmpAlpha, tmpAlpha + iElement + jElement,
646  alpha );
647 
648  // Computing some constants
649  FLTNB const i1 = ( event1[ 0 ] - (-mp_halfFOV[ 0 ]) + mp_sizeVox[0] ) / mp_sizeVox[0];
650  FLTNB const i2 = delta_pos[ 0 ] / mp_sizeVox[0];
651  FLTNB const j1 = ( event1[ 1 ] - (-mp_halfFOV[ 1 ]) + mp_sizeVox[1] ) / mp_sizeVox[1];
652  FLTNB const j2 = delta_pos[ 1 ] / mp_sizeVox[1];
653  FLTNB const k1 = ( event1[ 2 ] - (-mp_halfFOV[ 2 ]) + mp_sizeVox[2] ) / mp_sizeVox[2];
654  FLTNB const k2 = delta_pos[ 2 ] / mp_sizeVox[2];
655 
656  // Computing the index of the voxels
657  FLTNB alphaMid = 0.0;
658  INTNB i = 0, j = 0, k = 0; // indices of the voxel
659  FLTNB *pAlpha = &alpha[ 1 ];
660  FLTNB *pAlphaPrevious = &alpha[ 0 ];
661  FLTNB coeff = 0.0;
662  INTNB numVox = 0;
663 
664  // tof : erf of previous voxel edge - Gaussian center
665  FLTNB previous_edge_erf = erf( (length_LOR * (*pAlphaPrevious) - lor_tof_center)/ tof_sigma_sqrt2 );
666  FLTNB next_edge_erf;
667 
668  // Loop over the number of crossed planes
669  for( int nP = 0; nP < n - 1; ++nP, ++pAlpha, ++pAlphaPrevious )
670  {
671  alphaMid = ( *pAlpha + *pAlphaPrevious ) * 0.5;
672 
673  i = alphaMid * i2 + i1;
674  if( i < 1 || i > mp_nbVox[ 0 ] ) continue;
675 
676  j = alphaMid * j2 + j1;
677  if( j < 1 || j > mp_nbVox[ 1 ] ) continue;
678 
679  k = alphaMid * k2 + k1;
680  if( k < 1 || k > mp_nbVox[ 2 ] ) continue;
681 
682  numVox = ( i - 1 ) + ( j - 1 ) * mp_nbVox[0] + ( k - 1 ) * mp_nbVox[0] * mp_nbVox[1];
683 
684  // tof : erf of next voxel edge - Gaussian center
685  next_edge_erf = erf( (length_LOR * (*pAlpha) - lor_tof_center) / tof_sigma_sqrt2 );
686  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
687  coeff = 0.5 * fabs(previous_edge_erf - next_edge_erf) ;
688  // keeping record of the previous edge, so as to save 1 erf computation
689  previous_edge_erf = next_edge_erf;
690 
691  // if this voxel is masked, skip
692  if (!m_hasMask || mp_mask[numVox]) ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, numVox, coeff);
693 
694  }
695 
696  delete[] alpha;
697  delete[] tmpAlpha;
698  delete[] alphaX;
699  delete[] alphaY;
700  delete[] alphaZ;
701 
702  #ifdef CASTOR_VERBOSE
703  if (m_verbose>=10)
704  {
705  Cout("iProjectorClassicSiddon::Project with TOF measurement-> Exit function" << endl);
706  }
707  #endif
708 
709  return 0;
710 }
711 
712 // =====================================================================
713 // ---------------------------------------------------------------------
714 // ---------------------------------------------------------------------
715 // =====================================================================
716 
717 int iProjectorClassicSiddon::ProjectWithTOFBin(int a_direction, oProjectionLine* ap_ProjectionLine)
718 {
719  #ifdef CASTOR_DEBUG
720  if (!m_initialized)
721  {
722  Cerr("***** iProjectorClassicSiddon::ProjectWithTOFBin() -> Called while not initialized !" << endl);
723  Exit(EXIT_DEBUG);
724  }
725  #endif
726 
727  #ifdef CASTOR_VERBOSE
728  if (m_verbose>=10)
729  {
730  string direction = "";
731  if (a_direction==FORWARD) direction = "forward";
732  else direction = "backward";
733  Cout("iProjectorClassicSiddon::Project with TOF bins -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
734  }
735  #endif
736 
737  // Simpler way now, hopefully it works
738  FLTNB* event1_position = ap_ProjectionLine->GetPosition1();
739  FLTNB* event2_position = ap_ProjectionLine->GetPosition2();
740 
741  FLTNB event1[3] = { event1_position[0], event1_position[1], event1_position[2] };
742  FLTNB event2[3] = { event2_position[0], event2_position[1], event2_position[2] };
743 
744  // **************************************
745  // STEP 1: LOR length calculation
746  // **************************************
747  FLTNB length_LOR = ap_ProjectionLine->GetLength();
748  FLTNB length_LOR_half = length_LOR * 0.5;
749 
750  // **************************************
751  // STEP 2: Compute entrance voxel indexes
752  // **************************************
753  FLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
754  FLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
755 
756  FLTNB alphaMin = 0.0, alphaMax = 1.0;
757  FLTNB delta_pos[] = {
758  event2[ 0 ] - event1[ 0 ],
759  event2[ 1 ] - event1[ 1 ],
760  event2[ 2 ] - event1[ 2 ]
761  };
762 
763  // Get TOF info
764  // TOF temporal resolution (FWHM)
765  HPFLTNB tof_resolutionT = ap_ProjectionLine->GetTOFResolution();
766  // TOF bin temporal size
767  HPFLTNB tof_bin_sizeT = ap_ProjectionLine->GetTOFBinSize();
768  INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
769  INTNB tof_half_nb_bins = tof_nb_bins/2;
770 
771  // TOF spatial standard deviation (simple TOF Gaussian, not convolved with the TOF bin width, maybe TODO)
772  HPFLTNB tof_sigma = tof_resolutionT * SPEED_OF_LIGHT * 0.5 / TWO_SQRT_TWO_LN_2;
773  //HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
774  HPFLTNB tof_half_span = tof_sigma * m_TOFnbSigmas;
775 
776  // normalized Gaussian (integral=1) : precomputation of the coefficient that multiplies the exponential
777  HPFLTNB gaussian_norm_coef = INV_SQRT_2_PI * 1./tof_sigma;
778 
779  // spatial size of the TOF bin in mm
780  HPFLTNB tof_bin_size = tof_bin_sizeT * SPEED_OF_LIGHT * 0.5;
781 
782  // minimum and maximum TOF bins
783  INTNB tof_bin_last = tof_half_nb_bins;
784  INTNB tof_bin_first = -tof_half_nb_bins;
785 
786  // distance between the first event1 and the center of the Gaussian distribution of the first TOF bin along the LOR
787  //HPFLTNB tof_bin_center_first = length_LOR_half + tof_bin_first * tof_bin_size;
788  // distance between the first event1 and the center of the Gaussian distribution of the current TOF bin along the LOR
789  HPFLTNB lor_tof_center = 0.;
790  // normalization coefficient used for making the sum of TOF bin coefficients for a voxel equal the nonTOF voxel coefficient
791  HPFLTNB tof_norm_coef = 0.;
792 
793  // tof help variables
794  //HPFLTNB previous_edge_erf, next_edge_erf;
795  HPFLTNB tof_weight;
796 
797  // Computation of alphaMin et alphaMax values
798  // entrance and exit point of the ray at voxel grid (edges), limited by the FOV,
799  // absolute normalized distance from event1
800  for( int i = 0; i < 3; ++i )
801  {
802  if( delta_pos[ i ] != 0.0 )
803  {
804  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
805  alphaLast[i] = (mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
806  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
807  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
808  }
809  }
810 
811  // if alphaMax is less than or equal to alphaMin no intersection
812  // and return an empty buffer
813  if( alphaMax <= alphaMin ) return 0;
814 
815  // Min/max indices of voxels intersected by the LOR along each axis, 0-N indices (same order as absolute coordinates) match the FOV
816  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
817  int iMin = 0, iMax = 0;
818  int jMin = 0, jMax = 0;
819  int kMin = 0, kMax = 0;
820 
821  // For the X-axis
822  if( delta_pos[ 0 ] > 0.0 )
823  {
824  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
825  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
826  }
827  else if( delta_pos[ 0 ] < 0.0 )
828  {
829  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
830  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
831  }
832  if( delta_pos[ 0 ] == 0. )
833  {
834  iMin = 1, iMax = 0;
835  }
836 
837  // For the Y-axis
838  if( delta_pos[ 1 ] > 0. )
839  {
840  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
841  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
842  }
843  else if( delta_pos[ 1 ] < 0. )
844  {
845  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
846  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
847  }
848  else if( delta_pos[ 1 ] == 0. )
849  {
850  jMin = 1, jMax = 0;
851  }
852 
853  // For the Z-axis
854  if( delta_pos[ 2 ] > 0. )
855  {
856  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
857  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
858  }
859  else if( delta_pos[ 2 ] < 0. )
860  {
861  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
862  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
863  }
864  else if( delta_pos[ 2 ] == 0. )
865  {
866  kMin = 1, kMax = 0;
867  }
868 
869  // Computing the last term n number of intersection
870  int n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
871  + ( kMax - kMin + 1 );
872 
873  // We create a buffer storing the merging data
874  // We merge alphaMin, alphaMax, alphaX, alphaY and alphaZ
875  FLTNB *alpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
876  FLTNB *tmpAlpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
877  FLTNB *alphaX = new FLTNB[ ( mp_nbVox[ 0 ] + 1 ) ];
878  FLTNB *alphaY = new FLTNB[ ( mp_nbVox[ 1 ] + 1 ) ];
879  FLTNB *alphaZ = new FLTNB[ ( mp_nbVox[ 2 ] + 1 ) ];
880 
881  // temporary storage for TOF bins Gaussian integrals over the current voxel
882  // allocation after potential returns
883  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
884  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
885 
886  INTNB iElement = iMax - iMin + 1;
887  if( iElement > 0 )
888  {
889  FLTNB *idx = alphaX;
890  if( delta_pos[ 0 ] > 0. )
891  {
892  for( int i = iMin; i <= iMax; ++i )
893  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
894  }
895  else if( delta_pos[ 0 ] < 0. )
896  {
897  for( int i = iMax; i >= iMin; --i )
898  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
899  }
900  }
901 
902  // For alphaY
903  INTNB jElement = jMax - jMin + 1;
904  if( jElement > 0 )
905  {
906  FLTNB *idx = alphaY;
907  if( delta_pos[ 1 ] > 0. )
908  {
909  for( int j = jMin; j <= jMax; ++j )
910  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
911  }
912  else if( delta_pos[ 1 ] < 0. )
913  {
914  for( int j = jMax; j >= jMin; --j )
915  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
916  }
917  }
918 
919  // For alphaZ
920  INTNB kElement = kMax - kMin + 1;
921  if( kElement > 0 )
922  {
923  FLTNB *idx = alphaZ;
924  if( delta_pos[ 2 ] > 0. )
925  {
926  for( int k = kMin; k <= kMax; ++k )
927  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
928  }
929  else if( delta_pos[ 2 ] < 0. )
930  {
931  for( int k = kMax; k >= kMin; --k )
932  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
933  }
934  }
935 
936  std::merge(
937  alphaX, alphaX + iElement,
938  tmpAlpha, tmpAlpha,
939  alpha );
940 
941  ::memcpy( tmpAlpha, alpha, ( iElement ) * sizeof( FLTNB ) );
942 
943  std::merge(
944  alphaY, alphaY + jElement,
945  tmpAlpha, tmpAlpha + iElement,
946  alpha );
947 
948  ::memcpy( tmpAlpha, alpha, ( iElement + jElement ) * sizeof( FLTNB ) );
949 
950  std::merge(
951  alphaZ, alphaZ + kElement,
952  tmpAlpha, tmpAlpha + iElement + jElement,
953  alpha );
954 
955  // Computing some constants
956  FLTNB const i1 = ( event1[ 0 ] - (-mp_halfFOV[ 0 ]) + mp_sizeVox[0] ) / mp_sizeVox[0];
957  FLTNB const i2 = delta_pos[ 0 ] / mp_sizeVox[0];
958  FLTNB const j1 = ( event1[ 1 ] - (-mp_halfFOV[ 1 ]) + mp_sizeVox[1] ) / mp_sizeVox[1];
959  FLTNB const j2 = delta_pos[ 1 ] / mp_sizeVox[1];
960  FLTNB const k1 = ( event1[ 2 ] - (-mp_halfFOV[ 2 ]) + mp_sizeVox[2] ) / mp_sizeVox[2];
961  FLTNB const k2 = delta_pos[ 2 ] / mp_sizeVox[2];
962 
963  // Computing the index of the voxels
964  FLTNB alphaMid = 0.0;
965  INTNB i = 0, j = 0, k = 0; // indices of the voxel
966  FLTNB *pAlpha = &alpha[ 1 ];
967  FLTNB *pAlphaPrevious = &alpha[ 0 ];
968  FLTNB coeff = 0.0;
969  INTNB numVox = 0;
970  INTNB tof_bin_first_for_voxel = 0, tof_bin_last_for_voxel = 0;
971 
972  // Loop over the number of crossed planes
973  for( int nP = 0; nP < n - 1; ++nP, ++pAlpha, ++pAlphaPrevious )
974  {
975  alphaMid = ( *pAlpha + *pAlphaPrevious ) * 0.5;
976 
977  i = alphaMid * i2 + i1;
978  if( i < 1 || i > mp_nbVox[ 0 ] ) continue;
979 
980  j = alphaMid * j2 + j1;
981  if( j < 1 || j > mp_nbVox[ 1 ] ) continue;
982 
983  k = alphaMid * k2 + k1;
984  if( k < 1 || k > mp_nbVox[ 2 ] ) continue;
985 
986  numVox = ( i - 1 ) + ( j - 1 ) * mp_nbVox[0] + ( k - 1 ) * mp_nbVox[0] * mp_nbVox[1];
987 
988  // if this voxel is masked, skip
989  if (m_hasMask && !mp_mask[numVox]) continue;
990 
991  coeff = length_LOR * ( *pAlpha - *pAlphaPrevious );
992 
993  // Compute the first and the last relevant TOF bin for this voxel
994  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(trunc( ((*pAlphaPrevious) * length_LOR - tof_half_span - length_LOR_half ) / tof_bin_size )));
995  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(trunc( ((*pAlpha) * length_LOR + tof_half_span - length_LOR_half) / tof_bin_size )));
996  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * tof_bin_size;
997 
998  // if min/max TOF bins for this voxel do not fall into the total available TOF bins range, skip
999  if(tof_bin_first_for_voxel>tof_bin_last || tof_bin_last_for_voxel<tof_bin_first) continue;
1000 
1001  // shift tof bin indices from -/+ to 0:nbBins range
1002  tof_bin_first_for_voxel += tof_half_nb_bins;
1003  tof_bin_last_for_voxel += tof_half_nb_bins;
1004 
1005  // first compute the normalization for TOF bin coefficients for the current voxel (simple sum)
1006  tof_norm_coef = 0.;
1007  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1008  {
1009  // erf ( voxel edge projected onto the LOR - TOF bin center along the LOR )
1010  //previous_edge_erf = erf( (length_LOR * (*pAlphaPrevious) - lor_tof_center) / tof_sigma_sqrt2 );
1011  //next_edge_erf = erf( (length_LOR * (*pAlpha) - lor_tof_center) / tof_sigma_sqrt2 );
1012  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
1013  //tof_weight = 0.5 * fabs(previous_edge_erf - next_edge_erf);
1014  // approximation of the integration of the Gaussian over the voxel
1015  // TOF coefficient = value of the Gaussian at voxel center (projected onto the LOR) * voxel size (projected onto the LOR)
1016  HPFLTNB temp = (alphaMid * length_LOR - lor_tof_center) / tof_sigma;
1017  tof_weight = exp(- 0.5 * temp * temp ) * gaussian_norm_coef * coeff;
1018  // add the weight to the sum for normalization
1019  tof_norm_coef += tof_weight;
1020  // save the weight temporarily
1021  tof_weights_temp[tof_bin] = tof_weight;
1022  // update TOF center along the LOR for the next TOF bin
1023  lor_tof_center += tof_bin_size;
1024  }
1025 
1026  // compute the final TOF bin coefficients for the current voxel
1027  if (tof_norm_coef>0.)
1028  {
1029  // first normalize TOF bin coefficients
1030  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1031  {
1032  tof_weights_temp[tof_bin] /= tof_norm_coef;
1033  }
1034  // then write all TOF bins for the current voxel
1035  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, numVox, coeff, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1036  }
1037  }
1038 
1039  delete[] alpha;
1040  delete[] tmpAlpha;
1041  delete[] alphaX;
1042  delete[] alphaY;
1043  delete[] alphaZ;
1044  delete[] tof_weights_temp;
1045 
1046  #ifdef CASTOR_VERBOSE
1047  if (m_verbose>=10)
1048  {
1049  Cout("iProjectorClassicSiddon::Project with TOF bins -> Exit function" << endl);
1050  }
1051  #endif
1052 
1053  return 0;
1054 }
1055 
1056 // =====================================================================
1057 // ---------------------------------------------------------------------
1058 // ---------------------------------------------------------------------
1059 // =====================================================================
void ShowHelpSpecific()
A function used to show help about the child projector.
bool m_compatibleWithSPECTAttenuationCorrection
Definition: vProjector.hh:355
FLTNB mp_sizeVox[3]
Definition: vProjector.hh:338
INTNB mp_nbVox[3]
Definition: vProjector.hh:339
#define FLTNB
Definition: gVariables.hh:81
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:344
#define HPFLTNB
Definition: gVariables.hh:83
This class is designed to generically described any on-the-fly projector.
Definition: vProjector.hh:76
FLTNB GetTOFMeasurement()
This function is used to get the TOF measurement.
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.
void Exit(int code)
FLTNB GetTOFResolution()
This function is used to get the TOF resolution.
iProjectorClassicSiddon()
The constructor of iProjectorClassicSiddon.
#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...
int ProjectWithTOFBin(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF binned information.
int ProjectWithTOFPos(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF continuous information.
~iProjectorClassicSiddon()
The destructor of iProjectorClassicSiddon.
Declaration of class iProjectorClassicSiddon.
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child projector.
bool m_hasMask
Definition: vProjector.hh:366
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...
INTNB EstimateMaxNumberOfVoxelsPerLine()
This function is used to compute and provide an estimate of the maximum number of voxels that could c...
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
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)
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project without TOF.
bool m_compatibleWithCompression
Definition: vProjector.hh:358
void AddVoxelInTOFBin(int a_direction, int a_TOFBin, INTNB a_voxelIndice, FLTNB a_voxelWeight)
This function is used to add a voxel contribution to the line and provided TOF bin.
int InitializeSpecific()
This function is used to initialize specific stuff to the child projector.
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.