![]() |
CASToR
1.0
Tomographic Reconstruction (PET/SPECT)
|
00001 00002 /* 00003 Implementation of class iScannerSPECTConv 00004 00005 - separators: X 00006 - doxygen: X 00007 - default initialization: X 00008 - CASTOR_DEBUG: none 00009 - CASTOR_VERBOSE: X 00010 */ 00011 00018 #include "iScannerSPECTConv.hh" 00019 #include "sOutputManager.hh" 00020 #include "sScannerManager.hh" 00021 00022 00023 00024 00025 00026 // ===================================================================== 00027 // --------------------------------------------------------------------- 00028 // --------------------------------------------------------------------- 00029 // ===================================================================== 00030 /* 00031 \brief iScannerSPECTConv constructor. 00032 Initialize the member variables to their default values. 00033 */ 00034 iScannerSPECTConv::iScannerSPECTConv() : vScanner() 00035 { 00036 // Set member variables to default values 00037 m_scannerType = 2; 00038 m_nbCrystals = -1; 00039 m_nbHeads = -1; 00040 m_nbPixelsTrans = 0; 00041 m_pixelsSizeTrans = -1.; 00042 m_gapSizeTrans = -1.; 00043 00044 m_nbPixelsAxial = 0; 00045 m_pixelsSizeAxial = -1.; 00046 m_gapSizeAxial = -1.; 00047 00048 m_vPixelsSizeTrans = -1.; 00049 m_vPixelsSizeAxial = -1.; 00050 00051 m_vNbPixelsTrans = 0; 00052 m_vNbPixelsAxial = 0; 00053 00054 mp_nbOfBins[0] = 0; 00055 mp_nbOfBins[1] = 0; 00056 00057 m_crystalDepth = -1.; 00058 00059 mp_focalModelTrans = NULL; 00060 mp_nbCoefModelTrans = NULL; 00061 m2p_transFocalParameters = NULL; 00062 00063 mp_focalModelAxial = NULL; 00064 mp_nbCoefModelAxial = NULL; 00065 m2p_axialFocalParameters = NULL; 00066 00067 m_nbOfProjections = 0; 00068 mp_projectionAngles = NULL; 00069 mp_radius = NULL; 00070 mp_CORtoDetectorDistance = NULL; 00071 m_rotDirection = GEO_ROT_CW; 00072 00073 mp_crystalCentralPositionX = NULL; 00074 mp_crystalCentralPositionY = NULL; 00075 mp_crystalCentralPositionZ = NULL; 00076 00077 mp_crystalOrientationX = NULL; 00078 mp_crystalOrientationY = NULL; 00079 mp_crystalOrientationZ = NULL; 00080 00081 mp_crystalFocalPositionX = NULL; 00082 mp_crystalFocalPositionY = NULL; 00083 mp_crystalFocalPositionZ = NULL; 00084 00085 mp_positionMatrix_ref = NULL; 00086 mp_positionMatrix_out = NULL; 00087 mp_rotationMatrix = NULL; 00088 } 00089 00090 00091 00092 00093 // ===================================================================== 00094 // --------------------------------------------------------------------- 00095 // --------------------------------------------------------------------- 00096 // ===================================================================== 00097 /* 00098 \brief iScannerSPECTConv destructor. 00099 */ 00100 iScannerSPECTConv::~iScannerSPECTConv() 00101 { 00102 if( mp_focalModelTrans ) delete[] mp_focalModelTrans; 00103 if( mp_nbCoefModelTrans ) delete[] mp_nbCoefModelTrans; 00104 00105 if( m2p_transFocalParameters ) 00106 { 00107 for( int i = 0; i < m_nbHeads; ++i ) 00108 delete m2p_transFocalParameters[ i ]; 00109 00110 delete[] m2p_transFocalParameters; 00111 } 00112 00113 00114 if( mp_focalModelAxial ) delete[] mp_focalModelAxial; 00115 if( mp_nbCoefModelAxial ) delete[] mp_nbCoefModelAxial; 00116 00117 if( m2p_axialFocalParameters ) 00118 { 00119 for( int i = 0; i < m_nbHeads; ++i ) 00120 delete m2p_axialFocalParameters[ i ]; 00121 00122 delete[] m2p_axialFocalParameters; 00123 } 00124 00125 if( mp_projectionAngles ) delete[] mp_projectionAngles; 00126 if( mp_radius ) delete[] mp_radius; 00127 if( mp_CORtoDetectorDistance ) delete[] mp_CORtoDetectorDistance; 00128 00129 if (mp_positionMatrix_ref) delete mp_positionMatrix_ref; 00130 if (mp_positionMatrix_out) delete mp_positionMatrix_out; 00131 if (mp_rotationMatrix) delete mp_rotationMatrix; 00132 00133 00134 if(mp_crystalCentralPositionX) delete mp_crystalCentralPositionX; 00135 if(mp_crystalCentralPositionY) delete mp_crystalCentralPositionY; 00136 if(mp_crystalCentralPositionZ) delete mp_crystalCentralPositionZ; 00137 00138 if(mp_crystalOrientationX) delete mp_crystalOrientationX; 00139 if(mp_crystalOrientationY) delete mp_crystalOrientationY; 00140 if(mp_crystalOrientationZ) delete mp_crystalOrientationZ; 00141 00142 if(mp_crystalFocalPositionX) delete mp_crystalFocalPositionX; 00143 if(mp_crystalFocalPositionY) delete mp_crystalFocalPositionY; 00144 if(mp_crystalFocalPositionZ) delete mp_crystalFocalPositionZ; 00145 } 00146 00147 00148 00149 00150 // ===================================================================== 00151 // --------------------------------------------------------------------- 00152 // --------------------------------------------------------------------- 00153 // ===================================================================== 00154 00155 int iScannerSPECTConv::Instantiate(bool a_scannerFileIsLUT) 00156 { 00157 if(m_verbose>=2) Cout("iScannerSPECTConv::Instantiate ..."<< endl); 00158 00159 sScannerManager* p_scannerManager; 00160 p_scannerManager = sScannerManager::GetInstance(); 00161 00162 // Get the number of layers in the scanner 00163 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of detector heads", &m_nbHeads, 1, KEYWORD_MANDATORY)) 00164 { 00165 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the number of SPECT heads !" << endl); 00166 return 1; 00167 } 00168 00169 mp_radius = new FLTNB[m_nbHeads]; 00170 00171 // Get default radius 00172 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "scanner radius", mp_radius, m_nbHeads, KEYWORD_MANDATORY)) 00173 { 00174 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the number of SPECT heads !" << endl); 00175 return 1; 00176 } 00177 00178 for( int i = 0; i < m_nbHeads; i++ ) 00179 if(mp_radius[i] == 0) 00180 { 00181 Cerr("***** iScannerSPECTConv::Instantiate() -> scanner radius == 0 ? really ? :) ... " << endl); 00182 return 1; 00183 } 00184 00185 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans number of pixels", &m_nbPixelsTrans, 1, KEYWORD_MANDATORY)) 00186 { 00187 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial number of pixels !" << endl); 00188 return 1; 00189 } 00190 00191 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans pixel size", &m_pixelsSizeTrans, 1, KEYWORD_MANDATORY)) 00192 { 00193 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial pixel size !" << endl); 00194 return 1; 00195 } 00196 00197 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial number of pixels", &m_nbPixelsAxial, 1, KEYWORD_MANDATORY)) 00198 { 00199 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial number of pixels !" << endl); 00200 return 1; 00201 } 00202 00203 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial pixel size", &m_pixelsSizeAxial, 1, KEYWORD_MANDATORY)) 00204 { 00205 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial pixel size !" << endl); 00206 return 1; 00207 } 00208 00209 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "detector depth", &m_crystalDepth, 1, KEYWORD_MANDATORY)) 00210 { 00211 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read detector depth !" << endl); 00212 return 1; 00213 } 00214 00215 00216 string *head_name = new string[ m_nbHeads + 1 ]; 00217 for( int i = 0; i < m_nbHeads; i++ ) 00218 { 00219 ostringstream oss( ostringstream::out ); 00220 oss << "head" << i+1; 00221 head_name[ i ] = oss.str(); 00222 } 00223 head_name[ m_nbHeads ] = "eof"; 00224 00225 00226 if(m_verbose>=2) Cout("iScannerSPECTConv::Allocating memory ..."<< endl); 00227 00228 // Allocating memories 00229 mp_focalModelTrans = new string[ m_nbHeads ]; 00230 mp_focalModelAxial = new string[ m_nbHeads ]; 00231 mp_nbCoefModelTrans = new uint8_t[ m_nbHeads ]; 00232 mp_nbCoefModelAxial = new uint8_t[ m_nbHeads ]; 00233 m2p_transFocalParameters = new FLTNB*[ m_nbHeads ]; 00234 m2p_axialFocalParameters = new FLTNB*[ m_nbHeads ]; 00235 00236 00237 00238 // Read informations about SPECT head 00239 for( int i = 0; i < m_nbHeads; i++ ) 00240 { 00241 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans focal model", &mp_focalModelTrans[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ])) 00242 { 00243 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial focal model of head " << i << " !" << endl); 00244 return 1; 00245 } 00246 00247 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans number of coef model", &mp_nbCoefModelTrans[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ])) 00248 { 00249 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial number of coef model of head " << i << " !" << endl); 00250 return 1; 00251 } 00252 00253 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial focal model", &mp_focalModelAxial[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ])) 00254 { 00255 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read axial focal model of head " << i << " !" << endl); 00256 return 1; 00257 } 00258 00259 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial number of coef model", &mp_nbCoefModelAxial[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ])) 00260 { 00261 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial focal model of head " << i << " !" << endl); 00262 return 1; 00263 } 00264 00265 m2p_transFocalParameters[i] = new FLTNB[mp_nbCoefModelTrans[ i ]]; 00266 m2p_axialFocalParameters[i] = new FLTNB[mp_nbCoefModelAxial[ i ]]; 00267 00268 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans parameters", m2p_transFocalParameters[i], mp_nbCoefModelTrans[ i ], KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ])) 00269 { 00270 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial focal model of head " << i << " !" << endl); 00271 return 1; 00272 } 00273 00274 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial parameters", m2p_axialFocalParameters[i], mp_nbCoefModelAxial[ i ], KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ])) 00275 { 00276 Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial focal model of head " << i << " !" << endl); 00277 return 1; 00278 } 00279 } 00280 00281 // Instanciation of objects for matrix calculation 00282 mp_positionMatrix_ref = new oMatrix(3,1); 00283 mp_positionMatrix_out = new oMatrix(3,1); 00284 mp_rotationMatrix = new oMatrix(3,3); 00285 00286 delete[] head_name; 00287 00288 return 0; 00289 } 00290 00291 00292 00293 00294 // ===================================================================== 00295 // --------------------------------------------------------------------- 00296 // --------------------------------------------------------------------- 00297 // ===================================================================== 00298 /* 00299 \fn BuildLUT 00300 \param a_scannerFileIsLUT : boolean indicating if the file describing 00301 the SPECT camera is a generic file (0) or custom Look-up-table (1) 00302 \brief Call the functions to generate the LUT or read the user-made LUT depending on the user choice 00303 \return 0 if success, positive value otherwise 00304 */ 00305 int iScannerSPECTConv::BuildLUT(bool a_scannerFileIsLUT) 00306 { 00307 if(m_verbose>=2) Cout("iScannerSPECTConv::BuildLUT ..."<< endl); 00308 00309 // Initialize nb_crystals 00310 if(mp_nbOfBins[0] == 0 && mp_nbOfBins[1] == 0) // number of bins not initialized in the datafile, then we read the number of crystals from the geom file 00311 { 00312 if(m_nbPixelsTrans==0 || m_nbPixelsAxial==0) 00313 { 00314 Cerr("***** iScannerSPECTConv::BuildLUT() -> Error, transaxial or axial number of pixels in the geometric file should be >0 !" << endl); 00315 return 1; 00316 } 00317 00318 m_nbCrystals = m_nbPixelsTrans*m_nbPixelsAxial; 00319 } 00320 else // initialization with number of bins 00321 { 00322 m_nbCrystals = mp_nbOfBins[0]*mp_nbOfBins[1]; 00323 } 00324 00325 mp_crystalCentralPositionX = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00326 mp_crystalCentralPositionY = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00327 mp_crystalCentralPositionZ = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00328 00329 mp_crystalOrientationX = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00330 mp_crystalOrientationY = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00331 mp_crystalOrientationZ = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00332 00333 mp_crystalFocalPositionX = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00334 mp_crystalFocalPositionY = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00335 mp_crystalFocalPositionZ = new FLTNB[m_nbOfProjections*m_nbCrystals]; 00336 00337 // Either generate the LUT from a generic file, or load the user precomputed LUT 00338 if (!a_scannerFileIsLUT) 00339 { 00340 if (ComputeLUT()) 00341 { 00342 Cerr("***** iScannerSPECTConv::BuildLUT() -> A problem occured while generating scanner LUT !" << endl); 00343 return 1; 00344 } 00345 } 00346 else 00347 { 00348 if (LoadLUT()) 00349 { 00350 Cerr("***** iScannerSPECTConv::BuildLUT() -> A problem occured while loading scanner LUT !" << endl); 00351 return 1; 00352 } 00353 } 00354 00355 return 0; 00356 } 00357 00358 00359 00360 00361 // ===================================================================== 00362 // --------------------------------------------------------------------- 00363 // --------------------------------------------------------------------- 00364 // ===================================================================== 00365 /* 00366 \fn CheckParameters 00367 \brief Check that all parameters have been correctly initialized. 00368 \return 0 if success, positive value otherwise 00369 \todo Keep the check on crystal depth ? 00370 */ 00371 int iScannerSPECTConv::CheckParameters() 00372 { 00373 if(m_verbose>=2) Cout("iScannerSPECTConv::CheckParameters ..."<< endl); 00374 00375 // Check if all parameters have been correctly initialized. Return error otherwise 00376 00377 if(m_nbCrystals<0) 00378 { 00379 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, number of crystals has not been initialized !" <<endl); 00380 return 1; 00381 } 00382 00383 if(m_nbHeads<0) 00384 { 00385 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, number of heads in the SPECT system has not been initialized !" <<endl); 00386 return 1; 00387 } 00388 00389 if(m_nbOfProjections<=0) 00390 { 00391 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, number of projection angles has not been initialized !" <<endl); 00392 return 1; 00393 } 00394 00395 if(m_nbPixelsTrans<=0 || m_nbPixelsAxial<=0 ) 00396 { 00397 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, number of transaxial/axial pixels have not correctly been initialized ! (should be >0)" <<endl); 00398 return 1; 00399 } 00400 00401 if(m_pixelsSizeTrans<=0 || m_pixelsSizeAxial<=0 ) 00402 { 00403 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, transaxial/axial pixel sizes have not correctly been initialized ! (should be >0)" <<endl); 00404 return 1; 00405 } 00406 00407 00408 if(m_vNbPixelsTrans<=0 || m_vNbPixelsAxial<=0 ) 00409 { 00410 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, transaxial/axial number of virtual pixels have not correctly been initialized ! (should be >0)" <<endl); 00411 return 1; 00412 } 00413 00414 00415 if(m_vPixelsSizeTrans<=0 || m_vPixelsSizeAxial<=0 ) 00416 { 00417 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, transaxial/axial virtual pixel sizes have not correctly been initialized ! (should be >0)" <<endl); 00418 return 1; 00419 } 00420 00421 00422 if(m_gapSizeTrans<0 || m_gapSizeAxial<0 ) 00423 { 00424 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, transaxial/axial pixel gap sizes have not correctly been initialized ! (should be >0)" <<endl); 00425 return 1; 00426 } 00427 00428 // todo : maybe delete this as we don't used it 00429 if(m_crystalDepth<=0) 00430 { 00431 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, crystal depth has not correctly been initialized ! (should be >0)" <<endl); 00432 return 1; 00433 } 00434 00435 if(mp_focalModelTrans == NULL || mp_focalModelAxial == NULL) 00436 { 00437 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, transaxial/axial focal models have not correctly been initialized !" <<endl); 00438 return 1; 00439 } 00440 00441 if(mp_nbCoefModelTrans == NULL || mp_nbCoefModelAxial == NULL) 00442 { 00443 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, number of coefficients for the transaxial/axial focal models have not correctly been initialized !" <<endl); 00444 return 1; 00445 } 00446 00447 if(m2p_transFocalParameters == NULL || m2p_axialFocalParameters == NULL) 00448 { 00449 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, parameters for the transaxial/axial focal models have not correctly been initialized !" <<endl); 00450 return 1; 00451 } 00452 00453 if(mp_projectionAngles == NULL) 00454 { 00455 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, projection angles have not correctly been initialized !" <<endl); 00456 return 1; 00457 } 00458 00459 if(mp_radius == NULL) 00460 { 00461 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, default scanner radius for each detector heads (scanner file) not correctly initialized !" <<endl); 00462 return 1; 00463 } 00464 00465 if(mp_CORtoDetectorDistance == NULL) 00466 { 00467 Cerr("*****iScannerSPECTConv::CheckParameters()-> Error, distances between center of rotation and each detector heads have not correctly been initialized !" <<endl); 00468 return 1; 00469 } 00470 00471 if(mp_crystalCentralPositionX == NULL || 00472 mp_crystalCentralPositionY == NULL || 00473 mp_crystalCentralPositionZ == NULL ) 00474 { 00475 Cerr("*****iScannerSPECTConv::CheckParameters()-> LUT elements (crystal central positions) have not correctly been initialized !" <<endl); 00476 return 1; 00477 } 00478 00479 if(mp_crystalOrientationX == NULL || 00480 mp_crystalOrientationY == NULL || 00481 mp_crystalOrientationZ == NULL ) 00482 { 00483 Cerr("*****iScannerSPECTConv::CheckParameters()-> LUT elements (crystal orientations) have not correctly been initialized !" <<endl); 00484 return 1; 00485 } 00486 00487 if(mp_crystalFocalPositionX == NULL || 00488 mp_crystalFocalPositionY == NULL || 00489 mp_crystalFocalPositionZ == NULL ) 00490 { 00491 Cerr("*****iScannerSPECTConv::CheckParameters()-> LUT elements (crystal focal positions) have not correctly been initialized !" <<endl); 00492 return 1; 00493 } 00494 00495 // No need to check mp_nbOfBins, Default values = 0 00496 00497 m_allParametersChecked = true; 00498 00499 return 0; 00500 } 00501 00502 00503 00504 // ===================================================================== 00505 // --------------------------------------------------------------------- 00506 // --------------------------------------------------------------------- 00507 // ===================================================================== 00508 /* 00509 \fn Initialize 00510 \brief Check general initialization and set several parameters to their default value 00511 \return 0 if success, positive value otherwise 00512 */ 00513 int iScannerSPECTConv::Initialize() 00514 { 00515 if(m_verbose>=2) Cout("iScannerSPECTConv::Initialize ..."<< endl); 00516 00517 if(m_allParametersChecked == false) 00518 { 00519 Cerr("***** iScannerSPECTConv::Initialize() -> Parameters have not been checked !" << endl); 00520 return 1; 00521 } 00522 00523 // Any initialization 00524 00525 return 0; 00526 } 00527 00528 00529 00530 // ===================================================================== 00531 // --------------------------------------------------------------------- 00532 // --------------------------------------------------------------------- 00533 // ===================================================================== 00534 /* 00535 \fn LoadLUT 00536 \brief Load a precomputed scanner LUT 00537 \details Read mandatory data from the header of the LUT. Then load the LUT elements for each crystal 00538 \todo Not yet implemented 00539 \return 0 if success, positive value otherwise 00540 */ 00541 int iScannerSPECTConv::LoadLUT() 00542 { 00543 if(m_verbose>=2) Cout("iScannerSPECTConv::LoadLUT ..."<< endl); 00544 00545 Cerr("iScannerSPECTConv::LoadLUT() -> Not yet implemented !" << endl); 00546 return 1; 00547 // Read LUT according to SPECT camera hscan/LUT files. Check the Castor presentation file to see mandatory/optional fields to read 00548 } 00549 00550 00551 00552 // ===================================================================== 00553 // --------------------------------------------------------------------- 00554 // --------------------------------------------------------------------- 00555 // ===================================================================== 00556 /* 00557 \fn ComputeLUT 00558 \brief Computes the LUT of the scanner from a generic (.geom) file. 00559 \details Read mandatory data from the geom file. Then compute the LUT elements for each crystal from the geometry described in the file 00560 Compute the look-up-tables of the system containing the locations of the scanner elements center in space and their orientations 00561 \todo center of rotation & head first angles : get this from acquisition header file 00562 \return 0 if success, positive value otherwise 00563 */ 00564 int iScannerSPECTConv::ComputeLUT() 00565 { 00566 if(m_verbose>=2) Cout("iScannerSPECTConv::ComputeLUT() -> Start LUT generation" << endl); 00567 00568 // Compute LUT according to SPECT camera geom files. Check the Castor presentation file for more details about the related mandatory/optional fields to be read 00569 00570 // ============================================================================================================ 00571 // Parameters declarations 00572 // ============================================================================================================ 00573 00574 // todo center of rotation & head first angles : get this from acquisition header file 00575 FLTNB *CORx, *CORy, *CORz; 00576 FLTNB *head_angleX, *head_angleY, *head_angleZ; 00577 00578 CORx = new FLTNB[m_nbHeads]; 00579 CORy = new FLTNB[m_nbHeads]; 00580 CORz = new FLTNB[m_nbHeads]; 00581 head_angleX = new FLTNB[m_nbHeads]; 00582 head_angleY = new FLTNB[m_nbHeads]; 00583 head_angleZ = new FLTNB[m_nbHeads]; 00584 00585 // for(int hId=0 ; hId<m_nbHeads ; hId++) 00586 // todo Initialisation COR and head_angle 00587 00588 // ============================================================================================================ 00589 // Generate the LUT 00590 // ============================================================================================================ 00591 00592 // oMatrix crystal_center_ref will be used to gather cartesian positions of the crystals center (on the surface of the crystals) 00593 // in the reference head (directly above isocenter) 00594 oMatrix** crystal_center_ref = new oMatrix *[m_nbCrystals]; 00595 // oMatrix crystal_center_out will be used to recover positions of each crystal after each rotation (each projection angles) 00596 oMatrix* crystal_center_out = new oMatrix(3,1); 00597 00598 // same method for matrices recovering the positions of focal point 00599 oMatrix* focal_projection_position_mtx = new oMatrix(3,3); 00600 oMatrix* focal_projection_position_mtx_output = new oMatrix(3,3); 00601 00602 for (int c = 0; c<m_nbCrystals; c++) 00603 crystal_center_ref[c] = new oMatrix(3,1); 00604 00605 00606 for(int i=0 ; i<3 ; i++) 00607 { 00608 crystal_center_out->SetMatriceElt(i,0,0); 00609 00610 for(int j=0 ; j<3 ; j++) 00611 { 00612 focal_projection_position_mtx->SetMatriceElt(i,j,0); 00613 focal_projection_position_mtx_output->SetMatriceElt(i,j,0); 00614 } 00615 } 00616 00617 // Generation of the rotation matrix allowing to compute the position of all the rsectors. 00618 oMatrix** rotation_mtx = new oMatrix*[m_nbOfProjections]; 00619 00620 for(int i=0; i<m_nbOfProjections; i++) 00621 { 00622 rotation_mtx[i] = new oMatrix(3,3); 00623 00624 int dir = (m_rotDirection == GEO_ROT_CCW) ? -1 : 1; 00625 00626 rotation_mtx[i]->SetMatriceElt(0,0, cos(mp_projectionAngles[i] * M_PI/180.) ); 00627 rotation_mtx[i]->SetMatriceElt(1,0, -dir*sin(mp_projectionAngles[i] * M_PI/180.) ); 00628 rotation_mtx[i]->SetMatriceElt(2,0,0); 00629 rotation_mtx[i]->SetMatriceElt(0,1, dir*sin(mp_projectionAngles[i] * M_PI/180.) ); 00630 rotation_mtx[i]->SetMatriceElt(1,1, cos(mp_projectionAngles[i] * M_PI/180.) ); 00631 rotation_mtx[i]->SetMatriceElt(2,1,0); 00632 rotation_mtx[i]->SetMatriceElt(0,2,0); 00633 rotation_mtx[i]->SetMatriceElt(1,2,0); 00634 rotation_mtx[i]->SetMatriceElt(2,2,1); 00635 } 00636 00637 // Initialization of pixel size 00638 FLTNB size_pixel_trans = 0., size_pixel_axial = 0.; 00639 int nb_pixel_trans=0, nb_pixel_axial=0; 00640 00641 // nb of bins have been recovered from the datafile (>0) 00642 if(mp_nbOfBins[0]>0 && mp_nbOfBins[1]>0) 00643 { 00644 size_pixel_trans = m_pixelsSizeTrans/mp_nbOfBins[0]; 00645 size_pixel_axial = m_pixelsSizeAxial/mp_nbOfBins[1]; 00646 00647 // Initialize pixel value 00648 nb_pixel_trans = mp_nbOfBins[0]; 00649 nb_pixel_axial = mp_nbOfBins[1]; 00650 00651 // Set the member variable recovering the pixel value 00652 m_vNbPixelsTrans = mp_nbOfBins[0]; 00653 m_vNbPixelsAxial = mp_nbOfBins[1]; 00654 } 00655 else // Otherwise, we recover these informations from the geom file 00656 { 00657 size_pixel_trans = m_pixelsSizeTrans; 00658 size_pixel_axial = m_pixelsSizeAxial; 00659 nb_pixel_trans = m_nbPixelsTrans; 00660 nb_pixel_axial = m_nbPixelsAxial; 00661 00662 // Set the member variable recovering the pixel value 00663 m_vNbPixelsTrans = m_nbPixelsTrans; 00664 m_vNbPixelsAxial = m_nbPixelsAxial; 00665 } 00666 00667 // Recover the actual trans/axial size of the pixels in member variable, in case we need them during reconstruction 00668 m_vPixelsSizeTrans = size_pixel_trans; 00669 m_vPixelsSizeAxial = size_pixel_axial; 00670 00671 00672 // Recover the trans/axial gap size from the geom file 00673 sScannerManager* p_scannerManager; 00674 p_scannerManager = sScannerManager::GetInstance(); 00675 00676 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans gap size", &m_gapSizeTrans, 1, KEYWORD_MANDATORY)) 00677 { 00678 Cerr("***** iScannerSPECTConv::ComputeLUT() -> An error occurred while trying to read the transaxial gap size !" << endl); 00679 return 1; 00680 } 00681 00682 if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial gap size", &m_gapSizeAxial, 1, KEYWORD_MANDATORY)) 00683 { 00684 Cerr("***** iScannerSPECTConv::ComputeLUT() -> An error occurred while trying to read the axial gap size !" << endl); 00685 return 1; 00686 } 00687 00688 00689 // Generate cartesian coordinates of the crystal centers for the SPECT at the reference position (directly above isocenter) 00690 // Starting position of the SPECT camera for all axis 00691 FLTNB x_start = -(nb_pixel_trans*size_pixel_trans + (nb_pixel_trans-1)*m_gapSizeTrans) / 2 + (size_pixel_trans/2); 00692 FLTNB z_start = -(nb_pixel_axial*size_pixel_axial + (nb_pixel_axial-1)*m_gapSizeAxial) / 2 + (size_pixel_axial/2); 00693 00694 for( int jj = 0; jj < nb_pixel_axial; ++jj ) 00695 { 00696 FLTNB Zcrist = z_start + jj * (size_pixel_axial + m_gapSizeAxial); 00697 for( int ii = 0; ii < nb_pixel_trans; ++ ii ) 00698 { 00699 FLTNB Xcrist = x_start + ii * (size_pixel_trans + m_gapSizeTrans); 00700 00701 crystal_center_ref[ii + nb_pixel_trans * jj]->SetMatriceElt(0,0,Xcrist); 00702 crystal_center_ref[ii + nb_pixel_trans * jj]->SetMatriceElt(1,0, 0); // This value will be set for each projection angles 00703 crystal_center_ref[ii + nb_pixel_trans * jj]->SetMatriceElt(2,0,Zcrist); 00704 } 00705 } 00706 00707 // ============================================================================================================ 00708 // Loop over all the projection angles 00709 // Positions of the scanner elements are progressively stored in the LUT file. 00710 // ============================================================================================================ 00711 if (m_verbose>=2) Cout(" --> Generate positions for each projection angle"<< endl); 00712 00713 for (int a=0 ; a<m_nbOfProjections ; a++) 00714 { 00715 // Set y-position of the crystal reference matrix according to the distance between SPECT camera and center of rotation of the specific head.(mp_CORtoDetectorDistance) 00716 int hId = a*m_nbHeads/m_nbOfProjections; 00717 00718 // Get surface crystal position (and not center crystal positions for SPECT), and set the reference crystal matrix 00719 FLTNB Ycrist = mp_CORtoDetectorDistance[hId]; 00720 00721 for (int c=0 ; c < m_nbCrystals ; c++) 00722 crystal_center_ref[c]->SetMatriceElt(1,0,Ycrist); 00723 00724 for (int c=0 ; c<m_nbCrystals ; c++) 00725 { 00726 // crystal indexation 00727 int cryID = a*m_nbCrystals + c; 00728 00729 // SET CARTESIAN COORDINATES OF THE CRYSTAL SURFACE CENTER POSITIONS 00730 // Rotation 00731 rotation_mtx[a]->Multiplication(crystal_center_ref[c], crystal_center_out); 00732 00733 // Get crystal surface positions 00734 mp_crystalCentralPositionX[cryID] = crystal_center_out->GetMatriceElt(0,0); 00735 mp_crystalCentralPositionY[cryID] = crystal_center_out->GetMatriceElt(1,0); 00736 mp_crystalCentralPositionZ[cryID] = crystal_center_out->GetMatriceElt(2,0); 00737 00738 // COMPUTE FOCAL POSITIONS AND SET CARTESIAN COORDINATES OF THE CRYSTAL FOCAL POSITIONS 00739 if(ComputeFocalPositions(crystal_center_ref[c]->GetMatriceElt(0,0), 00740 crystal_center_ref[c]->GetMatriceElt(1,0), 00741 crystal_center_ref[c]->GetMatriceElt(2,0), 00742 hId, 00743 cryID) ) 00744 { 00745 Cerr("***** iScannerSPECTConv::ComputeLUT() -> An error occurred while computing the focal positions ! " << endl); 00746 return 1; 00747 } 00748 00749 // Set elements of the focal matrix 00750 focal_projection_position_mtx->SetMatriceElt(0,0,mp_crystalFocalPositionX[cryID] ); 00751 focal_projection_position_mtx->SetMatriceElt(1,0,mp_crystalFocalPositionY[cryID] ); 00752 focal_projection_position_mtx->SetMatriceElt(2,0,mp_crystalFocalPositionZ[cryID] ); 00753 focal_projection_position_mtx->SetMatriceElt(0,1,0); 00754 focal_projection_position_mtx->SetMatriceElt(1,1,0); 00755 focal_projection_position_mtx->SetMatriceElt(2,1,0); 00756 focal_projection_position_mtx->SetMatriceElt(0,2,0); 00757 focal_projection_position_mtx->SetMatriceElt(1,2,0); 00758 focal_projection_position_mtx->SetMatriceElt(2,2,1); 00759 00760 // Rotate and compute projections of focal position for this crystal 00761 rotation_mtx[a]->Multiplication(focal_projection_position_mtx, focal_projection_position_mtx_output); 00762 mp_crystalFocalPositionX[cryID] = focal_projection_position_mtx_output->GetMatriceElt(0,0); 00763 mp_crystalFocalPositionY[cryID] = focal_projection_position_mtx_output->GetMatriceElt(1,0); 00764 mp_crystalFocalPositionZ[cryID] = focal_projection_position_mtx_output->GetMatriceElt(2,0); 00765 00766 // GET CRYSTAL ORIENTATIONS 00767 mp_crystalOrientationX[cryID] = rotation_mtx[a]->GetMatriceElt(0,0); 00768 mp_crystalOrientationY[cryID] = rotation_mtx[a]->GetMatriceElt(0,1); 00769 mp_crystalOrientationZ[cryID] = 0; 00770 } 00771 } 00772 00773 for(int i=0; i<m_nbOfProjections; i++) 00774 delete rotation_mtx[i]; 00775 00776 delete[] rotation_mtx; 00777 00778 00779 for (int c=0; c<m_nbCrystals; c++) 00780 delete crystal_center_ref[c]; 00781 00782 delete[] crystal_center_ref; 00783 delete crystal_center_out; 00784 delete focal_projection_position_mtx; 00785 delete focal_projection_position_mtx_output; 00786 00787 delete[] head_angleX; 00788 delete[] head_angleY; 00789 delete[] head_angleZ; 00790 delete[] CORx; 00791 delete[] CORy; 00792 delete[] CORz; 00793 00794 if (m_verbose>=2) Cout(" --> LUT generation completed" << endl); 00795 00796 return 0; 00797 } 00798 00799 00800 00801 00802 // ===================================================================== 00803 // --------------------------------------------------------------------- 00804 // --------------------------------------------------------------------- 00805 // ===================================================================== 00806 /* 00807 \fn ComputeFocalPositions 00808 \param a_posX : cartesian position of the crystal on the x-axis 00809 \param a_posY : cartesian position of the crystal on the y-axis 00810 \param a_posZ : cartesian position of the crystal on the z-axis 00811 \param a_headID : head index of the crystal 00812 (required to select the related focal model parameters 00813 and distance to center of rotation) 00814 \param a_cryID : crystal index in the LUT 00815 \brief Compute focal positions for a specific crystal ID 00816 \details Compute the focal positions using the implemented "constant", 00817 "polynomial", "slanthole" focal models related to the gamma cameras 00818 The "custom" focal model is dedicated to user-made focal model 00819 and should be implemented by the user 00820 \return 0 if success, positive value otherwise 00821 */ 00822 int iScannerSPECTConv::ComputeFocalPositions(FLTNB a_posX, FLTNB a_posY, FLTNB a_posZ, int a_headID, int a_cryID) 00823 { 00824 #ifdef CASTOR_DEBUG 00825 if(m_verbose>=4) Cout("iScannerSPECTConv::ComputeFocalPositions ..." << endl); 00826 #endif 00827 00828 mp_crystalFocalPositionY[a_cryID] = -a_posY; 00829 00830 // =================================================================== 00831 // AXIAL FOCAL POSITIONS 00832 // =================================================================== 00833 // constant 00834 if(mp_focalModelAxial[a_headID] == "constant") 00835 { 00836 mp_crystalFocalPositionZ[a_cryID] = a_posZ; 00837 } 00838 00839 // polynomial 00840 else if(mp_focalModelAxial[a_headID] == "polynomial" ) 00841 { 00842 if (mp_nbCoefModelAxial[a_headID] >= 1 && mp_nbCoefModelAxial[a_headID] <= 3) 00843 { 00844 FLTNB focal_posZ = 0; 00845 00846 for(int coef=0 ; coef<mp_nbCoefModelAxial[a_headID] ; coef++) 00847 focal_posZ += m2p_axialFocalParameters[a_headID][coef]*abs(pow(a_posZ,coef)); 00848 00849 // Compute the projection of the focal position on the Y plane (thales) in order to be sure that the focal positions are not in the field of view (LOR would not go through the whole FOV) 00850 mp_crystalFocalPositionZ[a_cryID] = -(2*mp_CORtoDetectorDistance[a_headID]-focal_posZ)*a_posZ/focal_posZ; 00851 00852 } 00853 else if (mp_nbCoefModelAxial[a_headID] > 3) // Error (nb parameters >3) 00854 { 00855 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the axial model should be <4 (max : polynom order 2 = 3 coeffs) ! " << endl); 00856 return 1; 00857 } 00858 else // Error (nb parameters == 0) 00859 { 00860 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the axial model should be >0 ('axial number of coef model' in geom file) ! " << endl); 00861 return 1; 00862 } 00863 } 00864 00865 // slanthole 00866 else if(mp_focalModelAxial[a_headID] == "slanthole" ) 00867 { 00868 if (mp_nbCoefModelAxial[a_headID] == 1) 00869 { 00870 FLTNB cst = a_posZ - m2p_axialFocalParameters[a_headID][0]*mp_CORtoDetectorDistance[a_headID]; 00871 00872 // Compute the projection of the focal position on the Y plane 00873 mp_crystalFocalPositionZ[a_cryID] = -m2p_axialFocalParameters[a_headID][0]*mp_CORtoDetectorDistance[a_headID] + cst; 00874 } 00875 else // Error (nb parameters != 1) 00876 { 00877 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for a slanthole model should be equal to 1 (slope) : 'axial number of coef model' in geom file ! " << endl); 00878 return 1; 00879 } 00880 } 00881 00882 // custom 00883 else if(mp_focalModelAxial[a_headID] == "custom" ) 00884 { 00885 if (mp_nbCoefModelAxial[a_headID] == 1) // Uni-Parameter 00886 { 00887 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl); 00888 return 1; 00889 } 00890 else if (mp_nbCoefModelAxial[a_headID] > 1) // Multi-Parameters 00891 { 00892 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl); 00893 return 1; 00894 } 00895 else // Error (nb parameters == 0) 00896 { 00897 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl); 00898 return 1; 00899 } 00900 } 00901 else // Error, unknown model 00902 { 00903 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, current model " << mp_focalModelAxial[a_headID] << " is unknown !" << endl); 00904 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Should be either 'constant' (parallel), 'polynomial', 'hyperbolic, or 'custom'" << endl); 00905 return 1; 00906 } 00907 00908 00909 00910 // =================================================================== 00911 // TRANSAXIAL FOCAL POSITIONS 00912 // =================================================================== 00913 // constant 00914 if(mp_focalModelTrans[a_headID] == "constant") 00915 { 00916 mp_crystalFocalPositionX[a_cryID] = a_posX; 00917 } 00918 00919 // polynomial 00920 else if(mp_focalModelTrans[a_headID] == "polynomial" ) 00921 { 00922 if (mp_nbCoefModelTrans[a_headID] >= 1 && mp_nbCoefModelTrans[a_headID] <= 3) 00923 { 00924 FLTNB focal_posX = 0; 00925 00926 for(int coef=0 ; coef<mp_nbCoefModelTrans[a_headID] ; coef++) 00927 focal_posX += m2p_transFocalParameters[a_headID][coef]*abs(pow(a_posX,coef)); 00928 00929 // Compute the projection of the focal position on the Y plane (thales) in order to be sure that the focal positions are not in the field of view (LOR would not go through the whole FOV) 00930 mp_crystalFocalPositionX[a_cryID] = -(2*mp_CORtoDetectorDistance[a_headID]-focal_posX)*a_posX/focal_posX; 00931 00932 } 00933 else if (mp_nbCoefModelTrans[a_headID] > 3) // Error (nb parameters >3) 00934 { 00935 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the trans model should be <4 (max : polynom order 2 = 3 coeffs) ! " << endl); 00936 return 1; 00937 } 00938 else // Error (nb parameters == 0) 00939 { 00940 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the axial model should be >0 ('trans number of coef model' in geom file) ! " << endl); 00941 return 1; 00942 } 00943 } 00944 00945 // slanthole 00946 else if(mp_focalModelTrans[a_headID] == "slanthole" ) 00947 { 00948 if (mp_nbCoefModelTrans[a_headID] == 1) 00949 { 00950 FLTNB cst = a_posX - m2p_transFocalParameters[a_headID][0]*mp_CORtoDetectorDistance[a_headID]; 00951 00952 // Compute the projection of the focal position on the Y plane 00953 mp_crystalFocalPositionX[a_cryID] = -m2p_transFocalParameters[a_headID][0]*mp_CORtoDetectorDistance[a_headID] + cst; 00954 00955 } 00956 else // Error (nb parameters != 1 00957 { 00958 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for a slanthole model should be equal to 1 (slope) : 'trans number of coef model' in geom file ! " << endl); 00959 return 1; 00960 } 00961 } 00962 00963 // custom 00964 else if(mp_focalModelTrans[a_headID] == "custom" ) 00965 { 00966 if (mp_nbCoefModelTrans[a_headID] == 1) // Uni-Parameter 00967 { 00968 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl); 00969 return 1; 00970 } 00971 else if (mp_nbCoefModelTrans[a_headID] > 1) // Multi-Parameters 00972 { 00973 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl); 00974 return 1; 00975 } 00976 else // Error (nb parameters == 0) 00977 { 00978 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl); 00979 return 1; 00980 } 00981 } 00982 else // Error, unknown model 00983 { 00984 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, current model " << mp_focalModelTrans[a_headID] << " is unknown !" << endl); 00985 Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Should be either 'constant' (parallel), 'polynomial', 'hyperbolic, or 'custom'" << endl); 00986 return 1; 00987 } 00988 00989 return 0; 00990 } 00991 00992 00993 00994 // ===================================================================== 00995 // --------------------------------------------------------------------- 00996 // --------------------------------------------------------------------- 00997 // ===================================================================== 00998 /* 00999 \fn GetPositionsAndOrientations 01000 \param a_index1 : 1st index of the event (projection angle) 01001 \param a_index2 : 2nd index of the event (crystal index in the gamma camera) 01002 \param ap_Position1[3] : x,y,z cartesian position of the focal point 01003 \param ap_Position2[3] : x,y,z cartesian position of the crystal 01004 \param ap_Orientation1[3] : return -1 by default (no orientation components required for the focal point) 01005 \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the crystal 01006 \param ap_POI1 : ignored in SPECT (no POI component required for the focal point) 01007 \param ap_POI2 : x,y,z components of the Point Of Interation related to the crystal. 01008 Currently ignored (POI management not implemented in SPECT) 01009 \brief Get the central positions and orientations of the scanner elements from their indices. 01010 \todo : Point Of Interactions management is NOT implemented and ignored 01011 \return 0 if success, positive value otherwise 01012 */ 01013 int iScannerSPECTConv::GetPositionsAndOrientations( int a_index1, int a_index2, 01014 FLTNB ap_Position1[3], FLTNB ap_Position2[3], 01015 FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3], 01016 FLTNB* ap_POI1, FLTNB* ap_POI2 ) 01017 { 01018 #ifdef CASTOR_VERBOSE 01019 if(m_verbose>=4) Cout("iScannerSPECTConv::GetPositionsAndOrientations ..." << endl); 01020 #endif 01021 01022 // SPECT indexes : 1st for the projection, 2nd for the crystal 01023 01024 // First check projection angle existency 01025 if (a_index1<0 || a_index1>=m_nbOfProjections) 01026 { 01027 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Projection index (" << a_index1 << ") out of range [0:" << m_nbOfProjections-1 << "] !" << endl); 01028 return 1; 01029 } 01030 01031 // Second check crystals existency 01032 if (a_index2<0 || a_index2>=m_nbCrystals) 01033 { 01034 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Crystal index (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl); 01035 return 1; 01036 } 01037 01038 // Get crystal index related to the projection 01039 int index = a_index1*m_nbCrystals + a_index2; 01040 01041 // Get position of the focal point related to the crystal 01042 ap_Position1[0] = mp_crystalFocalPositionX[index]; 01043 ap_Position1[1] = mp_crystalFocalPositionY[index]; 01044 ap_Position1[2] = mp_crystalFocalPositionZ[index]; 01045 01046 // todo : Due to the current implementation of SPECT projection, POI 01047 // and DOI are not handled and ignored. 01048 // An error is returned if POI are provided 01049 01050 // Case when POI is not provided 01051 if (ap_POI2==NULL) 01052 { 01053 //FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index2)] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2; 01054 //ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2]; 01055 //ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2]; 01056 //ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2]; 01057 ap_Position2[0] = mp_crystalCentralPositionX[index]; 01058 ap_Position2[1] = mp_crystalCentralPositionY[index]; 01059 ap_Position2[2] = mp_crystalCentralPositionZ[index]; 01060 } 01061 // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account) 01062 else if (ap_POI2[2]<0.) 01063 { 01064 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> POI management not implemented yet for SPECT !" << endl); 01065 return 1; 01066 } 01067 // Case when only the DOI is provided 01068 else if (ap_POI2[0]==0. && ap_POI2[1]==0.) 01069 { 01070 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> POI management not implemented yet for SPECT !" << endl); 01071 return 1; 01072 } 01073 // Case when the full POI is taken into account 01074 else 01075 { 01076 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> POI management not implemented yet for SPECT !" << endl); 01077 return 1; 01078 } 01079 01080 // Get orientations 01081 ap_Orientation1[0] = -mp_crystalOrientationX[a_index2]; 01082 ap_Orientation1[1] = -mp_crystalOrientationY[a_index2]; 01083 ap_Orientation1[2] = -mp_crystalOrientationZ[a_index2]; 01084 ap_Orientation2[0] = mp_crystalOrientationX[a_index2]; 01085 ap_Orientation2[1] = mp_crystalOrientationY[a_index2]; 01086 ap_Orientation2[2] = mp_crystalOrientationZ[a_index2]; 01087 01088 return 0; 01089 } 01090 01091 01092 01093 // ===================================================================== 01094 // --------------------------------------------------------------------- 01095 // --------------------------------------------------------------------- 01096 // ===================================================================== 01097 /* 01098 \fn GetRdmPositionsAndOrientations() 01099 \param a_index1 : 1st index of the event (projection angle) 01100 \param a_index2 : 2nd index of the event (crystal index in the gamma camera) 01101 \param ap_Position1[3] : x,y,z cartesian position of the focal point 01102 \param ap_Position2[3] : x,y,z cartesian position of the crystal 01103 \param ap_Orientation1[3] : return -1 by default (no orientation components required for the focal point) 01104 \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the crystal 01105 \brief Get the focal point and random positions on the crystal surface and its orientations from the event indices. 01106 \details - Computed the LUT index described by the projection angle and crystal index passed in parameters. 01107 - Compute random positions on the surface of the crystal 01108 - Write the corresponding random cartesian coordinates in the positions parameters. 01109 \todo This implementation has to be checked and adapted, 01110 as the current implementation of SPECT projection assumes crystal position located on the center of the crystal surface 01111 \return 0 if success, positive value otherwise 01112 */ 01113 int iScannerSPECTConv::GetRdmPositionsAndOrientations(int a_index1, int a_index2, 01114 FLTNB ap_Position1[3], FLTNB ap_Position2[3], 01115 FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3] ) 01116 { 01117 #ifdef CASTOR_VERBOSE 01118 if(m_verbose>=4) Cout("iScannerSPECTConv::GetRdmPositionsAndOrientations ..." << endl); 01119 #endif 01120 01121 // SPECT indexes : 1st for the projection, 2nd for the crystal 01122 01123 // First check projection angle existency 01124 if (a_index1<0 || a_index1>=m_nbOfProjections) 01125 { 01126 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Projection index (" << a_index1 << ") out of range [0:" << m_nbOfProjections-1 << "] !" << endl); 01127 return 1; 01128 } 01129 01130 // Second check crystals existency 01131 if (a_index2<0 || a_index2>=m_nbCrystals) 01132 { 01133 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Crystal index (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl); 01134 return 1; 01135 } 01136 01137 // Get crystal index related to the projection 01138 int index = a_index1*m_nbCrystals + a_index2; 01139 01140 // Get position of the focal point related to the crystal 01141 ap_Position1[0] = mp_crystalFocalPositionX[index]; 01142 ap_Position1[1] = mp_crystalFocalPositionY[index]; 01143 ap_Position1[2] = mp_crystalFocalPositionZ[index]; 01144 01145 // Get instance of random number generator 01146 sRNG* p_RNG = sRNG::GetInstance(); 01147 01148 // Get random numbers for the first crystal 01149 FLTNB axial = (p_RNG->GenerateRdmNber()-0.5) * m_pixelsSizeAxial; 01150 FLTNB trans = (p_RNG->GenerateRdmNber()-0.5) * m_pixelsSizeTrans; 01151 // Do not consider random depth (position on the surface of the crystal) 01152 //FLTNB depth = (p_RNG->GenerateRdmNber()-0.5) * m_crystalDepth; 01153 01154 ap_Position2[0] = mp_crystalCentralPositionX[index] + trans*mp_crystalOrientationY[index] + axial*mp_crystalOrientationX[index]*mp_crystalOrientationZ[index]; 01155 ap_Position2[1] = mp_crystalCentralPositionY[index] + trans*mp_crystalOrientationX[index] + axial*mp_crystalOrientationY[index]*mp_crystalOrientationZ[index]; 01156 ap_Position2[2] = mp_crystalCentralPositionZ[index] + axial*sqrt(1-mp_crystalOrientationZ[index]*mp_crystalOrientationZ[index]); 01157 01158 // Get orientations 01159 ap_Orientation1[0] = -1.; 01160 ap_Orientation1[1] = -1.; 01161 ap_Orientation1[2] = -1.; 01162 ap_Orientation2[0] = mp_crystalOrientationX[a_index2]; 01163 ap_Orientation2[1] = mp_crystalOrientationY[a_index2]; 01164 ap_Orientation2[2] = mp_crystalOrientationZ[a_index2]; 01165 01166 return 0; 01167 } 01168 01169 01170 01171 // ===================================================================== 01172 // --------------------------------------------------------------------- 01173 // --------------------------------------------------------------------- 01174 // ===================================================================== 01175 /* 01176 \fn GetPositionWithRandomDepth 01177 \param a_index1 : 01178 \param a_index2 : index of the crystal 01179 \param ap_Position1[3] : 01180 \param ap_Position2[3] : x,y,z cartesian position of the point related to the crystal 01181 \brief Get the positions and orientations of scanner elements from their indices, with a random depth. 01182 \todo Not yet implemented 01183 \return 0 if success, positive value otherwise 01184 */ 01185 int iScannerSPECTConv::GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3]) 01186 { 01187 #ifdef CASTOR_VERBOSE 01188 if(m_verbose>=4) Cout("iScannerSPECTConv::GetPositionWithRandomDepth ..." << endl); 01189 #endif 01190 01191 // This function was first implemented for PET testing purposed. Not implemented yet. 01192 Cerr("***** iScannerSPECTConv::GetPositionWithRandomDepth() -> This function was implemented for PET testing purposes. Not implemented for SPECT !" << endl); 01193 return 1; 01194 } 01195 01196 01197 01198 // ===================================================================== 01199 // --------------------------------------------------------------------- 01200 // --------------------------------------------------------------------- 01201 // ===================================================================== 01202 /* 01203 \fn GetTwoCorners 01204 \param a_index1 : index of the projection angle 01205 \param a_index2 : index of the crystal 01206 \param ap_CornerInf1[3] 01207 \param ap_CornerSup1[3] 01208 \param ap_CornerInf2[3] 01209 \param ap_CornerSup2[3] 01210 \brief Get the cartesian coordinaters of the two opposite corners of a scanner element. 01211 \todo Not yet implemented 01212 \return 0 if success, positive value otherwise 01213 */ 01214 int iScannerSPECTConv::GetTwoCorners(int a_index1, int a_index2, 01215 FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3], 01216 FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3]) 01217 { 01218 #ifdef CASTOR_VERBOSE 01219 if(m_verbose>=4) Cout("iScannerSPECTConv::GetTwoCorners ..." << endl); 01220 #endif 01221 01222 // First check projection angle existency 01223 if (a_index1<0 || a_index1>=m_nbOfProjections) 01224 { 01225 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Projection index (" << a_index1 << ") out of range [0:" << m_nbOfProjections-1 << "] !" << endl); 01226 return 1; 01227 } 01228 01229 // Second check crystals existency 01230 if (a_index2<0 || a_index2>=m_nbCrystals) 01231 { 01232 Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Crystal index (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl); 01233 return 1; 01234 } 01235 01236 Cerr("***** iScannerSPECTConv::GetTwoCorners() -> Not implemented yet !" << endl); 01237 return 1; 01238 } 01239 01240 01241 01242 01243 // ===================================================================== 01244 // --------------------------------------------------------------------- 01245 // --------------------------------------------------------------------- 01246 // ===================================================================== 01247 /* 01248 \fn GetGeometricInfoFromDatafile 01249 \param a_pathToDF : string containing the path to datafile header 01250 \brief Recover geometric informations specific to the scanner class from the datafile header 01251 \details -Recover nb of bins and projections 01252 -Recover the projection angles from the header 01253 (directly read from the datafile, or extrapolated from a first and last angle) 01254 -Recover the distance between the gamma camera detector surfaces and the center of rotation 01255 (directly read from the datafile, or extracted from the gamma camera configuratino file) 01256 \return 0 if success, positive value otherwise 01257 */ 01258 int iScannerSPECTConv::GetGeometricInfoFromDatafile(string a_pathToDF) 01259 { 01260 if(m_verbose>=2) Cout("iScannerSPECTConv::GetGeometricInfoFromDatafile ..." << endl); 01261 01262 if (ReadDataASCIIFile(a_pathToDF , "Number of bins", mp_nbOfBins, 2, KEYWORD_OPTIONAL) == 1) 01263 { 01264 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error while reading number of bins in the header data file " << endl); 01265 return 1; 01266 } 01267 01268 if (ReadDataASCIIFile(a_pathToDF , "Number of projections", &m_nbOfProjections, 1, KEYWORD_MANDATORY)) 01269 { 01270 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error while reading number of projections in the header data file " << endl); 01271 return 1; 01272 } 01273 01274 string rotation_direction = ""; 01275 if (ReadDataASCIIFile(a_pathToDF , "Head rotation direction", &rotation_direction, 1, KEYWORD_OPTIONAL) == 1) 01276 { 01277 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error while reading head rotation orientation in the header data file " << endl); 01278 return 1; 01279 } 01280 01281 if (PROJ_SetSPECTRotDirection(rotation_direction) ) 01282 { 01283 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error occured while trying to initialize head rotation orientation " << endl); 01284 return 1; 01285 } 01286 01287 // Allocate a one-dimensional vector to retrieve angles from the datafile, then copy it in the member variables 01288 FLTNB* angles = new FLTNB[m_nbOfProjections]; 01289 FLTNB first_and_last_angles[2] = {-1.,-1.}; 01290 01291 // Two possible initializations for projection angles : 01292 // - All angles are provided with the keyword "Angles" 01293 // - The first and last angles are provided, and the intermediate angles are extrapolated from the number of projections 01294 01295 // 'First/Last angles' tags : checking issue during data reading/conversion (==1) 01296 if (ReadDataASCIIFile(a_pathToDF, "First and last projection angles", first_and_last_angles, 2, KEYWORD_OPTIONAL) == 1) 01297 { 01298 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error while reading Angle mandatory field in the header data file '" << endl); 01299 return 1; 01300 } 01301 01302 // Check for 'Projection angles' tag 01303 int rvalue = ReadDataASCIIFile(a_pathToDF, "Projection angles", angles, m_nbOfProjections, KEYWORD_OPTIONAL); 01304 01305 // Error while reading "Angles" tag (==1) 01306 if(rvalue==1) 01307 { 01308 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error while reading Angles field in the header data file !'" << endl); 01309 return 1; 01310 } 01311 01312 // Check if information on projection angles has been provided 01313 if ( rvalue>=2 && // "Angles" tag not found 01314 (first_and_last_angles[0] <0 || first_and_last_angles[1] <0) ) // Tags first/last angles not found) 01315 { 01316 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> No information on projection angles provided in the datafile !'" << endl); 01317 Cerr(" This information should be provided using either the 'Angles' tag, or both 'First angles', 'Last angles' tags !'" << endl); 01318 return 1; 01319 } 01320 else if (rvalue>=2) // "Angles" tag not found, but first angle and last angle provided 01321 { 01322 for(int a=0 ; a<m_nbOfProjections ; a++) 01323 angles[a] = first_and_last_angles[0] + a*(first_and_last_angles[1]-first_and_last_angles[0]) / (m_nbOfProjections-1); 01324 } 01325 // else : Angles have been recovered using ReadDataASCIIFile() above 01326 01327 01328 // Instanciate here the projection angles variable 01329 mp_projectionAngles = new FLTNB[m_nbOfProjections]; 01330 01331 for(int a=0 ; a<m_nbOfProjections ; a++) 01332 mp_projectionAngles[a] = angles[a]; 01333 01334 01335 01336 // Recover distance between the detectors and scanner center of rotation 01337 // Allocate with the number of projections by default 01338 mp_CORtoDetectorDistance = new FLTNB[m_nbOfProjections]; 01339 01340 // Initialize by default with the scanner radius 01341 for(int hId=0 ; hId<m_nbHeads ; hId++) 01342 for(int a=0 ; a<m_nbOfProjections/m_nbHeads ; a++) 01343 mp_CORtoDetectorDistance[a+hId*m_nbOfProjections/m_nbHeads] = mp_radius[hId]; 01344 01345 // Read datafile value if any. First case : we have a radius specific to each projections 01346 int read_flag = 0; 01347 read_flag = ReadDataASCIIFile(a_pathToDF, "Distance camera surface to COR", mp_CORtoDetectorDistance, m_nbOfProjections, KEYWORD_OPTIONAL); 01348 01349 if(read_flag==1) 01350 { 01351 // Error during reading 01352 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error while reading the distance between the camera detectors to the center of rotation in the header data file " << endl); 01353 return 1; 01354 } 01355 // Check the second case : we have a global radius for each projection 01356 else if (read_flag==2) 01357 { 01358 read_flag = ReadDataASCIIFile(a_pathToDF, "Global distance camera surface to COR", mp_CORtoDetectorDistance, 1, KEYWORD_OPTIONAL); 01359 01360 if(read_flag==0) 01361 { 01362 // Field was found : Initialize the distance for each projection angle with the global value 01363 for(int a=1 ; a<m_nbOfProjections ; a++) 01364 mp_CORtoDetectorDistance[a] = mp_CORtoDetectorDistance[0]; 01365 } 01366 else if(read_flag==1) 01367 { 01368 // Error during reading 01369 Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDatafile() -> Error while reading the global distance between the camera detectors to the center of rotation in the header data file " << endl); 01370 return 1; 01371 } 01372 else if (read_flag==2) 01373 { 01374 // Initialization with default values from the scanner file 01375 for(int hId=0 ; hId<m_nbHeads ; hId++) 01376 for(int a=0 ; a<m_nbOfProjections/m_nbHeads ; a++) 01377 mp_CORtoDetectorDistance[a+hId*m_nbOfProjections/m_nbHeads] = mp_radius[hId]; 01378 } 01379 } 01380 01381 delete[] angles; 01382 01383 return 0; 01384 } 01385 01386 01387 01388 01389 // ===================================================================== 01390 // --------------------------------------------------------------------- 01391 // --------------------------------------------------------------------- 01392 // ===================================================================== 01393 /* 01394 \fn GetSPECTSpecificParameters 01395 \param ap_nbOfProjections : total number of views 01396 \param ap_nbHeads : number of heads in the system 01397 \param ap_nbOfBins : 2 elements array containing transaxial/axial number of pixels 01398 \param ap_pixSizeXY : 2 elements array containing transaxial/axial pixel sizes 01399 \param ap_angles : Array containing angles of each projection view 01400 \param ap_CORtoDetectorDistance : Radius (distance between FOV center and detector) 01401 \param ap_headRotDirection : head rotation direction 01402 \brief Set pointers passed in argument with the related SPECT specific variables 01403 This function is used to recover these values in the datafile object 01404 \return 0 if success, positive value otherwise 01405 */ 01406 int iScannerSPECTConv::GetSPECTSpecificParameters(uint16_t* ap_nbOfProjections, 01407 uint16_t* ap_nbHeads, 01408 uint16_t* ap_nbOfBins, 01409 FLTNB* ap_pixSizeXY, 01410 FLTNB*& ap_angles, 01411 FLTNB*& ap_CORtoDetectorDistance, 01412 int* ap_headRotDirection) 01413 { 01414 if(m_verbose>=2) Cout("iScannerSPECTConv::GetSPECTSpecificParameters ..." << endl); 01415 01416 // Verify that all parameters have been correctly checked 01417 if(m_allParametersChecked == false) 01418 { 01419 Cerr("***** iScannerSPECTConv::GetSPECTSpecificParameters() -> Parameters have not been checked !" << endl); 01420 return 1; 01421 } 01422 01423 *ap_nbOfProjections = m_nbOfProjections; 01424 *ap_nbHeads = m_nbHeads; 01425 ap_nbOfBins[0] = mp_nbOfBins[0]; 01426 ap_nbOfBins[1] = mp_nbOfBins[1]; 01427 ap_pixSizeXY[0] = m_vPixelsSizeTrans; 01428 ap_pixSizeXY[1] = m_vPixelsSizeAxial; 01429 ap_angles = mp_projectionAngles; 01430 ap_CORtoDetectorDistance = mp_CORtoDetectorDistance; 01431 *ap_headRotDirection = m_rotDirection; 01432 01433 return 0; 01434 } 01435 01436 01437 01438 01439 // ===================================================================== 01440 // --------------------------------------------------------------------- 01441 // --------------------------------------------------------------------- 01442 // ===================================================================== 01443 /* 01444 \fn PROJ_SetSPECTAngles 01445 \param ap_projectionAngles : an array containing the projection angles 01446 \brief Set the projection angles with the array provided in parameter 01447 \return 0 if success, positive value otherwise 01448 */ 01449 int iScannerSPECTConv::PROJ_SetSPECTAngles(FLTNB* ap_projectionAngles) 01450 { 01451 if(m_verbose>=2) Cout("iScannerSPECTConv::PROJ_SetSPECTAngles ..." << endl); 01452 01453 // Check initialization of the number of projections 01454 if(m_nbOfProjections <= 0) 01455 { 01456 Cerr("***** iScannerSPECTConv::PROJ_SetSPECTAngles -> Error number of projection should be >0 ! '" << endl); 01457 return 1; 01458 } 01459 else 01460 { 01461 mp_projectionAngles = new FLTNB[m_nbOfProjections]; 01462 01463 for(int a=0 ; a<m_nbOfProjections ; a++) 01464 mp_projectionAngles[a] = ap_projectionAngles[a]; 01465 } 01466 01467 return 0; 01468 } 01469 01470 01471 01472 // ===================================================================== 01473 // --------------------------------------------------------------------- 01474 // --------------------------------------------------------------------- 01475 // ===================================================================== 01476 /* 01477 \fn PROJ_SetSPECTCORtoDetectorDistance 01478 \param a_distance 01479 \brief Set distance between the center of rotation and SPECT detectors if arg value>0, 01480 Set with the geometric information in the scanner configuration file otherwise 01481 \return 0 if success, positive value otherwise 01482 */ 01483 int iScannerSPECTConv::PROJ_SetSPECTCORtoDetectorDistance(FLTNB a_distance) 01484 { 01485 if(m_verbose>=2) Cout("iScannerSPECTConv::PROJ_SetSPECTCORtoDetectorDistance ..." << endl); 01486 01487 // Check initialization of the number of projections 01488 if(m_nbOfProjections <= 0) 01489 { 01490 Cerr("***** iScannerSPECTConv::PROJ_SetSPECTCORtoDetectorDistance -> Error number of projection should be >0 ! '" << endl); 01491 return 1; 01492 } 01493 else 01494 { 01495 mp_CORtoDetectorDistance = new FLTNB[m_nbOfProjections]; 01496 01497 if(a_distance>0) 01498 { 01499 // Set all radius to the provided distance 01500 for(int a=0 ; a<m_nbOfProjections ; a++) 01501 mp_CORtoDetectorDistance[a] = a_distance; 01502 } 01503 else 01504 { 01505 // Set all distance according to scanner geometric informations (default) 01506 for(int hId=0 ; hId<m_nbHeads ; hId++) 01507 for(int a=0 ; a<m_nbOfProjections/m_nbHeads ; a++) 01508 mp_CORtoDetectorDistance[a+hId*m_nbOfProjections/m_nbHeads] = mp_radius[hId]; 01509 } 01510 } 01511 return 0; 01512 } 01513 01514 01515 01516 01517 // ===================================================================== 01518 // --------------------------------------------------------------------- 01519 // --------------------------------------------------------------------- 01520 // ===================================================================== 01521 /* 01522 \fn PROJ_GetSPECTNbBins 01523 \brief Get the number of SPECT heads in the pointer provided in parameter 01524 \return 0 by default (no error) 01525 */ 01526 int iScannerSPECTConv::PROJ_GetSPECTNbBins(uint16_t* ap_nbOfBins) 01527 { 01528 ap_nbOfBins = mp_nbOfBins; 01529 return 0; 01530 } 01531 01532 01533 01534 // ===================================================================== 01535 // --------------------------------------------------------------------- 01536 // --------------------------------------------------------------------- 01537 // ===================================================================== 01538 /* 01539 \fn PROJ_SetSPECTNbBins 01540 \param ap_nbOfBins 01541 \brief Set number of bins 01542 \return 0 by default (no error) 01543 */ 01544 int iScannerSPECTConv::PROJ_SetSPECTNbBins(uint16_t* ap_nbOfBins) 01545 { 01546 for(int i=0 ; i<2 ; i++) 01547 mp_nbOfBins[i]=ap_nbOfBins[i]; 01548 return 0; 01549 } 01550 01551 01552 01553 01554 // ===================================================================== 01555 // --------------------------------------------------------------------- 01556 // --------------------------------------------------------------------- 01557 // ===================================================================== 01558 /* 01559 \fn PROJ_SetSPECTNbProjections 01560 \param a_nbOfProjections 01561 \brief Set number of projections 01562 \return 0 by default (no error) 01563 */ 01564 int iScannerSPECTConv::PROJ_SetSPECTNbProjections(uint32_t a_nbOfProjections) 01565 { 01566 m_nbOfProjections = a_nbOfProjections; 01567 return 0; 01568 } 01569 01570 01571 01572 01573 // ===================================================================== 01574 // --------------------------------------------------------------------- 01575 // --------------------------------------------------------------------- 01576 // ===================================================================== 01577 /* 01578 \fn PROJ_SetSPECTRotOrientation 01579 \param a_rotDirection 01580 \brief Set head rotation orientation 01581 \details This function is surcharged by the SPECT scanner daughter classes 01582 Returns an error by default. 01583 \return 1 (error) if not surcharged by a daughter class 01584 */ 01585 int iScannerSPECTConv::PROJ_SetSPECTRotDirection( string a_rotDirection ) 01586 { 01587 if(a_rotDirection == "CCW" || 01588 a_rotDirection == "Ccw" || 01589 a_rotDirection == "ccw" ) 01590 m_rotDirection = GEO_ROT_CCW ; 01591 01592 else if(a_rotDirection == "" || // Default 01593 a_rotDirection == "CW" || 01594 a_rotDirection == "Cw" || 01595 a_rotDirection == "cw" ) 01596 m_rotDirection = GEO_ROT_CW ; 01597 01598 else 01599 { 01600 Cerr("***** iScannerSPECTConv::PROJ_SetSPECTRotDirection -> Error while initializing head rotation direction !" << endl); 01601 Cerr(" "<< a_rotDirection <<"' is unknown. Direction must be 'CW' (clockwise) or 'CCW' (counter-clockwise)."); 01602 return 1; 01603 } 01604 01605 return 0; 01606 } 01607 01608 01609 01610 01611 // ===================================================================== 01612 // --------------------------------------------------------------------- 01613 // --------------------------------------------------------------------- 01614 // ===================================================================== 01615 /* 01616 \fn ShowHelp 01617 \brief Display help 01618 \todo Provide informations about SPECT system initialization ? 01619 */ 01620 void iScannerSPECTConv::ShowHelp() 01621 { 01622 if(m_verbose>=2) Cout("iScannerSPECTConv::ShowHelp ..." << endl); 01623 01624 cout << "This scanner class is dedicated to the description of parallel, convergent and multi-convergent SPECT systems." << endl; 01625 }