CASToR  1.0
Tomographic Reconstruction (PET/SPECT)
oSensitivityGenerator.cc
Go to the documentation of this file.
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 }
 All Classes Files Functions Variables Typedefs Defines