CASToR  1.1
Tomographic Reconstruction (PET/SPECT)
 All Classes Files Functions Variables Typedefs Macros Groups Pages
iProjectorClassicSiddon.cc
Go to the documentation of this file.
1 
2 /*
3  Implementation of class iProjectorClassicSiddon
4 
5  - separators: done
6  - doxygen: done
7  - default initialization: done
8  - CASTOR_DEBUG:
9  - CASTOR_VERBOSE:
10 */
11 
18 #include <algorithm>
19 #include <cstring>
20 
22 #include "sOutputManager.hh"
23 
24 // =====================================================================
25 // ---------------------------------------------------------------------
26 // ---------------------------------------------------------------------
27 // =====================================================================
28 
30 {
31  // This projector is compatible with SPECT attenuation correction because
32  // all voxels contributing to a given line are ordered from point 1 (focal)
33  // to point 2 (scanner)
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  coeff = length_LOR * ( *pAlpha - *pAlphaPrevious );
361 
362  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
363  }
364 
365  delete[] alpha;
366  delete[] tmpAlpha;
367  delete[] alphaX;
368  delete[] alphaY;
369  delete[] alphaZ;
370 
371  #ifdef CASTOR_VERBOSE
372  if (m_verbose>=10)
373  {
374  Cout("iProjectorClassicSiddon::Project without TOF -> Exit function" << endl);
375  }
376  #endif
377 
378  return 0;
379 }
380 
381 // =====================================================================
382 // ---------------------------------------------------------------------
383 // ---------------------------------------------------------------------
384 // =====================================================================
385 
386 int iProjectorClassicSiddon::ProjectWithTOFPos(int a_direction, oProjectionLine* ap_ProjectionLine)
387 {
388  #ifdef CASTOR_DEBUG
389  if (!m_initialized)
390  {
391  Cerr("***** iProjectorClassicSiddon::ProjectWithTOFPos() -> Called while not initialized !" << endl);
392  Exit(EXIT_DEBUG);
393  }
394  #endif
395 
396  #ifdef CASTOR_VERBOSE
397  if (m_verbose>=10)
398  {
399  string direction = "";
400  if (a_direction==FORWARD) direction = "forward";
401  else direction = "backward";
402  Cout("iProjectorClassicSiddon::Project with TOF pos -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
403  }
404  #endif
405 
406  // Simpler way now, hopefully it works
407  FLTNB* event1_position = ap_ProjectionLine->GetPosition1();
408  FLTNB* event2_position = ap_ProjectionLine->GetPosition2();
409 
410  FLTNB event1[3] = { event1_position[0], event1_position[1], event1_position[2] };
411  FLTNB event2[3] = { event2_position[0], event2_position[1], event2_position[2] };
412 
413  // **************************************
414  // STEP 1: LOR length calculation
415  // **************************************
416  FLTNB length_LOR = ap_ProjectionLine->GetLength();
417 
418  // **************************************
419  // STEP 2: Compute entrance voxel indexes
420  // **************************************
421  FLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
422  FLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
423 
424  FLTNB alphaMin = 0.0, alphaMax = 1.0;
425  FLTNB delta_pos[] = {
426  event2[ 0 ] - event1[ 0 ],
427  event2[ 1 ] - event1[ 1 ],
428  event2[ 2 ] - event1[ 2 ]
429  };
430 
431  // Get TOF info
432  double tof_resolution = ap_ProjectionLine->GetTOFResolution();
433  double tof_measurement = ap_ProjectionLine->GetTOFMeasurement();
434 
435  // TOF standard deviation and truncation
436  double tof_sigma = tof_resolution * SPEED_OF_LIGHT * 0.5 / TWO_SQRT_TWO_LN_2;
437  double tof_half_span = tof_sigma * m_TOFnbSigmas;
438 
439  // convert delta time into delta length
440  double tof_deltaL = tof_measurement * SPEED_OF_LIGHT * 0.5;
441 
442  // special case for aberrant TOF measurements which belong to scattered or random counts
443  // return an empty line because we know that the count does not depict the emitting object that we want to reconstruct
444  if ( fabs(tof_deltaL) > length_LOR * 0.5)
445  {
446  return 0;
447  }
448 
449  // distance between the first event1 and the center of the Gaussian distribution along the LOR
450  double lor_tof_center = length_LOR * 0.5 + tof_deltaL;
451 
452  // coefficient for conversion from erf use to integral of actual TOF function (Gaussian with maximum=1)
453  double tof_norm_coef = tof_sigma * sqrt(2*M_PI);
454 
455  // index along each axis of the first voxel falling inside the truncated Gaussian distribution
456  double tof_edge_low[] = {0,0,0};
457  // index along each axis of the last voxel falling inside the truncated Gaussian distribution
458  double tof_edge_high[] = {0,0,0};
459  double tof_center;
460  INTNB tof_index;
461 
462  // low/high voxel edges (in absolute coordinates) for truncated TOF
463  for (int ax=0;ax<3;ax++)
464  {
465  // absolute coordinate along each axis of the center of the TOF distribution
466  tof_center = event1[ax] + lor_tof_center * delta_pos[ax] / length_LOR;
467 
468  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
469  tof_edge_low[ax] = tof_center - tof_half_span * fabs(delta_pos[ax]) / length_LOR;
470  tof_index = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), 0);
471  // if low TOF edge above the highest FOV edge, return empty line
472  if (tof_index>mp_nbVox[ax]-1) return 0;
473  tof_edge_low[ax] = (double)tof_index * mp_sizeVox[ax] - mp_halfFOV[ax];
474 
475  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
476  tof_edge_high[ax] = tof_center + tof_half_span * fabs(delta_pos[ax]) / length_LOR;
477  tof_index = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
478  // if high TOF edge below the lowest FOV edge, return empty line
479  if (tof_index<0) return 0;
480  tof_edge_high[ax] = (double)(tof_index+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
481  }
482 
483  // Computation of alphaMin et alphaMax values
484  // entrance and exit point of the ray at voxel grid (edges), with respect to the truncated TOF distribution, limited by the FOV,
485  // absolute normalized distance from event1
486  for( int i = 0; i < 3; ++i )
487  {
488  if( delta_pos[ i ] != 0.0 )
489  {
490  alphaFirst[i] = (tof_edge_low[i] - event1[i]) / (delta_pos[ i ]);
491  alphaLast[i] = (tof_edge_high[i] - event1[i]) / (delta_pos[ i ]);
492  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
493  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
494  }
495  }
496 
497  // if alphaMax is less than or equal to alphaMin no intersection
498  // and return an empty buffer
499  if( alphaMax <= alphaMin ) return 0;
500 
501  //Cout("tof_sigma "<<tof_sigma<<" tof_half_span "<<tof_half_span<<" tof_norm_coef "<<tof_norm_coef<<endl);
502  //Cout("tof_measurement "<<tof_measurement<<" lor_tof_center "<<lor_tof_center<<" delta_pos[0] "<<delta_pos[0]<<" lor_length "<<length_LOR<<endl);
503 
504  // Min/max indices of voxels intersected by the LOR along each axis, 0-N indices (same order as absolute coordinates) match the FOV
505  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
506  int iMin = 0, iMax = 0;
507  int jMin = 0, jMax = 0;
508  int kMin = 0, kMax = 0;
509 
510  // For the X-axis
511  if( delta_pos[ 0 ] > 0.0 )
512  {
513  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
514  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
515  }
516  else if( delta_pos[ 0 ] < 0.0 )
517  {
518  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
519  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
520  }
521  if( delta_pos[ 0 ] == 0. )
522  {
523  iMin = 1, iMax = 0;
524  }
525 
526  // For the Y-axis
527  if( delta_pos[ 1 ] > 0. )
528  {
529  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
530  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
531  }
532  else if( delta_pos[ 1 ] < 0. )
533  {
534  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
535  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
536  }
537  else if( delta_pos[ 1 ] == 0. )
538  {
539  jMin = 1, jMax = 0;
540  }
541 
542  // For the Z-axis
543  if( delta_pos[ 2 ] > 0. )
544  {
545  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
546  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
547  }
548  else if( delta_pos[ 2 ] < 0. )
549  {
550  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
551  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
552  }
553  else if( delta_pos[ 2 ] == 0. )
554  {
555  kMin = 1, kMax = 0;
556  }
557 
558  // Computing the last term n number of intersection
559  int n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
560  + ( kMax - kMin + 1 );
561 
562  // We create a buffer storing the merging data
563  // We merge alphaMin, alphaMax, alphaX, alphaY and alphaZ
564  FLTNB *alpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
565  FLTNB *tmpAlpha = new FLTNB[ mp_nbVox[ 0 ] + mp_nbVox[ 1 ] + mp_nbVox[ 2 ] + 3 ];
566  FLTNB *alphaX = new FLTNB[ ( mp_nbVox[ 0 ] + 1 ) ];
567  FLTNB *alphaY = new FLTNB[ ( mp_nbVox[ 1 ] + 1 ) ];
568  FLTNB *alphaZ = new FLTNB[ ( mp_nbVox[ 2 ] + 1 ) ];
569 
570  INTNB iElement = iMax - iMin + 1;
571  if( iElement > 0 )
572  {
573  FLTNB *idx = alphaX;
574  if( delta_pos[ 0 ] > 0. )
575  {
576  for( int i = iMin; i <= iMax; ++i )
577  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
578  }
579  else if( delta_pos[ 0 ] < 0. )
580  {
581  for( int i = iMax; i >= iMin; --i )
582  *idx++ = ( ( (-mp_halfFOV[ 0 ]) + ( i - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
583  }
584  }
585 
586  // For alphaY
587  INTNB jElement = jMax - jMin + 1;
588  if( jElement > 0 )
589  {
590  FLTNB *idx = alphaY;
591  if( delta_pos[ 1 ] > 0. )
592  {
593  for( int j = jMin; j <= jMax; ++j )
594  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
595  }
596  else if( delta_pos[ 1 ] < 0. )
597  {
598  for( int j = jMax; j >= jMin; --j )
599  *idx++ = ( ( (-mp_halfFOV[ 1 ]) + ( j - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
600  }
601  }
602 
603  // For alphaZ
604  INTNB kElement = kMax - kMin + 1;
605  if( kElement > 0 )
606  {
607  FLTNB *idx = alphaZ;
608  if( delta_pos[ 2 ] > 0. )
609  {
610  for( int k = kMin; k <= kMax; ++k )
611  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
612  }
613  else if( delta_pos[ 2 ] < 0. )
614  {
615  for( int k = kMax; k >= kMin; --k )
616  *idx++ = ( ( (-mp_halfFOV[ 2 ]) + ( k - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
617  }
618  }
619 
620  std::merge(
621  alphaX, alphaX + iElement,
622  tmpAlpha, tmpAlpha,
623  alpha );
624 
625  ::memcpy( tmpAlpha, alpha, ( iElement ) * sizeof( FLTNB ) );
626 
627  std::merge(
628  alphaY, alphaY + jElement,
629  tmpAlpha, tmpAlpha + iElement,
630  alpha );
631 
632  ::memcpy( tmpAlpha, alpha, ( iElement + jElement ) * sizeof( FLTNB ) );
633 
634  std::merge(
635  alphaZ, alphaZ + kElement,
636  tmpAlpha, tmpAlpha + iElement + jElement,
637  alpha );
638 
639  // Computing some constants
640  FLTNB const i1 = ( event1[ 0 ] - (-mp_halfFOV[ 0 ]) + mp_sizeVox[0] ) / mp_sizeVox[0];
641  FLTNB const i2 = delta_pos[ 0 ] / mp_sizeVox[0];
642  FLTNB const j1 = ( event1[ 1 ] - (-mp_halfFOV[ 1 ]) + mp_sizeVox[1] ) / mp_sizeVox[1];
643  FLTNB const j2 = delta_pos[ 1 ] / mp_sizeVox[1];
644  FLTNB const k1 = ( event1[ 2 ] - (-mp_halfFOV[ 2 ]) + mp_sizeVox[2] ) / mp_sizeVox[2];
645  FLTNB const k2 = delta_pos[ 2 ] / mp_sizeVox[2];
646 
647  // Computing the index of the voxels
648  FLTNB alphaMid = 0.0;
649  INTNB i = 0, j = 0, k = 0; // indices of the voxel
650  FLTNB *pAlpha = &alpha[ 1 ];
651  FLTNB *pAlphaPrevious = &alpha[ 0 ];
652  FLTNB coeff = 0.0;
653  INTNB numVox = 0;
654 
655  // tof : erf of previous voxel edge - Gaussian center
656  FLTNB previous_edge_erf = erf( (length_LOR * (*pAlphaPrevious) - lor_tof_center)/ (sqrt(2.)*tof_sigma) );
657  FLTNB next_edge_erf;
658 
659  // Loop over the number of crossed planes
660  for( int nP = 0; nP < n - 1; ++nP, ++pAlpha, ++pAlphaPrevious )
661  {
662  alphaMid = ( *pAlpha + *pAlphaPrevious ) * 0.5;
663 
664  i = alphaMid * i2 + i1;
665  if( i < 1 || i > mp_nbVox[ 0 ] ) continue;
666 
667  j = alphaMid * j2 + j1;
668  if( j < 1 || j > mp_nbVox[ 1 ] ) continue;
669 
670  k = alphaMid * k2 + k1;
671  if( k < 1 || k > mp_nbVox[ 2 ] ) continue;
672 
673  numVox = ( i - 1 ) + ( j - 1 ) * mp_nbVox[0] + ( k - 1 ) * mp_nbVox[0] * mp_nbVox[1];
674 
675  // tof : erf of next voxel edge - Gaussian center
676  next_edge_erf = erf( (length_LOR * (*pAlpha) - lor_tof_center) / (sqrt(2.)*tof_sigma) );
677  // integration of the Gaussian is done on the LOR portion matching the whole current voxel along X axis
678  coeff = 0.5 * fabs(previous_edge_erf - next_edge_erf) * tof_norm_coef ;
679  // keeping record of the previous edge, so as to save 1 erf computation
680  previous_edge_erf = next_edge_erf;
681 
682  ap_ProjectionLine->AddVoxelInTOFBin(a_direction, 0, numVox, coeff);
683  }
684 
685  delete[] alpha;
686  delete[] tmpAlpha;
687  delete[] alphaX;
688  delete[] alphaY;
689  delete[] alphaZ;
690 
691  #ifdef CASTOR_VERBOSE
692  if (m_verbose>=10)
693  {
694  Cout("iProjectorClassicSiddon::Project without TOF -> Exit function" << endl);
695  }
696  #endif
697 
698  return 0;
699 }
700 
701 // =====================================================================
702 // ---------------------------------------------------------------------
703 // ---------------------------------------------------------------------
704 // =====================================================================
705 
706 int iProjectorClassicSiddon::ProjectWithTOFBin(int a_direction, oProjectionLine* ap_ProjectionLine)
707 {
708  Cerr("***** iProjectorClassicSiddon::ProjectWithTOFBin() -> Not yet implemented !" << endl);
709  return 1;
710 }
711 
712 // =====================================================================
713 // ---------------------------------------------------------------------
714 // ---------------------------------------------------------------------
715 // =====================================================================
void ShowHelpSpecific()
A function used to show help about the child projector.
bool m_compatibleWithSPECTAttenuationCorrection
Definition: vProjector.hh:317
FLTNB mp_sizeVox[3]
Definition: vProjector.hh:301
INTNB mp_nbVox[3]
Definition: vProjector.hh:302
#define FLTNB
Definition: gVariables.hh:55
#define TWO_SQRT_TWO_LN_2
Definition: gVariables.hh:72
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Definition: vProjector.hh:307
This class is designed to generically described any on-the-fly projector.
Definition: vProjector.hh:54
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:323
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.
FLTNB m_TOFnbSigmas
Definition: vProjector.hh:312
#define INTNB
Definition: gVariables.hh:64
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.
FLTNB mp_halfFOV[3]
Definition: vProjector.hh:304
#define EXIT_DEBUG
Definition: gVariables.hh:69
#define SPEED_OF_LIGHT
Definition: gVariables.hh:75
FLTNB * GetPosition2()
This function is used to get the pointer to the mp_position2 (3-values tab).
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
#define FORWARD
#define Cout(MESSAGE)
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project without TOF.
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.