![]() |
CASToR
1.0
Tomographic Reconstruction (PET/SPECT)
|
00001 00002 /* 00003 Implementation of class iDataFileSPECT 00004 00005 - separators: X 00006 - doxygen: X 00007 - default initialization: X 00008 - CASTOR_DEBUG: X 00009 - CASTOR_VERBOSE: X 00010 */ 00011 00012 00013 00022 #include "iDataFileSPECT.hh" 00023 00024 00025 00026 // ===================================================================== 00027 // --------------------------------------------------------------------- 00028 // --------------------------------------------------------------------- 00029 // ===================================================================== 00030 /* 00031 \brief iDataFileSPECT constructor. 00032 Initialize the member variables to their default values. 00033 */ 00034 iDataFileSPECT::iDataFileSPECT() : vDataFile() 00035 { 00036 m_dataType = TYPE_SPECT; 00037 m_isotope = "unknown"; 00038 mp_nbOfBins[0] = 1; 00039 mp_nbOfBins[1] = 1; 00040 m_nbOfProjections = 0; 00041 mp_angles = NULL; 00042 m_nbHeads = 1; 00043 mp_CORtoDetectorDistance = NULL; 00044 m_eventKindFlag = false; 00045 m_normCorrectionFlag = false; 00046 m_ignoreNormCorrectionFlag = false; 00047 m_scatCorrectionFlag = false; 00048 m_ignoreScatCorrectionFlag = false; 00049 m_headRotDirection = GEO_ROT_CW; 00050 } 00051 00052 00053 00054 // ===================================================================== 00055 // --------------------------------------------------------------------- 00056 // --------------------------------------------------------------------- 00057 // ===================================================================== 00058 /* 00059 \brief iDataFileSPECT destructor. 00060 */ 00061 iDataFileSPECT::~iDataFileSPECT() 00062 { 00063 if (mp_angles) delete[] mp_angles; 00064 if (mp_CORtoDetectorDistance) delete[] mp_CORtoDetectorDistance; 00065 } 00066 00067 00068 00069 00070 // ===================================================================== 00071 // --------------------------------------------------------------------- 00072 // --------------------------------------------------------------------- 00073 // ===================================================================== 00074 /* 00075 \fn ReadSpecificInfoInHeader() 00076 \brief Read through the header file and recover specific SPECT information. 00077 \return 0 is success, positive value otherwise 00078 */ 00079 int iDataFileSPECT::ReadSpecificInfoInHeader() 00080 { 00081 // Verbose 00082 if (m_verbose>=3) Cout("iDataFileSPECT::ReadSpecificInfoInHeader() ..." << endl); 00083 00084 // Create pointers dedicated to recover the addresses of the member variables of the scanner object 00085 FLTNB* p_angles = NULL; 00086 FLTNB* p_CORtoDetectorDistance = NULL; 00087 FLTNB p_pixSizeXY[2]; // not used here 00088 00089 // Get Geometric parameters recovered from the scanner object 00090 sScannerManager* p_scannermanager; 00091 p_scannermanager = sScannerManager::GetInstance(); 00092 p_scannermanager->GetSPECTSpecificParameters(&m_nbOfProjections, 00093 &m_nbHeads, 00094 mp_nbOfBins, 00095 p_pixSizeXY, 00096 p_angles, 00097 p_CORtoDetectorDistance, 00098 &m_headRotDirection); 00099 00100 // Check mp_nbOfBins[2] 00101 /* 00102 if(mp_nbOfBins[0] == 0 || mp_nbOfBins[1] == 0) 00103 { 00104 Cerr("***** iDataFileSPECT::ReadSpecificInfoInHeader() -> Error, number of bins should be >0 '" << endl); 00105 return 1; 00106 } 00107 */ 00108 // Check m_nbOfProjections first before allocating projection angles and radius using this variable 00109 if(m_nbOfProjections == 0) 00110 { 00111 Cerr("***** iDataFileSPECT::ReadSpecificInfoInHeader() -> Error, number of projections should be >0 '" << endl); 00112 return 1; 00113 } 00114 00115 // Allocation and initialization of Projection angles and Center of rotation to SPECT detector surface distance (radius) : 00116 mp_angles = new FLTNB[m_nbOfProjections]; 00117 mp_CORtoDetectorDistance = new FLTNB[m_nbOfProjections]; 00118 00119 // Recover values 00120 for(int a=0 ; a<m_nbOfProjections ; a++) 00121 { 00122 mp_angles[a] = p_angles[a]; 00123 mp_CORtoDetectorDistance[a] = p_CORtoDetectorDistance[a]; 00124 } 00125 00126 // Feedback to user 00127 if(m_verbose ==3) 00128 { 00129 Cout("iDataFileSPECT::ReadSpecificInfoInHeader() -> Provided projection angles ; distance Center of Rotation (COR) to detector: "<< endl); 00130 for(int a=0 ; a<m_nbOfProjections ; a++) 00131 Cout(mp_angles[a] << " ; " << mp_CORtoDetectorDistance[a] << endl); 00132 Cout(endl); 00133 } 00134 00135 00136 // Read optional fields in the header, check if errors (issue during data reading/conversion (==1) ) 00137 if (ReadDataASCIIFile(m_headerFileName, "Isotope", &m_isotope, 1, 0)==1 || 00138 ReadDataASCIIFile(m_headerFileName, "Event kind flag", &m_eventKindFlag, 1, 0)==1 || 00139 ReadDataASCIIFile(m_headerFileName, "Normalization correction flag", &m_normCorrectionFlag, 1, 0)==1 || 00140 ReadDataASCIIFile(m_headerFileName, "Scatter correction flag", &m_scatCorrectionFlag, 1, 0)==1 ) 00141 { 00142 Cerr("***** iDataFileSPECT::ReadSpecificInfoInHeader() -> Error while reading optional field in the header data file '" << endl); 00143 return 1; 00144 } 00145 00146 // Give the SPECT isotope to the oImageDimensionsAndQuantification that manages the quantification factors 00147 if (m_dataMode!=MODE_NORMALIZATION && mp_ID->SetSPECTIsotope(m_bedIndex, m_isotope)) 00148 { 00149 Cerr("***** iDataFileSPECT::ReadSpecificInfoInHeader() -> A problem occured while setting the isotope to oImageDimensionsAndQuantification !" << endl); 00150 return 1; 00151 } 00152 00153 return 0; 00154 } 00155 00156 00157 00158 00159 // ===================================================================== 00160 // --------------------------------------------------------------------- 00161 // --------------------------------------------------------------------- 00162 // ===================================================================== 00163 /* 00164 \fn ComputeSizeEvent() 00165 \brief Computation of the size of each event according to the mandatory/optional correction fields 00166 \return 0 is success, positive value otherwise 00167 */ 00168 int iDataFileSPECT::ComputeSizeEvent() 00169 { 00170 // Verbose 00171 if (m_verbose>=3) Cout("iDataFileSPECT::ComputeSizeEvent() ..." << endl); 00172 00173 // For MODE_LIST events 00174 if (m_dataMode == MODE_LIST) 00175 { 00176 // Size of the mandatory element in a list-mode event: 00177 // Time + 2*crystalID 00178 m_sizeEvent = sizeof(uint32_t) + 2*sizeof(uint32_t); 00179 // Optional flags 00180 if (m_eventKindFlag) m_sizeEvent += sizeof(FLTNBDATA); 00181 if (m_scatCorrectionFlag) m_sizeEvent += sizeof(FLTNBDATA); 00182 if (m_normCorrectionFlag) m_sizeEvent += sizeof(FLTNBDATA); 00183 for (int i=0 ; i<3; i++) 00184 { 00185 // POI available for this direction 00186 if (mp_POIDirectionFlag[i]) m_sizeEvent += sizeof(FLTNBDATA); 00187 } 00188 } 00189 00190 // For MODE_HISTOGRAM events 00191 if(m_dataMode == MODE_HISTOGRAM) 00192 { 00193 // Size of the mandatory element in a histo event: 00194 // Time + event_value + 2*crystalID 00195 m_sizeEvent = sizeof(uint32_t) + sizeof(FLTNBDATA) + 2*sizeof(uint32_t); 00196 // Optional flags 00197 if (m_scatCorrectionFlag) m_sizeEvent += sizeof(FLTNBDATA); 00198 if (m_normCorrectionFlag) m_sizeEvent += sizeof(FLTNBDATA); 00199 } 00200 00201 // Verbose 00202 if (m_verbose>=3) Cout(" --> Event size: " << m_sizeEvent << " bytes" << endl); 00203 00204 return 0; 00205 } 00206 00207 00208 00209 // ===================================================================== 00210 // --------------------------------------------------------------------- 00211 // --------------------------------------------------------------------- 00212 // ===================================================================== 00213 /* 00214 \fn PrepareDataFile() 00215 \brief Store different kind of information inside arrays (data relative to specific correction as well as basic raw data for the case data is loaded in RAM) 00216 Use the flag provided by the user to determine how the data has to be sorted (preloaded or read on the fly) 00217 \return 0 is success, positive value otherwise 00218 */ 00219 int iDataFileSPECT::PrepareDataFile() 00220 { 00221 // Verbose 00222 if (m_verbose>=2) 00223 { 00224 if (m_dataMode==MODE_HISTOGRAM) Cout("iDataFileSPECT::PrepareDataFile() -> Build histogram events" << endl); 00225 else if (m_dataMode==MODE_LIST) Cout("iDataFileSPECT::PrepareDataFile() -> Build listmode events" << endl); 00226 else if (m_dataMode==MODE_NORMALIZATION) Cout("iDataFileSPECT::PrepareDataFile() -> Build normalization events" << endl); 00227 } 00228 00229 // ============================================================================== 00230 // Allocate event buffers (one for each thread) 00231 // ============================================================================== 00232 00233 if (m_verbose>=3) Cout(" --> Allocate an event buffer for each thread" << endl); 00234 // Instanciation of the event buffer according to the data type 00235 m2p_BufferEvent = new vEvent*[mp_ID->GetNbThreadsForProjection()]; 00236 00237 // Allocate the events per each thread 00238 for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++) 00239 { 00240 // For MODE_LIST events 00241 if (m_dataMode == MODE_LIST) 00242 { 00243 m2p_BufferEvent[th] = new iEventListSPECT(); 00244 } 00245 // For MODE_HISTOGRAM events 00246 if (m_dataMode == MODE_HISTOGRAM) 00247 { 00248 m2p_BufferEvent[th] = new iEventHistoSPECT(); 00249 } 00250 // Allocate crystal/view IDs 00251 if (m2p_BufferEvent[th]->AllocateID()) 00252 { 00253 Cerr("*****iDataFileSPECT::PrepareDataFile() -> Error while trying to allocate memory for the Event object!" << endl); 00254 return 1; 00255 } 00256 } 00257 00258 // ============================================================================== 00259 // Deal with specific corrections 00260 // ============================================================================== 00261 00262 // In case of normalization correction flag, see if we ignore this correction 00263 if (m_normCorrectionFlag) 00264 { 00265 // Affect the ignored flag from the ignored corrections list processed by the oImageDimensionsAndQuantification 00266 m_ignoreNormCorrectionFlag = mp_ID->GetIgnoreNormCorrectionFlag(); 00267 // Verbose 00268 if (m_verbose>=2) 00269 { 00270 if (m_ignoreNormCorrectionFlag) Cout(" --> Ignore normalization correction" << endl); 00271 else Cout(" --> Correct for normalization" << endl); 00272 } 00273 } 00274 // In case of scatter correction flag, see if we ignore this correction 00275 if (m_scatCorrectionFlag) 00276 { 00277 // Affect the ignored flag from the ignored corrections list processed by the oImageDimensionsAndQuantification 00278 m_ignoreScatCorrectionFlag = mp_ID->GetIgnoreScatCorrectionFlag(); 00279 // Verbose 00280 if (m_verbose>=2) 00281 { 00282 if (m_ignoreScatCorrectionFlag) Cout(" --> Ignore scatter correction" << endl); 00283 else Cout(" --> Correct for scatter events" << endl); 00284 } 00285 } 00286 00287 // Normal end 00288 return 0; 00289 } 00290 00291 00292 00293 // ===================================================================== 00294 // --------------------------------------------------------------------- 00295 // --------------------------------------------------------------------- 00296 // ===================================================================== 00297 /* 00298 \fn GetEventFromBuffer() 00299 \param ap_buffer : address pointing to the event to recover 00300 \param a_th : index of the thread from which the function was called 00301 \brief Read an event from the position pointed by 'ap_buffer', parse the generic or modality-specific information, and store them in the (multithreaded) 'm2p_BufferEvent' object 00302 \return the thread-specific 'm2p_BufferEvent' object containing the modality-specific information for the event 00303 */ 00304 vEvent* iDataFileSPECT::GetEventFromBuffer(char* ap_buffer, int a_th) 00305 { 00306 #ifdef CASTOR_VERBOSE 00307 // Verbose 00308 if(m_verbose>=4) Cout("iDataFileSPECT::GetEventFromBuffer() ..." << endl); 00309 #endif 00310 00311 // Work on a copy of the input pointer 00312 char* file_position = ap_buffer; 00313 00314 // For MODE_LIST SPECT data 00315 if (m_dataMode == MODE_LIST) 00316 { 00317 // Cast the event pointer 00318 iEventListSPECT* event = (dynamic_cast<iEventListSPECT*>(m2p_BufferEvent[a_th])); 00319 // Mandatory time field: [uint32_t (time)] 00320 event->SetTimeInMs(*reinterpret_cast<uint32_t*>(file_position)); 00321 file_position += sizeof(uint32_t); 00322 // Optional kind: [uint8_t kind] 00323 if (m_eventKindFlag) 00324 { 00325 event->SetKind(*reinterpret_cast<uint8_t*>(file_position)); 00326 file_position += sizeof(uint8_t); 00327 } 00328 // Optional scatter correction field: [FLTNBDATA (scatter)] 00329 if (m_scatCorrectionFlag) 00330 { 00331 if (!m_ignoreScatCorrectionFlag) event->SetScatterRate(*reinterpret_cast<FLTNBDATA*>(file_position)); 00332 file_position += sizeof(FLTNBDATA); 00333 } 00334 // Optional normalization correction field: [FLTNBDATA (norm)] 00335 if (m_normCorrectionFlag) 00336 { 00337 if (!m_ignoreNormCorrectionFlag) event->SetNormalizationFactor(*reinterpret_cast<FLTNBDATA*>(file_position)); 00338 file_position += sizeof(FLTNBDATA); 00339 } 00340 // Optional POI correction field: [FLTNBDATA (POI1[3])] 00341 for (int i=0; i<3; i++) 00342 { 00343 if (mp_POIDirectionFlag[i]) 00344 { 00345 if (!m_ignorePOIFlag) event->SetPOI(i,*reinterpret_cast<FLTNBDATA*>(file_position)); 00346 file_position += sizeof(FLTNBDATA); 00347 } 00348 } 00349 // Mandatory angular projection ID: [uint32_t (ID1)] 00350 event->SetID1(0, *reinterpret_cast<uint32_t*>(file_position)); 00351 file_position += sizeof(uint32_t); 00352 // Mandatory crystal ID: [uint32_t (ID2)] 00353 event->SetID2(0, *reinterpret_cast<uint32_t*>(file_position)); 00354 file_position += sizeof(uint32_t); 00355 } 00356 00357 // For MODE_HISTOGRAM SPECT DATA 00358 if (m_dataMode == MODE_HISTOGRAM) 00359 { 00360 // Cast the event pointer 00361 iEventHistoSPECT* event = (dynamic_cast<iEventHistoSPECT*>(m2p_BufferEvent[a_th])); 00362 // Mandatory time field: [uint32_t (time)] 00363 event->SetTimeInMs(*reinterpret_cast<uint32_t*>(file_position)); 00364 file_position += sizeof(uint32_t); 00365 // Mandatory bin value: [FLTNBDATA bin value] 00366 event->SetEventValue(0, *reinterpret_cast<FLTNBDATA*>(file_position)); 00367 file_position += sizeof(FLTNBDATA); 00368 // Optional scatter correction field: [FLTNBDATA (scatter)] 00369 if (m_scatCorrectionFlag) 00370 { 00371 if (!m_ignoreScatCorrectionFlag) event->SetScatterRate(*reinterpret_cast<FLTNBDATA*>(file_position)); 00372 file_position += sizeof(FLTNBDATA); 00373 } 00374 // Optional normalization correction field: [FLTNBDATA (norm)] 00375 if (m_normCorrectionFlag) 00376 { 00377 if (!m_ignoreNormCorrectionFlag) event->SetNormalizationFactor(*reinterpret_cast<FLTNBDATA*>(file_position)); 00378 file_position += sizeof(FLTNBDATA); 00379 } 00380 // Mandatory angular projection ID: [uint32_t (ID1)] 00381 event->SetID1(0, *reinterpret_cast<uint32_t*>(file_position)); 00382 file_position += sizeof(uint32_t); 00383 // Mandatory crystal ID: [uint32_t (ID2)] 00384 event->SetID2(0, *reinterpret_cast<uint32_t*>(file_position)); 00385 file_position += sizeof(uint32_t); 00386 } 00387 00388 // Return the updated event 00389 return m2p_BufferEvent[a_th]; 00390 } 00391 00392 // ===================================================================== 00393 // --------------------------------------------------------------------- 00394 // --------------------------------------------------------------------- 00395 // ===================================================================== 00396 /* 00397 \fn CheckSpecificParameters() 00398 \brief Check parameters specific to SPECT data 00399 \return 0 if success, and positive value otherwise. 00400 */ 00401 int iDataFileSPECT::CheckSpecificParameters() 00402 { 00403 // Verbose 00404 if (m_verbose>=3) Cout("iDataFileSPECT::CheckSpecificParameters()" << endl); 00405 00406 // Error if m_dataType != SPECT 00407 if (m_dataType != TYPE_SPECT) 00408 { 00409 Cerr("***** iDataFileSPECT::CheckSpecificParameters() -> Error, Data type should be SPECT (=1) !'" << endl); 00410 return 1; 00411 } 00412 00413 if (m_nbOfProjections == 0) 00414 { 00415 Cerr("***** iDataFileSPECT::CheckSpecificParameters() -> Error, number of projections not initialized (should be >0) !'" << endl); 00416 return 1; 00417 } 00418 00419 if (mp_angles == NULL) 00420 { 00421 Cerr("***** iDataFileSPECT::CheckSpecificParameters() -> Error, Projection angles not initialized !'" << endl); 00422 return 1; 00423 } 00424 00425 00426 return 0; 00427 } 00428 00429 00430 00431 // ===================================================================== 00432 // --------------------------------------------------------------------- 00433 // --------------------------------------------------------------------- 00434 // ===================================================================== 00435 /* 00436 \fn CheckFileSizeConsistency() 00437 \brief This function is implemented in child classes 00438 Check if file size is consistent. 00439 \return 0 if success, and positive value otherwise. 00440 */ 00441 int iDataFileSPECT::CheckFileSizeConsistency() 00442 { 00443 if(m_verbose >=3) Cout("iDataFileSPECT::CheckFileSizeConsistency() ..." << endl); 00444 00445 // CHECK IF DATAFILE SIZE CORRESPONDS TO THE USER DATA INITIALIZATION 00446 m2p_dataFile[0]->seekg(0, ios::end); 00447 int64_t sizeInBytes = m2p_dataFile[0]->tellg(); 00448 00449 if (m_totalNbEvents*m_sizeEvent != sizeInBytes) 00450 { 00451 Cerr("-----------------------------------------------------------------------------------------------------------------------------------------------" << endl); 00452 Cerr("***** iDataFileSPECT::CheckFileSizeConsistency() -> Error : Datafile size is not consistent with the information provided by the user/datafile!" << endl); 00453 Cerr("***** iDataFileSPECT::CheckFileSizeConsistency() -> Expected size : "<< m_totalNbEvents*m_sizeEvent << endl); 00454 Cerr("***** iDataFileSPECT::CheckFileSizeConsistency() -> Actual size : "<< sizeInBytes << endl << endl); 00455 00456 Cerr("***** iDataFileSPECT::CheckFileSizeConsistency() -> ADDITIONAL INFORMATION ABOUT THE DATAFILE INITIALIZATION : " << endl); 00457 00458 00459 if(m_eventKindFlag) 00460 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> Coincidence kind (trues, scatter, ...) term is enabled " << endl); 00461 else 00462 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> No informations about the kind of coincidences in the data " << endl); 00463 00464 if(m_normCorrectionFlag) 00465 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> Normalization correction term is enabled " << endl); 00466 else 00467 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> No normalization correction terms in the data " << endl); 00468 00469 if(m_scatCorrectionFlag) 00470 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> Scatter correction term is enabled " << endl); 00471 else 00472 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> No scatter correction terms in the data " << endl); 00473 00474 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> Calibration factor value is : " << m_calibrationFactor << endl); 00475 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> Number of bins are equal to "<<mp_nbOfBins[0]<<","<< mp_nbOfBins[1]<<" for X,Y axis respectively." << endl); 00476 00477 if(mp_POIResolution[0]<0) 00478 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> No POI enabled on the X axis " << endl); 00479 else 00480 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> POI resolution on the X axis is : " << mp_POIResolution[0] << endl); 00481 00482 if(mp_POIResolution[1]<0) 00483 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> No POI enabled on the Y axis " << endl); 00484 else 00485 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> POI resolution on the Y axis is : " << mp_POIResolution[1] << endl); 00486 00487 if(mp_POIResolution[2]<0) 00488 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> No POI enabled on the Z axis " << endl); 00489 else 00490 Cerr(" iDataFileSPECT::CheckFileSizeConsistency() -> POI resolution on the Z axis is : " << mp_POIResolution[2] << endl); 00491 Cerr("---------------------------------------------------------------------------------------------------------------------------------------------" << endl); 00492 00493 return 1; 00494 } 00495 00496 00497 00498 // Depending on the verbosity, output all the datafile initialization information as feedback for the user 00499 if(m_verbose >=4) 00500 { 00501 Cout("----------------------------------------------------------------Datafile initialization -------------------------------------------------------------" << endl); 00502 00503 if(m_eventKindFlag) 00504 Cout(" --> Coincidence kind (trues, scatter, ...) term is enabled " << endl); 00505 else 00506 Cout(" --> No informations about the kind of coincidences in the data " << endl); 00507 00508 if(m_normCorrectionFlag) 00509 Cout(" --> Normalization correction term is enabled " << endl); 00510 else 00511 Cout(" --> No normalization correction terms in the data " << endl); 00512 00513 if(m_scatCorrectionFlag) 00514 Cout(" --> Scatter correction term is enabled " << endl); 00515 else 00516 Cout(" --> No scatter correction terms in the data " << endl); 00517 00518 Cout(" --> Calibration factor value is : " << m_calibrationFactor << endl); 00519 Cout(" --> Number of bins are equal to "<<mp_nbOfBins[0]<<","<< mp_nbOfBins[1]<<" for X,Y axis respectively." << endl); 00520 00521 if(mp_POIResolution[0]<0) 00522 Cout(" --> No POI enabled on the X axis " << endl); 00523 else 00524 Cout(" --> POI resolution on the X axis is : " << mp_POIResolution[0] << endl); 00525 00526 if(mp_POIResolution[1]<0) 00527 Cout(" --> No POI enabled on the Y axis " << endl); 00528 else 00529 Cout(" --> POI resolution on the Y axis is : " << mp_POIResolution[1] << endl); 00530 00531 if(mp_POIResolution[2]<0) 00532 Cout(" --> No POI enabled on the Z axis " << endl); 00533 else 00534 Cout(" --> POI resolution on the Z axis is : " << mp_POIResolution[2] << endl); 00535 Cout("---------------------------------------------------------------------------------------------------------------------------------------------" << endl << endl); 00536 00537 } 00538 00539 // End 00540 return 0; 00541 } 00542 00543 00544 00545 00546 // ===================================================================== 00547 // --------------------------------------------------------------------- 00548 // --------------------------------------------------------------------- 00549 // ===================================================================== 00550 /* 00551 \fn iDataFileSPECT::InitAngles() 00552 \param ap_angles 00553 \brief allocate memory for the mp_angles variable using m_nbProjections 00554 and initialize the projection angles with the provided list of values 00555 \return 0 if success, positive value otherwise 00556 */ 00557 int iDataFileSPECT::InitAngles(FLTNB* ap_angles) 00558 { 00559 if(m_nbOfProjections == 0) 00560 { 00561 Cerr("***** iDataFileSPECT::InitAngles() -> Error, number of projection angles not initialized !'" << endl); 00562 return 1; 00563 } 00564 00565 mp_angles = new FLTNB[m_nbOfProjections]; 00566 00567 for(size_t a=0 ; a<m_nbOfProjections ; a++) 00568 mp_angles[a] = ap_angles[a]; 00569 00570 return 0; 00571 } 00572 00573 00574 00575 00576 // ===================================================================== 00577 // --------------------------------------------------------------------- 00578 // --------------------------------------------------------------------- 00579 // ===================================================================== 00580 /* 00581 \fn iDataFileSPECT::InitCorToDetectorDistance() 00582 \param ap_CORtoDetectorDistance 00583 \brief allocate memory for the ap_CORtoDetectorDistance variable 00584 using m_nbProjections, and initialize the projection angles 00585 with the provided list of values 00586 \return 0 if success, positive value otherwise 00587 */ 00588 int iDataFileSPECT::InitCorToDetectorDistance(FLTNB* ap_CORtoDetectorDistance) 00589 { 00590 if(m_nbOfProjections == 0) 00591 { 00592 Cerr("***** iDataFileSPECT::InitAngles() -> Error, number of projection angles not initialized !'" << endl); 00593 return 1; 00594 } 00595 00596 mp_CORtoDetectorDistance = new FLTNB[m_nbOfProjections]; 00597 00598 for(size_t a=0 ; a<m_nbOfProjections ; a++) 00599 mp_CORtoDetectorDistance[a] = ap_CORtoDetectorDistance[a]; 00600 00601 return 0; 00602 } 00603 00604 00605 00606 00607 //PROJECTION SCRIPT FUNCTIONS 00608 // ===================================================================== 00609 // --------------------------------------------------------------------- 00610 // --------------------------------------------------------------------- 00611 // ===================================================================== 00612 00613 /* 00614 \fn PROJ_InitFile() 00615 \brief Initialize the fstream objets for output writing as well as some other variables specific to the Projection script (Event-based correction flags, Estimated size of data file) 00616 \todo warn the user a datafile with the same name will be erased (eventually add an option to disable the warning) 00617 \return 0 if success, and positive value otherwise. 00618 */ 00619 int iDataFileSPECT::PROJ_InitFile() 00620 { 00621 if(m_verbose >=3) Cout("iDataFileSPECT::PROJ_InitFile() ..." << endl); 00622 00623 m_startTimeInSec = 0.; //Std initialization for projection 00624 m_durationInSec = 1.; //Std initialization for projection 00625 m_totalNbEvents = 0; //Std initialization for projection 00626 m_calibrationFactor = 1.; 00627 m_isotope = "unknown"; 00628 m_scatCorrectionFlag = false; 00629 m_normCorrectionFlag = false; 00630 mp_POIResolution[0] = -1.; 00631 mp_POIResolution[1] = -1.; 00632 mp_POIResolution[2] = -1.; 00633 00634 // Instanciate a fstream datafile for each thread 00635 m2p_dataFile = new fstream*[mp_ID->GetNbThreadsForProjection()]; 00636 00637 // Set name of the projection data header 00638 sOutputManager* p_OutMgr; 00639 p_OutMgr = sOutputManager::GetInstance(); 00640 string path_name = p_OutMgr->GetPathName(); 00641 string img_name = p_OutMgr->GetBaseName(); 00642 m_headerFileName = path_name.append(img_name).append("_CstrProj").append(".Cdh"); 00643 00644 for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++) 00645 { 00646 m_dataFileName = m_headerFileName.substr(0, m_headerFileName.find_last_of(".")).append(".Cdf"); // Generate datafile name from header file 00647 00648 // Projeted data will be written in several files (corresponding to the number of thread) to be concatenated at the end of the projection process. 00649 stringstream ss; 00650 ss << th; 00651 00652 string datafile_name = m_dataFileName; 00653 datafile_name.append("_").append(ss.str()); 00654 00655 m2p_dataFile[th] = new fstream( datafile_name.c_str(), ios::binary | ios::out | ios::trunc); 00656 } 00657 00658 //remove content from the output data file, in case it already exists 00659 //todo warn the user a datafile with the same name will be erased (eventually add an option to disable the warning) 00660 ofstream output_file(m_dataFileName.c_str(), ios::out | ios::trunc); 00661 output_file.close(); 00662 00663 if(m_verbose>=3) Cout("iDataFileSPECT::PROJ_InitFile()-> output datafile name :" << m_dataFileName << endl); 00664 00665 return 0; 00666 } 00667 00668 00669 00670 00671 // ===================================================================== 00672 // --------------------------------------------------------------------- 00673 // --------------------------------------------------------------------- 00674 // ===================================================================== 00675 /* 00676 \fn PROJ_WriteEvent(vEvent* ap_Event, int a_th) 00677 \param ap_Event : event containing the data to write 00678 \param a_th : index of the thread from which the function was called 00679 \brief Write event according to the chosen type of data 00680 \todo Depending of the RAM load FLAG, either write the data in *nbThreads* different files which will be concatenated at the end (current implementation), 00681 or write data in buffers, to be flushed at the end of projection loop. 00682 \todo Projection for list-mode 00683 \return 0 if success, and positive value otherwise. 00684 */ 00685 int iDataFileSPECT::PROJ_WriteEvent(vEvent* ap_Event, int a_th) 00686 { 00687 #ifdef CASTOR_VERBOSE 00688 // Verbose 00689 if(m_verbose>=4) Cout("iDataFileSPECT::PROJ_WriteEvent() ..."<< endl); 00690 #endif 00691 00692 if(m_dataMode == MODE_LIST) 00693 { 00694 // TODO should create as many vEvent as (int)fp. 00695 if(PROJ_WriteListEvent((iEventListSPECT*)ap_Event, a_th)) 00696 { 00697 Cerr("*****iDataFileSPECT::PROJ_WriteEvent() -> Error while trying to write projection datafile (list-mode)" << endl); 00698 return 1; 00699 } 00700 } 00701 00702 if(m_dataMode == MODE_HISTOGRAM) 00703 { 00704 if(PROJ_WriteHistoEvent((iEventHistoSPECT*)ap_Event, a_th)) 00705 { 00706 Cerr("*****iDataFileSPECT::PROJ_WriteEvent() -> Error while trying to write projection datafile (histogram)" << endl); 00707 return 1; 00708 } 00709 } 00710 00711 return 0; 00712 } 00713 00714 00715 // ===================================================================== 00716 // --------------------------------------------------------------------- 00717 // --------------------------------------------------------------------- 00718 // ===================================================================== 00719 /* 00720 \fn PROJ_WriteHistoEvent() 00721 \param ap_Event : event containing the data to write 00722 \param a_th : index of the thread from which the function was called 00723 \brief Write a SPECT histogram event 00724 \return 0 if success, and positive value otherwise. 00725 */ 00726 int iDataFileSPECT::PROJ_WriteHistoEvent(iEventHistoSPECT* ap_Event, int a_th) 00727 { 00728 #ifdef CASTOR_VERBOSE 00729 // Verbose 00730 if(m_verbose>=4) Cout("iDataFileSPECT::PROJ_WriteHistoEvent() ..."<< endl); 00731 #endif 00732 00733 // Write sequentially each field of the event according to the type of the event. 00734 m2p_dataFile[a_th]->clear(); 00735 00736 uint32_t time = ap_Event->GetTimeInMs(); 00737 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&time), sizeof(uint32_t)); 00738 00739 FLTNB event_value = ap_Event->GetEventValue(0); 00740 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&event_value), sizeof(FLTNB)); 00741 00742 if(m_scatCorrectionFlag) 00743 { 00744 FLTNB scat_rate = ap_Event->GetEventScatRate(); 00745 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&scat_rate), sizeof(FLTNB)); 00746 } 00747 00748 if(m_normCorrectionFlag) 00749 { 00750 FLTNB norm_corr_factor = ap_Event->GetNormFactor(); 00751 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&norm_corr_factor), sizeof(FLTNB)); 00752 } 00753 00754 uint32_t id1 = ap_Event->GetID1(0); 00755 uint32_t id2 = ap_Event->GetID2(0); 00756 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id1), sizeof(uint32_t)); 00757 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id2), sizeof(uint32_t)); 00758 00759 return 0; 00760 } 00761 00762 00763 // ===================================================================== 00764 // --------------------------------------------------------------------- 00765 // --------------------------------------------------------------------- 00766 // ===================================================================== 00767 /* 00768 \fn PROJ_WriteListEvent() 00769 \param ap_Event : event containing the data to write 00770 \param a_th : index of the thread from which the function was called 00771 \brief Write a SPECT list-mode event 00772 \return 0 if success, and positive value otherwise. 00773 */ 00774 int iDataFileSPECT::PROJ_WriteListEvent(iEventListSPECT* ap_Event, int a_th) 00775 { 00776 #ifdef CASTOR_VERBOSE 00777 // Verbose 00778 if(m_verbose>=4) Cout("iDataFileSPECT::PROJ_WriteListEvent() ..."<< endl); 00779 #endif 00780 00781 // Write sequentially each field of the event according to the type of the event. 00782 m2p_dataFile[a_th]->clear(); 00783 00784 uint32_t time = ap_Event->GetTimeInMs(); 00785 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&time), sizeof(uint32_t)); 00786 00787 if(m_eventKindFlag) 00788 { 00789 uint8_t event_kind = ap_Event->GetKind(); 00790 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&event_kind), sizeof(uint8_t)); 00791 } 00792 00793 if(m_scatCorrectionFlag) 00794 { 00795 FLTNB scat_rate = ap_Event->GetEventScatRate(); 00796 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&scat_rate), sizeof(FLTNB)); 00797 } 00798 00799 if(m_normCorrectionFlag) 00800 { 00801 FLTNB norm_corr_factor = ap_Event->GetNormFactor(); 00802 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&norm_corr_factor), sizeof(FLTNB)); 00803 } 00804 00805 if(mp_POIResolution[0]>0) 00806 { 00807 FLTNB POI = ap_Event->GetPOI(0); 00808 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&POI), sizeof(FLTNB)); 00809 } 00810 00811 if(mp_POIResolution[1]>0) 00812 { 00813 FLTNB POI = ap_Event->GetPOI(1); 00814 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&POI), sizeof(FLTNB)); 00815 } 00816 00817 if(mp_POIResolution[2]>0) 00818 { 00819 FLTNB POI = ap_Event->GetPOI(2); 00820 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&POI), sizeof(FLTNB)); 00821 } 00822 00823 uint32_t id1 = ap_Event->GetID1(0); 00824 uint32_t id2 = ap_Event->GetID2(0); 00825 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id1), sizeof(uint32_t)); 00826 m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id2), sizeof(uint32_t)); 00827 00828 return 0; 00829 } 00830 00831 00832 00833 00834 00835 // ===================================================================== 00836 // --------------------------------------------------------------------- 00837 // --------------------------------------------------------------------- 00838 // ===================================================================== 00839 /* 00840 \fn PROJ_WriteHeader() 00841 \brief Generate a header file according to the projection and data output informations. 00842 Used by Projection algorithm. 00843 \return 0 if success, and positive value otherwise. 00844 */ 00845 int iDataFileSPECT::PROJ_WriteHeader() 00846 { 00847 // Verbose 00848 if(m_verbose>=3) Cout("iDataFileSPECT::PROJ_WriteHeader() ..."<< endl); 00849 00850 fstream headerFile; 00851 stringstream ss; 00852 00853 headerFile.open(m_headerFileName.c_str(), ios::out); 00854 00855 headerFile << "Data filename:" << " " << GetFileFromPath(m_dataFileName).c_str() << endl; 00856 headerFile << "Number of events:" << " " << m_totalNbEvents << endl; 00857 00858 //- Flag: list-mode or histogram mode 00859 headerFile << "Data mode:" << " " << m_dataMode << endl; 00860 // Flag: PET, SPECT or TRANSMISSION 00861 headerFile << "Data type: " << " " << "SPECT" << endl; 00862 00863 headerFile << "Start time (s):" << " " << m_startTimeInSec << endl; 00864 00865 headerFile << "Duration (s):" << " " << m_durationInSec << endl; 00866 00867 sScannerManager* p_scannermanager; 00868 p_scannermanager = sScannerManager::GetInstance(); 00869 00870 headerFile << "Scanner name:" << " " << p_scannermanager->GetScannerName() << endl; 00871 00872 00873 if(mp_nbOfBins[0] != 0 && mp_nbOfBins[1] != 0) 00874 headerFile << "Number of bins:" << " " << mp_nbOfBins[0] << "," << mp_nbOfBins[1] << endl; 00875 00876 headerFile << "Number of projections:" << " " << m_nbOfProjections << endl; 00877 00878 headerFile << "Projection angles:" << " "; 00879 00880 headerFile << mp_angles[0]; 00881 for(int a=1 ; a<m_nbOfProjections ; a++) 00882 headerFile << ", " << mp_angles[a]; 00883 00884 headerFile << endl; 00885 if(m_nbHeads>1) 00886 { 00887 headerFile << "Global distance camera surface to COR:" << " " << mp_CORtoDetectorDistance[0]; 00888 for(int h=1 ; h<m_nbHeads ; h++) headerFile << "," << mp_CORtoDetectorDistance[h] << endl; 00889 headerFile << endl; 00890 } 00891 00892 else if(mp_CORtoDetectorDistance[0] > 0) 00893 headerFile << "Global distance camera surface to COR:" << " " << mp_CORtoDetectorDistance[0] << endl; 00894 00895 // Optional fields 00896 headerFile << "Calibration factor:" << " " << m_calibrationFactor << endl; 00897 headerFile << "Isotope:" << " " << m_isotope << endl; 00898 headerFile << "DOI capability:" << " " << mp_POIResolution[0] << "," << mp_POIResolution[1] << "," << mp_POIResolution[2] << endl; 00899 headerFile << "Normalization correction flag:" << " " << m_normCorrectionFlag << endl; 00900 headerFile << "Scatter correction flag:" << " " << m_scatCorrectionFlag << endl; 00901 string rot_direction = (m_headRotDirection == GEO_ROT_CCW) ? "CCW" : "CW"; 00902 headerFile << "Head rotation direction:" << " " << rot_direction << endl; 00903 00904 headerFile.close(); 00905 00906 return 0; 00907 } 00908 00909 00910 00911 // ===================================================================== 00912 // --------------------------------------------------------------------- 00913 // --------------------------------------------------------------------- 00914 // ===================================================================== 00915 /* 00916 \fn PROJ_GetScannerSpecificParameters() 00917 \brief Get SPECT specific parameters for projections from the scanner object, through the scannerManager. 00918 \return 0 if success, positive value otherwise 00919 */ 00920 int iDataFileSPECT::PROJ_GetScannerSpecificParameters() 00921 { 00922 // Verbose 00923 if(m_verbose>=3) Cout("iDataFileSPECT::PROJ_GetScannerSpecificParameters() ..."<< endl); 00924 00925 sScannerManager* p_scannermanager; 00926 p_scannermanager = sScannerManager::GetInstance(); 00927 00928 // Create pointers dedicated to recover the addresses of the member variables of the scanner object 00929 FLTNB* p_angles = NULL; 00930 FLTNB* p_CORtoDetectorDistance = NULL; 00931 FLTNB p_pixSizeXY[2] ; // not used here 00932 00933 // Get pointers to SPECT specific parameters in scanner 00934 if(p_scannermanager->GetSPECTSpecificParameters(&m_nbOfProjections, &m_nbHeads, mp_nbOfBins, p_pixSizeXY, p_angles, p_CORtoDetectorDistance, &m_headRotDirection) ) 00935 { 00936 Cerr("*****iDataFileSPECT::PROJ_GetScannerSpecificParameters() -> An error occurred while trying to get SPECT geometric parameters from the scanner object !" << endl); 00937 return 1; 00938 } 00939 00940 // In projection, mp_CORtoDetectorDistance is allocated according to the number of heads, and not the number of projections 00941 // (Projection does not currently allow to set a radius specific to the projection angles) 00942 mp_CORtoDetectorDistance = new FLTNB[m_nbHeads]; 00943 00944 // Retrieve SPECT distance between the detector and center of rotation 00945 for(int h=0 ; h<m_nbHeads ; h++) 00946 mp_CORtoDetectorDistance[h] = p_CORtoDetectorDistance[0]; 00947 00948 // Retrieve projection angles 00949 mp_angles = new FLTNB[m_nbOfProjections]; 00950 00951 for(int a=0 ; a<m_nbOfProjections ; a++) 00952 mp_angles[a] = p_angles[a]; 00953 00954 return 0; 00955 }