CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
code/src/scanner/iScannerCT.cc
Go to the documentation of this file.
1 
8 #include "iScannerCT.hh"
9 #include "sOutputManager.hh"
10 #include "sScannerManager.hh"
11 
12 // =====================================================================
13 // ---------------------------------------------------------------------
14 // ---------------------------------------------------------------------
15 // =====================================================================
16 
18 {
19  // Set member variables to default values
21  m_nbPixels = -1;
22  m_nbPixelsTrans = 0;
23  m_pixelsSizeTrans = -1.;
24  m_gapSizeTrans = -1.;
25  m_nbPixelsAxial = 0;
26  m_pixelsSizeAxial = -1.;
27  m_gapSizeAxial = -1.;
29  mp_projectionAngles = NULL;
33  m_detectorDepth = -1.;
34  m_spotSizeWidth = -1.;
35  m_spotSizeDepth = -1.;
39  mp_crystalOrientationX = NULL;
40  mp_crystalOrientationY = NULL;
41  mp_crystalOrientationZ = NULL;
42  mp_sourcePositionX = NULL;
43  mp_sourcePositionY = NULL;
44  mp_sourcePositionZ = NULL;
45 }
46 
47 // =====================================================================
48 // ---------------------------------------------------------------------
49 // ---------------------------------------------------------------------
50 // =====================================================================
51 
53 {
64 }
65 
66 
67 
68 
69 // =====================================================================
70 // ---------------------------------------------------------------------
71 // ---------------------------------------------------------------------
72 // =====================================================================
78 {
80  if (m_verbose==0) return;
81 
82  // Describe the scanner
83  Cout("iScannerCT::DescribeSpecific() -> Here is some specific content of the CT scanner" << endl);
84  Cout(" --> Total number of pixels: " << m_nbPixels << endl);
85  Cout(" --> Total number of projections: " << m_nbOfProjections << endl);
86  if( mp_projectionAngles )
87  {
88  // Display ten by ten
89  Cout(" --> Projection angles: " << endl);
90  if(m_nbOfProjections > 10)
91  {
92  int pr=0;
93  while ( pr<m_nbOfProjections-1 )
94  {
95  for (int p=0 ; p<10 ; p++)
96  {
97  if(pr == m_nbOfProjections)
98  break;
99  Cout(mp_projectionAngles[pr] << ", ");
100  pr++;
101  }
102  Cout(endl);
103  }
104  }
105  else
106  {
107  for (int p=0 ; p<m_nbOfProjections-1 ; p++)
108  Cout(mp_projectionAngles[p] << ", ");
109  Cout(mp_projectionAngles[m_nbOfProjections-1] << endl);
110  }
111  }
112 
113  Cout(" --> Distance between the center of rotation and the detector surface: " << m_CORtoDetectorDistance << endl);
114  Cout(" --> Distance between the center of rotation and the source surface: " << m_CORtoSourceDistance << endl);
115 
116  Cout(" --> Total number of transaxial pixels as defined in the system file: " << m_nbPixelsTrans << endl);
117  Cout(" --> Size of transaxial pixels as defined in the system file: " << m_pixelsSizeTrans << endl);
118  Cout(" --> Gap size between each transaxial pixe as defined in the system file: " << m_gapSizeTrans << endl);
119  Cout(" --> Total number of axial pixels as defined in the system file: " << m_nbPixelsAxial << endl);
120  Cout(" --> Size of axial pixels as defined in the system file: " << m_pixelsSizeAxial << endl);
121  Cout(" --> Gap size between each axial pixel as defined in the system file: " << m_gapSizeAxial << endl);
122  Cout(" --> Depth of detection pixels: " << m_detectorDepth << endl);
123  Cout(" --> Width of the source, along the direction tangential to the scanner radius: " << m_spotSizeWidth << endl);
124  Cout(" --> Depth of the source, along the direction of the scanner radius: " << m_spotSizeDepth << endl);
125 }
126 
127 
128 
129 // =====================================================================
130 // ---------------------------------------------------------------------
131 // ---------------------------------------------------------------------
132 // =====================================================================
133 
134 int iScannerCT::Instantiate(bool a_scannerFileIsLUT)
135 {
137 
138  // Verbose
139  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerCT::Instantiate() -> Create scanner structure and read parameters from configuration file" << endl);
140 
141  // Get scanner manager
142  sScannerManager* p_scannerManager = sScannerManager::GetInstance();
143 
144  // Detector dimensions
145  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans number of pixels", &m_nbPixelsTrans, 1, KEYWORD_MANDATORY))
146  {
147  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial number of pixels !" << endl);
148  return 1;
149  }
150  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans pixel size", &m_pixelsSizeTrans, 1, KEYWORD_MANDATORY))
151  {
152  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial pixel size !" << endl);
153  return 1;
154  }
155  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial number of pixels", &m_nbPixelsAxial, 1, KEYWORD_MANDATORY))
156  {
157  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial number of pixels !" << endl);
158  return 1;
159  }
160  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial pixel size", &m_pixelsSizeAxial, 1, KEYWORD_MANDATORY))
161  {
162  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial pixel size !" << endl);
163  return 1;
164  }
165  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "detector depth", &m_detectorDepth, 1, KEYWORD_MANDATORY))
166  {
167  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read detector depth !" << endl);
168  return 1;
169  }
170  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "detector radius", &m_CORtoDetectorDistance, 1, KEYWORD_MANDATORY))
171  {
172  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read detector to COR distance as \"detector radius\" !" << endl);
173  return 1;
174  }
175  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "source radius", &m_CORtoSourceDistance, 1, KEYWORD_MANDATORY))
176  {
177  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read source to COR distance as \"source radius\" !" << endl);
178  return 1;
179  }
180  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "spot size width", &m_spotSizeWidth, 1, KEYWORD_MANDATORY))
181  {
182  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the spot size width !" << endl);
183  return 1;
184  }
185  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "spot size height", &m_spotSizeDepth, 1, KEYWORD_MANDATORY))
186  {
187  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the spot size height !" << endl);
188  return 1;
189  }
190 
191  // Bed displacement
193  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "multiple bed displacement", &m_defaultBedDisplacementInMm, 1, KEYWORD_OPTIONAL) == 1)
194  {
195  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the multiple bed displacement in the scanner header file !" << endl);
196  return 1;
197  }
198 
199  // End
200  return 0;
201 }
202 
203 // =====================================================================
204 // ---------------------------------------------------------------------
205 // ---------------------------------------------------------------------
206 // =====================================================================
207 
208 int iScannerCT::BuildLUT(bool a_scannerFileIsLUT)
209 {
211 
212  // Verbose
213  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerCT::BuildLUT -> Build LUT for scanner elements coordinates and orientations"<< endl);
214 
215  // Initialize nb_pixels
217 
218  // Allocate LUT structures
228 
229  // Either generate the LUT from a generic file, or load the user precomputed LUT
230  if (!a_scannerFileIsLUT)
231  {
232  if (ComputeLUT())
233  {
234  Cerr("***** iScannerCT::BuildLUT() -> A problem occurred while generating scanner LUT !" << endl);
235  return 1;
236  }
237  }
238  else
239  {
240  if (LoadLUT())
241  {
242  Cerr("***** iScannerCT::BuildLUT() -> A problem occurred while loading scanner LUT !" << endl);
243  return 1;
244  }
245  }
246 
247  // End
248  return 0;
249 }
250 
251 // =====================================================================
252 // ---------------------------------------------------------------------
253 // ---------------------------------------------------------------------
254 // =====================================================================
255 
257 {
259 
260  // Check if all parameters have been correctly initialized. Return error otherwise
261  if (m_nbPixels<0)
262  {
263  Cerr("***** iScannerCT::CheckParameters()-> Number of crystals has not been initialized !" <<endl);
264  return 1;
265  }
266  if (m_nbOfProjections<=0)
267  {
268  Cerr("***** iScannerCT::CheckParameters()-> Number of projection angles has not been initialized !" <<endl);
269  return 1;
270  }
271  if (m_nbPixelsTrans<=0 || m_nbPixelsAxial<=0)
272  {
273  Cerr("***** iScannerCT::CheckParameters()-> Number of transaxial/axial pixels have not correctly been initialized ! (should be >0)" <<endl);
274  return 1;
275  }
277  {
278  Cerr("***** iScannerCT::CheckParameters()-> Transaxial/axial pixel sizes have not correctly been initialized ! (should be >0)" <<endl);
279  return 1;
280  }
281  if (m_gapSizeTrans<0. || m_gapSizeAxial<0.)
282  {
283  Cerr("***** iScannerCT::CheckParameters()-> Transaxial/axial pixel gap sizes have not correctly been initialized ! (should be >0)" <<endl);
284  return 1;
285  }
286  if (m_detectorDepth<=0)
287  {
288  Cerr("***** iScannerCT::CheckParameters()-> Crystal depth has not correctly been initialized ! (should be >0)" <<endl);
289  return 1;
290  }
291  if (mp_projectionAngles == NULL)
292  {
293  Cerr("***** iScannerCT::CheckParameters()-> Projection angles have not correctly been initialized !" <<endl);
294  return 1;
295  }
296  if (m_CORtoDetectorDistance<=0.)
297  {
298  Cerr("***** iScannerCT::CheckParameters()-> Distance between center of rotation and detector surface has not correctly been initialized !" <<endl);
299  return 1;
300  }
301  if (m_CORtoSourceDistance<=0.)
302  {
303  Cerr("***** iScannerCT::CheckParameters()-> Distance between center of rotation and source surface has not correctly been initialized !" <<endl);
304  return 1;
305  }
306  if (mp_crystalCentralPositionX == NULL ||
307  mp_crystalCentralPositionY == NULL ||
309  {
310  Cerr("***** iScannerCT::CheckParameters()-> LUT elements (crystal central positions) have not correctly been initialized !" <<endl);
311  return 1;
312  }
313  if (mp_crystalOrientationX == NULL ||
314  mp_crystalOrientationY == NULL ||
315  mp_crystalOrientationZ == NULL )
316  {
317  Cerr("***** iScannerCT::CheckParameters()-> LUT elements (crystal orientations) have not correctly been initialized !" <<endl);
318  return 1;
319  }
320  if (mp_sourcePositionX == NULL ||
321  mp_sourcePositionY == NULL ||
322  mp_sourcePositionZ == NULL )
323  {
324  Cerr("***** iScannerCT::CheckParameters()-> LUT elements (crystal focal positions) have not correctly been initialized !" <<endl);
325  return 1;
326  }
327 
328  // End
329  m_allParametersChecked = true;
330  return 0;
331 }
332 
333 // =====================================================================
334 // ---------------------------------------------------------------------
335 // ---------------------------------------------------------------------
336 // =====================================================================
337 
339 {
341 
342  // Verbose
343  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerCT::Initialize() -> Initialize remaining stuff for scanner to be ready"<< endl);
344 
345  // Parameters checked ?
347  {
348  Cerr("***** iScannerCT::Initialize() -> Parameters have not been checked !" << endl);
349  return 1;
350  }
351 
352  // Any initialization
353 
354  return 0;
355 }
356 
357 // =====================================================================
358 // ---------------------------------------------------------------------
359 // ---------------------------------------------------------------------
360 // =====================================================================
361 
363 {
365  // This has not been designed yet
366  Cerr("iScannerCT::LoadLUT() -> Not yet implemented !" << endl);
367  return 1;
368 }
369 
370 // =====================================================================
371 // ---------------------------------------------------------------------
372 // ---------------------------------------------------------------------
373 // =====================================================================
374 
376 {
378 
379  if(m_verbose>=VERBOSE_DETAIL) Cout("iScannerCT::ComputeLUT() -> Start LUT generation" << endl);
380 
381  // Use multi-threading here as the rotation of the pixels and source can take some times if many projection angles are used
382  int nb_threads = 1;
383  if (mp_ID!=NULL) nb_threads = mp_ID->GetNbThreadsMax();
384  #ifdef CASTOR_OMP
385  omp_set_num_threads(nb_threads);
386  #endif
387 
388  // ============================================================================================================
389  // Generate the LUT
390  // ============================================================================================================
391 
392  // oMatrix crystal_center_ref will be used to gather cartesian positions of the crystals center (on the surface of the crystals)
393  // in the reference head (directly above isocenter = +y). Same for the source reference position but below the isocenter ;-)
394  oMatrix** crystal_center_ref = new oMatrix *[m_nbPixels];
395  for (int c = 0; c<m_nbPixels; c++)
396  crystal_center_ref[c] = new oMatrix(3,1);
397  oMatrix* source_center_ref = new oMatrix(3,1);
398  // oMatrix crystal_center_out will be used to recover positions of each crystal after each rotation (each projection angles).
399  // Same for the source_center_out
400  oMatrix** crystal_center_out = new oMatrix *[nb_threads];
401  oMatrix** source_center_out = new oMatrix *[nb_threads];
402  for (int th=0; th<nb_threads; th++)
403  {
404  crystal_center_out[th] = new oMatrix(3,1);
405  source_center_out[th] = new oMatrix(3,1);
406  }
407 
408  // Generation of the rotation matrix allowing to compute the position of all the projections.
409  oMatrix** rotation_mtx = new oMatrix*[m_nbOfProjections];
410  for(int i=0; i<m_nbOfProjections; i++)
411  {
412  rotation_mtx[i] = new oMatrix(3,3);
413  rotation_mtx[i]->SetMatriceElt(0,0, cos(mp_projectionAngles[i] * M_PI/180.) );
414  rotation_mtx[i]->SetMatriceElt(1,0, sin(mp_projectionAngles[i] * M_PI/180.) );
415  rotation_mtx[i]->SetMatriceElt(2,0,0);
416  rotation_mtx[i]->SetMatriceElt(0,1, -sin(mp_projectionAngles[i] * M_PI/180.) );
417  rotation_mtx[i]->SetMatriceElt(1,1, cos(mp_projectionAngles[i] * M_PI/180.) );
418  rotation_mtx[i]->SetMatriceElt(2,1,0);
419  rotation_mtx[i]->SetMatriceElt(0,2,0);
420  rotation_mtx[i]->SetMatriceElt(1,2,0);
421  rotation_mtx[i]->SetMatriceElt(2,2,1);
422  }
423 
424  // Recover the trans/axial gap size from the geom file
425  sScannerManager* p_scannerManager = sScannerManager::GetInstance();
426  if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans gap size", &m_gapSizeTrans, 1, KEYWORD_MANDATORY))
427  {
428  Cerr("***** iScannerCT::ComputeLUT() -> An error occurred while trying to read the transaxial gap size !" << endl);
429  return 1;
430  }
431 
432  if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial gap size", &m_gapSizeAxial, 1, KEYWORD_MANDATORY))
433  {
434  Cerr("***** iScannerCT::ComputeLUT() -> An error occurred while trying to read the axial gap size !" << endl);
435  return 1;
436  }
437 
438  // Generate cartesian coordinates of the pixels centers for the CT at the reference position (directly above isocenter = +y)
441  for(uint32_t jj = 0; jj < m_nbPixelsAxial; ++jj )
442  {
443  FLTNB Zcryst = z_start + jj * (m_pixelsSizeAxial + m_gapSizeAxial);
444  for(uint32_t ii = 0; ii < m_nbPixelsTrans; ++ ii )
445  {
446  FLTNB Xcryst = x_start + ii * (m_pixelsSizeTrans + m_gapSizeTrans);
447  crystal_center_ref[ii + m_nbPixelsTrans * jj]->SetMatriceElt(0,0,Xcryst);
448  // Set y-position of the crystal reference matrix according to the distance between CT detector and center of rotation of the scanner (m_CORtoDetectorDistance)
449  crystal_center_ref[ii + m_nbPixelsTrans * jj]->SetMatriceElt(1,0,m_CORtoDetectorDistance);
450  crystal_center_ref[ii + m_nbPixelsTrans * jj]->SetMatriceElt(2,0,Zcryst);
451  }
452  }
453  // Generate cartesian coordinates of the source for the CT at the reference position (directly above isocenter = +y)
454  source_center_ref->SetMatriceElt(0,0,0.);
455  // Set y-position of the source reference matrix according to the distance between CT source and center of rotation of the scanner (m_CORtoSourceDistance)
456  source_center_ref->SetMatriceElt(1,0,-m_CORtoSourceDistance);
457  source_center_ref->SetMatriceElt(2,0,0.); // The source is supposed to be centered along the Z axis
458 
459  // ============================================================================================================
460  // Loop over all the projection angles
461  // Positions of the scanner elements are progressively stored in the LUT file.
462  // ============================================================================================================
463  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Generate positions for each projection angle"<< endl);
464 
465  // Multi-threading loop over projection angles to compute the LUT
466  int a;
467  #pragma omp parallel for private(a) schedule(guided)
468  for (a=0 ; a<m_nbOfProjections ; a++)
469  {
470  // Get the thread index
471  int th = 0;
472  #ifdef CASTOR_OMP
473  th = omp_get_thread_num();
474  #endif
475  // Get surface crystal position (and not center crystal positions for SPECT), and set the reference crystal matrix
476  for (int c=0 ; c < m_nbPixels ; c++)
477  {
478  // crystal indexation
479  int cryID = a*m_nbPixels + c;
480 
481  // Compute rotation of the crystal center position from the reference and the rotation matrix associated to the projection
482  rotation_mtx[a]->Multiplication(crystal_center_ref[c], crystal_center_out[th]);
483  // Store crystal surface positions
484  mp_crystalCentralPositionX[cryID] = crystal_center_out[th]->GetMatriceElt(0,0);
485  mp_crystalCentralPositionY[cryID] = crystal_center_out[th]->GetMatriceElt(1,0);
486  mp_crystalCentralPositionZ[cryID] = crystal_center_out[th]->GetMatriceElt(2,0);
487  // Store crystal orientations
488  mp_crystalOrientationX[cryID] = rotation_mtx[a]->GetMatriceElt(0,1);
489  mp_crystalOrientationY[cryID] = rotation_mtx[a]->GetMatriceElt(0,0);
490  mp_crystalOrientationZ[cryID] = 0;
491  }
492  // Compute rotation of the source center position from the reference and the rotation matrix associated to the projection
493  rotation_mtx[a]->Multiplication(source_center_ref, source_center_out[th]);
494  // Store crystal surface positions
495  mp_sourcePositionX[a] = source_center_out[th]->GetMatriceElt(0,0);
496  mp_sourcePositionY[a] = source_center_out[th]->GetMatriceElt(1,0);
497  mp_sourcePositionZ[a] = source_center_out[th]->GetMatriceElt(2,0);
498  }
499 
500  for(int i=0; i<m_nbOfProjections; i++)
501  delete rotation_mtx[i];
502  delete[] rotation_mtx;
503  for (int c=0; c<m_nbPixels; c++)
504  delete crystal_center_ref[c];
505  delete[] crystal_center_ref;
506  delete source_center_ref;
507  for (int th=0; th<nb_threads; th++)
508  {
509  delete crystal_center_out[th];
510  delete source_center_out[th];
511  }
512  delete[] crystal_center_out;
513  delete[] source_center_out;
514 
515  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> LUT generation completed" << endl);
516 
517  return 0;
518 }
519 
520 // =====================================================================
521 // ---------------------------------------------------------------------
522 // ---------------------------------------------------------------------
523 // =====================================================================
524 
525 int iScannerCT::GetPositionsAndOrientations( int a_index1, int a_index2,
526  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
527  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3],
528  FLTNB* ap_POI1, FLTNB* ap_POI2 )
529 {
531 
532  // CT indices : 1st for the projection, 2nd for the pixel
533 
534  // First check projection angle existency
535  if (a_index1<0 || a_index1>=m_nbOfProjections)
536  {
537  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> Projection index (" << a_index1 << ") out of range [0:" << m_nbOfProjections-1 << "] !" << endl);
538  return 1;
539  }
540 
541  // Second check pixels existency
542  if (a_index2<0 || a_index2>=m_nbPixels)
543  {
544  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> Pixel index (" << a_index2 << ") out of range [0:" << m_nbPixels-1 << "] !" << endl);
545  return 1;
546  }
547 
548  // Get crystal index related to the projection
549  int index = a_index1*m_nbPixels + a_index2;
550 
551  // Get position of the source
552  ap_Position1[0] = mp_sourcePositionX[a_index1];
553  ap_Position1[1] = mp_sourcePositionY[a_index1];
554  ap_Position1[2] = mp_sourcePositionZ[a_index1];
555 
556  // todo : Due to the current implementation of CT projection, POI
557  // and DOI are not handled and ignored.
558  // An error is returned if POI are provided
559 
560  // Case when POI is not provided
561  if (ap_POI2==NULL)
562  {
563  ap_Position2[0] = mp_crystalCentralPositionX[index];
564  ap_Position2[1] = mp_crystalCentralPositionY[index];
565  ap_Position2[2] = mp_crystalCentralPositionZ[index];
566  }
567  // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account)
568  else if (ap_POI2[2]<0.)
569  {
570  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> POI management not implemented yet for CT !" << endl);
571  return 1;
572  }
573  // Case when only the DOI is provided
574  else if (ap_POI2[0]==0. && ap_POI2[1]==0.)
575  {
576  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> POI management not implemented yet for CT !" << endl);
577  return 1;
578  }
579  // Case when the full POI is taken into account
580  else
581  {
582  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> POI management not implemented yet for CT !" << endl);
583  return 1;
584  }
585 
586  // Get orientations
587  ap_Orientation1[0] = -mp_crystalOrientationX[a_index2];
588  ap_Orientation1[1] = -mp_crystalOrientationY[a_index2];
589  ap_Orientation1[2] = -mp_crystalOrientationZ[a_index2];
590  ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
591  ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
592  ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
593 
594  return 0;
595 }
596 
597 // =====================================================================
598 // ---------------------------------------------------------------------
599 // ---------------------------------------------------------------------
600 // =====================================================================
601 
602 int iScannerCT::GetRdmPositionsAndOrientations(int a_index1, int a_index2,
603  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
604  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3] )
605 {
607 
608  Cerr("***** iScannerCT::GetRdmPositionsAndOrientations() -> Not yet implemented for CT !" << endl);
609 
610  return 0;
611 }
612 
613 // =====================================================================
614 // ---------------------------------------------------------------------
615 // ---------------------------------------------------------------------
616 // =====================================================================
617 
618 int iScannerCT::GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3])
619 {
621  // This function was first implemented for PET testing purpose. Not implemented yet.
622  Cerr("***** iScannerCT::GetPositionWithRandomDepth() -> This function was implemented for PET testing purpose. Not implemented for CT !" << endl);
623  return 1;
624 }
625 
626 // =====================================================================
627 // ---------------------------------------------------------------------
628 // ---------------------------------------------------------------------
629 // =====================================================================
630 
631 int iScannerCT::GetTwoCorners(int a_index1, int a_index2,
632  FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3],
633  FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3])
634 {
636  Cerr("***** iScannerCT::GetTwoCorners() -> Not implemented yet !" << endl);
637  return 1;
638 }
639 
640 // =====================================================================
641 // ---------------------------------------------------------------------
642 // ---------------------------------------------------------------------
643 // =====================================================================
644 
645 int iScannerCT::GetEdgesCenterPositions( int a_index1, int a_index2,
646  FLTNB ap_pos_line_point1[3], FLTNB ap_pos_line_point2[3],
647  FLTNB ap_pos_point1_x[4], FLTNB ap_pos_point1_y[4], FLTNB ap_pos_point1_z[4],
648  FLTNB ap_pos_point2_x[4], FLTNB ap_pos_point2_y[4], FLTNB ap_pos_point2_z[4]
649 )
650 {
652 
653  if ( a_index1<0 || a_index1>=m_nbOfProjections
654  || a_index2<0 || a_index2>=m_nbPixels )
655  {
656  Cerr("***** iScannerCT::GetPositionEdge() -> Crystal index or projection index out of range !" << endl);
657  return 1;
658  }
659 
660  // Computing global index
661  int global_index = m_nbPixels * a_index1 + a_index2;
662 
663  // Getting the half size of axial/trans crystal 1 and 2 depending on the layer
664  FLTNB half_spot_size_trans = m_spotSizeWidth / 2.0;
665  FLTNB half_spot_size_axial = m_spotSizeDepth / 2.0;
666  FLTNB half_pixel_trans = m_pixelsSizeTrans / 2.0;
667  FLTNB half_pixel_axial = m_pixelsSizeAxial / 2.0;
668 
670  // Computing coordinates depending on the orientation for point1
671  // X-axis
672  ap_pos_point1_x[ 0 ] = ap_pos_line_point1[ 0 ] - half_spot_size_trans * -mp_crystalOrientationY[ global_index ];
673  ap_pos_point1_x[ 1 ] = ap_pos_line_point1[ 0 ] + half_spot_size_trans * -mp_crystalOrientationY[ global_index ];
674  ap_pos_point1_x[ 2 ] = ap_pos_line_point1[ 0 ];
675  ap_pos_point1_x[ 3 ] = ap_pos_line_point1[ 0 ];
676 
677  // Y-axis
678  ap_pos_point1_y[ 0 ] = ap_pos_line_point1[ 1 ] - half_spot_size_trans * mp_crystalOrientationX[ global_index ];
679  ap_pos_point1_y[ 1 ] = ap_pos_line_point1[ 1 ] + half_spot_size_trans * mp_crystalOrientationX[ global_index ];
680  ap_pos_point1_y[ 2 ] = ap_pos_line_point1[ 1 ];
681  ap_pos_point1_y[ 3 ] = ap_pos_line_point1[ 1 ];
682 
683  // Z-axis
684  ap_pos_point1_z[ 0 ] = ap_pos_line_point1[ 2 ];
685  ap_pos_point1_z[ 1 ] = ap_pos_line_point1[ 2 ];
686  ap_pos_point1_z[ 2 ] = ap_pos_line_point1[ 2 ] - half_spot_size_axial;
687  ap_pos_point1_z[ 3 ] = ap_pos_line_point1[ 2 ] + half_spot_size_axial;
688 
690  // Computing coordinates depending on the orientation for point2
691  // X-axis
692  ap_pos_point2_x[ 0 ] = ap_pos_line_point2[ 0 ] + half_pixel_trans * mp_crystalOrientationY[ global_index ];
693  ap_pos_point2_x[ 1 ] = ap_pos_line_point2[ 0 ] - half_pixel_trans * mp_crystalOrientationY[ global_index ];
694  ap_pos_point2_x[ 2 ] = ap_pos_line_point2[ 0 ];
695  ap_pos_point2_x[ 3 ] = ap_pos_line_point2[ 0 ];
696 
697  // Y-axis
698  ap_pos_point2_y[ 0 ] = ap_pos_line_point2[ 1 ] + half_pixel_trans * -mp_crystalOrientationX[ global_index ];
699  ap_pos_point2_y[ 1 ] = ap_pos_line_point2[ 1 ] - half_pixel_trans * -mp_crystalOrientationX[ global_index ];
700  ap_pos_point2_y[ 2 ] = ap_pos_line_point2[ 1 ];
701  ap_pos_point2_y[ 3 ] = ap_pos_line_point2[ 1 ];
702 
703  // Z-axis
704  ap_pos_point2_z[ 0 ] = ap_pos_line_point2[ 2 ];
705  ap_pos_point2_z[ 1 ] = ap_pos_line_point2[ 2 ];
706  ap_pos_point2_z[ 2 ] = ap_pos_line_point2[ 2 ] - half_pixel_axial;
707  ap_pos_point2_z[ 3 ] = ap_pos_line_point2[ 2 ] + half_pixel_axial;
708 
709  // Normal end
710  return 0;
711 }
712 
713 // =====================================================================
714 // ---------------------------------------------------------------------
715 // ---------------------------------------------------------------------
716 // =====================================================================
717 
718 // TODO SS: If possible, simplify all this ping-pong between scanner and datafile information. For example, this function should
719 // be implemented inside the datafile, and then the information are passed to the scanner, instead of mixing everything.
721 {
723 
724  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerCT::GetGeometricInfoFromDataFile() -> Specific to CT" << endl);
725 
726  // This function is intended to be called after the scanner initialization, so that any field present in the datafile header, similar to
727  // one in the scanner configuration file, may overload the scanner value.
728 
729  if (ReadDataASCIIFile(a_pathToDF , "Number of projections", &m_nbOfProjections, 1, KEYWORD_MANDATORY))
730  {
731  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading number of projections in the header data file " << endl);
732  return 1;
733  }
734 
735  // Allocate a one-dimensional vector to retrieve angles from the datafile, then copy it in the member variables
736  FLTNB* angles = new FLTNB[m_nbOfProjections];
737  FLTNB first_and_last_angles[2] = {-1.,-1.};
738 
739  // Two possible initializations for projection angles :
740  // - All angles are provided with the keyword "Angles"
741  // - The first and last angles are provided, and the intermediate angles are extrapolated from the number of projections and the rotation direction
742 
743  // 'First/Last angles' tags : checking issue during data reading/conversion (==1)
744  if (ReadDataASCIIFile(a_pathToDF, "First and last projection angles", first_and_last_angles, 2, KEYWORD_OPTIONAL) == 1)
745  {
746  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading Angle mandatory field in the header data file '" << endl);
747  return 1;
748  }
749  // Scanner rotation direction
750  string rotation_direction = "";
751  if (ReadDataASCIIFile(a_pathToDF , "Scanner rotation direction", &rotation_direction, 1, KEYWORD_OPTIONAL) == 1)
752  {
753  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading head rotation orientation in the header data file " << endl);
754  return 1;
755  }
756  if (SetRotDirection(rotation_direction) )
757  {
758  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() ->Error occurred while trying to initialize scanner rotation orientation " << endl);
759  return 1;
760  }
761 
762  // Check for 'Projection angles' tag
763  int rvalue = ReadDataASCIIFile(a_pathToDF, "Projection angles", angles, m_nbOfProjections, KEYWORD_OPTIONAL);
764 
765  // Error while reading "Angles" tag (==1)
766  if(rvalue==1)
767  {
768  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading Angles field in the header data file !'" << endl);
769  return 1;
770  }
771 
772  // Check if information on projection angles has been provided
773  if ( rvalue>=2 && // "Angles" tag not found
774  (first_and_last_angles[0] <0 || first_and_last_angles[1] <0) ) // Tags first/last angles not found)
775  {
776  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> No information on projection angles provided in the datafile !'" << endl);
777  Cerr(" This information should be provided using either the 'Angles' tag, or both 'First angles', 'Last angles' tags !'" << endl);
778  return 1;
779  }
780  else if (rvalue>=2) // "Angles" tag not found, but first angle and last angle provided
781  {
782  // Put first and last angles in [0:360[
783  while (first_and_last_angles[0]>=360.) first_and_last_angles[0] -= 360.;
784  while (first_and_last_angles[0]<0.) first_and_last_angles[0] += 360.;
785  while (first_and_last_angles[1]>=360.) first_and_last_angles[1] -= 360.;
786  while (first_and_last_angles[1]<0.) first_and_last_angles[1] += 360.;
787  // Rotation direction
788  FLTNB dir = (m_rotDirection == GEO_ROT_CCW) ? -1. : 1.;
789  // Compute angle increment
790  FLTNB angle_increment = dir*(first_and_last_angles[1] - first_and_last_angles[0]);
791  while (angle_increment>=360.) angle_increment -= 360.;
792  while (angle_increment<0.) angle_increment += 360.;
793  angle_increment /= ((FLTNB)(m_nbOfProjections-1));
794  // Compute all projection angles
795  for(int a=0 ; a<m_nbOfProjections ; a++)
796  {
797  angles[a] = first_and_last_angles[0] + dir * angle_increment * ((FLTNB)a);
798  while (angles[a]>=360.) angles[a] -= 360.;
799  while (angles[a]<0.) angles[a] += 360.;
800  }
801  // Set the last angle to be sure it is the one requested (rounding errors may change it a bit)
802  angles[m_nbOfProjections-1] = first_and_last_angles[1];
803  }
804  // else : Angles have been recovered using ReadDataASCIIFile() above
805 
806  // Instanciate here the projection angles variable
808  for(int a=0 ; a<m_nbOfProjections ; a++) mp_projectionAngles[a] = angles[a];
809 
810  delete[] angles;
811 
812  return 0;
813 }
814 
815 // =====================================================================
816 // ---------------------------------------------------------------------
817 // ---------------------------------------------------------------------
818 // =====================================================================
819 
820 int iScannerCT::GetCTSpecificParameters(uint16_t* ap_nbOfProjections,
821  FLTNB*& ap_angles,
822  int* ap_detectorRotDirection)
823 {
825  // Verbose
826  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerCT::GetCTSpecificParameters() -> Copy pointers inside scanner to the datafile to get information" << endl
827  << " depending on the datafile inside the scanner" << endl);
828  // Verify that all parameters have been correctly checked
830  {
831  Cerr("***** iScannerCT::GetCTSpecificParameters() -> Parameters have not been checked !" << endl);
832  return 1;
833  }
834  // Get them
835  *ap_nbOfProjections = m_nbOfProjections;
836  ap_angles = mp_projectionAngles;
837  *ap_detectorRotDirection = m_rotDirection;
838  // End
839  return 0;
840 }
841 
842 // =====================================================================
843 // ---------------------------------------------------------------------
844 // ---------------------------------------------------------------------
845 // =====================================================================
846 
848 {
850  cout << "This scanner class is dedicated to the description of CT systems." << endl;
851 }
852 
853 // =====================================================================
854 // ---------------------------------------------------------------------
855 // ---------------------------------------------------------------------
856 // =====================================================================
iScannerCT()
iScannerCT constructor. Initialize the member variables to their default values.
oImageDimensionsAndQuantification * mp_ID
int LoadLUT()
Load a precomputed scanner LUT.
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define Cerr(MESSAGE)
int GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3])
#define GEO_ROT_CCW
int ComputeLUT()
Computes the LUT of the scanner from a generic (.geom) file.
int GetRdmPositionsAndOrientations(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3], FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3])
int Initialize()
Check general initialization and set several parameters to their default value.
int GetGeometricInfoFromDataFile(string a_pathToDF)
int BuildLUT(bool a_scannerFileIsLUT)
virtual int SetRotDirection(string a_rotDirection)
int CheckParameters()
Check that all parameters have been correctly initialized.
#define DEBUG_VERBOSE(IGNORED1, IGNORED2)
int GetNbThreadsMax()
Get the maximum between the number of threads used for projections and image operations.
Singleton class that Instantiate and initialize the scanner object.
void DescribeSpecific()
Implementation of the pure virtual eponym function that simply prints info about the scanner...
HPFLTNB GetMatriceElt(uint16_t l, uint16_t c)
#define KEYWORD_MANDATORY
#define KEYWORD_OPTIONAL
int GetTwoCorners(int a_index1, int a_index2, FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3], FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3])
int GetEdgesCenterPositions(int a_index1, int a_index2, FLTNB ap_pos_line_point1[3], FLTNB ap_pos_line_point2[3], FLTNB ap_pos_point1_x[4], FLTNB ap_pos_point1_y[4], FLTNB ap_pos_point1_z[4], FLTNB ap_pos_point2_x[4], FLTNB ap_pos_point2_y[4], FLTNB ap_pos_point2_z[4])
~iScannerCT()
iScannerCT destructor.
Structure designed for basic matrices operations.
int GetPositionsAndOrientations(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3], FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3], FLTNB *ap_POI1=NULL, FLTNB *ap_POI2=NULL)
int Multiplication(oMatrix *ap_Mtx, oMatrix *ap_MtxResult)
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...
int GetCTSpecificParameters(uint16_t *ap_nbOfProjections, FLTNB *&ap_angles, int *ap_detectorRotDirection)
int SetMatriceElt(uint16_t l, uint16_t c, HPFLTNB a_val)
int Instantiate(bool a_scannerFileIsLUT)
#define Cout(MESSAGE)
#define GEO_ROT_CW
Generic class for scanner objects.
void ShowHelp()
Display help.