CASToR  1.0
Tomographic Reconstruction (PET/SPECT)
oAnalyticProjection.cc
Go to the documentation of this file.
00001 
00002 /*
00003   Implementation of class oAnalyticProjection
00004 
00005   - separators: 
00006   - doxygen: 
00007   - default initialization: 
00008   - CASTOR_DEBUG: 
00009   - CASTOR_VERBOSE: 
00010 */
00011 
00012 
00021 #include "gVariables.hh"
00022 #include "oAnalyticProjection.hh"
00023 
00024 
00025 /*
00026   \brief oAnalyticProjection constructor.
00027          Initialize the member variables to their default values.
00028 */
00029 oAnalyticProjection::oAnalyticProjection()
00030 {
00031   m_verbose = -1;
00032   m_flagGPU = false;
00033   mp_ID = NULL;
00034   m2p_DataFile = NULL;
00035   mp_ProjectorManager = NULL;
00036   mp_ImageConvolverManager = NULL;
00037   mp_ImageSpace = NULL;
00038   mp_ComputeProjection = NULL;
00039   m_nbBeds = -1;
00040   m_pathToInitialImg = "";
00041   m_pathToAtnImg = "";
00042   mp_Scanner = NULL;
00043 }
00044 
00045 
00046 
00047 /*
00048   \brief oAnalyticProjection destructor. 
00049 */
00050 oAnalyticProjection::~oAnalyticProjection()
00051 {}
00052 
00053 
00054 
00055 /*
00056   \fn Launch
00057   \brief Just call either the LaunchCPU or the LaunchGPU function as asked for
00058   \return 0 if success, positive value otherwise
00059 */
00060 int oAnalyticProjection::Launch()
00061 {
00062   #ifdef CASTOR_GPU
00063   if (m_flagGPU) return LaunchGPU();
00064   else return LaunchCPU();
00065   #else
00066   return LaunchCPU();
00067   #endif
00068 }
00069 
00070 
00071 
00072 
00073 // =====================================================================
00074 // ---------------------------------------------------------------------
00075 // ---------------------------------------------------------------------
00076 // =====================================================================
00077 /*
00078   \fn LaunchGPU
00079   \brief Perform the projection by executing each possible LOR of the scanner,
00080          call the different object for optimization, projection, update, etc.
00081          Function designed to be executed on the GPU only.
00082          Returns error by default as it is not implemented
00083   \return 0 if success, positive value otherwise
00084 */
00085 #ifdef CASTOR_GPU
00086 int oAnalyticProjection::LaunchGPU()
00087 {
00088   Cerr("***** oAnalyticProjection::LaunchGPU() -> The GPU specific function is not implemented !" << endl);
00089   return 1;
00090 }
00091 #endif
00092 
00093 
00094 
00095 
00096 // =====================================================================
00097 // ---------------------------------------------------------------------
00098 // ---------------------------------------------------------------------
00099 // =====================================================================
00100 /*
00101   \fn LaunchCPU
00102   \brief Perform the projection by executing each possible LOR of the scanner,
00103          call the different object for optimization, projection, update, etc.
00104          Function designed to be executed on the GPU only.
00105          Returns error by default as it is not implemented
00106   \return 0 if success, positive value otherwise
00107 */
00108 int oAnalyticProjection::LaunchCPU()
00109 {
00110   // Verbose
00111   if (m_verbose>=1) Cout("oAnalyticProjection::LaunchCPU() -> Start analytic projection" << endl);
00112     
00113   // Spread verbosity
00114   mp_ComputeProjection->SetVerbose(m_verbose);
00115 
00116   // Image Space allocations
00117   mp_ImageSpace->LMS_InstantiateImage();
00118   mp_ImageSpace->LMS_InstantiateForwardImage();
00119 
00120   // Instanciate and initialize projection image for SPECT
00121   if(mp_Scanner->GetScannerType() == SCANNER_SPECT_CONVERGENT) 
00122     mp_ImageSpace->PROJ_InstantiateProjectionImage(mp_Scanner->PROJ_GetSPECTNbProjections() , mp_Scanner->PROJ_GetSPECTNbPixels());
00123   
00124   // Initialize the image to project
00125   if(mp_ImageSpace->PROJ_InitImage(m_pathToInitialImg) )
00126   {
00127     Cerr("***** oAnalyticProjection::LaunchCPU()-> Error during image initialization !" << endl);  
00128     return 1;
00129   }
00130 
00131   if(mp_ImageSpace->InitAttenuationImage(m_pathToAtnImg) )
00132   {
00133     Cerr("***** oAnalyticProjection::LaunchCPU()-> Error during attenuation image initialization !" << endl);  
00134     return 1;
00135   }
00136 
00137   // Copy current image in forward-image buffer
00138   mp_ImageSpace->LMS_PrepareForwardImage();
00139 
00140   if (mp_ImageConvolverManager->ConvolveForward(mp_ImageSpace))
00141   {
00142     Cerr("***** oAnalyticProjection::LaunchCPU() -> A problem occured while applying image convolver to forward image !" << endl);
00143     return 1;
00144   }
00145   
00146   
00147   // Initial clock
00148   clock_t clock_start = clock();
00149   time_t time_start = time(NULL);
00150   
00151   sScannerManager* p_scannerManager; 
00152   p_scannerManager = sScannerManager::GetInstance();
00153 
00154   for(int bed=0 ; bed<m_nbBeds ; bed++)
00155   {
00156     // Initialize main loop start and stop values
00157     unsigned int main_loop_start_index = 0 ;
00158     unsigned int main_loop_stop_index = 0;
00159     
00160     // Check beforehand any issue with the loop start/stop values 
00161     // (not possible to do this check in the inner multithreaded loop)
00162     if(p_scannerManager->PROJ_GetModalityStopValueMainLoop() > 0)
00163       main_loop_stop_index = p_scannerManager->PROJ_GetModalityStopValueMainLoop();
00164     else
00165     {
00166       Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occured when trying to initialize main loop stop index !" << endl); 
00167       return 1;
00168     }
00169 
00170     if(p_scannerManager->PROJ_GetModalityStartValueInnerLoop(0) < 0)
00171     {
00172       Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occured when trying to initialize inner loop start index !" << endl); 
00173       return 1;
00174     }
00175 
00176     // Prepare pre-computed sums of events to avoid the exponential evolution of the percentage (for PET)
00177     // TODO Perhaps replace this with a call to scannerManager for potential error management
00178     int32_t nb_total_elts = mp_Scanner->GetSystemNbElts();
00179     int64_t* elements_sum = (int64_t*)malloc(nb_total_elts*sizeof(int64_t));
00180     elements_sum[0] = 0;
00181     
00182     for (int64_t idx_elt=1 ; idx_elt<nb_total_elts ; idx_elt++) 
00183       elements_sum[idx_elt] = elements_sum[idx_elt-1] + (uint64_t)(nb_total_elts-idx_elt);
00184 
00185     uint64_t* total_events = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
00186     double* total_prompts = (double*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(double));
00187 
00188     // Index for progression printing
00189     uint64_t printing_index = 0;
00190     uint64_t progression_index_total = p_scannerManager->PROJ_GetProgressionFinalValue()*
00191                                                            mp_ID->GetNbTimeFrames()*
00192                                                            mp_ID->GetNbRespGates()*
00193                                                            mp_ID->GetNbCardGates();
00194 
00195     // Time reference to initialize the timestamp of the events
00196     uint32_t timestamp = 0;
00197       
00198     // look on the dynamic frames
00199     for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
00200     {
00201       uint64_t* total_events_in_frame = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
00202       double* total_prompts_in_frame = (double*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(double));
00203     
00204       for(int rg=0 ; rg<mp_ID->GetNbRespGates() ; rg++)
00205       {
00206         uint64_t* total_events_in_rgate = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
00207         double* total_prompts_in_rgate = (double*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(double));
00208         
00209         for(int cg=0 ; cg<mp_ID->GetNbCardGates() ; cg++)
00210         {
00211           uint64_t* total_events_in_cgate = (uint64_t*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(uint64_t));
00212           double* total_prompts_in_cgate = (double*)calloc(mp_ID->GetNbThreadsForProjection(),sizeof(double));
00213 
00214           if(mp_ID->GetNbTimeFrames() > 1)
00215             if(m_verbose>=0) Cout(endl << "Processing frame #" << fr+1 << " out of " << mp_ID->GetNbTimeFrames() << endl);
00216           if(mp_ID->GetNbRespGates() > 1)
00217             if(m_verbose>=0) Cout(endl << "Processing resp gate #" << rg+1 << " out of " << mp_ID->GetNbRespGates() << endl);
00218           if(mp_ID->GetNbCardGates() > 1)
00219             if(m_verbose>=0) Cout(endl << "Processing card gate #" << cg+1 << " out of " << mp_ID->GetNbCardGates() << endl);
00220 
00221           // Used to catch errors inside the parallel loop
00222           bool problem = false;
00223 
00224           // Multi-threaded loop on the scanner elements
00225           // Use (static,1) as dynamic mode actually different output histogram with multithreading.
00226           int64_t idx_elt1; //openMP throws error with unsigned
00227           #pragma omp parallel for private(idx_elt1) schedule(static, 1)
00228           for (idx_elt1=main_loop_start_index ; idx_elt1<(int)main_loop_stop_index ; idx_elt1++)
00229           {
00230             // Get the thread index
00231             int th = 0;
00232             #ifdef CASTOR_OMP
00233             th = omp_get_thread_num();
00234             #endif
00235 
00236             // Initialize inner loop start and stop values
00237             int64_t inner_loop_start_index = p_scannerManager->PROJ_GetModalityStartValueInnerLoop(idx_elt1) ;
00238             int64_t inner_loop_stop_index = mp_Scanner->GetSystemNbElts();
00239                     
00240             // Inner loop on scanner elements (idx_elt2) which are used to form LOR using the first scanner element (idx_elt1)
00241             for (int64_t idx_elt2=inner_loop_start_index ; idx_elt2<inner_loop_stop_index ; idx_elt2++)
00242             {
00243               // Print progression (do not log out with Cout here)
00244               if (th==0)
00245               {
00246                 if (printing_index%10000==0)
00247                 {
00248                   int64_t progression_index_current = p_scannerManager->PROJ_GetCurrentProgression(idx_elt1, idx_elt2, elements_sum, 
00249                                                                                                          mp_ID->GetNbRespGates(), 
00250                                                                                                          mp_ID->GetNbCardGates(), 
00251                                                                                                                                   fr, rg, cg);
00252 
00253                   FLTNB percent = ( (FLTNB)progression_index_current  /((FLTNB)progression_index_total) ) * ((FLTNB)100);
00254                   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      "
00255                      << percent << " %                    ";
00256 
00257                 }
00258                 printing_index++;
00259               }
00260             
00261               // Allocate an event using the iDataFile
00262               vEvent* event = m2p_DataFile[bed]->PROJ_GenerateEvent(idx_elt1, idx_elt2, th);
00263       
00264               // TODO : Perhaps replace this with a call to scannerManager for potential error management
00265               if (!mp_Scanner->IsAvailableLOR(event->GetID1(0), event->GetID2(0))) continue;
00266 
00267               // Generate the projection event and compute the projection line 
00268               oProjectionLine* line = mp_ProjectorManager->ComputeProjectionLine(event, th);
00269               if (line==NULL)
00270               {
00271                 Cerr("***** oAnalyticProjection::LaunchCPU() -> A problem occured while computing the projection line !" << endl);
00272                 // Specify that there was a problem
00273                 problem = true;
00274                 // We must continue here because we are inside an OpenMP loop
00275                 continue;
00276               }
00277 
00278               // Optimize in the data space (forward-proj, update, backward-proj) if the line has been performed
00279               if (line->NotEmptyLine())
00280               {
00281                 if(mp_ComputeProjection->DataUpdateStep(m2p_DataFile[bed], line, mp_ImageSpace, event, fr, rg, cg, th, timestamp) )
00282                 {
00283                   Cerr("***** oAnalyticProjection::LaunchCPU() -> An error occured during the data update step !" << endl); 
00284                   //return 1; TODO : return in open MP structured block (stop all threads)
00285                 }
00286                 
00287                 // Increment number of events
00288                 total_events[th]++;
00289                 total_events_in_cgate[th]++;
00290                 total_events_in_rgate[th]++;
00291                 total_events_in_frame[th]++;
00292                 double event_value = event->GetEventValue(0);
00293                 total_prompts[th] += event_value;
00294                 total_prompts_in_cgate[th] += event_value;
00295                 total_prompts_in_rgate[th] += event_value;
00296                 total_prompts_in_frame[th] += event_value;
00297               }
00298               
00299             }
00300 
00301           } // End thread loop
00302 
00303           // If a problem was encountered, then report it here
00304           if (problem)
00305           {
00306             Cerr("***** oAnalyticProjection::LaunchCPU() -> A problem occured inside the parallel loop over events !" << endl);
00307             return 1;
00308           }
00309 
00310           // Write data for this frame
00311           if (m2p_DataFile[bed]->PROJ_WriteData() )
00312           {
00313             Cerr("***** oAnalyticProjection::LaunchCPU()-> An error occured during the data writing step" << endl); 
00314             return 1;
00315           }
00316 
00317           // Merge counters
00318           for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events_in_cgate[0] += total_events_in_cgate[th];
00319           for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts_in_cgate[0] += total_prompts_in_cgate[th];
00320           
00321           if(m_verbose>=2 && mp_ID->GetNbCardGates() > 1)
00322           {
00323             Cout(endl << "Total events in cgate #" << cg+1 << ": " << total_events_in_cgate[0] << endl);
00324             Cout("Total prompts in cgate #" << cg+1 << ": " << total_prompts_in_cgate[0] << endl);
00325           }
00326           
00327           free(total_prompts_in_cgate);
00328           free(total_events_in_cgate);
00329         }
00330         
00331         // Merge counters
00332         for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events_in_rgate[0] += total_events_in_rgate[th];
00333         for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts_in_rgate[0] += total_prompts_in_rgate[th];
00334         
00335         if(m_verbose>=2 && mp_ID->GetNbRespGates() > 1)
00336         {
00337           Cout(endl << "Total events in rgate #" << rg+1 << ": " << total_events_in_rgate[0] << endl);
00338           Cout("Total prompts in rgate #" << rg+1 << ": " << total_prompts_in_rgate[0] << endl);
00339         }
00340         free(total_prompts_in_rgate);
00341         free(total_events_in_rgate);
00342       }
00343       
00344       // Get time reference for this frame in order to initialize the timestamp (in ms) of the events
00345       timestamp += mp_ID->GetFrameDurationInMs(bed,fr);
00346 
00347       // Merge counters
00348       for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events_in_frame[0] += total_events_in_frame[th];
00349       for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts_in_frame[0] += total_prompts_in_frame[th];
00350 
00351       if(m_verbose>=2 && mp_ID->GetNbTimeFrames() > 1)
00352       {
00353         Cout(endl << "Total events in frame #" << fr+1 << ": " << total_events_in_frame[0] << endl);
00354         Cout("Total prompts in frame #" << fr+1 << ": " << total_prompts_in_frame[0] << endl);
00355       }
00356         
00357       free(total_prompts_in_frame);
00358       free(total_events_in_frame);
00359     }
00360     
00361     // Compute simulated acquisition duration for 4D projection
00362     int acquisition_duration = 0;
00363     for(int fr=0 ; fr<mp_ID->GetNbTimeFrames() ; fr++)
00364       acquisition_duration += mp_ID->GetFrameDurationInMs(bed,fr);
00365      
00366     // If 3D, set the acquisition duration to 1 by default
00367     if(acquisition_duration <= 0 ) acquisition_duration = 1;  // Set default value for acquisition duration
00368     
00369     // End of progression printing (do not log out with Cout here)
00370     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      100 %                    " << endl;
00371 
00372     // Save a projection image for output visualization (currently only for SPECT)
00373     mp_ImageSpace->PROJ_SaveProjectionImage();
00374     
00375     // Merge counters
00376     if (m_verbose>=2)
00377     {
00378       Cout("Total number of projected events:" << endl);
00379       for (int th=0; th<mp_ID->GetNbThreadsForProjection(); th++) Cout("  --> Thread " << th << " | " << total_events[th] << " events" << endl);
00380     }
00381     for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_events[0] += total_events[th];
00382     if (m_verbose>=1) Cout("Final total number of events projected: " << total_events[0] << endl);
00383     for (int th=1; th<mp_ID->GetNbThreadsForProjection(); th++) total_prompts[0] += total_prompts[th];
00384     if (m_verbose>=1) Cout("Final total number of prompts: " << total_prompts[0] << endl);
00385     
00386     // Write header (Cdh)
00387     m2p_DataFile[bed]->SetDuration(acquisition_duration);
00388     m2p_DataFile[bed]->SetNbEvents(total_events[0]);
00389     m2p_DataFile[bed]->PROJ_WriteHeader();
00390     m2p_DataFile[bed]->PROJ_DeleteTmpDatafile();
00391 
00392     free(total_prompts);
00393     free(total_events);
00394     free(elements_sum);
00395   }
00396 
00397   // Free memory
00398   if(mp_Scanner->GetScannerType() == SCANNER_SPECT_CONVERGENT) mp_ImageSpace->PROJ_DeallocateProjectionImage(mp_Scanner->PROJ_GetSPECTNbProjections());  
00399   mp_ImageSpace->LMS_DeallocateAttenuationImage();
00400   mp_ImageSpace->LMS_DeallocateForwardImage();
00401   mp_ImageSpace->LMS_DeallocateImage();
00402   
00403  // Clock total
00404   clock_t clock_stop = clock();
00405   time_t time_stop = time(NULL);
00406   Cout (endl << endl << "  Total Time spent - Analytic Projection | User: " << time_stop-time_start << " sec | CPU: " << (clock_stop-clock_start)/((FLTNB)CLOCKS_PER_SEC) << " sec" << endl);
00407   
00408   return 0;
00409 }
 All Classes Files Functions Variables Typedefs Defines