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