![]() |
CASToR
1.0
Tomographic Reconstruction (PET/SPECT)
|
00001 00002 /* 00003 Implementation of class oSensitivityGenerator 00004 00005 - separators: 00006 - doxygen: 00007 - default initialization: 00008 - CASTOR_DEBUG: 00009 - CASTOR_VERBOSE: 00010 */ 00011 00018 #include "gVariables.hh" 00019 #include "oSensitivityGenerator.hh" 00020 #include "iDataFilePET.hh" 00021 00022 // ===================================================================== 00023 // --------------------------------------------------------------------- 00024 // --------------------------------------------------------------------- 00025 // ===================================================================== 00026 00027 oSensitivityGenerator::oSensitivityGenerator() 00028 { 00029 // Simply default all members to "empty" values 00030 m_pathToSensitivityImage = ""; 00031 mp_ImageDimensionsAndQuantification = NULL; 00032 mp_ImageSpace = NULL; 00033 mp_Scanner = NULL; 00034 mp_ProjectorManager = NULL; 00035 mp_ImageConvolverManager = NULL; 00036 mp_DeformationManager = NULL; 00037 m2p_DataFile = NULL; 00038 mp_pathToNormalizationFileName = {}; 00039 m3p_NormalizationDataFile = NULL; 00040 m_OneNormalizationFileForAllBeds = false; 00041 m_OneNormalizationFileForAllFrames = false; 00042 m_pathToAttenuationImage = ""; 00043 m_attenuationFlag = false; 00044 m_forwardProjectAttenuation = false; 00045 m_nbAtnRespGateImages = -1; 00046 m_nbAtnCardGateImages = -1; 00047 mp_lineCounter = NULL; 00048 m_flagGPU = false; 00049 m_verbose = -1; 00050 m_checked = false; 00051 m_initialized = false; 00052 } 00053 00054 // ===================================================================== 00055 // --------------------------------------------------------------------- 00056 // --------------------------------------------------------------------- 00057 // ===================================================================== 00058 00059 oSensitivityGenerator::~oSensitivityGenerator() 00060 { 00061 // Delete all normalization data files if any 00062 if (m3p_NormalizationDataFile) 00063 { 00064 for (int bed=0; bed<mp_ImageDimensionsAndQuantification->GetNbBeds(); bed++) 00065 { 00066 if (m3p_NormalizationDataFile[bed]) 00067 { 00068 for (int fr=0; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++) 00069 if (m3p_NormalizationDataFile[bed][fr]) delete m3p_NormalizationDataFile[bed][fr]; 00070 free(m3p_NormalizationDataFile[bed]); 00071 } 00072 free(m3p_NormalizationDataFile); 00073 } 00074 } 00075 if (mp_lineCounter) free(mp_lineCounter); 00076 } 00077 00078 // ===================================================================== 00079 // --------------------------------------------------------------------- 00080 // --------------------------------------------------------------------- 00081 // ===================================================================== 00082 00083 int oSensitivityGenerator::CheckParameters() 00084 { 00085 // Check all mandatory parameters 00086 if (mp_ImageDimensionsAndQuantification==NULL) 00087 { 00088 Cerr("***** oSensitivityGenerator::CheckParameters() -> Image dimensions and quantification object is null !" << endl); 00089 return 1; 00090 } 00091 if (mp_ImageSpace==NULL) 00092 { 00093 Cerr("***** oSensitivityGenerator::CheckParameters() -> Image space object is null !" << endl); 00094 return 1; 00095 } 00096 if (mp_Scanner==NULL) 00097 { 00098 Cerr("***** oSensitivityGenerator::CheckParameters() -> Scanner object is null !" << endl); 00099 return 1; 00100 } 00101 if (mp_ProjectorManager==NULL) 00102 { 00103 Cerr("***** oSensitivityGenerator::CheckParameters() -> Projector manager object is null !" << endl); 00104 return 1; 00105 } 00106 if (mp_ImageConvolverManager==NULL) 00107 { 00108 Cerr("***** oSensitivityGenerator::CheckParameters() -> Convolver manager object is null !" << endl); 00109 return 1; 00110 } 00111 if (mp_DeformationManager==NULL) 00112 { 00113 Cerr("***** oSensitivityGenerator::CheckParameters() -> Deformation manager object is null !" << endl); 00114 return 1; 00115 } 00116 if (m2p_DataFile==NULL) 00117 { 00118 Cerr("***** oSensitivityGenerator::CheckParameters() -> Data file array is null !" << endl); 00119 return 1; 00120 } 00121 for (int b=0; b<mp_ImageDimensionsAndQuantification->GetNbBeds(); b++) 00122 { 00123 if (m2p_DataFile[b]==NULL) 00124 { 00125 Cerr("***** oSensitivityGenerator::CheckParameters() -> Data file object for bed " << b+1 << " is null !" << endl); 00126 return 1; 00127 } 00128 } 00129 if (m_verbose<0) 00130 { 00131 Cerr("***** oSensitivityGenerator::CheckParameters() -> Verbose level is negative !" << endl); 00132 return 1; 00133 } 00134 // In SPECT with attenuation correction 00135 if (m_pathToAttenuationImage!="" && m2p_DataFile[0]->GetDataType()==TYPE_SPECT) 00136 { 00137 // Check that the projection line strategy is not IMAGE_COMPUTATION_STRATEGY (this is not compatible) 00138 // Note that we cannot do this check in the oProjectionLine directly, because we cannot now in advance 00139 // that the projection method including attenuation will be used... 00140 if (mp_ProjectorManager->GetComputationStrategy()==IMAGE_COMPUTATION_STRATEGY) 00141 { 00142 Cerr("***** oSensitivityGenerator::CheckParameters() -> The image-computation strategy of the oProjectionLine is not compatible with SPECT attenuation correction !"); 00143 return 1; 00144 } 00145 // Check that the projector is compatible with SPECT with attenuation correction 00146 if (!mp_ProjectorManager->IsBackwardOperatorCompatibleWithSPECTAttenuationCorrection()) 00147 { 00148 Cerr("***** oSensitivityGenerator::CheckParameters() -> The backward projector is not compatible with SPECT attenuation correction !" << endl); 00149 return 1; 00150 } 00151 } 00152 // Now it is checked 00153 m_checked = true; 00154 // End 00155 return 0; 00156 } 00157 00158 // ===================================================================== 00159 // --------------------------------------------------------------------- 00160 // --------------------------------------------------------------------- 00161 // ===================================================================== 00162 00163 int oSensitivityGenerator::Initialize() 00164 { 00165 // First check that the parameters has been checked ! 00166 if (!m_checked) 00167 { 00168 Cerr("***** oSensitivityGenerator::Initialize() -> Must call the CheckParameters() function before initialization !" << endl); 00169 return 1; 00170 } 00171 // Verbose 00172 if (m_verbose>=2) Cout("oSensitivityGenerator::Initialize() -> Start initialization" << endl); 00173 // For the moment, SPECT sensitivity is not implemented 00174 if (m2p_DataFile[0]->GetDataType()==TYPE_SPECT) 00175 { 00176 Cerr("***** oSensitivityGenerator::Initialize() -> Sensitivity for SPECT not yet implemented !" << endl); 00177 return 1; 00178 } 00179 // For the moment, PET sensitivity when using TOF is not possible, will soon be ! 00180 if (m2p_DataFile[0]->GetDataType()==TYPE_PET && (dynamic_cast<iDataFilePET*>(m2p_DataFile[0]))->GetTOFInfoFlag()) 00181 { 00182 Cerr("***** oSensitivityGenerator::Initialize() -> Sensitivity for PET with TOF is not yet possible, will soon be !" << endl); 00183 return 1; 00184 } 00185 // Initialize normalization files (must be done before attenuation files, because attenuation can be inside the normalization data file) 00186 if (InitializeNormalizationFiles()) 00187 { 00188 Cerr("***** oSensitivityGenerator::Initialize() -> A problem occured while initializing the normalization data files !" << endl); 00189 return 1; 00190 } 00191 // Initialize attenuation related stuff 00192 if (InitializeAttenuationFiles()) 00193 { 00194 Cerr("***** oSensitivityGenerator::Initialize() -> A problem occured while initializing the attenuation files !" << endl); 00195 return 1; 00196 } 00197 // We want to know if we will need to forward project into the attenuation image, so here the full condition 00198 if ( 00199 m_attenuationFlag && // 1. we must have an attenuation image 00200 m2p_DataFile[0]->GetDataType()==TYPE_PET && // 2. This is only for PET 00201 ( m3p_NormalizationDataFile==NULL || // 3. Either there are no normalization data file (potentially including ACF) 00202 !(dynamic_cast<iDataFilePET*>(m3p_NormalizationDataFile[0][0]))->GetAtnCorrectionFlag() ) // 4. Or the normalization data file does not contain ACF 00203 ) 00204 { 00205 // We use a specific boolean that says we need to forward project into the attenuation image 00206 m_forwardProjectAttenuation = true; 00207 } 00208 else 00209 { 00210 // Otherwise, we don't 00211 m_forwardProjectAttenuation = false; 00212 } 00213 // Allocate the backward image for multi-thread 00214 mp_ImageSpace->LMS_InstantiateBackwardImage(); 00215 // Create sensitivity image file name 00216 sOutputManager* p_outputManager = sOutputManager::GetInstance(); 00217 m_pathToSensitivityImage = p_outputManager->GetPathName() + p_outputManager->GetBaseName() + "_sensitivity"; 00218 // Allocate line counters 00219 mp_lineCounter = (uint64_t*)calloc(mp_ImageDimensionsAndQuantification->GetNbThreadsForProjection(),sizeof(uint64_t)); 00220 // End 00221 return 0; 00222 } 00223 00224 // ===================================================================== 00225 // --------------------------------------------------------------------- 00226 // --------------------------------------------------------------------- 00227 // ===================================================================== 00228 00229 int oSensitivityGenerator::InitializeNormalizationFiles() 00230 { 00231 // TODO: check the consistency between all normalization files (atnflag, normfactor, etc) 00232 00233 // If no normalization file name provided, then we will exit, but we do some checks before 00234 if (mp_pathToNormalizationFileName.size()==0) 00235 { 00236 // If the ignore-normalization-correction-flag is on, then we can exit the function 00237 if (mp_ImageDimensionsAndQuantification->GetIgnoreNormCorrectionFlag()) return 0; 00238 // In PET, throw an error if normalization correction is in the datafile and is taken into account 00239 if (m2p_DataFile[0]->GetDataType()==TYPE_PET) 00240 { 00241 // Cast the vDataFile into iDataFilePET 00242 iDataFilePET* p_pet_file = (dynamic_cast<iDataFilePET*>(m2p_DataFile[0])); 00243 // Check if the normalization data are in the file and we do not ignore it 00244 if (p_pet_file->GetNormCorrectionFlag()) 00245 { 00246 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Normalization correction is included in the data file while it is not in the sensitivity computation !" << endl); 00247 return 1; 00248 } 00249 } 00250 // We can exit now 00251 return 0; 00252 } 00253 00254 // Verbose 00255 if (m_verbose>=2) Cout("oSensitivityGenerator::InitializeNormalizationFiles() -> Initialize normalization files" << endl); 00256 00257 // Check that the number of normalization files options is the same as the number of beds 00258 if (mp_pathToNormalizationFileName.size()!=((size_t)(mp_ImageDimensionsAndQuantification->GetNbBeds()))) 00259 { 00260 // If only one is provided, then we assume that it is the same for all bed positions 00261 if (mp_pathToNormalizationFileName.size()==1) m_OneNormalizationFileForAllBeds = true; 00262 // Otherwise, throw an error 00263 else 00264 { 00265 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Number of normalization files options should be one or equal to the number of data files !" << endl); 00266 return 1; 00267 } 00268 } 00269 00270 // Allocate the normalization data files array for the bed positions 00271 m3p_NormalizationDataFile = (vDataFile***)malloc(mp_ImageDimensionsAndQuantification->GetNbBeds()*sizeof(vDataFile**)); 00272 00273 // Get the scanner manager 00274 sScannerManager* p_ScannerManager = sScannerManager::GetInstance(); 00275 00276 // Loop on the number of beds 00277 for (int bed=0; bed<mp_ImageDimensionsAndQuantification->GetNbBeds(); bed++) 00278 { 00279 // -------------------------------------------------------------------------- 00280 // Step 0: When only one normalization file names is provided for all beds 00281 // -------------------------------------------------------------------------- 00282 if (m_OneNormalizationFileForAllBeds) 00283 { 00284 // If not the first bed 00285 if (bed!=0) 00286 { 00287 // We copy the pointer of the first bed into this one 00288 m3p_NormalizationDataFile[bed] = m3p_NormalizationDataFile[0]; 00289 // And we continue the loop 00290 continue; 00291 } 00292 } 00293 // -------------------------------------------------------------------------- 00294 // Step 1: Get filenames and check consistency with number of beds and frames 00295 // -------------------------------------------------------------------------- 00296 // We will get the normalization file name for this bed 00297 vector<string> normalization_files; 00298 // Then we search for commas separating the file name associated to each frame 00299 size_t comma = mp_pathToNormalizationFileName[bed].find_first_of(","); 00300 // Loop to get all file names 00301 while (comma!=string::npos) 00302 { 00303 // Get the file name before the comma 00304 normalization_files.push_back(mp_pathToNormalizationFileName[bed].substr(0,comma)); 00305 // Remove this part from the collection of file names 00306 mp_pathToNormalizationFileName[bed] = mp_pathToNormalizationFileName[bed].substr(comma+1); 00307 // Search for the next comma 00308 comma = mp_pathToNormalizationFileName[bed].find_first_of(","); 00309 } 00310 // Get the last file name 00311 normalization_files.push_back(mp_pathToNormalizationFileName[bed]); 00312 // Check that the number of file names found is equal to the number of frames 00313 if (normalization_files.size()!=((size_t)(mp_ImageDimensionsAndQuantification->GetNbTimeFrames()))) 00314 { 00315 // If only one is provided, then we assume that it is the same for all frames 00316 if (mp_pathToNormalizationFileName.size()==1) m_OneNormalizationFileForAllFrames = true; 00317 // Otherwise, throw an error 00318 else 00319 { 00320 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Number of normalization files for bed " << bed+1 << " should be one or equal to the number of frames !" << endl); 00321 return 1; 00322 } 00323 } 00324 // If this is the case, then check for previous beds that it was so 00325 else if (bed!=0 && !m_OneNormalizationFileForAllFrames) 00326 { 00327 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> If only one normalization file is provided for all frames, then it must be so for all beds !" << endl); 00328 return 1; 00329 } 00330 // -------------------------------------------------------------------------- 00331 // Step 2: Create the normalization data file objects 00332 // -------------------------------------------------------------------------- 00333 // Allocate the normalization data file array for the frames 00334 m3p_NormalizationDataFile[bed] = (vDataFile**)malloc(mp_ImageDimensionsAndQuantification->GetNbTimeFrames()*sizeof(vDataFile*)); 00335 // Loop on the number of frames 00336 for (int fr=0; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames(); fr++) 00337 { 00338 // If one normalization file for all frames 00339 if (m_OneNormalizationFileForAllFrames) 00340 { 00341 // Then, if not the first frame 00342 if (fr!=0) 00343 { 00344 // We copy the pointer of the first frame into this one 00345 m3p_NormalizationDataFile[bed][fr] = m3p_NormalizationDataFile[bed][0]; 00346 // And we continue the loop 00347 continue; 00348 } 00349 } 00350 // Switch on the scanner type and create the specific data files 00351 if (p_ScannerManager->GetScannerType() == SCANNER_PET) m3p_NormalizationDataFile[bed][fr] = new iDataFilePET(); 00352 else 00353 { 00354 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> Unknown scanner type (" << p_ScannerManager->GetScannerType() << ") or not yet implemented !" << endl); 00355 return 1; 00356 } 00357 m3p_NormalizationDataFile[bed][fr]->SetHeaderDataFileName(normalization_files[fr]); 00358 m3p_NormalizationDataFile[bed][fr]->SetBedIndex(bed); 00359 m3p_NormalizationDataFile[bed][fr]->SetPercentageLoad(m2p_DataFile[0]->GetPercentageLoad()); 00360 m3p_NormalizationDataFile[bed][fr]->SetVerbose(m2p_DataFile[0]->GetVerbose()); 00361 m3p_NormalizationDataFile[bed][fr]->SetImageDimensionsAndQuantification(mp_ImageDimensionsAndQuantification); 00362 if (m3p_NormalizationDataFile[bed][fr]->ReadInfoInHeader()) 00363 { 00364 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred during normalization datafile header reading !" << endl); 00365 return 1; 00366 } 00367 if (m3p_NormalizationDataFile[bed][fr]->CheckParameters()) 00368 { 00369 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred while checking normalization datafile parameters !" << endl); 00370 return 1; 00371 } 00372 if (m3p_NormalizationDataFile[bed][fr]->ComputeSizeEvent()) 00373 { 00374 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred in normalization datafile event size computation !" << endl); 00375 return 1; 00376 } 00377 if (m3p_NormalizationDataFile[bed][fr]->InitializeFile()) 00378 { 00379 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occurred in normalization datafile initialization !" << endl); 00380 return 1; 00381 } 00382 if (m3p_NormalizationDataFile[bed][fr]->PrepareDataFile()) 00383 { 00384 Cerr("***** oSensitivityGenerator::InitializeNormalizationFiles() -> A problem occured in normalization datafile preparation !" << endl); 00385 return 1; 00386 } 00387 } 00388 } 00389 00390 // End 00391 return 0; 00392 } 00393 00394 // ===================================================================== 00395 // --------------------------------------------------------------------- 00396 // --------------------------------------------------------------------- 00397 // ===================================================================== 00398 00399 int oSensitivityGenerator::InitializeAttenuationFiles() 00400 { 00401 // If it is asked to ignore attenuation correction, then nothing to do 00402 if (mp_ImageDimensionsAndQuantification->GetIgnoreAttnCorrectionFlag()) return 0; 00403 00404 // If attenuation image file name is empty, then we do some checks before 00405 if (m_pathToAttenuationImage=="") 00406 { 00407 // Case for PET: if no normalization data file OR no ACF in the normalization data file 00408 if (m2p_DataFile[0]->GetDataType()==TYPE_PET && (m3p_NormalizationDataFile==NULL || !(dynamic_cast<iDataFilePET*>(m3p_NormalizationDataFile[0][0]))->GetAtnCorrectionFlag())) 00409 { 00410 // Cast the vDataFile into iDataFilePET 00411 iDataFilePET* p_pet_file = (dynamic_cast<iDataFilePET*>(m2p_DataFile[0])); 00412 // Throw an error if attenuation correction is in the datafile (at this step we know we do not ignore it) 00413 if (p_pet_file->GetAtnCorrectionFlag()) 00414 { 00415 Cerr("***** oSensitivityGenerator::InitializeAttenuationFiles() -> Attenuation correction is included in the data file while it is not in the sensitivity computation !" << endl); 00416 return 1; 00417 } 00418 } 00419 // We can exit now 00420 return 0; 00421 } 00422 00423 // In PET, if the ACF is already present in the normalization data file, then say it and ignore mumap 00424 if (m2p_DataFile[0]->GetDataType()==TYPE_PET && m3p_NormalizationDataFile!=NULL && (dynamic_cast<iDataFilePET*>(m3p_NormalizationDataFile[0][0]))->GetAtnCorrectionFlag()) 00425 { 00426 Cout("oSensitivityGenerator::InitializeAttenuationFiles() -> Ignore provided mumap and consider ACF provided in the normalization data file" << endl); 00427 return 0; 00428 } 00429 00430 // Verbose 00431 if (m_verbose>=2) Cout("oSensitivityGenerator::InitializeAttenuationFiles() -> Allocate and read attenuation images (assumed to be in cm-1)" << endl); 00432 00433 // Allocate and read the attenuation image (this puts the image into the m4p_attenuation buffer of oImageSpace) 00434 if (mp_ImageSpace->InitAttenuationImage(m_pathToAttenuationImage)) 00435 { 00436 Cerr("***** oSensitivityGenerator::Initialize() -> A problem occured while initializing the attenuation image into the image space !" << endl); 00437 return 1; 00438 } 00439 00440 // Allocate the foward image, where the attenuation will be copied and deformed if needed 00441 mp_ImageSpace->LMS_InstantiateForwardImage(); 00442 00443 // Copy attenuation image into forward-image buffer 00444 mp_ImageSpace->LMS_CopyAtnToForwardImage(); 00445 00446 // We do not need the m4p_attenuation buffer of oImageSpace anymore, so free it now 00447 mp_ImageSpace->LMS_DeallocateAttenuationImage(); 00448 00449 // TODO: Think about adding a field in the attenuation header to specify if the attenuation images have the same spatial resolution as the 00450 // scanner in use, in order to apply a smoothing on it. For the moment, we assume it has been done beforehand. 00451 00452 // TODO: for the moment, we assume that the attenuation image is in cm-1, maybe use a header field to detect that and convert it 00453 // automatically. 00454 00455 // Put attenuation flag on 00456 m_attenuationFlag = true; 00457 00458 // End 00459 return 0; 00460 } 00461 00462 // ===================================================================== 00463 // --------------------------------------------------------------------- 00464 // --------------------------------------------------------------------- 00465 // ===================================================================== 00466 00467 int oSensitivityGenerator::Launch() 00468 { 00469 // Verbose 00470 if (m_verbose>=1) Cout("oSensitivityGenerator::Launch() -> Start the sensitivity computation" << endl); 00471 // Simply switches between the CPU and GPU versions 00472 #ifdef CASTOR_GPU 00473 if (m_flagGPU) return LaunchGPU(); 00474 else return LaunchCPU(); 00475 #else 00476 return LaunchCPU(); 00477 #endif 00478 } 00479 00480 // ===================================================================== 00481 // --------------------------------------------------------------------- 00482 // --------------------------------------------------------------------- 00483 // ===================================================================== 00484 00485 int oSensitivityGenerator::LaunchCPU() 00486 { 00487 // Loop on the bed positions 00488 for (int bed=0; bed<mp_ImageDimensionsAndQuantification->GetNbBeds(); bed++) 00489 { 00490 // Reset line counters 00491 for (int th=0; th<mp_ImageDimensionsAndQuantification->GetNbThreadsForProjection(); th++) mp_lineCounter[th] = 0; 00492 // If a normalization file is provided, then use them to iterate on the elements to be taken into account 00493 if (m3p_NormalizationDataFile!=NULL && ComputeSensitivityFromNormalizationFile(bed)) 00494 { 00495 Cerr("***** oSensitivityGenerator::LaunchCPU() -> A problem occured while computing the sensitivity using a normalization file for bed " << bed+1 << " !" << endl); 00496 return 1; 00497 } 00498 // Otherwise, use a generic method from the scanner elements 00499 if (m3p_NormalizationDataFile==NULL && ComputeSensitivityFromScanner(bed)) 00500 { 00501 Cerr("***** oSensitivityGenerator::LaunchCPU() -> A problem occured while computing the sensitivity from scanner elements for bed " << bed+1 << " !" << endl); 00502 return 1; 00503 } 00504 // Sum up the line counter into the first thread 00505 for (int th=1; th<mp_ImageDimensionsAndQuantification->GetNbThreadsForProjection(); th++) mp_lineCounter[0] += mp_lineCounter[th]; 00506 // Verbose 00507 if (m_verbose>=2) Cout(" --> Number of effectively projected lines: " << mp_lineCounter[0] << endl); 00508 } 00509 // Deallocate the forward image only if attenuation was used 00510 if (m_attenuationFlag) mp_ImageSpace->LMS_DeallocateForwardImage(); 00511 // Reduce all results (merging parallel computation) 00512 for (int fr=0 ; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames() ; fr++) 00513 for (int rg=0 ; rg<mp_ImageDimensionsAndQuantification->GetSensNbRespGates() ; rg++) 00514 for (int cg=0 ; cg<mp_ImageDimensionsAndQuantification->GetSensNbCardGates() ; cg++) 00515 { 00516 // Merge parallel results into the backward image 00517 mp_ImageSpace->LMS_Reduce(fr, rg, cg); 00518 // Perform any deformations on the backward image 00519 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, BACKWARD_DEFORMATION, 0, fr, rg, cg); 00520 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, BACKWARD_DEFORMATION, 1, fr, rg, cg); 00521 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, BACKWARD_DEFORMATION, 2, fr, rg, cg); 00522 } 00523 // Allocate sensitivity image 00524 mp_ImageSpace->LMS_InstantiateSensitivityImage(); 00525 // Copy backward image to sensitivity image 00526 mp_ImageSpace->LMS_CopyBackwardToSensitivity(); 00527 // Deallocate backward image 00528 mp_ImageSpace->LMS_DeallocateBackwardImage(); 00529 // Apply PSF if needed 00530 mp_ImageConvolverManager->ConvolveSensitivity(mp_ImageSpace); 00531 // Save sensitivity image 00532 mp_ImageSpace->LMS_SaveSensitivityImage(m_pathToSensitivityImage, mp_DeformationManager); 00533 // Deallocate sensitivity image 00534 mp_ImageSpace->LMS_DeallocateSensitivityImage(); 00535 // End 00536 return 0; 00537 } 00538 00539 // ===================================================================== 00540 // --------------------------------------------------------------------- 00541 // --------------------------------------------------------------------- 00542 // ===================================================================== 00543 // TODO: implement a function ComputeSensitivityFromDataFile when in histogram mode 00544 int oSensitivityGenerator::ComputeSensitivityFromNormalizationFile(int a_bed) 00545 { 00546 // Verbose 00547 if (m_verbose>=2) 00548 { 00549 if (mp_ImageDimensionsAndQuantification->GetNbBeds()>1) Cout("oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> Start computation for bed " << a_bed+1 << endl); 00550 else Cout("oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> Start computation" << endl); 00551 } 00552 00553 // Initial clock 00554 clock_t clock_start = clock(); 00555 time_t time_start = time(NULL); 00556 00557 // Loop on frames 00558 for (int fr=0 ; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames() ; fr++) 00559 { 00560 // Verbose 00561 if (m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetNbTimeFrames()>1) 00562 cout << " --> Processing frame " << fr+1 << endl; 00563 // Loop on respiratory gates 00564 for (int rg=0 ; rg<mp_ImageDimensionsAndQuantification->GetSensNbRespGates() ; rg++) 00565 { 00566 // Verbose 00567 if (m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetSensNbRespGates()>1) 00568 cout << " --> Processing respiratory gate " << rg+1 << endl; 00569 // Loop on cardiac gates 00570 for (int cg=0 ; cg<mp_ImageDimensionsAndQuantification->GetSensNbCardGates() ; cg++) // TODO: why NbCardGates and not SensNbCardGates here ? 00571 { 00572 // Verbose 00573 if (m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetSensNbCardGates()>1) 00574 cout << " --> Processing cardiac gate " << cg+1 << endl; 00575 // Perform any forward deformations on the forward image (only if attenuation is used) 00576 if (m_attenuationFlag) 00577 { 00578 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, FORWARD_DEFORMATION, 0, fr, rg, cg); 00579 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, FORWARD_DEFORMATION, 1, fr, rg, cg); 00580 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, FORWARD_DEFORMATION, 2, fr, rg, cg); 00581 } 00582 // Reset the file buffer range before launching the loop over the datafile (this is done only if the percentage load is under 100) 00583 m3p_NormalizationDataFile[a_bed][fr]->ResetBufferRange(); 00584 // In case a problem occurs in the parallel loop 00585 bool problem = false; 00586 // Set the number of threads for projections (right after this loop, we set back the number of threads for image computations) 00587 #ifdef CASTOR_OMP 00588 omp_set_num_threads(mp_ImageDimensionsAndQuantification->GetNbThreadsForProjection()); 00589 #endif 00590 // Compute the index limit over which we consider that threads have finished the loop 00591 // (to be valid, the omp loop need to use a static scheduling with a chunck size of 1) 00592 int64_t index_limit = m3p_NormalizationDataFile[a_bed][fr]->GetSize() - 1 - 2*((int64_t)(mp_ImageDimensionsAndQuantification->GetNbThreadsForProjection())); 00593 // Launch the loop on all events 00594 int64_t index = 0, printing_index = 0; 00595 #pragma omp parallel for private(index) schedule(static, 1) 00596 for (index=0; index<m3p_NormalizationDataFile[a_bed][fr]->GetSize(); index++) 00597 { 00598 // Get the thread index 00599 int th = 0; 00600 #ifdef CASTOR_OMP 00601 th = omp_get_thread_num(); 00602 #endif 00603 // Verbose 00604 if (th==0 && m_verbose>=2) 00605 { 00606 if (printing_index%1000==0) 00607 { 00608 int percent = ((int)( ((FLTNB)(index)) * 100. / ((FLTNB)(m3p_NormalizationDataFile[a_bed][fr]->GetSize())) )); 00609 cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b " 00610 << percent << " % " << flush; 00611 } 00612 printing_index++; 00613 } 00614 // Get the current event for that thread index 00615 vEvent* event = m3p_NormalizationDataFile[a_bed][fr]->GetEventWithAscendingOrderAssumption(index, index_limit, th); 00616 if (event==NULL) 00617 { 00618 Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> An error occured while getting the event from index " 00619 << index << " (thread " << th << ") !" << endl); 00620 // A problem was encountered 00621 problem = true; 00622 // We must continue here because we are inside an OpenMP loop 00623 continue; 00624 } 00625 // Compute the projection line 00626 oProjectionLine *line = mp_ProjectorManager->ComputeProjectionLine(event, th); 00627 if (line==NULL) 00628 { 00629 Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> A problem occured while computing the projection line !" << endl); 00630 // Specify that there was a problem 00631 problem = true; 00632 // We must continue here because we are inside an OpenMP loop 00633 continue; 00634 } 00635 // Process this line 00636 if (line->NotEmptyLine() && ProcessThisLine(line, event, a_bed, fr, rg, cg, th)) 00637 { 00638 Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> A problem occured while processing a line !" << endl); 00639 } 00640 } 00641 // End of progression printing (do not log out with Cout here) 00642 if (m_verbose>=2) cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" 00643 << " --> 100 % " << endl; 00644 // If a problem was encountered, then report it here 00645 if (problem) 00646 { 00647 Cerr("***** oSensitivityGenerator::ComputeSensitivityFromNormalizationFile() -> A problem occured inside the parallel loop over events !" << endl); 00648 return 1; 00649 } 00650 } 00651 } 00652 } 00653 00654 // Clock total 00655 clock_t clock_stop = clock(); 00656 time_t time_stop = time(NULL); 00657 if (mp_ImageDimensionsAndQuantification->GetNbBeds()>1) 00658 Cout(" --> Time spent for sensitivity generation for bed " << a_bed+1 << " | User: " << time_stop-time_start 00659 << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl); 00660 else 00661 Cout(" --> Time spent for sensitivity generation | User: " << time_stop-time_start 00662 << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl); 00663 00664 // End 00665 return 0; 00666 } 00667 00668 // ===================================================================== 00669 // --------------------------------------------------------------------- 00670 // --------------------------------------------------------------------- 00671 // ===================================================================== 00672 00673 int oSensitivityGenerator::ComputeSensitivityFromScanner(int a_bed) 00674 { 00675 // Verbose 00676 if (m_verbose>=2) 00677 { 00678 if (mp_ImageDimensionsAndQuantification->GetNbBeds()>1) Cout("oSensitivityGenerator::ComputeSensitivityFromScanner() -> Start computation for bed " << a_bed+1 << endl); 00679 else Cout("oSensitivityGenerator::ComputeSensitivityFromScanner() -> Start computation" << endl); 00680 } 00681 00682 // Get the scanner manager 00683 sScannerManager* p_scannerManager = sScannerManager::GetInstance(); 00684 00685 // Total number of elements 00686 int nb_total_elts = mp_Scanner->GetSystemNbElts(); 00687 00688 // Initial clock 00689 clock_t clock_start = clock(); 00690 time_t time_start = time(NULL); 00691 00692 // Initialize main loop start and stop values 00693 int64_t main_loop_start_index = 0 ; 00694 int64_t main_loop_stop_index = 0; 00695 00696 // Check beforehand any issue with the loop start/stop values (not possible in the inner multithreaded loop) 00697 if (p_scannerManager->PROJ_GetModalityStopValueMainLoop()>0) main_loop_stop_index = p_scannerManager->PROJ_GetModalityStopValueMainLoop(); 00698 else 00699 { 00700 Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occured when trying to initialize main loop stop index !" << endl); 00701 return 1; 00702 } 00703 if (p_scannerManager->PROJ_GetModalityStartValueInnerLoop(0)<=0) 00704 { 00705 Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occured when trying to initialize inner loop start index !" << endl); 00706 return 1; 00707 } 00708 00709 // Prepare pre-computed sums of events to avoid the exponential evolution of the percentage (for PET) 00710 // TODO Perhaps replace this with a call to scannerManager for error management 00711 int64_t* progression_elts_array = new int64_t[nb_total_elts*sizeof(int64_t)]; 00712 progression_elts_array[0] = 0; 00713 for (int idx_elt=1 ; idx_elt<mp_Scanner->GetSystemNbElts() ; idx_elt++) 00714 progression_elts_array[idx_elt] = progression_elts_array[idx_elt-1] + (mp_Scanner->GetSystemNbElts()-idx_elt); 00715 00716 // Index for progression printing 00717 uint64_t printing_index = 0; 00718 uint64_t progression_index_total = p_scannerManager->PROJ_GetProgressionFinalValue()* 00719 mp_ImageDimensionsAndQuantification->GetNbTimeFrames()* 00720 mp_ImageDimensionsAndQuantification->GetSensNbRespGates()* 00721 mp_ImageDimensionsAndQuantification->GetSensNbCardGates(); 00722 // Loop on frames 00723 for (int fr=0 ; fr<mp_ImageDimensionsAndQuantification->GetNbTimeFrames() ; fr++) 00724 { 00725 // Verbose 00726 if (m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetNbTimeFrames()>1) 00727 cout << " --> Processing frame " << fr+1 << endl; 00728 // Loop on respiratory gates 00729 for (int rg=0 ; rg<mp_ImageDimensionsAndQuantification->GetSensNbRespGates() ; rg++) 00730 { 00731 // Verbose 00732 if (m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetSensNbRespGates()>1) 00733 cout << " --> Processing respiratory gate " << rg+1 << endl; 00734 // Loop on cardiac gates 00735 for (int cg=0 ; cg<mp_ImageDimensionsAndQuantification->GetSensNbCardGates() ; cg++) 00736 { 00737 // Verbose 00738 if (m_verbose>=2 && mp_ImageDimensionsAndQuantification->GetSensNbCardGates()>1) 00739 cout << " --> Processing cardiac gate " << cg+1 << endl; 00740 // Perform any forward deformations on the forward image (only if attenuation is used) 00741 if (m_attenuationFlag) 00742 { 00743 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, FORWARD_DEFORMATION, 0, fr, rg, cg); 00744 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, FORWARD_DEFORMATION, 1, fr, rg, cg); 00745 mp_DeformationManager->ApplyDeformationForSensitivityGeneration(mp_ImageSpace, FORWARD_DEFORMATION, 2, fr, rg, cg); 00746 } 00747 // In case a problem occurs in the parallel loop 00748 bool problem = false; 00749 // Set the number of threads for projections (right after this loop, we set back the number of threads for image computations) 00750 #ifdef CASTOR_OMP 00751 omp_set_num_threads(mp_ImageDimensionsAndQuantification->GetNbThreadsForProjection()); 00752 #endif 00753 00754 // Start the loop 00755 int64_t idx_elt1; 00756 00757 #pragma omp parallel for private(idx_elt1) schedule(static, 1) 00758 for(idx_elt1=main_loop_start_index ; idx_elt1<main_loop_stop_index ; idx_elt1++) 00759 { 00760 // Get the thread index 00761 int th = 0; 00762 #ifdef CASTOR_OMP 00763 th = omp_get_thread_num(); 00764 #endif 00765 00766 // Initialize inner loop start and stop values 00767 int64_t inner_loop_start_index = p_scannerManager->PROJ_GetModalityStartValueInnerLoop(idx_elt1) ; 00768 int64_t inner_loop_stop_index = mp_Scanner->GetSystemNbElts(); 00769 00770 // Inner loop on scanner elements (idx_elt2) which are used to form LOR using the first scanner element (idx_elt1) 00771 for (int64_t idx_elt2=inner_loop_start_index ; idx_elt2<inner_loop_stop_index ; idx_elt2++) 00772 { 00773 // Print progression (do not log out with Cout here) 00774 if (th==0) 00775 { 00776 if (m_verbose>=2 && printing_index%10000==0) 00777 { 00778 int64_t progression_index_current = p_scannerManager->PROJ_GetCurrentProgression(idx_elt1, idx_elt2, progression_elts_array, 00779 mp_ImageDimensionsAndQuantification->GetSensNbRespGates(), 00780 mp_ImageDimensionsAndQuantification->GetSensNbCardGates(), 00781 fr, rg, cg); 00782 int64_t percent = (int64_t) (( ((FLTNB)progression_index_current)/((FLTNB)progression_index_total) ) * ((FLTNB)100)); 00783 cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b " 00784 << percent << " % "; 00785 } 00786 printing_index++; 00787 } 00788 00789 // Allocate an event using the iDataFile 00790 vEvent* event = m2p_DataFile[a_bed]->PROJ_GenerateEvent(idx_elt1, idx_elt2, th); 00791 00792 // Check from the scanner requirements that this LOR is allowed 00793 if (!mp_Scanner->IsAvailableLOR(event->GetID1(0), event->GetID2(0))) continue; 00794 00795 // Generate the projection event and compute the projection line 00796 oProjectionLine* line = mp_ProjectorManager->ComputeProjectionLine(event, th); 00797 if (line==NULL) 00798 { 00799 Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> A problem occured while computing the projection line !" << endl); 00800 // Specify that there was a problem 00801 problem = true; 00802 // We must continue here because we are inside an OpenMP loop 00803 continue; 00804 } 00805 00806 // Process this line 00807 if (line->NotEmptyLine() && ProcessThisLine(line, event, a_bed, fr, rg, cg, th)) 00808 { 00809 Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> A problem occured while processing a line !" << endl); 00810 } 00811 } 00812 } 00813 // End of progression printing (do not log out with Cout here) 00814 if (m_verbose>=2) cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" 00815 << " --> 100 % " << endl; 00816 // If a problem was encountered, then report it here 00817 if (problem) 00818 { 00819 Cerr("***** oSensitivityGenerator::ComputeSensitivityFromScanner() -> A problem occured inside the parallel loop over events !" << endl); 00820 return 1; 00821 } 00822 } 00823 } 00824 } 00825 delete[] progression_elts_array; 00826 00827 // Clock total 00828 clock_t clock_stop = clock(); 00829 time_t time_stop = time(NULL); 00830 if (mp_ImageDimensionsAndQuantification->GetNbBeds()>1) 00831 Cout(" --> Time spent for sensitivity generation for bed " << a_bed+1 << " | User: " << time_stop-time_start 00832 << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl); 00833 else 00834 Cout(" --> Time spent for sensitivity generation | User: " << time_stop-time_start 00835 << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl); 00836 00837 // End 00838 return 0; 00839 } 00840 00841 // ===================================================================== 00842 // --------------------------------------------------------------------- 00843 // --------------------------------------------------------------------- 00844 // ===================================================================== 00845 00846 int oSensitivityGenerator::ProcessThisLine(oProjectionLine* ap_Line, vEvent* ap_Event, int a_bed, int a_frame, int a_respGate, int a_cardGate, int a_thread) 00847 { 00848 // ---------------------------------------------------------------------------------------------- 00849 // Part 0: get the multiplicative corrections from the vEvent and the quantification factor 00850 // ---------------------------------------------------------------------------------------------- 00851 00852 // The multiplicative corrections from the vEvent can be: 00853 // 1: generated from vDataFile::GenerateEvent(), this will be a default value, so this will be 1. 00854 // 2: taken from the normalization data file, so this will be the normalization correction factor times the attenuation correction factor 00855 FLTNB multiplicative_factor = ap_Event->GetMultiplicativeCorrections(); 00856 // If null, then skip this event 00857 if (multiplicative_factor<=0.) return 0; 00858 // Get the quantification factor 00859 FLTNB quantification_factor = mp_ImageDimensionsAndQuantification->GetQuantificationFactor(a_bed, a_frame, a_respGate, a_cardGate); 00860 // If null, then skip this event 00861 if (quantification_factor<=0.) return 0; 00862 00863 // ---------------------------------------------------------------------------------------------- 00864 // Part 1: deal with the attenuation forward projection for PET if any 00865 // ---------------------------------------------------------------------------------------------- 00866 00867 // The ACF 00868 FLTNB attenuation_correction_factor = 1.; 00869 00870 // Do a forward projection only if the attenuation image was provided and the ACF was not included in the normalization data file 00871 if ( m_forwardProjectAttenuation ) 00872 { 00873 // Cumulative mu (in cm-1) 00874 FLTNB cumulative_mu = 0.; 00875 // Loop on TOF bins 00876 for (int b=0; b<ap_Line->GetNbTOFBins(); b++) 00877 { 00878 // Projection operation (the attenuation image in the m4p_forwardImage buffer of the oImageSpace) 00879 for (int vl=0 ; vl<ap_Line->GetCurrentNbVoxels(FORWARD, b) ; vl++) 00880 cumulative_mu += ap_Line->GetVoxelWeights(FORWARD, b, vl) * mp_ImageSpace->m4p_forwardImage[a_frame][a_respGate][a_cardGate][ap_Line->GetVoxelIndex(FORWARD, b,vl)]; 00881 } 00882 // Update the ACF (converting the cumulative mu in mm-1) 00883 attenuation_correction_factor = max(((FLTNB)1.),exp(cumulative_mu*((FLTNB)0.1))); 00884 } 00885 00886 // ---------------------------------------------------------------------------------------------- 00887 // Part 2: back-projection of the LOR sensitivity 00888 // ---------------------------------------------------------------------------------------------- 00889 00890 // Compute the global sensitivity (calibration / (ACF * norm)) 00891 FLTNB lor_sensitivity = 1. / (quantification_factor * attenuation_correction_factor * multiplicative_factor); 00892 00893 // Loop on TOF bins 00894 for (int b=0; b<ap_Line->GetNbTOFBins(); b++) 00895 { 00896 // Backprojection operation into the backward image 00897 for (int vl=0 ; vl<ap_Line->GetCurrentNbVoxels(BACKWARD, b) ; vl++) 00898 mp_ImageSpace->m6p_backwardImage[0][a_thread][a_frame][a_respGate][a_cardGate][ap_Line->GetVoxelIndex(BACKWARD, b, vl)] += ap_Line->GetVoxelWeights(BACKWARD, b, vl) 00899 * lor_sensitivity; 00900 } 00901 00902 // Increment line counters 00903 mp_lineCounter[a_thread]++; 00904 00905 // End 00906 return 0; 00907 } 00908 00909 // ===================================================================== 00910 // --------------------------------------------------------------------- 00911 // --------------------------------------------------------------------- 00912 // ===================================================================== 00913 00914 string oSensitivityGenerator::GetPathToSensitivityImage() 00915 { 00916 // Get the flag that says if we merge the dynamic files or not 00917 bool merge_dynamic_files = sOutputManager::GetInstance()->MergeDynImages(); 00918 // If we merge dynamic file, or we are in a static case (no dynamic), we stay with the hdr extension 00919 if ( ( mp_ImageDimensionsAndQuantification->GetSensNbCardGates()==1 && 00920 mp_ImageDimensionsAndQuantification->GetSensNbRespGates()==1 && 00921 mp_ImageDimensionsAndQuantification->GetNbTimeFrames()==1 ) || 00922 merge_dynamic_files ) 00923 return m_pathToSensitivityImage + ".hdr"; 00924 else 00925 return m_pathToSensitivityImage + ".mhd"; 00926 }