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