![]() |
CASToR
1.0
Tomographic Reconstruction (PET/SPECT)
|
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 }