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