![]() |
CASToR
1.0
Tomographic Reconstruction (PET/SPECT)
|
00001 00002 /* 00003 Implementation of class iScannerPET 00004 00005 - separators: X 00006 - doxygen: X 00007 - default initialization: X 00008 - CASTOR_DEBUG: none 00009 - CASTOR_VERBOSE: X 00010 */ 00011 00018 #include "iScannerPET.hh" 00019 #include "sOutputManager.hh" 00020 #include "sScannerManager.hh" 00021 00022 00023 // ===================================================================== 00024 // --------------------------------------------------------------------- 00025 // --------------------------------------------------------------------- 00026 // ===================================================================== 00027 /* 00028 \brief iScannerPET constructor. 00029 Initialize the member variables to their default values. 00030 */ 00031 iScannerPET::iScannerPET() : vScanner() 00032 { 00033 m_scannerType = 0; 00034 m_nbLayers = -1; 00035 m_nbCrystals = -1; 00036 m_minAngleDifference = -1.; 00037 mp_nbCrystalsInLayer = NULL; 00038 mp_nbRings = NULL; 00039 00040 mp_crystalCentralPositionX = NULL; 00041 mp_crystalCentralPositionY = NULL; 00042 mp_crystalCentralPositionZ = NULL; 00043 00044 mp_crystalOrientationX = NULL; 00045 mp_crystalOrientationY = NULL; 00046 mp_crystalOrientationZ = NULL; 00047 00048 mp_sizeCrystalTrans = NULL; 00049 mp_sizeCrystalAxial = NULL; 00050 mp_sizeCrystalDepth = NULL; 00051 mp_meanDepthOfInteraction = NULL; 00052 00053 mp_positionMatrix_ref = NULL; 00054 mp_positionMatrix_out = NULL; 00055 mp_rotationMatrix = NULL; 00056 00057 // Variables depending on the acquisition 00058 m_maxRingDiff = -1; 00059 } 00060 00061 00062 00063 // ===================================================================== 00064 // --------------------------------------------------------------------- 00065 // --------------------------------------------------------------------- 00066 // ===================================================================== 00067 /* 00068 \brief iScannerPET destructor. 00069 */ 00070 iScannerPET::~iScannerPET() 00071 { 00072 if (mp_sizeCrystalTrans != NULL) delete mp_sizeCrystalTrans; 00073 if (mp_sizeCrystalAxial != NULL) delete mp_sizeCrystalAxial; 00074 if (mp_sizeCrystalDepth != NULL) delete mp_sizeCrystalDepth; 00075 if (mp_meanDepthOfInteraction != NULL) delete mp_meanDepthOfInteraction; 00076 if (mp_crystalCentralPositionX != NULL) delete mp_crystalCentralPositionX; 00077 if (mp_crystalCentralPositionY != NULL) delete mp_crystalCentralPositionY; 00078 if (mp_crystalCentralPositionZ != NULL) delete mp_crystalCentralPositionZ; 00079 if (mp_crystalOrientationX != NULL) delete mp_crystalOrientationX; 00080 if (mp_crystalOrientationY != NULL) delete mp_crystalOrientationY; 00081 if (mp_crystalOrientationZ != NULL) delete mp_crystalOrientationZ; 00082 if (mp_nbCrystalsInLayer != NULL) delete mp_nbCrystalsInLayer; 00083 if (mp_positionMatrix_ref != NULL) delete mp_positionMatrix_ref; 00084 if (mp_positionMatrix_out != NULL) delete mp_positionMatrix_out; 00085 if (mp_rotationMatrix != NULL) delete mp_rotationMatrix; 00086 } 00087 00088 00089 00090 00091 // ===================================================================== 00092 // --------------------------------------------------------------------- 00093 // --------------------------------------------------------------------- 00094 // ===================================================================== 00095 00096 int iScannerPET::Instantiate(bool a_scannerFileIsLUT) 00097 { 00098 if(m_verbose>=2) Cout("iScannerPET::Instantiate ..."<< endl); 00099 00100 sScannerManager* p_scannerManager; 00101 p_scannerManager = sScannerManager::GetInstance(); 00102 00103 // Get the number of layers in the scanner 00104 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of layers", &m_nbLayers, 1, KEYWORD_MANDATORY)) 00105 { 00106 Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl); 00107 return 1; 00108 } 00109 00110 // Check the correct initialization of the number of layers 00111 if(m_nbLayers<=0) 00112 { 00113 Cerr("***** iScannerPET::Instantiate() -> Incorrect value for the number of layer (should be >0) !" << endl); 00114 return 1; 00115 } 00116 00117 // Get the number of elements in the system 00118 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of elements", &m_nbCrystals, 1, KEYWORD_MANDATORY)) 00119 { 00120 Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl); 00121 return 1; 00122 } 00123 00124 // Instanciation of layer dependent member variables 00125 mp_nbCrystalsInLayer = new int[m_nbLayers]; 00126 mp_nbRings = new int[m_nbLayers]; 00127 mp_sizeCrystalTrans = new FLTNB[m_nbLayers]; 00128 mp_sizeCrystalAxial = new FLTNB[m_nbLayers]; 00129 mp_sizeCrystalDepth = new FLTNB[m_nbLayers]; 00130 mp_meanDepthOfInteraction = new FLTNB[m_nbLayers]; 00131 00132 // Instanciation of number of crystals dependent member variables 00133 mp_crystalCentralPositionX = new FLTNB[m_nbCrystals]; 00134 mp_crystalCentralPositionY = new FLTNB[m_nbCrystals]; 00135 mp_crystalCentralPositionZ = new FLTNB[m_nbCrystals]; 00136 00137 mp_crystalOrientationX = new FLTNB[m_nbCrystals]; 00138 mp_crystalOrientationY = new FLTNB[m_nbCrystals]; 00139 mp_crystalOrientationZ = new FLTNB[m_nbCrystals]; 00140 00141 // Initialize layer size with default value (=0); 00142 for(int l=0 ; l<m_nbLayers ; l++) 00143 { 00144 mp_sizeCrystalTrans[l] = 0; 00145 mp_sizeCrystalAxial[l] = 0; 00146 mp_sizeCrystalDepth[l] = 0; 00147 } 00148 00149 // Size of crystals 00150 if (a_scannerFileIsLUT) 00151 { 00152 // For the moment, only the depth is mandatory as the others are not yet implemented 00153 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size trans", mp_sizeCrystalTrans, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00154 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size axial", mp_sizeCrystalAxial, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00155 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size depth", mp_sizeCrystalDepth, m_nbLayers, KEYWORD_MANDATORY) ) 00156 { 00157 Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl); 00158 return 1; 00159 } 00160 } 00161 else 00162 { 00163 // Mandatory parameter 00164 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size trans", mp_sizeCrystalTrans, m_nbLayers, KEYWORD_MANDATORY) || 00165 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size axial", mp_sizeCrystalAxial, m_nbLayers, KEYWORD_MANDATORY) || 00166 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size depth", mp_sizeCrystalDepth, m_nbLayers, KEYWORD_MANDATORY) ) 00167 { 00168 Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read a parameter in the scanner header file !" << endl); 00169 return 1; 00170 } 00171 } 00172 00173 // Optional parameters 00174 00175 // Initialize with default values 00176 for(int l=0 ; l<m_nbLayers ; l++) 00177 mp_meanDepthOfInteraction[l] = -1; 00178 00179 m_minAngleDifference = 0.; 00180 00181 // Check if value is provided in the scanner conf file 00182 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "mean depth of interaction", mp_meanDepthOfInteraction, m_nbLayers, KEYWORD_OPTIONAL) == 1|| 00183 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "min angle difference", &m_minAngleDifference, 1, KEYWORD_OPTIONAL) == 1) 00184 { 00185 Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read an optional parameter in the scanner header file !" << endl); 00186 return 1; 00187 } 00188 00189 // Instanciation of objects for matrix calculation during reconstruction 00190 mp_positionMatrix_ref = new oMatrix(3,1); 00191 mp_positionMatrix_out = new oMatrix(3,1); 00192 mp_rotationMatrix = new oMatrix(3,3); 00193 00194 return 0; 00195 } 00196 00197 00198 00199 00200 // ===================================================================== 00201 // --------------------------------------------------------------------- 00202 // --------------------------------------------------------------------- 00203 // ===================================================================== 00204 /* 00205 \fn BuildLUT 00206 \param a_scannerFileIsLUT : boolean indicating if the file describing 00207 the SPECT camera is a generic file (0) or custom Look-up-table (1) 00208 \brief Call the functions to generate the LUT or read the user-made LUT depending on the user choice 00209 \return 0 if success, positive value otherwise 00210 \todo default values to max ring diff 00211 */ 00212 int iScannerPET::BuildLUT(bool a_scannerFileIsLUT) 00213 { 00214 if (m_verbose>=2) Cout("iScannerPET::BuildLUT ..."<< endl); 00215 00216 // Either generate the LUT from a generic file, or load the user precomputed LUT 00217 if (!a_scannerFileIsLUT) 00218 { 00219 if (ComputeLUT()) 00220 { 00221 Cerr("***** iScannerPET::BuildLUT() -> A problem occured while generating scanner LUT !" << endl); 00222 return 1; 00223 } 00224 } 00225 else 00226 { 00227 if (LoadLUT()) 00228 { 00229 Cerr("***** iScannerPET::BuildLUT() -> A problem occured while loading scanner LUT !" << endl); 00230 return 1; 00231 } 00232 } 00233 00234 // Set default values to parameters 00235 if(m_maxRingDiff < 0) // Not initialized 00236 m_maxRingDiff = mp_nbRings[0]; //TODO // How to do with scanner with several layers ? For now setmaximal ring difference number to the number of rings in the first layer by default 00237 00238 return 0; 00239 } 00240 00241 00242 00243 00244 // ===================================================================== 00245 // --------------------------------------------------------------------- 00246 // --------------------------------------------------------------------- 00247 // ===================================================================== 00248 /* 00249 \fn CheckParameters 00250 \brief Check if all parameters have been correctly initialized 00251 \return 0 if success, positive value otherwise 00252 */ 00253 int iScannerPET::CheckParameters() 00254 { 00255 if(m_verbose>=2) Cout("iScannerPET::CheckParameters ..."<< endl); 00256 00257 if(m_scannerType == -1) 00258 { 00259 Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl); 00260 return 1; 00261 } 00262 00263 if(m_verbose == -1) 00264 { 00265 Cerr("***** iScannerPET::CheckParameters() -> Verbosity not initialized !" << endl); 00266 return 1; 00267 } 00268 00269 if(m_nbLayers <= 0) 00270 { 00271 Cerr("***** iScannerPET::CheckParameters() -> Incorrect value for the number of layer (should be >0) !" << endl); 00272 return 1; 00273 } 00274 00275 if(m_nbCrystals <0) 00276 { 00277 Cerr("***** iScannerPET::CheckParameters() -> Number of crystals not initialized !" << endl); 00278 return 1; 00279 } 00280 00281 if(mp_nbRings == NULL) 00282 { 00283 Cerr("***** iScannerPET::CheckParameters() -> Number of rings not initialized !" << endl); 00284 return 1; 00285 } 00286 00287 if(mp_nbCrystalsInLayer == NULL) 00288 { 00289 Cerr("***** iScannerPET::CheckParameters() -> Number of crystals in layer(s) not initialized !" << endl); 00290 return 1; 00291 } 00292 00293 if(mp_crystalCentralPositionX==NULL || mp_crystalCentralPositionY==NULL || mp_crystalCentralPositionZ==NULL) 00294 { 00295 Cerr("***** iScannerPET::CheckParameters() -> Crystals central positions not initialized !" << endl); 00296 return 1; 00297 } 00298 00299 if(mp_crystalOrientationX==NULL || mp_crystalOrientationY==NULL || mp_crystalOrientationZ==NULL) 00300 { 00301 Cerr("***** iScannerPET::CheckParameters() -> Crystals orientations not initialized !" << endl); 00302 return 1; 00303 } 00304 00305 if(mp_sizeCrystalTrans==NULL || mp_sizeCrystalAxial==NULL || mp_sizeCrystalDepth==NULL) 00306 { 00307 Cerr("***** iScannerPET::CheckParameters() -> Crystals dimensions not initialized !" << endl); 00308 return 1; 00309 } 00310 00311 if(mp_meanDepthOfInteraction == NULL) 00312 { 00313 Cout("***** iScannerPET::CheckParameters() -> Mean depth of interaction not initialized !" << endl); 00314 return 1; 00315 } 00316 00317 if(m_minAngleDifference < 0) 00318 { 00319 Cerr("***** iScannerPET::CheckParameters() -> Minimum angle difference not initialized !" << endl); 00320 return 1; 00321 } 00322 00323 if(m_maxRingDiff < 0) 00324 { 00325 Cerr("***** iScannerPET::CheckParameters() -> Maximal ring difference not initialized !" << endl); 00326 return 1; 00327 } 00328 00329 if(m_scannerType == -1) 00330 { 00331 Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl); 00332 return 1; 00333 } 00334 00335 if(m_scannerType == -1) 00336 { 00337 Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl); 00338 return 1; 00339 } 00340 00341 00342 m_allParametersChecked = true; 00343 00344 return 0; 00345 } 00346 00347 00348 00349 00350 // ===================================================================== 00351 // --------------------------------------------------------------------- 00352 // --------------------------------------------------------------------- 00353 // ===================================================================== 00354 /* 00355 \fn Initialize 00356 \brief Check general initialization and set several parameters to their default value 00357 \return 0 if success, positive value otherwise 00358 */ 00359 int iScannerPET::Initialize() 00360 { 00361 if(m_verbose>=2) Cout("iScannerPET::Initialize ..."<< endl); 00362 00363 if(m_allParametersChecked == false) 00364 { 00365 Cerr("***** iScannerPET::Initialize() -> Parameters have not been checked !" << endl); 00366 return 1; 00367 } 00368 00369 // Set mean depth of interaction to the center of crystal if not initialized by default. 00370 for(int l=0 ; l<m_nbLayers ; l++) 00371 if (mp_meanDepthOfInteraction[l]<0) mp_meanDepthOfInteraction[l] = mp_sizeCrystalDepth[l]/2; 00372 00373 // Conversion of the minimal angle difference into radians 00374 m_minAngleDifference *= M_PI/180; 00375 00376 return 0; 00377 } 00378 00379 00380 00381 00382 // ===================================================================== 00383 // --------------------------------------------------------------------- 00384 // --------------------------------------------------------------------- 00385 // ===================================================================== 00386 /* 00387 \fn LoadLUT 00388 \brief Load a precomputed scanner LUT 00389 \details Read mandatory data from the header of the LUT. 00390 Then load the LUT elements for each crystal 00391 \return 0 if success, positive value otherwise 00392 */ 00393 int iScannerPET::LoadLUT() 00394 { 00395 if(m_verbose >= 2) Cout("iScannerPET::LoadLUT() -> Start loading LUT from user-provided file... " << endl); 00396 00397 sScannerManager* p_scannerManager; 00398 p_scannerManager = sScannerManager::GetInstance(); 00399 00400 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals in layer", mp_nbCrystalsInLayer, m_nbLayers, 1) || 00401 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of rings in scanner", mp_nbRings, m_nbLayers, 1)) 00402 { 00403 Cerr("***** iScannerPET::LoadLUT() -> An error occurred while trying to read a mandatory parameter in the scanner header file !" << endl); 00404 return 1; 00405 } 00406 00407 // Open the scanner LUT file 00408 string scanner_lut_file = p_scannerManager->GetPathToScannerFile(); 00409 //todo : maybe more practical to directly read the path from the header. 00410 // Do not put restrictions on the file extension (.lut) 00411 scanner_lut_file = scanner_lut_file.substr(0, scanner_lut_file.find_last_of(".")).append(".lut"); 00412 00413 // Open file 00414 FILE* LUT_file = fopen(scanner_lut_file.c_str(), "rb"); 00415 if (LUT_file==NULL) 00416 { 00417 Cerr("***** iScannerPET::LoadLUT() -> Input LUT file '" << scanner_lut_file << "' is missing or corrupted !" << endl); 00418 return 1; 00419 } 00420 00421 // Read data for each index 00422 int nb_data_read = 0; 00423 for (int i=0; i<m_nbCrystals; i++) 00424 { 00425 FLTNBLUT buffer; 00426 // Read central crystal position X, then Y and Z 00427 nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file); 00428 mp_crystalCentralPositionX[i] = ((FLTNB)buffer); 00429 nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file); 00430 mp_crystalCentralPositionY[i] = ((FLTNB)buffer); 00431 nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file); 00432 mp_crystalCentralPositionZ[i] = ((FLTNB)buffer); 00433 // Read crystal orientation X, then Y and Z 00434 nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file); 00435 mp_crystalOrientationX[i] = ((FLTNB)buffer); 00436 nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file); 00437 mp_crystalOrientationY[i] = ((FLTNB)buffer); 00438 nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file); 00439 mp_crystalOrientationZ[i] = ((FLTNB)buffer); 00440 } 00441 00442 // Close file 00443 fclose(LUT_file); 00444 00445 // Check reading 00446 if (nb_data_read!=m_nbCrystals*6) 00447 { 00448 Cerr("***** iScannerPET::LoadLUT() -> Failed to read all data in input LUT file '" << scanner_lut_file << "' !" << endl); 00449 return 1; 00450 } 00451 00452 if(m_verbose >= 2) Cout("iScannerPET::LoadLUT() -> LUT successfully loaded" << endl); 00453 00454 return 0; 00455 } 00456 00457 00458 00459 00460 // ===================================================================== 00461 // --------------------------------------------------------------------- 00462 // --------------------------------------------------------------------- 00463 // ===================================================================== 00464 /* 00465 \fn ComputeLUT 00466 \brief Compute the LUT of the scanner from a generic (.geom) file 00467 \details Read mandatory data from the geom file. Then compute the LUT elements for each crystal from the geometry described in the file 00468 \details Compute the look-up-tables of the system containing the locations of the scanner elements center in space and their orientations 00469 \todo Add option to inverse rsector if transaxial orientation is counter-clockwise.? 00470 \todo rotation for non-perfectly cylindric scanner (angles along the Z axis) 00471 \return 0 if success, positive value otherwise 00472 */ 00473 int iScannerPET::ComputeLUT() 00474 { 00475 if(m_verbose >= 2) Cout("iScannerPET::ComputeLUT() -> Start LUT generation ..." << endl); 00476 00477 // ============================================================================================================ 00478 // Init local variables to recover data from scanner file 00479 // ============================================================================================================ 00480 00481 // Layer dependent variables 00482 int *nb_rsectors_lyr, 00483 *nb_trans_mod_lyr, 00484 *nb_axial_mod_lyr, 00485 *nb_trans_submod_lyr, 00486 *nb_axial_submod_lyr, 00487 *nb_trans_crystal_lyr, 00488 *nb_axial_crystal_lyr; 00489 00490 FLTNBLUT *radius_lyr, 00491 *rsector_first_angle_lyr, 00492 *rsector_angular_span_lyr, 00493 *gap_trans_mod_lyr, 00494 *gap_axial_mod_lyr, 00495 *gap_trans_submod_lyr, 00496 *gap_axial_submod_lyr, 00497 *gap_trans_crystal_lyr, 00498 *gap_axial_crystal_lyr; 00499 00500 nb_rsectors_lyr = new int[m_nbLayers]; 00501 nb_trans_mod_lyr = new int[m_nbLayers]; 00502 nb_axial_mod_lyr = new int[m_nbLayers]; 00503 nb_trans_submod_lyr = new int[m_nbLayers]; 00504 nb_axial_submod_lyr = new int[m_nbLayers]; 00505 nb_trans_crystal_lyr = new int[m_nbLayers]; 00506 nb_axial_crystal_lyr = new int[m_nbLayers]; 00507 00508 radius_lyr = new FLTNBLUT[m_nbLayers]; 00509 gap_trans_mod_lyr = new FLTNBLUT[m_nbLayers]; 00510 gap_axial_mod_lyr = new FLTNBLUT[m_nbLayers]; 00511 gap_trans_submod_lyr = new FLTNBLUT[m_nbLayers]; 00512 gap_axial_submod_lyr = new FLTNBLUT[m_nbLayers]; 00513 gap_trans_crystal_lyr = new FLTNBLUT[m_nbLayers]; 00514 gap_axial_crystal_lyr = new FLTNBLUT[m_nbLayers]; 00515 rsector_first_angle_lyr = new FLTNBLUT[m_nbLayers]; 00516 rsector_angular_span_lyr = new FLTNBLUT[m_nbLayers]; 00517 00518 00519 // ------------------------------------------------------------------- 00520 // Recover mandatory data from scanner file 00521 sScannerManager* p_scannerManager; 00522 p_scannerManager = sScannerManager::GetInstance(); 00523 00524 if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of rsectors", nb_rsectors_lyr, m_nbLayers, KEYWORD_MANDATORY) || 00525 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals transaxial", nb_trans_crystal_lyr, m_nbLayers, KEYWORD_MANDATORY) || 00526 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals axial", nb_axial_crystal_lyr, m_nbLayers, KEYWORD_MANDATORY) || 00527 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "scanner radius", radius_lyr, m_nbLayers, KEYWORD_MANDATORY) ) 00528 { 00529 Cerr("***** iScannerPET::ComputeLUT() -> An error occurred while trying to read a mandatory parameter for scanner LUT generation in the scanner header file !" << endl); 00530 return 1; 00531 } 00532 00533 // ------------------------------------------------------------------- 00534 // Recover optionnal data from scanner file 00535 00536 // Initialize defaulf values 00537 for(int l=0 ; l<m_nbLayers ; l++) 00538 { 00539 nb_trans_mod_lyr[ l ] =1; 00540 nb_axial_mod_lyr[ l ] =1; 00541 nb_trans_submod_lyr[ l ] = 1; 00542 nb_axial_submod_lyr[ l ] = 1; 00543 gap_trans_mod_lyr[ l ] = 0.; 00544 gap_axial_mod_lyr[ l ] = 0.; 00545 gap_trans_submod_lyr[ l ] = 0.; 00546 gap_axial_submod_lyr[ l ] = 0.; 00547 gap_trans_crystal_lyr[ l ] = 0.; 00548 gap_axial_crystal_lyr[ l ] = 0.; 00549 rsector_first_angle_lyr[ l ] = 0; 00550 rsector_angular_span_lyr[ l ] = 360.; 00551 } 00552 00553 if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of modules transaxial", nb_trans_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00554 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of modules axial", nb_axial_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00555 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of submodules transaxial", nb_trans_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00556 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of submodules axial", nb_axial_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00557 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "module gap transaxial", gap_trans_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00558 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "module gap axial", gap_axial_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00559 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "submodule gap transaxial", gap_trans_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00560 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "submodule gap axial", gap_axial_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00561 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystal gap transaxial", gap_trans_crystal_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00562 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystal gap axial", gap_axial_crystal_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00563 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors first angle", rsector_first_angle_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 || 00564 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors angular span", rsector_angular_span_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ) 00565 { 00566 Cerr("***** iScannerPET::ComputeLUT() -> An error occurred while trying to read an optionnal parameter for scanner LUT generation in the scanner header file !" << endl); 00567 return 1; 00568 } 00569 00570 00571 for(int lyr=0 ; lyr<m_nbLayers ; lyr++) 00572 mp_nbRings[lyr] = nb_axial_mod_lyr[ lyr ] 00573 * nb_axial_submod_lyr[ lyr ] 00574 * nb_axial_crystal_lyr[ lyr ]; 00575 00576 00577 // Z-SHIFT MANAGEMENT 00578 00579 // Variables 00580 int rsector_nb_zshift; 00581 FLTNBLUT *zshift; 00582 00583 int rvalue = ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors nbZShift", &rsector_nb_zshift, 1, KEYWORD_OPTIONAL); 00584 00585 // reading error 00586 if( rvalue == 1 ) 00587 { 00588 Cerr("***** iScannerPET::ComputeLUT() -> Error occurred when trying to read z-shift nb !" << endl); 00589 return 1; 00590 } 00591 // z-shift not found, or == 0 00592 else if( rvalue > 1 || rsector_nb_zshift==0) 00593 { 00594 rsector_nb_zshift = 1; // set to default value (=1) 00595 zshift = new FLTNBLUT[rsector_nb_zshift]; 00596 zshift[0] = 0; 00597 } 00598 // (==0, zhift value provided) 00599 else 00600 { 00601 zshift = new FLTNBLUT[rsector_nb_zshift]; 00602 00603 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors ZShift", zshift, rsector_nb_zshift, KEYWORD_MANDATORY) ) 00604 { 00605 Cerr("***** iScannerPET::ComputeLUT() -> No data found about modules z-shift in the scanner header file, whereas z-shift is enabled !" << endl); 00606 return 1; 00607 } 00608 } 00609 00610 // keep track of the number of crystals already indexed between layer 00611 int nb_crystals_cur = 0; 00612 00613 // loop on layer, crystals indexed according to the layer they belong to 00614 for(int lyr=0 ; lyr<m_nbLayers ; lyr++) 00615 { 00616 if (lyr>0) 00617 nb_crystals_cur+=mp_nbCrystalsInLayer[lyr-1]; 00618 00619 int nb_rsectors = nb_rsectors_lyr[lyr], 00620 nb_trans_mod = nb_trans_mod_lyr[lyr], 00621 nb_axial_mod = nb_axial_mod_lyr[lyr], 00622 nb_trans_submod = nb_trans_submod_lyr[lyr], 00623 nb_axial_submod = nb_axial_submod_lyr[lyr], 00624 nb_trans_crystal = nb_trans_crystal_lyr[lyr], 00625 nb_axial_crystal = nb_axial_crystal_lyr[lyr]; 00626 00627 FLTNBLUT radius = radius_lyr[lyr], 00628 rsector_first_angle = rsector_first_angle_lyr[lyr], 00629 angular_span = rsector_angular_span_lyr[lyr], 00630 gap_trans_mod = gap_trans_mod_lyr[lyr], 00631 gap_axial_mod = gap_axial_mod_lyr[lyr], 00632 gap_trans_submod = gap_trans_submod_lyr[lyr], 00633 gap_axial_submod = gap_axial_submod_lyr[lyr], 00634 gap_trans_crystal = gap_trans_crystal_lyr[lyr], 00635 gap_axial_crystal = gap_axial_crystal_lyr[lyr]; 00636 00637 FLTNBLUT size_trans_submod = nb_trans_crystal*mp_sizeCrystalTrans[lyr] + (nb_trans_crystal-1)*gap_trans_crystal; 00638 FLTNBLUT size_axial_submod = nb_axial_crystal*mp_sizeCrystalAxial[lyr] + (nb_axial_crystal-1)*gap_axial_crystal; 00639 FLTNBLUT size_trans_mod = nb_trans_submod*size_trans_submod + (nb_trans_submod-1)*gap_trans_submod; 00640 FLTNBLUT size_axial_mod = nb_axial_submod*size_axial_submod + (nb_axial_submod-1)*gap_axial_submod; 00641 00642 int nb_mod = nb_axial_mod * nb_trans_mod; 00643 int nb_submod = nb_axial_submod * nb_trans_submod; 00644 int nb_crystal = nb_axial_crystal * nb_trans_crystal; 00645 00646 // Check the number of crystals < nb elements provided by the scanner configuration file 00647 int nb_crystals = nb_rsectors*nb_mod*nb_submod*nb_crystal + nb_crystals_cur; 00648 if(m_nbCrystals<nb_crystals) 00649 { 00650 Cerr("***** iScannerPET::ComputeLUT() -> Computed number of crystals computed from the geom file ("<< nb_crystals 00651 <<") > not consistent with the total number of crystals (including potential layers) provided in the geom file ("<< m_nbCrystals <<") !" << endl); 00652 return 1; 00653 } 00654 00655 mp_nbCrystalsInLayer[lyr] = nb_rsectors*nb_mod*nb_submod*nb_crystal; 00656 int number_crystals_in_ring = mp_nbCrystalsInLayer[lyr]/mp_nbRings[lyr]; 00657 00658 // ============================================================================================================ 00659 // Main part of the function: Generate the LUT 00660 // ============================================================================================================ 00661 00662 if(m_verbose>=2) Cout("iScannerPET::ComputeLUT : Generate positions for each crystals ..."<< endl); 00663 00664 // loop to nb_rsectors+1. crystal_center[0] will be used to gather position of the reference rsector (directly above isocenter) 00665 oMatrix *****crystal_center = new oMatrix ****[nb_rsectors+1]; 00666 00667 for(int i = 0; i < nb_rsectors+1 ; i++) 00668 { 00669 crystal_center[i] = new oMatrix ***[nb_axial_mod * nb_trans_mod]; 00670 00671 for (int j = 0; j<nb_axial_mod*nb_trans_mod ; j++) 00672 { 00673 crystal_center[i][j] = new oMatrix **[nb_axial_submod * nb_trans_submod]; 00674 00675 for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++) 00676 { 00677 crystal_center[i][j][k] = new oMatrix*[nb_axial_crystal * nb_trans_crystal]; 00678 00679 for (int l = 0; l<nb_axial_crystal*nb_trans_crystal; l++) 00680 { 00681 crystal_center[i][j][k][l] = new oMatrix(3,1); 00682 } 00683 } 00684 } 00685 } 00686 00687 // Generation of the rotation matrix allowing to compute the position of all the rsectors. 00688 oMatrix** rotation_mtx = new oMatrix*[nb_rsectors]; 00689 00690 for(int i=0; i<nb_rsectors; i++) 00691 rotation_mtx[i] = new oMatrix(3,3); 00692 00693 // convert first angle and angular span in radians 00694 FLTNBLUT rsector_first_angle_rad = rsector_first_angle*M_PI/180.; 00695 FLTNBLUT angular_span_rad = angular_span*M_PI/180.; 00696 00697 for (int i = 0; i<nb_rsectors; i++) 00698 { 00699 FLTNBLUT angle = remainderf(rsector_first_angle_rad + ((FLTNBLUT)i)*angular_span_rad/((FLTNBLUT)nb_rsectors), 2.*M_PI); 00700 00701 rotation_mtx[i]->SetMatriceElt(0,0,cos(angle) ); 00702 rotation_mtx[i]->SetMatriceElt(1,0,-sin(angle) ); 00703 rotation_mtx[i]->SetMatriceElt(2,0,0); 00704 rotation_mtx[i]->SetMatriceElt(0,1,sin(angle) ); 00705 rotation_mtx[i]->SetMatriceElt(1,1,cos(angle) ); 00706 rotation_mtx[i]->SetMatriceElt(2,1,0); 00707 rotation_mtx[i]->SetMatriceElt(0,2,0); 00708 rotation_mtx[i]->SetMatriceElt(1,2,0); 00709 rotation_mtx[i]->SetMatriceElt(2,2,1); 00710 } 00711 00712 // Compute scanner elements positions for the first rsector 00713 for (int i=0; i < nb_mod ; i++) 00714 { 00715 // Define the transaxial and axial edge start positions for the rsector 00716 FLTNBLUT x_start_m = -(nb_trans_mod*size_trans_mod + (nb_trans_mod-1)*gap_trans_mod) / 2.; 00717 FLTNBLUT z_start_m = -(nb_axial_mod*size_axial_mod + (nb_axial_mod-1)*gap_axial_mod) / 2.; 00718 00719 // Define the transaxial and axial edge start positions for the i-Module in the rsector. 00720 // Enumeration starting with the transaxial modules. 00721 x_start_m += (i%nb_trans_mod) * (size_trans_mod + gap_trans_mod); 00722 z_start_m += int(i/nb_trans_mod) * (size_axial_mod + gap_axial_mod); 00723 00724 for (int j=0 ; j < nb_submod ; j++) 00725 { 00726 FLTNBLUT x_start_sm = x_start_m; 00727 FLTNBLUT z_start_sm = z_start_m; 00728 00729 x_start_sm += (j%nb_trans_submod) * (size_trans_submod + gap_trans_submod); 00730 z_start_sm += int(j/nb_trans_submod) * (size_axial_submod + gap_axial_submod); 00731 00732 for (int k=0 ; k < nb_crystal ; k++) 00733 { 00734 // Define the transaxial and axial center positions for the j-SubModule (crystal) i-Module of the rsector. 00735 // Enumeration starting with the transaxial submodules. 00736 FLTNBLUT Xcrist = x_start_sm + (k%nb_trans_crystal) * (mp_sizeCrystalTrans[lyr] + gap_trans_crystal) + mp_sizeCrystalTrans[lyr]/2; 00737 FLTNBLUT Ycrist = radius + mp_sizeCrystalDepth[lyr]/2; 00738 FLTNBLUT Zcrist = z_start_sm + int(k/nb_trans_crystal) * (mp_sizeCrystalAxial[lyr] + gap_axial_crystal) + mp_sizeCrystalAxial[lyr]/2; 00739 00740 crystal_center[0][i][j][k]->SetMatriceElt(0,0,Xcrist); 00741 crystal_center[0][i][j][k]->SetMatriceElt(1,0,Ycrist); 00742 crystal_center[0][i][j][k]->SetMatriceElt(2,0,Zcrist); 00743 } 00744 } 00745 } 00746 00747 // ============================================================================================================ 00748 // Loop over all the other rsectors. 00749 // Positions of the scanner elements are progressively stored in the LUT file. 00750 // ============================================================================================================ 00751 for (int i=0 ; i<nb_rsectors ; i++) 00752 for (int j=0 ; j<nb_mod ; j++) 00753 for (int k=0 ; k<nb_submod ; k++) 00754 for (int l=0 ; l<nb_crystal ; l++) 00755 { 00756 int rs=i, c=l; 00757 00758 // crystal indexation 00759 int cryID = int(j/nb_trans_mod)*nb_axial_submod*nb_axial_crystal*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) modules 00760 + int(k/nb_trans_submod)*nb_axial_crystal*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) submodules 00761 + int(l/nb_trans_crystal)*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) crystals 00762 + i*nb_trans_mod*nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous rsectors 00763 + j%nb_trans_mod*nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous (transaxial) modules 00764 + k%nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous (transaxial) submodules 00765 + l%nb_trans_crystal // previous crystals in the submodule 00766 + nb_crystals_cur; // nb indexed crystals in the previous layer(s) 00767 00768 rotation_mtx[rs]->Multiplication(crystal_center[0][j][k][c], crystal_center[i+1][j][k][c]); 00769 00770 mp_crystalCentralPositionX[cryID] = (FLTNB)crystal_center[i+1][j][k][c]->GetMatriceElt(0,0); 00771 mp_crystalCentralPositionY[cryID] = (FLTNB)crystal_center[i+1][j][k][c]->GetMatriceElt(1,0); 00772 mp_crystalCentralPositionZ[cryID] = (FLTNB)crystal_center[i+1][j][k][c]->GetMatriceElt(2,0); 00773 mp_crystalCentralPositionZ[cryID] += (FLTNB)zshift[rs%rsector_nb_zshift] ; 00774 00775 // TODO Rotation for non-perfectly cylindric scanner (angles along the Z axis) 00776 mp_crystalOrientationX[cryID] = (FLTNB)rotation_mtx[rs]->GetMatriceElt(0,1); 00777 mp_crystalOrientationY[cryID] = (FLTNB)rotation_mtx[rs]->GetMatriceElt(0,0); 00778 mp_crystalOrientationZ[cryID] = 0.; 00779 } 00780 00781 // ============================================================================================================ 00782 // End 00783 // ============================================================================================================ 00784 00785 // Delete objects 00786 00787 for (int i = 0; i < nb_rsectors+1 ; i++) 00788 for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++) 00789 for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++) 00790 for (int l = 0; l<nb_axial_crystal*nb_trans_crystal; l++) 00791 { 00792 delete crystal_center[i][j][k][l]; 00793 } 00794 00795 00796 for(int i = 0; i < nb_rsectors+1 ; i++) 00797 for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++) 00798 for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++) 00799 { 00800 delete crystal_center[i][j][k]; 00801 } 00802 00803 00804 for(int i = 0; i < nb_rsectors+1 ; i++) 00805 for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++) 00806 { 00807 delete[] crystal_center[i][j]; 00808 } 00809 00810 00811 for(int i = 0; i < nb_rsectors+1 ; i++) 00812 delete[] crystal_center[i]; 00813 00814 for(int i = 0; i < nb_rsectors ; i++) 00815 delete rotation_mtx[i]; 00816 00817 delete[] crystal_center; 00818 delete[] rotation_mtx; 00819 } 00820 00821 // ============================================================================================================ 00822 // Save LUT if required 00823 // ============================================================================================================ 00824 00825 if(p_scannerManager->SaveLUTFlag()) 00826 { 00827 if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() -> SaveLUT ..." << endl); 00828 00829 string path_to_geom_file = p_scannerManager->GetPathToScannerFile(); 00830 00831 string path_to_LUT = path_to_geom_file.substr(0, path_to_geom_file.find_last_of(".")); 00832 string path_to_header_LUT = path_to_LUT + ".ghscan"; 00833 path_to_LUT.append(".glut"); 00834 00835 ofstream LUT_file, header_LUT_file; 00836 00837 LUT_file.open(path_to_LUT.c_str(), ios::binary | ios::out); 00838 00839 // Write the corresponding crystal parameters in the LUT 00840 for(int i=0 ; i<m_nbCrystals ; i++) 00841 { 00842 LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionX[i]), sizeof(FLTNB)); 00843 LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionY[i]), sizeof(FLTNB)); 00844 LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionZ[i]), sizeof(FLTNB)); 00845 LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationX[i]), sizeof(FLTNB)); 00846 LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationY[i]), sizeof(FLTNB)); 00847 LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationZ[i]), sizeof(FLTNB)); 00848 } 00849 00850 LUT_file.close(); 00851 if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() -> Binary LUT writing completed" << endl); 00852 00853 // ============================================================================================================ 00854 // --> Write header file 00855 // ============================================================================================================ 00856 00857 if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() -> Start writing header LUT file..." << endl); 00858 header_LUT_file.open(path_to_header_LUT.c_str(), ios::out); 00859 00860 string scanner_name = path_to_geom_file.substr(0, path_to_geom_file.find_last_of(".")); 00861 header_LUT_file << "scanner name:" << " " << GetFileFromPath(scanner_name) << endl; 00862 header_LUT_file << "modality:" << " " << "PET" << endl; 00863 00864 header_LUT_file << "scanner radius:" << " " << radius_lyr[0]; 00865 for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 00866 header_LUT_file << "," << radius_lyr[lyr] ; header_LUT_file << endl; 00867 00868 header_LUT_file << "number of rings in scanner:" << " " << mp_nbRings[0]; 00869 for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 00870 header_LUT_file << ","<< mp_nbRings[lyr] ; header_LUT_file << endl; 00871 header_LUT_file << "number of elements:" << " " << m_nbCrystals << endl; 00872 header_LUT_file << "number of layers:" << " " << m_nbLayers << endl; 00873 header_LUT_file << "number of crystals in layer(s):" << " " << mp_nbCrystalsInLayer[0]; // TODO nb_cry_in_layer 00874 for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 00875 header_LUT_file << ","<< mp_nbCrystalsInLayer[lyr] ; header_LUT_file << endl; 00876 00877 header_LUT_file << "crystals size depth:" << " " << mp_sizeCrystalDepth[0]; 00878 for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 00879 header_LUT_file << ","<< mp_sizeCrystalDepth[lyr] ; header_LUT_file << endl; 00880 00881 header_LUT_file << "crystals size transaxial:" << " " << mp_sizeCrystalTrans[0]; 00882 for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 00883 header_LUT_file << ","<< mp_sizeCrystalTrans[lyr] ; header_LUT_file << endl; 00884 00885 header_LUT_file << "crystals size axial:" << " " << mp_sizeCrystalAxial[0]; 00886 for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 00887 header_LUT_file << ","<< mp_sizeCrystalAxial[lyr] ; header_LUT_file << endl; 00888 00889 00890 //default reconstruction parameters 00891 uint32_t def_dim_trans = 0, def_dim_axial = 0; // default number of voxels 00892 FLTNB def_FOV_trans = -1., def_FOV_axial = -1; // computed from modules width and gaps of the 1st layer 00893 00894 if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "voxels number transaxial", &def_dim_trans, 1, KEYWORD_MANDATORY) || 00895 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "voxels number axial", &def_dim_axial, 1, KEYWORD_MANDATORY) || 00896 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "field of view transaxial", &def_FOV_trans, 1, KEYWORD_MANDATORY) || 00897 ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "field of view axial", &def_FOV_axial, 1, KEYWORD_MANDATORY) ) 00898 { 00899 Cerr("***** iScannerPET::ComputeLUT() -> Error occurred when trying to read transaxial/axial dimensions and voxel sizes from scanner geom file !" << endl); 00900 return 1; 00901 } 00902 00903 header_LUT_file << "voxels number transaxial:" << " " << def_dim_trans << endl; 00904 header_LUT_file << "voxels number axial:" << " " << def_dim_axial << endl; 00905 00906 header_LUT_file << "field of view transaxial:" << " " << def_FOV_trans << endl; 00907 header_LUT_file << "field of view axial:" << " " << def_FOV_axial << endl; 00908 00909 header_LUT_file << "min angle difference:" << " " << m_minAngleDifference << " #deg" << endl; 00910 00911 header_LUT_file << "mean depth of interaction:" << " " << mp_meanDepthOfInteraction[0]; 00912 for (int lyr=1 ; lyr<m_nbLayers ; lyr++) 00913 header_LUT_file << ","<< mp_meanDepthOfInteraction[lyr] ; 00914 header_LUT_file << " #optional (default value : center of crystal ). Input value must correspond to the distance from the crystal surface, or negative value if default" << endl; 00915 00916 header_LUT_file << "description:" << " " << "LUT generated from geom file: " << GetFileFromPath(p_scannerManager->GetPathToScannerFile()) << endl; 00917 00918 if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() -> Header LUT file writing completed" << endl); 00919 } 00920 00921 00922 00923 delete nb_rsectors_lyr; 00924 delete nb_trans_mod_lyr; 00925 delete nb_axial_mod_lyr; 00926 delete nb_trans_submod_lyr; 00927 delete nb_axial_submod_lyr; 00928 delete nb_trans_crystal_lyr; 00929 delete nb_axial_crystal_lyr; 00930 00931 delete radius_lyr; 00932 delete rsector_angular_span_lyr; 00933 delete rsector_first_angle_lyr; 00934 delete zshift; 00935 delete gap_trans_mod_lyr; 00936 delete gap_axial_mod_lyr; 00937 delete gap_trans_submod_lyr; 00938 delete gap_axial_submod_lyr; 00939 delete gap_trans_crystal_lyr; 00940 delete gap_axial_crystal_lyr; 00941 00942 if(m_verbose >= 2)Cout("iScannerPET::ComputeLUT() -> LUT generation completed" << endl); 00943 00944 return 0; 00945 } 00946 00947 00948 00949 // ===================================================================== 00950 // --------------------------------------------------------------------- 00951 // --------------------------------------------------------------------- 00952 // ===================================================================== 00953 /* 00954 \fn GetPositionsAndOrientations 00955 \param a_index1 : index of the 1st crystal 00956 \param a_index2 : index of the 2nd crystal 00957 \param ap_Position1[3] : x,y,z cartesian position of center of the 1st crystal 00958 \param ap_Position2[3] : x,y,z cartesian position of center of the 2nd crystal 00959 \param ap_Orientation1[3] : x,y,z components of the orientation vector related to the 1st crystal 00960 \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the 2nd crystal 00961 \param ap_POI1 : x,y,z components of the Point Of Interation related to the 1st crystal 00962 \param ap_POI2 : x,y,z components of the Point Of Interation related to the 2nd crystal 00963 \brief Get the central positions and orientations of the scanner elements from their indices. 00964 \details This method is very general and is used by the vProjector. 00965 From these positions and orientations, other methods can be used by specific projectors to get specific positions. 00966 Position calculations include POI and mean depth of interaction 00967 \todo some cases depending on POI are not implemented 00968 \return 0 if success, positive value otherwise 00969 */ 00970 int iScannerPET::GetPositionsAndOrientations( int a_index1, int a_index2, 00971 FLTNB ap_Position1[3], FLTNB ap_Position2[3], 00972 FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3], 00973 FLTNB* ap_POI1, FLTNB* ap_POI2 ) 00974 { 00975 #ifdef CASTOR_VERBOSE 00976 if(m_verbose>=4) Cout("iScannerPET::GetPositionsAndOrientations ..." << endl); 00977 #endif 00978 00979 // First check crystals existency 00980 if (a_index1<0 || a_index1>=m_nbCrystals) 00981 { 00982 Cerr("***** iScannerPET::GetPositionsAndOrientations() -> Crystal index 1 (" << a_index1 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl); 00983 return 1; 00984 } 00985 00986 if (a_index2<0 || a_index2>=m_nbCrystals) 00987 { 00988 Cerr("***** iScannerPET::GetPositionsAndOrientations() -> Crystal index 2 (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl); 00989 return 1; 00990 } 00991 00992 // The POI specification is as follows: 00993 // - POI[0] and POI[1] express the local x and y coordinates: x is the tangential axis, and y is colinear to the global z axis. 00994 // Their origins are in the middle of the scanner elements so they range from minus half to half the element dimensions. 00995 // - POI[2] express the DOI along the local z axis which is colinear to the crystal orientation (which itself is oriented from 00996 // the center of the scanner to the exterior). 00997 // Its origin is at the entrance of the element and thus ranges from 0 to the local z size of the element (crystal depth). 00998 // - If POI[2] has a negative value, this means that the mean depth of interaction should be considered instead, while still 00999 // using POI[0] and POI[1]. 01000 // Remember here that the crystal position is at its center. 01001 01002 // Case when POI is not provided (we use the mean depth of interaction) 01003 if (ap_POI1==NULL) 01004 { 01005 FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index1)] - mp_sizeCrystalDepth[GetLayer(a_index1)]/2; 01006 ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth*mp_crystalOrientationX[a_index1]; 01007 ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth*mp_crystalOrientationY[a_index1]; 01008 ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth*mp_crystalOrientationZ[a_index1]; 01009 } 01010 // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account) 01011 else if (ap_POI1[2]<0.) 01012 { 01013 // TODO 01014 } 01015 // Case when only the DOI is provided 01016 else if (ap_POI1[0]==0. && ap_POI1[1]==0.) 01017 { 01018 FLTNB depth = ap_POI1[2] - mp_sizeCrystalDepth[GetLayer(a_index1)]/2; 01019 ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth*mp_crystalOrientationX[a_index1]; 01020 ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth*mp_crystalOrientationY[a_index1]; 01021 ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth*mp_crystalOrientationZ[a_index1]; 01022 } 01023 // Case when the full POI is taken into account 01024 else 01025 { 01026 // TODO 01027 } 01028 01029 // Case when POI is not provided (we use the mean depth of interaction) 01030 if (ap_POI2==NULL) 01031 { 01032 FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index2)] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2; 01033 ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2]; 01034 ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2]; 01035 ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2]; 01036 } 01037 // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account) 01038 else if (ap_POI2[2]<0.) 01039 { 01040 // TODO 01041 } 01042 // Case when only the DOI is provided 01043 else if (ap_POI2[0]==0. && ap_POI2[1]==0.) 01044 { 01045 FLTNB depth = ap_POI2[2] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2; 01046 ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2]; 01047 ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2]; 01048 ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2]; 01049 } 01050 // Case when the full POI is taken into account 01051 else 01052 { 01053 // TODO 01054 } 01055 01056 // Get orientations 01057 ap_Orientation1[0] = mp_crystalOrientationX[a_index1]; 01058 ap_Orientation1[1] = mp_crystalOrientationY[a_index1]; 01059 ap_Orientation1[2] = mp_crystalOrientationZ[a_index1]; 01060 ap_Orientation2[0] = mp_crystalOrientationX[a_index2]; 01061 ap_Orientation2[1] = mp_crystalOrientationY[a_index2]; 01062 ap_Orientation2[2] = mp_crystalOrientationZ[a_index2]; 01063 01064 // End 01065 return 0; 01066 } 01067 01068 01069 01070 01071 // ===================================================================== 01072 // --------------------------------------------------------------------- 01073 // --------------------------------------------------------------------- 01074 // ===================================================================== 01075 /* 01076 \fn GetRdmPositionsAndOrientations 01077 \param a_index1 : index of the 1st crystal 01078 \param a_index2 : index of the 2nd crystal 01079 \param ap_Position1[3] : x,y,z cartesian position of center of the 1st crystal 01080 \param ap_Position2[3] : x,y,z cartesian position of center of the 2nd crystal 01081 \param ap_Orientation1[3] : x,y,z components of the orientation vector related to the 1st crystal 01082 \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the 2nd crystal 01083 \brief Get random positions of the scanner elements and their orientations from their indices. 01084 \details - Find the scanner elements described by the two indexes passed in parameters. 01085 \details - Compute random positions inside the elements described by the indexes passed in parameters 01086 \details - Find the scanner elements described by the two indexes passed in parameters. 01087 \details - Write the corresponding random cartesian coordinates in the positions parameters. 01088 \details Position calculations include POI and mean depth of interaction 01089 \todo fix the possibility to draw LOR outside the actual crystal volume (if mp_meanDepthOfInteraction != 0.5) 01090 \todo some cases depending on POI are not implemented 01091 \return 0 if success, positive value otherwise 01092 */ 01093 int iScannerPET::GetRdmPositionsAndOrientations(int a_index1, int a_index2, 01094 FLTNB ap_Position1[3], FLTNB ap_Position2[3], 01095 FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3] ) 01096 { 01097 #ifdef CASTOR_VERBOSE 01098 if(m_verbose>=4) Cout("iScannerPET::GetRdmPositionsAndOrientations ..." << endl); 01099 #endif 01100 01101 // First check crystals existency 01102 if (a_index1<0 || a_index1>=m_nbCrystals) 01103 { 01104 Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal index 1 (" << a_index1 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl); 01105 return 1; 01106 } 01107 if (a_index2<0 || a_index2>=m_nbCrystals) 01108 { 01109 Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal index 2 (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl); 01110 return 1; 01111 } 01112 01113 if (mp_sizeCrystalTrans[GetLayer(a_index1)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index1)]<=0. || 01114 mp_sizeCrystalTrans[GetLayer(a_index2)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index2)]<=0. ) 01115 { 01116 Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal sizes are unknown or equal to 0. Crystal dimensions are mandatory for this function !" << endl); 01117 return 1; 01118 } 01119 01120 sRNG* p_RNG = sRNG::GetInstance(); 01121 01122 // Get random numbers for the first crystal 01123 FLTNB rdm_axial1 = p_RNG->GenerateRdmNber(); 01124 FLTNB rdm_trans1 = p_RNG->GenerateRdmNber(); 01125 FLTNB rdm_depth1 = p_RNG->GenerateRdmNber(); 01126 01127 FLTNB size_crystalTrans1 = mp_sizeCrystalTrans[GetLayer(a_index1)]; 01128 FLTNB size_crystalAxial1 = mp_sizeCrystalAxial[GetLayer(a_index1)]; 01129 FLTNB size_crystalDepth1 = mp_sizeCrystalDepth[GetLayer(a_index1)]; 01130 01131 FLTNB axial1 = (rdm_axial1-0.5) * size_crystalAxial1; 01132 FLTNB trans1 = (rdm_trans1-0.5) * size_crystalTrans1; 01133 FLTNB depth1 = (rdm_depth1-0.5) * size_crystalDepth1; 01134 01135 ap_Position1[0] = mp_crystalCentralPositionX[a_index1] 01136 + depth1 * mp_crystalOrientationX[a_index1] 01137 + trans1 * mp_crystalOrientationY[a_index1] 01138 + axial1 * mp_crystalOrientationX[a_index1] * mp_crystalOrientationZ[a_index1]; 01139 01140 ap_Position1[1] = mp_crystalCentralPositionY[a_index1] 01141 + depth1 * mp_crystalOrientationY[a_index1] 01142 + trans1 * mp_crystalOrientationX[a_index1] 01143 + axial1 * mp_crystalOrientationY[a_index1] * mp_crystalOrientationZ[a_index1]; 01144 01145 ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] 01146 + depth1 * mp_crystalOrientationZ[a_index1] 01147 + axial1 * sqrt(1-mp_crystalOrientationZ[a_index1] * mp_crystalOrientationZ[a_index1]); 01148 01149 // Get orientations 01150 ap_Orientation1[0] = mp_crystalOrientationX[a_index1]; 01151 ap_Orientation1[1] = mp_crystalOrientationY[a_index1]; 01152 ap_Orientation1[2] = mp_crystalOrientationZ[a_index1]; 01153 01154 // Get random numbers for the second crystal 01155 FLTNB rdm_axial2 = p_RNG->GenerateRdmNber(); 01156 FLTNB rdm_trans2 = p_RNG->GenerateRdmNber(); 01157 FLTNB rdm_depth2 = p_RNG->GenerateRdmNber(); 01158 01159 FLTNB size_crystalTrans2 = mp_sizeCrystalTrans[GetLayer(a_index2)]; 01160 FLTNB size_crystalAxial2 = mp_sizeCrystalAxial[GetLayer(a_index2)]; 01161 FLTNB size_crystalDepth2 = mp_sizeCrystalDepth[GetLayer(a_index2)]; 01162 01163 FLTNB axial2 = (rdm_axial2-0.5) * size_crystalAxial2; 01164 FLTNB trans2 = (rdm_trans2-0.5) * size_crystalTrans2; 01165 FLTNB depth2 = (rdm_depth2-0.5) * size_crystalDepth2; 01166 01167 ap_Position2[0] = mp_crystalCentralPositionX[a_index2] 01168 + depth2 * mp_crystalOrientationX[a_index2] 01169 + trans2 * mp_crystalOrientationY[a_index2] 01170 + axial2 * mp_crystalOrientationX[a_index2] * mp_crystalOrientationZ[a_index2]; 01171 01172 ap_Position2[1] = mp_crystalCentralPositionY[a_index2] 01173 + depth2 * mp_crystalOrientationY[a_index2] 01174 + trans2 * mp_crystalOrientationX[a_index2] 01175 + axial2 * mp_crystalOrientationY[a_index2] * mp_crystalOrientationZ[a_index2]; 01176 01177 ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] 01178 + depth2 * mp_crystalOrientationZ[a_index2] 01179 + axial2 * sqrt(1-mp_crystalOrientationZ[a_index2] * mp_crystalOrientationZ[a_index2]); 01180 01181 // Get orientations 01182 ap_Orientation2[0] = mp_crystalOrientationX[a_index2]; 01183 ap_Orientation2[1] = mp_crystalOrientationY[a_index2]; 01184 ap_Orientation2[2] = mp_crystalOrientationZ[a_index2]; 01185 01186 // End 01187 return 0; 01188 } 01189 01190 01191 01192 01193 // ===================================================================== 01194 // --------------------------------------------------------------------- 01195 // --------------------------------------------------------------------- 01196 // ===================================================================== 01197 /* 01198 \fn GetPositionWithRandomDepth 01199 \param a_index1 : index of the 1st crystal 01200 \param a_index2 : index of the 2nd crystal 01201 \param ap_Position1[3] : x,y,z cartesian position of the point related to the 1st index (see child function for more details) 01202 \param ap_Position2[3] : x,y,z cartesian position of the point related to the 2st index (see child function for more details) 01203 \brief Get the positions and orientations of scanner elements from their indices, with a random depth. 01204 \details Method for testing purposes. Does not include POI and mean depth of interaction 01205 \return 0 if success, positive value otherwise 01206 */ 01207 int iScannerPET::GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3]) 01208 { 01209 #ifdef CASTOR_VERBOSE 01210 if(m_verbose>=4) Cout("iScannerPET::GetPositionWithRandomDepth ..." << endl); 01211 #endif 01212 01213 // Get instance of random number generator 01214 sRNG* p_RNG = sRNG::GetInstance(); 01215 01216 // Shoot first random number 01217 FLTNB shoot1 = p_RNG->GenerateRdmNber(); 01218 // Compute associated depth 01219 FLTNB depth1 = (shoot1-0.5) * mp_sizeCrystalDepth[GetLayer(a_index1)]; 01220 01221 // Compute associated position 01222 ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth1*mp_crystalOrientationX[a_index1]; 01223 ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth1*mp_crystalOrientationY[a_index1]; 01224 ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth1*mp_crystalOrientationZ[a_index1]; 01225 01226 // Shoot second random number 01227 FLTNB shoot2 = p_RNG->GenerateRdmNber(); 01228 // Compute associated depth 01229 FLTNB depth2 = (shoot2-0.5) * mp_sizeCrystalDepth[GetLayer(a_index2)]; 01230 // Compute associated position 01231 ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth2*mp_crystalOrientationX[a_index2]; 01232 ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth2*mp_crystalOrientationY[a_index2]; 01233 ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth2*mp_crystalOrientationZ[a_index2]; 01234 // End 01235 return 0; 01236 } 01237 01238 01239 01240 01241 // ===================================================================== 01242 // --------------------------------------------------------------------- 01243 // --------------------------------------------------------------------- 01244 // ===================================================================== 01245 /* 01246 \fn GetTwoCorners 01247 \param a_index1 : index of the 1st crystal 01248 \param a_index2 : index of the 2nd crystal 01249 \param ap_CornerInf1[3] 01250 \param ap_CornerSup1[3] 01251 \param ap_CornerInf2[3] 01252 \param ap_CornerSup2[3] 01253 \brief Get the cartesian coordinaters of the two opposite corners of a scanner element. 01254 \todo Not implemented yet 01255 \return 0 if success, positive value otherwise 01256 */ 01257 int iScannerPET::GetTwoCorners(int a_index1, int a_index2, 01258 FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3], 01259 FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3]) 01260 { 01261 01262 #ifdef CASTOR_VERBOSE 01263 if(m_verbose>=4) Cout("iScannerPET::GetTwoCorners ..." << endl); 01264 #endif 01265 01266 if (a_index1<0 || a_index1>=m_nbCrystals || a_index2<0 || a_index2>=m_nbCrystals) 01267 { 01268 Cerr("***** iScannerPET::GetTwoCorners() -> Crystal index out of range !" << endl); 01269 return 1; 01270 } 01271 01272 if (mp_sizeCrystalTrans[GetLayer(a_index1)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index1)]<=0. || 01273 mp_sizeCrystalTrans[GetLayer(a_index2)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index2)]<=0. ) 01274 { 01275 Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal sizes are unknown or equal to 0. Crystal dimensions are mandatory for this function !" << endl); 01276 return 1; 01277 } 01278 01279 Cerr("***** iScannerPET::GetTwoCorners() -> Not implemented yet !" << endl); 01280 return 1; 01281 } 01282 01283 01284 01285 01286 // ===================================================================== 01287 // --------------------------------------------------------------------- 01288 // --------------------------------------------------------------------- 01289 // ===================================================================== 01290 /* 01291 \fn GetLayer 01292 \param a_idx : index of the crystal in the loaded LUT 01293 \brief Get the layer from which the 'a_idx' crystal belongs to 01294 \return layer index 01295 */ 01296 int iScannerPET::GetLayer(int a_idx) 01297 { 01298 #ifdef CASTOR_VERBOSE 01299 if(m_verbose>=4) Cout("iScannerSPECTConv::GetLayer ..." << endl); 01300 #endif 01301 01302 int layer=0; 01303 int sum_crystals = mp_nbCrystalsInLayer[layer]; 01304 01305 // Get the layer which the crystal belongs to 01306 while (a_idx >= sum_crystals) 01307 { 01308 layer++; 01309 sum_crystals += mp_nbCrystalsInLayer[layer]; 01310 } 01311 01312 return layer; 01313 } 01314 01315 01316 01317 01318 // ===================================================================== 01319 // --------------------------------------------------------------------- 01320 // --------------------------------------------------------------------- 01321 // ===================================================================== 01322 /* 01323 \fn GetRingID 01324 \param a_idx : index of the crystal in the loaded LUT 01325 \brief Get the ring which the #a_idx crystal belongs to 01326 \todo /!\ Current implementation only works with scanner whose elements are ring-indexed 01327 \todo Need to find a solution to deal with potential geometry containing several layers of crystals with different number of rings 01328 We could use the geometrical position of each elements to get this information, 01329 however maybe too complicated for potential scanners with non-standard geometry 01330 \return layer index 01331 */ 01332 int iScannerPET::GetRingID(int a_idx) 01333 { 01334 #ifdef CASTOR_VERBOSE 01335 if(m_verbose>=4) Cout("iScannerSPECTConv::GetRingID ..." << endl); 01336 #endif 01337 01338 int layer = GetLayer(a_idx); 01339 int start_index = 0; 01340 01341 if(layer>0) 01342 { 01343 // Get the index of the first crystal of the layer 01344 int layer_id = 0; 01345 while(layer_id < layer) 01346 { 01347 start_index += mp_nbCrystalsInLayer[layer_id]; 01348 layer_id++; 01349 } 01350 } 01351 01352 // Get the number of crystals in ring for this layer 01353 int nb_crystals_in_ring = mp_nbCrystalsInLayer[layer]/mp_nbRings[layer]; 01354 01355 // Recover the ring ID 01356 int ringID = (int)((a_idx-start_index)/nb_crystals_in_ring); 01357 01358 // TODO : Need to know how to deal with potential geometry containing several layers of crystals with different number of rings ? 01359 // In this actual situation, crystals from different layers could have similar rings, but not similar axial positions 01360 // which is what we want to know 01361 01362 return ringID; 01363 } 01364 01365 01366 01367 01368 // ===================================================================== 01369 // --------------------------------------------------------------------- 01370 // --------------------------------------------------------------------- 01371 // ===================================================================== 01372 /* 01373 \fn IsAvailableLOR 01374 \param a_elt1 : index of the 1st crystal 01375 \param a_elt2 : index of the 2nd crystal 01376 \brief Check if the LOR formed by the crystalf whose indices are passed 01377 in parameters is available according to the scanner restrictions 01378 \details This function is dedicated to analytic projection and list-mode sensitivity image generation 01379 The PET implementation checks the LOR availability according to the minimal (transaxial) angle difference 01380 and the maximal ring difference between two crystals 01381 \todo min angle difference (currently just system dependent) may be estimated from the FOV requested by the user ? 01382 \todo Restriction for ring_ID NOT safe for user using their own LUT !!! 01383 Perhaps throw warning in this case 01384 \return 1 if the LOR is available, 0 otherwise 01385 */ 01386 int iScannerPET::IsAvailableLOR(int a_elt1, int a_elt2) 01387 { 01388 #ifdef CASTOR_VERBOSE 01389 if(m_verbose>=4) Cout("iScannerSPECTConv::IsAvailableLOR ..." << endl); 01390 #endif 01391 01392 // Get absolute angle between the two crystals 01393 FLTNB abs_angle = mp_crystalOrientationX[a_elt1]*mp_crystalOrientationX[a_elt2] 01394 + mp_crystalOrientationY[a_elt1]*mp_crystalOrientationY[a_elt2]; 01395 01396 // Handle boundaries (arg of acos() outside range [-1. ; +1]) 01397 abs_angle = (abs_angle>1.) ? 1 : abs_angle; 01398 abs_angle = (abs_angle<-1.) ? -1 : abs_angle; 01399 01400 FLTNB angle_diff = acos(abs_angle); 01401 01402 // Restrictions on LORs between different rings 01403 int ring_diff = 0; 01404 ring_diff = abs(GetRingID(a_elt1)-GetRingID(a_elt2)); 01405 01406 // Check restrictions 01407 // TODO : Restriction for ring_ID still NOT safe for user using their own LUT !!! 01408 // TODO : min angle difference (currently just system dependent) may be estimated from the FOV requested by the user ? 01409 if ( ring_diff<m_maxRingDiff && angle_diff>=m_minAngleDifference ) 01410 { 01411 return 1; 01412 } 01413 01414 return 0; 01415 } 01416 01417 01418 01419 01420 // ===================================================================== 01421 // --------------------------------------------------------------------- 01422 // --------------------------------------------------------------------- 01423 // ===================================================================== 01424 /* 01425 \fn GetGeometricInfoFromDatafile 01426 \brief Retrieve PET geometric informations from the datafile 01427 \details Recover the (axial) max ring difference 01428 \return 0 if success, positive value otherwise 01429 */ 01430 int iScannerPET::GetGeometricInfoFromDatafile(string a_pathToDF) 01431 { 01432 if(m_verbose>=2) Cout("iScannerPET::GetGeometricInfoFromDatafile ..." << endl); 01433 01434 // These values may change depending on the optional parameters from the datafile header 01435 if (ReadDataASCIIFile(a_pathToDF, "Maximum ring difference", &m_maxRingDiff, 1, KEYWORD_OPTIONAL)==1) 01436 { 01437 Cerr("***** iScannerPET::GetGeometricInfoFromDatafile() -> Error while reading max number of ring difference in the header data file '" << endl); 01438 return 1; 01439 } 01440 return 0; 01441 } 01442 01443 01444 01445 01446 // ===================================================================== 01447 // --------------------------------------------------------------------- 01448 // --------------------------------------------------------------------- 01449 // ===================================================================== 01450 /* 01451 \fn PROJ_GetPETSpecificParameters 01452 \param ap_maxRingDiff 01453 \brief Set pointers passed in argument with the related PET specific variables 01454 This function is used to recover these values in the datafile object 01455 \return 0 if success, positive value otherwise 01456 */ 01457 int iScannerPET::PROJ_GetPETSpecificParameters(int* ap_maxRingDiff) 01458 { 01459 if(m_verbose>=2) Cout("iScannerPET::PROJ_GetPETSpecificParameters ..." << endl); 01460 01461 // Verify that all parameters have been correctly checked 01462 if(m_allParametersChecked == false) 01463 { 01464 Cerr("***** iScannerSPECTConv::GetSPECTSpecificParameters() -> Parameters have not been checked !" << endl); 01465 return 1; 01466 } 01467 01468 *ap_maxRingDiff = m_maxRingDiff; 01469 return 0; 01470 } 01471 01472 01473 01474 01475 // ===================================================================== 01476 // --------------------------------------------------------------------- 01477 // --------------------------------------------------------------------- 01478 // ===================================================================== 01479 /* 01480 \fn ShowHelp 01481 \brief Display help 01482 \todo Provide informations about PET system initialization ? 01483 */ 01484 void iScannerPET::ShowHelp() 01485 { 01486 if(m_verbose>=2) Cout("iScannerPET::ShowHelp ..." << endl); 01487 01488 cout << "This scanner class is dedicated to the description of PET systems." << endl; 01489 }