CASToR  1.1
Tomographic Reconstruction (PET/SPECT)
 All Classes Files Functions Variables Typedefs Macros Groups Pages
iProjectorIRIS.cc
Go to the documentation of this file.
1 
2 /*
3  Implementation of class iProjectorIRIS
4 
5  - separators: done
6  - doxygen: done
7  - default initialization: done
8  - CASTOR_DEBUG:
9  - CASTOR_VERBOSE:
10 */
11 
18 #include "iProjectorIRIS.hh"
19 #include "sOutputManager.hh"
20 
21 // =====================================================================
22 // ---------------------------------------------------------------------
23 // ---------------------------------------------------------------------
24 // =====================================================================
25 
27 {
28  mp_pathToIDRFFiles = NULL;
29  mp_IDRF = NULL;
30  m2p_IDRF_CDFs = NULL;
31  m_nbLinesPerLOR = -1;
32  m_nVoxDepthIDRF = -1;
34  m_nVoxAxialIDRF = -1;
35  m_nVoxXYZIDRF = -1;
36  m_nBetaAnglesIDRF = -1;
37  m_nAlphaAnglesIDRF = -1;
38  m_sizeVoxDepthIDRF = -1.;
40  m_sizeVoxAxialIDRF = -1.;
43  // This projector is not compatible with SPECT attenuation correction because
44  // the voxels contributing to the line are not strictly ordered with respect to
45  // their distance to point 2 (due to the use of multiple lines that are
46  // stack one after the other)
48 }
49 
50 // =====================================================================
51 // ---------------------------------------------------------------------
52 // ---------------------------------------------------------------------
53 // =====================================================================
54 
56 {
58 
60  for (int a=0 ; a<m_nAlphaAnglesIDRF*m_nBetaAnglesIDRF ; a++)
61  if (m2p_IDRF_CDFs[a]) delete[] m2p_IDRF_CDFs[a];
62 
63  if (m2p_IDRF_CDFs) delete[] m2p_IDRF_CDFs;
64 
65  if (mp_IDRF) delete[] mp_IDRF;
66 }
67 
68 // =====================================================================
69 // ---------------------------------------------------------------------
70 // ---------------------------------------------------------------------
71 // =====================================================================
72 
73 int iProjectorIRIS::ReadConfigurationFile(const string& a_configurationFile)
74 {
75  if (ReadDataASCIIFile(a_configurationFile, "number lines per LOR", &m_nbLinesPerLOR, 1, 1) )
76  {
77  Cerr("***** iProjectorIRIS::ReadConfigurationFile() -> An error occurred while trying to read the number of lines per LOR from the IRIS configuration file at :" << a_configurationFile << " !" << endl);
78  return 1;
79  }
80 
81  if (ReadDataASCIIFile(a_configurationFile, "number voxels depth", &m_nVoxDepthIDRF, 1, 1) ||
82  ReadDataASCIIFile(a_configurationFile, "number voxels transaxial", &m_nVoxTransaxialIDRF, 1, 1) ||
83  ReadDataASCIIFile(a_configurationFile, "number voxels axial", &m_nVoxAxialIDRF, 1, 1) )
84  {
85  Cerr("***** iProjectorIRIS::ReadConfigurationFile() -> An error occurred while trying to read the IDRF volume dimensions (nb voxels) from the IRIS configuration file at :" << a_configurationFile << " !" << endl);
86  return 1;
87  }
88 
89  // Total number of voxels in the IDRF
91 
92  if (ReadDataASCIIFile(a_configurationFile, "size voxels depth", &m_sizeVoxDepthIDRF, 1, 1) ||
93  ReadDataASCIIFile(a_configurationFile, "size voxels transaxial", &m_sizeVoxTransaxialIDRF, 1, 1) ||
94  ReadDataASCIIFile(a_configurationFile, "size voxels axial", &m_sizeVoxAxialIDRF, 1, 1) )
95  {
96  Cerr("***** iProjectorIRIS::ReadConfigurationFile() -> An error occurred while trying to read the voxel sizes of the IDRF volumes from the IRIS configuration file at :" << a_configurationFile << " !" << endl);
97  return 1;
98  }
99 
100  if (ReadDataASCIIFile(a_configurationFile, "number beta angles", &m_nBetaAnglesIDRF, 1, 1) ||
101  ReadDataASCIIFile(a_configurationFile, "number alpha angles", &m_nAlphaAnglesIDRF, 1, 1) )
102  {
103  Cerr("***** iProjectorIRIS::ReadConfigurationFile() -> An error occurred while trying to read the number of alpha/beta angles from the IRIS configuration file at :" << a_configurationFile << " !" << endl);
104  return 1;
105  }
106 
107  if (ReadDataASCIIFile(a_configurationFile, "step beta angles", &m_stepBetaAnglesIDRF, 1, 1) ||
108  ReadDataASCIIFile(a_configurationFile, "step alpha angles", &m_stepAlphaAnglesIDRF, 1, 1) )
109  {
110  Cerr("***** iProjectorIRIS::ReadConfigurationFile() -> An error occurred while trying to read the alpha/beta angles steps from the IRIS configuration file at :" << a_configurationFile << " !" << endl);
111  return 1;
112  }
113 
114  // Convert angle steps in radians
115  m_stepAlphaAnglesIDRF *= M_PI/180.;
116  m_stepBetaAnglesIDRF *= M_PI/180.;
117 
119 
120  mp_IDRF = new float[m_nVoxXYZIDRF];
122 
123  for(int a=0 ; a<m_nAlphaAnglesIDRF*m_nBetaAnglesIDRF ; a++)
124  m2p_IDRF_CDFs[a] = new float[m_nVoxXYZIDRF];
125 
126  if (ReadDataASCIIFile(a_configurationFile, "IDRFs path", mp_pathToIDRFFiles, 1, m_nAlphaAnglesIDRF*m_nBetaAnglesIDRF, 1) )
127  {
128  Cerr("***** iProjectorIRIS::ReadConfigurationFile() -> An error occurred while trying to read the path to the IDRF files, from the IRIS configuration file at :" << a_configurationFile << " !" << endl);
129  Cerr("***** ("<< m_nAlphaAnglesIDRF*m_nBetaAnglesIDRF << " lines expected)" << endl);
130  return 1;
131  }
132 
133  // Normal end
134  return 0;
135 }
136 
137 // =====================================================================
138 // ---------------------------------------------------------------------
139 // ---------------------------------------------------------------------
140 // =====================================================================
141 
142 int iProjectorIRIS::ReadOptionsList(const string& a_optionsList)
143 {
144  // Configuration is possible only through a configuration file (no command-line options)
145  Cerr("***** iProjectorIRIS::ReadOptionsList() -> This projector should be initialized with a configuration file, and not parameters (-proj IRIS:path_to_configuration_file) !" << endl);
146  return 1;
147 }
148 
149 // =====================================================================
150 // ---------------------------------------------------------------------
151 // ---------------------------------------------------------------------
152 // =====================================================================
153 
155 {
156  cout << "Multiray projector requiring Intrinsic Detector Response Functions (IDRFs) models" << endl;
157  cout << "The projector will generate an user-defined number of lines using from pairs of random points generated using the IDRFs models" << endl;
158  cout << "The lines will be rendered using the Incremental Siddon projector (incrementalSiddon)" << endl;
159  cout << "This projector requires an initialization file and Monte-Carlo generated IDRFs. For more informations, please read [article]" << endl;
160  cout << "INITIALIZATION FILE FIELDS :" << endl;
161  cout << "number lines per LOR : number of lines generated to estimate the CDRF (int32)" << endl;
162  cout << "number voxels depth : number of voxels in the depth direction in the IDRF volumes (int32)" << endl;
163  cout << "number voxels transaxial : number of voxels in the transaxial direction in the IDRF volumes (int32)" << endl;
164  cout << "number voxels axial : number of voxels in the axial direction in the IDRF volumes (int32)" << endl;
165  cout << "size voxels depth : size of voxels in the depth direction of the IDRF volumes (float32)" << endl;
166  cout << "size voxels transaxial : size of voxels in the transaxial direction of the IDRF volume (float32)" << endl;
167  cout << "size voxels axial : size of voxels in the axial direction of the IDRF volume (float32)" << endl;
168  cout << "number beta angles : number of axial angles in the IDRF volumes (int32)" << endl;
169  cout << "number alpha angles : number of transaxial angles in the IDRF volumes (int32)" << endl;
170  cout << "step beta angles : axial angular steps in the IDRF volumes (float32)" << endl;
171  cout << "step alpha angles : transaxial angular steps in the IDRF volumes (float32)" << endl;
172  cout << "IDRFs path : path to the IDRF .vol files (string)" << endl;
173  cout << " : each path should be entered on a separated line" << endl;
174  cout << " " << endl;
175  cout << "Each .vol files should contain a header of 6 float32 fields :" << endl;
176  cout << "number voxels axial ; number voxels transaxial ; number voxels depth ; size voxels axial ; size voxels transaxial ; size voxels depth" << endl;
177  cout << "... followed by number voxels depth*number voxels transaxial*number voxels axial float32 elements corresponding to the IDRF coefficients" << endl;
178 }
179 
180 // =====================================================================
181 // ---------------------------------------------------------------------
182 // ---------------------------------------------------------------------
183 // =====================================================================
184 
186 {
187  if(mp_pathToIDRFFiles==NULL)
188  {
189  Cerr("***** iProjectorIRIS::CheckSpecificParameters() -> path to IDRF files not initialized !" << endl);
190  return 1;
191  }
192  if(mp_IDRF==NULL || m2p_IDRF_CDFs==NULL)
193  {
194  Cerr("***** iProjectorIRIS::CheckSpecificParameters() -> IDRF coefficients not initialized !" << endl);
195  return 1;
196  }
197  if(m_nbLinesPerLOR<0)
198  {
199  Cerr("***** iProjectorIRIS::CheckSpecificParameters() -> number of lines per LOR not initialized !" << endl);
200  return 1;
201  }
203  {
204  Cerr("***** iProjectorIRIS::CheckSpecificParameters() -> number of voxels for the IDRF volumes not initialized !" << endl);
205  return 1;
206  }
208  {
209  Cerr("***** iProjectorIRIS::CheckSpecificParameters() -> voxel sizes of the IDRF volumes not initialized !" << endl);
210  return 1;
211  }
213  {
214  Cerr("***** iProjectorIRIS::CheckSpecificParameters() -> number of angles for the IDRF volumes not initialized !" << endl);
215  return 1;
216  }
218  {
219  Cerr("***** iProjectorIRIS::CheckSpecificParameters() -> step of the IDRF angles not initialized !" << endl);
220  return 1;
221  }
222  // Normal end
223  return 0;
224 }
225 
226 // =====================================================================
227 // ---------------------------------------------------------------------
228 // ---------------------------------------------------------------------
229 // =====================================================================
230 
232 {
233  // Verbose
234  if (m_verbose>=2) Cout("iProjectorIRIS::InitializeSpecific() -> Use IRIS projector" << endl);
235 
236  // Read IDRFs coefficients from the raw data files, and write them on a single vector
237  for(int a=0 ; a<m_nAlphaAnglesIDRF ; a++) // loop on alpha angles
238  for(int b=0 ; b<m_nBetaAnglesIDRF ; b++) // loop on beta angles
239  {
240  // Open & check file existence
241  ifstream IDRF_file(mp_pathToIDRFFiles[a*m_nBetaAnglesIDRF+b].c_str(), ios::binary| ios::in);
242  if (!IDRF_file.is_open())
243  {
244  Cerr("***** iProjectorIRIS::InitializeSpecific() -> Error reading the IRDF .vol file at the path: '" << mp_pathToIDRFFiles[a*m_nBetaAnglesIDRF+b] << "'" << endl);
245  return 1;
246  }
247  // Check file size consistency
248  IDRF_file.seekg(0, ios::end);
249  size_t size_in_bytes = IDRF_file.tellg();
250 
251  if(((size_t)((6+m_nVoxXYZIDRF)*sizeof(float))) != size_in_bytes)
252  {
253  Cerr("***** iProjectorIRIS::InitializeSpecific() -> Error : Size of the .vol " << mp_pathToIDRFFiles[a*m_nBetaAnglesIDRF+b] << " is not consistent with the information provided by the user configuration file!" << endl);
254  Cerr("***** iProjectorIRIS::InitializeSpecific() -> Expected size : "<< (6+m_nVoxXYZIDRF)*sizeof(float) << endl);
255  Cerr("***** iProjectorIRIS::InitializeSpecific() -> Actual size : "<< size_in_bytes << endl << endl);
256  return 1;
257  }
258  // Check header of the volume
259  float vol_file_header[6];
260  // Go back to first character
261  IDRF_file.seekg(0);
262  for(int i=0 ; i<6 ; i++)
263  IDRF_file.read((char*)&vol_file_header[i], sizeof(float));
264  // TODO : Take out header from .vol files. Just use the other checks and delete this condition
265  if ((int)vol_file_header[0]!=m_nVoxAxialIDRF ||
266  (int)vol_file_header[1]!=m_nVoxTransaxialIDRF ||
267  (int)vol_file_header[2]!=m_nVoxDepthIDRF ||
268  fabs(vol_file_header[3]-m_sizeVoxAxialIDRF) > 0.0001 ||
269  fabs(vol_file_header[4]-m_sizeVoxTransaxialIDRF) > 0.0001 ||
270  fabs(vol_file_header[5]-m_sizeVoxDepthIDRF) > 0.0001 )
271  {
272  Cerr("***** iProjectorIRIS::InitializeSpecific() -> Inconsistency between initialized and read header file in the .vol: '" << mp_pathToIDRFFiles[a*m_nBetaAnglesIDRF+b] << "'" << endl);
273  return 1;
274  }
275  // Read raw data
276  for(int i=0 ; i<m_nVoxXYZIDRF ; i++)
277  {
278  IDRF_file.read((char*)&vol_file_header[0], sizeof(float));
279  mp_IDRF[i] = vol_file_header[0];
280  }
281  if(ComputeIDRF_CDF(a*m_nBetaAnglesIDRF + b) )
282  {
283  Cerr("***** iProjectorIRIS::InitializeSpecific() -> An error occurs while computing the coefficient of the following IDRF volume: alpha " << a*m_stepAlphaAnglesIDRF << " beta " << b*m_stepBetaAnglesIDRF << endl);
284  return 1;
285  }
286  IDRF_file.close();
287  }
288 
289  // Normal end
290  return 0;
291 }
292 
293 // =====================================================================
294 // ---------------------------------------------------------------------
295 // ---------------------------------------------------------------------
296 // =====================================================================
297 
299 {
300  // Find the maximum number of voxels along a given dimension
301  INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
302  if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
303  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
304  // We should have at most 4 voxels in a given plane, so multiply by 4
305  // (note: this is not true however it ensures no overflow and is already quite optimized for RAM usage !)
306  max_nb_voxels_in_dimension *= 4;
307  // Finally multiply by the number of lines
308  max_nb_voxels_in_dimension *= ((INTNB)m_nbLinesPerLOR);
309  // Return the value
310  return max_nb_voxels_in_dimension;
311 }
312 
313 // =====================================================================
314 // ---------------------------------------------------------------------
315 // ---------------------------------------------------------------------
316 // =====================================================================
317 
318 int iProjectorIRIS::ProjectWithoutTOF(int a_direction, oProjectionLine* ap_ProjectionLine )
319 {
320  #ifdef CASTOR_DEBUG
321  if (!m_initialized)
322  {
323  Cerr("***** iProjectorIRIS::ProjectWithoutTOF() -> Called while not initialized !" << endl);
324  Exit(EXIT_DEBUG);
325  }
326  #endif
327 
328  #ifdef CASTOR_VERBOSE
329  if (m_verbose>=10)
330  {
331  string direction = "";
332  if (a_direction==FORWARD) direction = "forward";
333  else direction = "backward";
334  Cout("iProjectorIRIS::Project without TOF -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
335  }
336  #endif
337 
338  // Get central positions of the crystals
339  FLTNB* event1 = ap_ProjectionLine->GetPosition1();
340  FLTNB* event2 = ap_ProjectionLine->GetPosition2();
341 
342  // Local variables to recover crystal central positions and orientations
343  float x1, y1, z1, crystal_alpha1, x2, y2, z2, crystal_alpha2;
344 
345  x1 = event1[0];
346  y1 = event1[1];
347  z1 = event1[2];
348  x2 = event2[0];
349  y2 = event2[1];
350  z2 = event2[2];
351 
352  // Get transaxial orientations of the two crystals // CHECK HERE
353  FLTNB* orientation1 = ap_ProjectionLine->GetOrientation1();
354  FLTNB* orientation2 = ap_ProjectionLine->GetOrientation2();
355 
356  // Compute transaxial angular orientation of the crystals
357  crystal_alpha1 = atan2f(orientation1[0], orientation1[1]); //LUT_alpha[id1]; //HERE
358  crystal_alpha2 = atan2f(orientation2[0], orientation2[1]); //LUT_alpha[id2]; //HERE
359 
360  // -------
361  // Compute transaxial incident angles (alpha) between the LOR and the crystals
362  // -------
363 
364  float alpha1, alpha2, alpha_lor;
365 
366  // Compute transaxial incident angle of the LOR
367  alpha_lor = atan2f(x1-x2, y1-y2); // alpha_lor = atan2f( y1-y2 , x1-x2 ); //CHECK HERE
368 
369  // Compute incident angle on each crystal surfaces
370  alpha1 = remainderf(alpha_lor-M_PI-crystal_alpha1, M_PI);
371  if(alpha1>(M_PI/2.)) // Check if this operation and other surrounding should require double instead of float
372  alpha1 -= M_PI;
373 
374  alpha2 = remainderf(alpha_lor-M_PI-crystal_alpha2, M_PI);
375  if(alpha2>(M_PI/2.))
376  alpha2 -= M_PI;
377 
378  //alpha1 = remainderf(alpha_lor-Pi-crystal_alpha1, M_PI_2);
379  //alpha2 = remainderf(alpha_lor-crystal_alpha2, M_PI_2);
380 
381 
382 
383  // -------
384  // Compute axial incident angles (beta) between the LOR and the crystals
385  // -------
386 
387  float beta1, beta2, H;
388 
389  // Compute transaxial distance
390  H = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
391 
392  // Compute axial incident angle
393  beta1 = atan2f( (z1-z2), H );
394 
395  // Normalize with Pi
396  beta1 = remainderf(beta1, M_PI);
397  if(beta1>(M_PI/2.))
398  beta1 -= M_PI;
399 
400  beta2 = -beta1;
401  beta2 = remainderf(beta2, M_PI);
402  if(beta2>(M_PI/2.))
403  beta2 -= M_PI;
404 
405 
406  /*
407  float r_to_deg = 180./M_PI;
408 
409  //cout << "pos1 X: " << event1[0] << " Y: " << event1[1] << " Z: " << event1[2] << " vs Cry: " << alpha1*r_to_deg << " ang " << crystal_alpha1*r_to_deg << " bet " << beta1*r_to_deg << endl;
410  //cout << "pos2 X: " << event2[0] << " Y: " << event2[1] << " Z: " << event2[2] << " vs Cry: " << alpha2*r_to_deg << " ang " << crystal_alpha2*r_to_deg << " bet " << beta2*r_to_deg << endl;
411  cout << "pos1 X: " << event1[0] << " Y: " << event1[1] << " Z: " << event1[2] << " vs Cry: " << alpha1*r_to_deg << " ang " << crystal_alpha1*r_to_deg << endl;
412  cout << "pos2 X: " << event2[0] << " Y: " << event2[1] << " Z: " << event2[2] << " vs Cry: " << alpha2*r_to_deg << " ang " << crystal_alpha2*r_to_deg << endl;
413  cout << ap_ProjectionLine->GetIndex1() << " : " << ap_ProjectionLine->GetIndex2() << " : " << alpha_lor*r_to_deg << endl;
414 
415  if(ap_ProjectionLine->GetIndex2()== 17556)
416  {
417  int r;
418  scanf("%d" , &r);
419  }
420 
421  if(ap_ProjectionLine->GetIndex1() == 0
422  && ap_ProjectionLine->GetIndex2() == 50)
423  {
424  cout << endl;
425  cout << "pos1 X: " << event1[0] << " Y: " << event1[1] << " Z: " << event1[2] << " aTrs: " << alpha1*r_to_deg << " aAxl: " << beta1*r_to_deg << endl;
426  cout << "pos2 X: " << event2[0] << " Y: " << event2[1] << " Z: " << event2[2] << " aTrs: " << alpha2*r_to_deg << " aAxl: " << beta2*r_to_deg << endl;
427  }
428 
429 
430  if(ap_ProjectionLine->GetIndex1() == 0
431  && ap_ProjectionLine->GetIndex2() == 308)
432  {
433  cout << endl;
434  cout << "pos1 X: " << event1[0] << " Y: " << event1[1] << " Z: " << event1[2] << " aTrs: " << alpha1*r_to_deg << " aAxl: " << beta1*r_to_deg << endl;
435  cout << "pos2 X: " << event2[0] << " Y: " << event2[1] << " Z: " << event2[2] << " aTrs: " << alpha2*r_to_deg << " aAxl: " << beta2*r_to_deg << endl;
436  }
437 
438  if(ap_ProjectionLine->GetIndex1() == 0
439  && ap_ProjectionLine->GetIndex2() == 17298)
440  {
441  cout << endl;
442  cout << "pos1 X: " << event1[0] << " Y: " << event1[1] << " Z: " << event1[2] << " aTrs: " << alpha1*r_to_deg << " aAxl: " << beta1*r_to_deg << endl;
443  cout << "pos2 X: " << event2[0] << " Y: " << event2[1] << " Z: " << event2[2] << " aTrs: " << alpha2*r_to_deg << " aAxl: " << beta2*r_to_deg << endl;
444  }
445 
446  if(ap_ProjectionLine->GetIndex1() == 0
447  && ap_ProjectionLine->GetIndex2() == 17556)
448  {
449  cout << endl;
450  cout << "pos1 X: " << event1[0] << " Y: " << event1[1] << " Z: " << event1[2] << " aTrs: " << alpha1*r_to_deg << " aAxl: " << beta1*r_to_deg << endl;
451  cout << "pos2 X: " << event2[0] << " Y: " << event2[1] << " Z: " << event2[2] << " aTrs: " << alpha2*r_to_deg << " aAxl: " << beta2*r_to_deg << endl;
452  }
453  */
454 
455 
457  for (int i_line=0; i_line<m_nbLinesPerLOR; ++i_line)
458  {
459  // Generate two random points using the IDRFs.
460  float X1, Y1, Z1;
461  float X2, Y2, Z2;
462  float pos1[3], pos2[3];
463  if (GenerateIRISRdmPos(pos1, alpha1, beta1) )
464  {
465  Cerr("***** iProjectorIRIS::ProjectWithoutTOF() -> An error occurred while trying to generate random position with the IRIS projector" << endl);
466  return 1;
467  }
468 
469  X1 = pos1[0];
470  Y1 = pos1[1];
471  Z1 = pos1[2];
472 
473  if (GenerateIRISRdmPos(pos2, alpha2, beta2) )
474  {
475  Cerr("***** iProjectorIRIS::ProjectWithoutTOF() -> An error occurred while trying to generate random position with the IRIS projector" << endl);
476  return 1;
477  }
478 
479  X2 = pos2[0];
480  Y2 = pos2[1];
481  Z2 = pos2[2];
482 
483  // Move the random points in the frame of reference of the scanner.
484  // IRIS ref:
485  // X = depth
486  // Y = transaxial
487  // Z = axial
488 
489  float tmp_X, tmp_Y;
490  tmp_X = X1; tmp_Y = Y1;
491  X1 = x1 + tmp_X*orientation1[0]-tmp_Y*orientation1[1]; //HERE
492  Y1 = y1 + tmp_X*orientation1[1]+tmp_Y*orientation1[0]; //HERE
493  Z1 += z1;
494  /*
495  cout << "pos1 X: " << event1[0] << " Y: " << event1[1] << " Z: " << event1[2] << endl ;
496  cout << orientation1[0] << " ; " << orientation1[1] << endl;
497  cout << "tmp_X1: " << tmp_X << " tmp_Y1 " << tmp_Y << " --- X1: " << X1 << " Y1 " << Y1 << endl;
498  */
499 
500  tmp_X = X2; tmp_Y = Y2;
501  X2 = x2 + tmp_X*orientation2[0]-tmp_Y*orientation2[1]; //HERE
502  Y2 = y2 + tmp_X*orientation2[1]+tmp_Y*orientation2[0]; //HERE
503  Z2 += z2;
504 
505 /*
506  cout << "pos2 X: " << event2[0] << " Y: " << event2[1] << " Z: " << event2[2] << endl;
507  cout << orientation2[0] << " ; " << orientation2[1] << endl;
508  cout << "tmp_X2: " << tmp_X << " tmp_Y2 " << tmp_Y << " --- X2: " << X2 << " Y2 " << Y2 << endl;
509  int r;
510  scanf("%d", &r);*/
511 
512  // SIDDON ALGORITHM
513 
514  // **************************************
515  // STEP 1: LOR length calculation
516  // **************************************
517  double length_LOR = ap_ProjectionLine->GetLength();
518 
519  // **************************************
520  // STEP 2: Compute entrance voxel indexes
521  // **************************************
522  double alphaFirst[] = { 0.0, 0.0, 0.0 };
523  double alphaLast[] = { 0.0, 0.0, 0.0 };
524 
525  double alphaMin = 0.0f, alphaMax = 1.0f;
526  double delta_pos[] = {
527  X2 - X1, // event2[ 0 ] - event1[ 0 ]
528  Y2 - Y1, // event2[ 1 ] - event1[ 1 ]
529  Z2 - Z1 // event2[ 2 ] - event1[ 2 ]
530  };
531 
532  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
533  for( int i = 0; i < 3; ++i )
534  {
535  if( delta_pos[ i ] != 0.0 )
536  {
537  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
538  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
539  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
540  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
541  }
542  }
543 
544  // if alphaMax is less than or equal to alphaMin no intersection
545  // and return an empty buffer
546  if( alphaMax <= alphaMin ) return 0;
547 
548  // Now we have to find the indices of the particular plane
549  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
550  int iMin = 0, iMax = 0;
551  int jMin = 0, jMax = 0;
552  int kMin = 0, kMax = 0;
553 
554  // For the X-axis
555  if( delta_pos[ 0 ] > 0.0f )
556  {
557  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
558  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
559  }
560  else if( delta_pos[ 0 ] < 0.0f )
561  {
562  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
563  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
564  }
565  if( delta_pos[ 0 ] == 0 )
566  {
567  iMin = 1, iMax = 0;
568  }
569 
570  // For the Y-axis
571  if( delta_pos[ 1 ] > 0 )
572  {
573  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
574  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
575  }
576  else if( delta_pos[ 1 ] < 0 )
577  {
578  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
579  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
580  }
581  else if( delta_pos[ 1 ] == 0 )
582  {
583  jMin = 1, jMax = 0;
584  }
585 
586  // For the Z-axis
587  if( delta_pos[ 2 ] > 0 )
588  {
589  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
590  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
591  }
592  else if( delta_pos[ 2 ] < 0 )
593  {
594  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
595  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
596  }
597  else if( delta_pos[ 2 ] == 0 )
598  {
599  kMin = 1, kMax = 0;
600  }
601 
602  // Computing the last term n number of intersection
603  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
604  + ( kMax - kMin + 1 );
605 
606  // Array storing the first alpha in X, Y and Z
607  double alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
608 
609  // Computing the first alpha in X
610  if( delta_pos[ 0 ] > 0 )
611  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
612  else if( delta_pos[ 0 ] < 0 )
613  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
614 
615  // Computing the first alpha in Y
616  if( delta_pos[ 1 ] > 0 )
617  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
618  else if( delta_pos[ 1 ] < 0 )
619  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
620 
621  // Computing the first alpha in Z
622  if( delta_pos[ 2 ] > 0 )
623  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
624  else if( delta_pos[ 2 ] < 0 )
625  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
626 
627  // Computing the alpha updating
628  double alpha_u[ 3 ] = {
629  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
630  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
631  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
632  };
633 
634  // Computing the index updating
635  INTNB index_u[ 3 ] = {
636  (delta_pos[ 0 ] < 0) ? -1 : 1,
637  (delta_pos[ 1 ] < 0) ? -1 : 1,
638  (delta_pos[ 2 ] < 0) ? -1 : 1
639  };
640 
641  // Check which alpha is the min/max and increment
642  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
643  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
644  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
645 
646  // Computing the minimum value in the alpha_XYZ buffer
647  double const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ],
648  (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
649 
650  // Computing the first index of intersection
651  double const alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
652  INTNB index_ijk[ 3 ] = {
653  1 + (int)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
654  1 + (int)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
655  1 + (int)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
656  };
657 
658  INTNB const w = mp_nbVox[ 0 ];
659  INTNB const wh = w * mp_nbVox[ 1 ];
660 
661  // Loop over the number of plane to cross
662  double alpha_c = alphaMin;
663  FLTNB coeff = 0.0f;
664  INTNB numVox = 0;
665  for( int nP = 0; nP < n - 1; ++nP )
666  {
667  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
668  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
669  {
670  // Storing values
671  if( ( alpha_XYZ[ 0 ] >= alphaMin )
672  && ( index_ijk[ 0 ] - 1 >= 0 )
673  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
674  && ( index_ijk[ 1 ] - 1 >= 0 )
675  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
676  && ( index_ijk[ 2 ] - 1 >= 0 )
677  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
678  {
679  coeff = ( alpha_XYZ[ 0 ] - alpha_c ) * length_LOR;
680  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
681  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff/m_nbLinesPerLOR); // TODO Normalizations according to the number of lines are performed in these lines. Perhaps we should do this at a upper level (vProjector)
682  }
683 
684  // Increment values
685  alpha_c = alpha_XYZ[ 0 ];
686  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
687  index_ijk[ 0 ] += index_u[ 0 ];
688  }
689  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
690  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
691  {
692  // Storing values
693  if( ( alpha_XYZ[ 1 ] >= alphaMin )
694  && ( index_ijk[ 0 ] - 1 >= 0 )
695  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
696  && ( index_ijk[ 1 ] - 1 >= 0 )
697  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
698  && ( index_ijk[ 2 ] - 1 >= 0 )
699  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
700  {
701  coeff = ( alpha_XYZ[ 1 ] - alpha_c ) * length_LOR;
702  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
703  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff/m_nbLinesPerLOR);
704  }
705 
706  // Increment values
707  alpha_c = alpha_XYZ[ 1 ];
708  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
709  index_ijk[ 1 ] += index_u[ 1 ];
710  }
711  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
712  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
713  {
714  // Storing values
715  if( ( alpha_XYZ[ 2 ] >= alphaMin )
716  && ( index_ijk[ 0 ] - 1 >= 0 )
717  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
718  && ( index_ijk[ 1 ] - 1 >= 0 )
719  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
720  && ( index_ijk[ 2 ] - 1 >= 0 )
721  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
722  {
723  coeff = ( alpha_XYZ[ 2 ] - alpha_c ) * length_LOR;
724  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
725  ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff/m_nbLinesPerLOR);
726  }
727 
728  // Increment values
729  alpha_c = alpha_XYZ[ 2 ];
730  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
731  index_ijk[ 2 ] += index_u[ 2 ];
732  }
733  }
734 
735  }
736 
737  return 0;
738 }
739 
740 // =====================================================================
741 // ---------------------------------------------------------------------
742 // ---------------------------------------------------------------------
743 // =====================================================================
744 
745 int iProjectorIRIS::ProjectWithTOFPos(int a_Projector, oProjectionLine* ap_ProjectionLine)
746 {
747  Cerr("***** iProjectorIRIS::ProjectWithTOFPos() -> Not yet implemented !" << endl);
748  return 1;
749 }
750 
751 // =====================================================================
752 // ---------------------------------------------------------------------
753 // ---------------------------------------------------------------------
754 // =====================================================================
755 
756 int iProjectorIRIS::ProjectWithTOFBin(int a_Projector, oProjectionLine* ap_ProjectionLine)
757 {
758  Cerr("***** iProjectorIRIS::ProjectWithTOFBin() -> Not yet implemented !" << endl);
759  return 1;
760 }
761 
762 // =====================================================================
763 // ---------------------------------------------------------------------
764 // ---------------------------------------------------------------------
765 // =====================================================================
766 
768 {
769  // Compute cumulated sum
770  m2p_IDRF_CDFs[a_angleId][0] = mp_IDRF[0];
771 
772  for(int i = 1; i<m_nVoxXYZIDRF; ++i)
773  m2p_IDRF_CDFs[a_angleId][i] = m2p_IDRF_CDFs[a_angleId][i-1] + mp_IDRF[i];
774 
775  // Normalization
776  if(m2p_IDRF_CDFs[a_angleId][m_nVoxXYZIDRF-1] > 0) // check if not null
777  for(int i = 0; i<m_nVoxXYZIDRF; ++i)
778  m2p_IDRF_CDFs[a_angleId][i] /= m2p_IDRF_CDFs[a_angleId][m_nVoxXYZIDRF-1];
779 
780  return 0;
781 }
782 
783 // =====================================================================
784 // ---------------------------------------------------------------------
785 // ---------------------------------------------------------------------
786 // =====================================================================
787 
788 int iProjectorIRIS::GenerateIRISRdmPos(float ap_generatedPos[3], float a_alpha, float a_beta)
789 {
791 
792  float rdm_pos[3];
793 
794  int alpha_id = (int) (fabsf(a_alpha)/m_stepAlphaAnglesIDRF);
795  int beta_id = (int) (fabsf(a_beta) /m_stepBetaAnglesIDRF);
796 
797  alpha_id = min(alpha_id, m_nAlphaAnglesIDRF-1);
798  beta_id = min(beta_id , m_nBetaAnglesIDRF -1);
799 
800  int id = FindGreaterValue(m2p_IDRF_CDFs[alpha_id*m_nBetaAnglesIDRF+beta_id], p_RNG->GenerateRdmNber(), m_nVoxXYZIDRF);
801 
802  // IDRFs indices sorting are axial/transaxial/DOI, DOI_id being the smallest index.
803  int axial_id = id / (m_nVoxDepthIDRF*m_nVoxTransaxialIDRF);
804  int trans_id = (id-axial_id*m_nVoxTransaxialIDRF*m_nVoxDepthIDRF) / m_nVoxDepthIDRF;
805  int DOI_id = id-axial_id*m_nVoxTransaxialIDRF*m_nVoxDepthIDRF - trans_id*m_nVoxDepthIDRF;
806 
807  // Use random number on each axis to generate a random position on the IDRF voxel
808  rdm_pos[0] = (DOI_id + p_RNG->GenerateRdmNber() - m_nVoxDepthIDRF/2.0f )*m_sizeVoxDepthIDRF; // CHECK : should choose surface central position by default when using IRIS, or adress the difference here
809  rdm_pos[1] = (trans_id + p_RNG->GenerateRdmNber() - m_nVoxTransaxialIDRF/2.0f )*m_sizeVoxTransaxialIDRF;
810  rdm_pos[2] = (axial_id + p_RNG->GenerateRdmNber() - m_nVoxAxialIDRF/2.0f )*m_sizeVoxAxialIDRF;
811 
812  if(a_alpha<0) rdm_pos[1] = -rdm_pos[1];
813  if(a_beta<0) rdm_pos[2] = -rdm_pos[2];
814 
815  ap_generatedPos[0] = rdm_pos[0];
816  ap_generatedPos[1] = rdm_pos[1];
817  ap_generatedPos[2] = rdm_pos[2];
818 
819  return 0;
820 }
821 
822 // =====================================================================
823 // ---------------------------------------------------------------------
824 // ---------------------------------------------------------------------
825 // =====================================================================
826 
827 int iProjectorIRIS::FindGreaterValue(float *ap_val, float a_key, int a_maxValue, int a_minStart, int a_maxStart)
828 {
829  int min = a_minStart, max, mid;
830 
831  max = a_maxStart ? a_maxStart : a_maxValue;
832 
833  int cpt=0;
834 
835  while (min < max)
836  {
837  mid = (min + max) >> 1; //(min+max)/2
838  if (a_key > ap_val[mid])
839  {
840  min = mid + 1;
841  }
842  else
843  {
844  max = mid;
845  }
846 
847  cpt++;
848  }
849 
850  return min;
851 }
bool m_compatibleWithSPECTAttenuationCorrection
Definition: vProjector.hh:317
static sRandomNumberGenerator * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
FLTNB * GetOrientation1()
This function is used to get the pointer to the mp_orientation1 (3-values tab).
FLTNB mp_sizeVox[3]
Definition: vProjector.hh:301
INTNB mp_nbVox[3]
Definition: vProjector.hh:302
#define FLTNB
Definition: gVariables.hh:55
double GenerateRdmNber()
Generate a random number for the thread whose index is recovered from the ompenMP function...
string * mp_pathToIDRFFiles
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Definition: vProjector.hh:307
This class is designed to generically described any on-the-fly projector.
Definition: vProjector.hh:54
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project without TOF.
FLTNB GetLength()
This function is used to get the length of the line.
FLTNB * GetOrientation2()
This function is used to get the pointer to the mp_orientation2 (3-values tab).
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child projector.
void ShowHelpSpecific()
A function used to show help about the child module.
void Exit(int code)
iProjectorIRIS()
The constructor of iProjectorIRIS.
#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
int FindGreaterValue(float *ap_val, float a_key, int a_maxValue, int a_minStart=0, int a_maxStart=0)
Find in the array ap_val (arranged in ascending order) the index of the first element greater than va...
FLTNB m_stepAlphaAnglesIDRF
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 ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
Definition: gOptions.cc:111
int ProjectWithTOFBin(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF binned information.
Declaration of class iProjectorIRIS.
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
Singleton class that generate a thread-safe random generator number for openMP As singleton...
#define INTNB
Definition: gVariables.hh:64
This class is designed to manage and store system matrix elements associated to a vEvent...
~iProjectorIRIS()
The destructor of iProjectorIRIS.
Declaration of class sOutputManager.
int ComputeIDRF_CDF(int a_angleId)
Compute the IDRFs coefficients (arrange the IDRFs coefficients in ascending orders, and normalize).
INTNB EstimateMaxNumberOfVoxelsPerLine()
This function is used to compute and provide an estimate of the maximum number of voxels that could c...
FLTNB m_sizeVoxTransaxialIDRF
FLTNB m_stepBetaAnglesIDRF
int GenerateIRISRdmPos(float ap_generatedPos[3], float a_alpha, float a_beta)
Generate a random point using the IDRF that correspond to the (alpha, beta) incident angle...
float ** m2p_IDRF_CDFs
FLTNB mp_halfFOV[3]
Definition: vProjector.hh:304
#define EXIT_DEBUG
Definition: gVariables.hh:69
int ProjectWithTOFPos(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF continuous information.
FLTNB * GetPosition2()
This function is used to get the pointer to the mp_position2 (3-values tab).
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
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 InitializeSpecific()
This function is used to initialize specific stuff to the child projector.
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.