- Timestamp:
- 02/02/05 15:42:50 (20 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/SDLineFinder.cc
r344 r351 38 38 #include <algorithm> 39 39 #include <iostream> 40 #include <fstream> 40 41 41 42 using namespace asap; … … 101 102 /////////////////////////////////////////////////////////////////////////////// 102 103 // 103 // LFRunningMean - a running mean algorithm for line detection 104 // 105 // 106 107 // An auxiliary class implementing one pass of the line search algorithm, 108 // which uses a running mean. We define this class here because it is 109 // used in SDLineFinder only. The incapsulation of this code into a separate 110 // class will provide a possibility to add new algorithms with minor changes 111 class LFRunningMean { 104 // RunningBox - a running box calculator. This class implements 105 // interations over the specified spectrum and calculates 106 // running box filter statistics. 107 // 108 109 class RunningBox { 112 110 // The input data to work with. Use reference symantics to avoid 113 111 // an unnecessary copying … … 117 115 // to work with 118 116 119 // statistics for running mean filtering 120 casa::Float sum; // sum of fluxes 121 casa::Float sumsq; // sum of squares of fluxes 117 // statistics for running box filtering 118 casa::Float sumf; // sum of fluxes 119 casa::Float sumf2; // sum of squares of fluxes 120 casa::Float sumch; // sum of channel numbers (for linear fit) 121 casa::Float sumch2; // sum of squares of channel numbers (for linear fit) 122 casa::Float sumfch; // sum of flux*(channel number) (for linear fit) 123 122 124 int box_chan_cntr; // actual number of channels in the box 123 125 int max_box_nchan; // maximum allowed number of channels in the box 124 126 // (calculated from boxsize and actual spectrum size) 125 127 // cache for derivative statistics 128 mutable casa::Bool need2recalculate; // if true, values of the statistics 129 // below are invalid 130 mutable casa::Float linmean; // a value of the linear fit to the 131 // points in the running box 132 mutable casa::Float linvariance; // the same for variance 133 int cur_channel; // the number of the current channel 134 int start_advance; // number of channel from which the box can 135 // be moved (the middle of the box, if there is no 136 // masking) 137 public: 138 // set up the object with the references to actual data 139 // as well as the number of channels in the running box 140 RunningBox(const casa::Vector<casa::Float> &in_spectrum, 141 const casa::Vector<casa::Bool> &in_mask, 142 const std::pair<int,int> &in_edge, 143 int in_max_box_nchan) throw(AipsError); 144 145 // access to the statistics 146 const casa::Float& getLinMean() const throw(AipsError); 147 const casa::Float& getLinVariance() const throw(AipsError); 148 const casa::Float aboveMean() const throw(AipsError); 149 int getChannel() const throw(); 150 151 // actual number of channels in the box (max_box_nchan, if no channels 152 // are masked) 153 int getNumberOfBoxPoints() const throw(); 154 155 // next channel 156 void next() throw(AipsError); 157 158 // checking whether there are still elements 159 casa::Bool haveMore() const throw(); 160 161 // go to start 162 void rewind() throw(AipsError); 163 164 protected: 165 // supplementary function to control running mean calculations. 166 // It adds a specified channel to the running mean box and 167 // removes (ch-maxboxnchan+1)'th channel from there 168 // Channels, for which the mask is false or index is beyond the 169 // allowed range, are ignored 170 void advanceRunningBox(int ch) throw(casa::AipsError); 171 172 // calculate derivative statistics. This function is const, because 173 // it updates the cache only 174 void updateDerivativeStatistics() const throw(AipsError); 175 }; 176 177 // 178 /////////////////////////////////////////////////////////////////////////////// 179 180 /////////////////////////////////////////////////////////////////////////////// 181 // 182 // LFAboveThreshold An algorithm for line detection using running box 183 // statistics. Line is detected if it is above the 184 // specified threshold at the specified number of 185 // consequtive channels. Prefix LF stands for Line Finder 186 // 187 class LFAboveThreshold { 126 188 // temporary line edge channels and flag, which is True if the line 127 189 // was detected in the previous channels. … … 134 196 casa::Float threshold; // detection threshold - the 135 197 // minimal signal to noise ratio 198 std::list<pair<int,int> > &lines; // list where detections are saved 199 // (pair: start and stop+1 channel) 200 RunningBox *running_box; // running box filter 136 201 public: 137 // set up the object with the references to actual data 138 // as well as the detection criterion (min_nchan and threshold, see above) 139 // and the number of channels in the running box 140 LFRunningMean(const casa::Vector<casa::Float> &in_spectrum, 141 const casa::Vector<casa::Bool> &in_mask, 142 const std::pair<int,int> &in_edge, 143 int in_max_box_nchan, 144 int in_min_nchan = 3, 145 casa::Float in_threshold = 5); 146 202 203 // set up the detection criterion 204 LFAboveThreshold(std::list<pair<int,int> > &in_lines, 205 int in_min_nchan = 3, 206 casa::Float in_threshold = 5) throw(); 207 virtual ~LFAboveThreshold() throw(); 208 147 209 // replace the detection criterion 148 210 void setCriterion(int in_min_nchan, casa::Float in_threshold) throw(); … … 151 213 // if statholder is not NULL, the accumulate function of it will be 152 214 // called for each channel to save statistics 153 void findLines(std::list<pair<int,int> > &lines, 215 // spectrum, mask and edge - reference to the data 216 // max_box_nchan - number of channels in the running box 217 void findLines(const casa::Vector<casa::Float> &spectrum, 218 const casa::Vector<casa::Bool> &mask, 219 const std::pair<int,int> &edge, 220 int max_box_nchan, 154 221 IStatHolder* statholder = NULL) throw(casa::AipsError); 155 222 156 223 protected: 157 // supplementary function to control running mean calculations.158 // It adds a specified channel to the running mean box and159 // removes (ch-maxboxnchan+1)'th channel from there160 // Channels, for which the mask is false or index is beyond the161 // allowed range, are ignored162 void advanceRunningBox(int ch) throw(casa::AipsError);163 164 165 // test a channel against current running mean & rms166 // if channel specified is masked out or beyond the allowed indexes,167 // false is returned168 casa::Bool testChannel(int ch) const169 throw(std::exception, casa::AipsError);170 224 171 225 // process a channel: update curline and is_detected before and 172 226 // add a new line to the list, if necessary using processCurLine() 173 void processChannel(std::list<pair<int,int> > &lines, 174 int ch) throw(casa::AipsError); 227 // detect=true indicates that the current channel satisfies the criterion 228 void processChannel(Bool detect, const casa::Vector<casa::Bool> &mask) 229 throw(casa::AipsError); 175 230 176 231 // process the interval of channels stored in curline 177 232 // if it satisfies the criterion, add this interval as a new line 178 void processCurLine( std::list<pair<int,int> > &lines) throw(casa::AipsError);179 233 void processCurLine(const casa::Vector<casa::Bool> &mask) 234 throw(casa::AipsError); 180 235 }; 181 236 182 237 // 183 238 /////////////////////////////////////////////////////////////////////////////// 239 184 240 } // namespace asap 185 241 … … 203 259 // box_nchan - number of channels in the box 204 260 // 205 void SignAccumulator::accumulate(int ch, Float sum, Float , int box_nchan)261 void SignAccumulator::accumulate(int ch, Float sum, Float sum2, int box_nchan) 206 262 throw(AipsError) 207 263 { … … 210 266 DebugAssert(ch<=spectrum.nelements(), AipsError); 211 267 if (box_nchan) { 212 Float buf=spectrum[ch]-sum /Float(box_nchan);268 Float buf=spectrum[ch]-sum; ///Float(box_nchan); 213 269 if (buf>0) sign[ch]=1; 214 270 else if (buf<0) sign[ch]=-1; … … 227 283 /////////////////////////////////////////////////////////////////////////////// 228 284 229 230 // /////////////////////////////////////////////////////////////////////////////231 // 232 // LFRunningMean - a running mean algorithm for line detection233 // 285 /////////////////////////////////////////////////////////////////////////////// 286 // 287 // RunningBox - a running box calculator. This class implements 288 // interations over the specified spectrum and calculates 289 // running box filter statistics. 234 290 // 235 291 236 292 // set up the object with the references to actual data 237 // as well as the detection criterion (min_nchan and threshold, see above)238 293 // and the number of channels in the running box 239 LFRunningMean::LFRunningMean(const casa::Vector<casa::Float> &in_spectrum, 240 const casa::Vector<casa::Bool> &in_mask, 241 const std::pair<int,int> &in_edge, 242 int in_max_box_nchan, 243 int in_min_nchan, casa::Float in_threshold) : 294 RunningBox::RunningBox(const casa::Vector<casa::Float> &in_spectrum, 295 const casa::Vector<casa::Bool> &in_mask, 296 const std::pair<int,int> &in_edge, 297 int in_max_box_nchan) throw(AipsError) : 244 298 spectrum(in_spectrum), mask(in_mask), edge(in_edge), 245 max_box_nchan(in_max_box_nchan), 246 min_nchan(in_min_nchan),threshold(in_threshold) {} 247 248 // replace the detection criterion 249 void LFRunningMean::setCriterion(int in_min_nchan, casa::Float in_threshold) 250 throw() 251 { 252 min_nchan=in_min_nchan; 253 threshold=in_threshold; 254 } 255 299 max_box_nchan(in_max_box_nchan) 300 { 301 rewind(); 302 } 303 304 void RunningBox::rewind() throw(AipsError) { 305 // fill statistics for initial box 306 box_chan_cntr=0; // no channels are currently in the box 307 sumf=0.; // initialize statistics 308 sumf2=0.; 309 sumch=0.; 310 sumch2=0.; 311 sumfch=0.; 312 int initial_box_ch=edge.first; 313 for (;initial_box_ch<edge.second && box_chan_cntr<max_box_nchan; 314 ++initial_box_ch) 315 advanceRunningBox(initial_box_ch); 316 317 if (initial_box_ch==edge.second) 318 throw AipsError("RunningBox::rewind - too much channels are masked"); 319 320 cur_channel=edge.first; 321 start_advance=initial_box_ch-max_box_nchan/2; 322 } 323 324 // access to the statistics 325 const casa::Float& RunningBox::getLinMean() const throw(AipsError) 326 { 327 DebugAssert(cur_channel<edge.second, AipsError); 328 if (need2recalculate) updateDerivativeStatistics(); 329 return linmean; 330 } 331 332 const casa::Float& RunningBox::getLinVariance() const throw(AipsError) 333 { 334 DebugAssert(cur_channel<edge.second, AipsError); 335 if (need2recalculate) updateDerivativeStatistics(); 336 return linvariance; 337 } 338 339 const casa::Float RunningBox::aboveMean() const throw(AipsError) 340 { 341 DebugAssert(cur_channel<edge.second, AipsError); 342 if (need2recalculate) updateDerivativeStatistics(); 343 return spectrum[cur_channel]-linmean; 344 } 345 346 int RunningBox::getChannel() const throw() 347 { 348 return cur_channel; 349 } 350 351 // actual number of channels in the box (max_box_nchan, if no channels 352 // are masked) 353 int RunningBox::getNumberOfBoxPoints() const throw() 354 { 355 return box_chan_cntr; 356 } 256 357 257 358 // supplementary function to control running mean calculations. … … 260 361 // Channels, for which the mask is false or index is beyond the 261 362 // allowed range, are ignored 262 void LFRunningMean::advanceRunningBox(int ch) throw(AipsError)363 void RunningBox::advanceRunningBox(int ch) throw(AipsError) 263 364 { 264 365 if (ch>=edge.first && ch<edge.second) 265 366 if (mask[ch]) { // ch is a valid channel 266 367 ++box_chan_cntr; 267 sum+=spectrum[ch]; 268 sumsq+=square(spectrum[ch]); 368 sumf+=spectrum[ch]; 369 sumf2+=square(spectrum[ch]); 370 sumch+=Float(ch); 371 sumch2+=square(Float(ch)); 372 sumfch+=spectrum[ch]*Float(ch); 373 need2recalculate=True; 269 374 } 270 375 int ch2remove=ch-max_box_nchan; … … 272 377 if (mask[ch2remove]) { // ch2remove is a valid channel 273 378 --box_chan_cntr; 274 sum-=spectrum[ch2remove]; 275 sumsq-=square(spectrum[ch2remove]); 379 sumf-=spectrum[ch2remove]; 380 sumf2-=square(spectrum[ch2remove]); 381 sumch-=Float(ch2remove); 382 sumch2-=square(Float(ch2remove)); 383 sumfch-=spectrum[ch2remove]*Float(ch2remove); 384 need2recalculate=True; 276 385 } 277 386 } 278 387 279 // test a channel against current running mean & rms 280 // if channel specified is masked out or beyond the allowed indexes, 281 // false is returned 282 Bool LFRunningMean::testChannel(int ch) const throw(exception, AipsError) 283 { 284 if (ch<edge.first || ch>=edge.second) return False; 285 if (!mask[ch]) return False; 286 DebugAssert(box_chan_cntr, AipsError); 287 Float mean=sum/Float(box_chan_cntr); 288 Float variance=sqrt(sumsq/Float(box_chan_cntr)-square(mean)); 289 /* 290 if (ch>3900 && ch<4100) 291 cout<<"Tested "<<ch<<" mean="<<mean<<" variance="<<variance<<" sp-mean="<<spectrum[ch]-mean<<endl; 292 */ 293 return fabs(spectrum[ch]-mean)>=threshold*variance; 294 } 388 // next channel 389 void RunningBox::next() throw(AipsError) 390 { 391 AlwaysAssert(cur_channel<edge.second,AipsError); 392 ++cur_channel; 393 if (cur_channel+max_box_nchan/2<edge.second && cur_channel>=start_advance) 394 advanceRunningBox(cur_channel+max_box_nchan/2); // update statistics 395 } 396 397 // checking whether there are still elements 398 casa::Bool RunningBox::haveMore() const throw() 399 { 400 return cur_channel<edge.second; 401 } 402 403 // calculate derivative statistics. This function is const, because 404 // it updates the cache only 405 void RunningBox::updateDerivativeStatistics() const throw(AipsError) 406 { 407 AlwaysAssert(box_chan_cntr, AipsError); 408 409 Float mean=sumf/Float(box_chan_cntr); 410 411 // linear LSF formulae 412 Float meanch=sumch/Float(box_chan_cntr); 413 Float meanch2=sumch2/Float(box_chan_cntr); 414 if (meanch==meanch2 || box_chan_cntr<3) { 415 // vertical line in the spectrum, can't calculate linmean and linvariance 416 linmean=0.; 417 linvariance=0.; 418 } else { 419 Float coeff=(sumfch/Float(box_chan_cntr)-meanch*mean)/ 420 (meanch2-square(meanch)); 421 linmean=coeff*(Float(cur_channel)-meanch)+mean; 422 linvariance=sqrt(sumf2/Float(box_chan_cntr)-square(mean)- 423 square(coeff)*(meanch2-square(meanch))); 424 } 425 need2recalculate=False; 426 } 427 428 429 // 430 /////////////////////////////////////////////////////////////////////////////// 431 432 /////////////////////////////////////////////////////////////////////////////// 433 // 434 // LFAboveThreshold - a running mean algorithm for line detection 435 // 436 // 437 438 439 // set up the detection criterion 440 LFAboveThreshold::LFAboveThreshold(std::list<pair<int,int> > &in_lines, 441 int in_min_nchan, 442 casa::Float in_threshold) throw() : 443 min_nchan(in_min_nchan), threshold(in_threshold), 444 lines(in_lines), running_box(NULL) {} 445 446 LFAboveThreshold::~LFAboveThreshold() throw() 447 { 448 if (running_box!=NULL) delete running_box; 449 } 450 451 // replace the detection criterion 452 void LFAboveThreshold::setCriterion(int in_min_nchan, casa::Float in_threshold) 453 throw() 454 { 455 min_nchan=in_min_nchan; 456 threshold=in_threshold; 457 } 458 295 459 296 460 // process a channel: update cur_line and is_detected before and 297 461 // add a new line to the list, if necessary 298 void LF RunningMean::processChannel(std::list<pair<int,int> > &lines,299 int ch) throw(casa::AipsError)462 void LFAboveThreshold::processChannel(Bool detect, 463 const casa::Vector<casa::Bool> &mask) throw(casa::AipsError) 300 464 { 301 465 try { 302 if ( testChannel(ch)) {466 if (detect) { 303 467 if (is_detected_before) 304 cur_line.second= ch+1;468 cur_line.second=running_box->getChannel()+1; 305 469 else { 306 470 is_detected_before=True; 307 cur_line.first= ch;308 cur_line.second= ch+1;471 cur_line.first=running_box->getChannel(); 472 cur_line.second=running_box->getChannel()+1; 309 473 } 310 } else processCurLine( lines);474 } else processCurLine(mask); 311 475 } 312 476 catch (const AipsError &ae) { … … 314 478 } 315 479 catch (const exception &ex) { 316 throw AipsError(String(" SDLineFinder::processChannel - STL error: ")+ex.what());480 throw AipsError(String("LFAboveThreshold::processChannel - STL error: ")+ex.what()); 317 481 } 318 482 } … … 320 484 // process the interval of channels stored in cur_line 321 485 // if it satisfies the criterion, add this interval as a new line 322 void LF RunningMean::processCurLine(std::list<pair<int,int> > &lines)486 void LFAboveThreshold::processCurLine(const casa::Vector<casa::Bool> &mask) 323 487 throw(casa::AipsError) 324 488 { … … 347 511 } 348 512 catch (const exception &ex) { 349 throw AipsError(String(" SDLineFinder::processCurLine - STL error: ")+ex.what());513 throw AipsError(String("LFAboveThreshold::processCurLine - STL error: ")+ex.what()); 350 514 } 351 515 } 352 516 353 517 // find spectral lines and add them into list 354 void LFRunningMean::findLines(std::list<pair<int,int> > &lines, 518 void LFAboveThreshold::findLines(const casa::Vector<casa::Float> &spectrum, 519 const casa::Vector<casa::Bool> &mask, 520 const std::pair<int,int> &edge, 521 int max_box_nchan, 355 522 IStatHolder* statholder) 356 523 throw(casa::AipsError) 357 524 { 358 525 const int minboxnchan=4; 359 360 // fill statistics for initial box 361 box_chan_cntr=0; // no channels are currently in the box 362 sum=0; // initialize statistics 363 sumsq=0; 364 int initial_box_ch=edge.first; 365 for (;initial_box_ch<edge.second && box_chan_cntr<max_box_nchan; 366 ++initial_box_ch) 367 advanceRunningBox(initial_box_ch); 526 try { 527 528 if (running_box!=NULL) delete running_box; 529 running_box=new RunningBox(spectrum,mask,edge,max_box_nchan); 368 530 369 if (initial_box_ch==edge.second) 370 throw AipsError("LFRunningMean::findLines - too much channels are masked"); 371 372 // actual search algorithm 373 is_detected_before=False; 374 375 if (statholder!=NULL) 376 for (int n=0;n<initial_box_ch-max_box_nchan/2;++n) 377 statholder->accumulate(n,sum,sumsq,box_chan_cntr); 378 379 if (box_chan_cntr>=minboxnchan) 380 // there is a minimum amount of data. We can search in the 381 // half of the initial box 382 for (int n=edge.first;n<initial_box_ch-max_box_nchan/2;++n) 383 processChannel(lines,n); 384 385 // now the box can be moved. n+max_box_nchan/2 is a new index which haven't 386 // yet been included in the running mean. 387 for (int n=initial_box_ch-max_box_nchan/2;n<edge.second;++n) { 388 advanceRunningBox(n+max_box_nchan/2); // update running mean & variance 389 if (statholder!=NULL) 390 statholder->accumulate(n,sum,sumsq,box_chan_cntr); 391 if (box_chan_cntr>=minboxnchan) // have enough data to process 392 processChannel(lines,n); 393 else processCurLine(lines); // just finish what was accumulated before 394 } 395 if (statholder!=NULL) 396 for (int n=edge.second;n<spectrum.nelements();++n) 397 statholder->accumulate(n,sum,sumsq,box_chan_cntr); 531 // actual search algorithm 532 is_detected_before=False; 533 534 for (;running_box->haveMore();running_box->next()) { 535 const int ch=running_box->getChannel(); 536 if (running_box->getNumberOfBoxPoints()>=minboxnchan) 537 processChannel(mask[ch] && (fabs(spectrum[ch]- 538 running_box->getLinMean()) >= 539 threshold*running_box->getLinVariance()), mask); 540 else processCurLine(mask); // just finish what was accumulated before 541 if (statholder!=NULL) 542 statholder->accumulate(running_box->getChannel(), 543 running_box->getLinMean(), 544 running_box->getLinVariance(), 545 running_box->getNumberOfBoxPoints()); 546 } 547 } 548 catch (const AipsError &ae) { 549 throw; 550 } 551 catch (const exception &ex) { 552 throw AipsError(String("LFAboveThreshold::findLines - STL error: ")+ex.what()); 553 } 398 554 } 399 555 … … 493 649 // detection threshold - the minimal signal to noise ratio 494 650 threshold=3.; // 3 sigma is a default 495 box_size=1./ 16.; // default box size for running mean calculations is496 // 1/ 16of the whole spectrum651 box_size=1./5.; // default box size for running mean calculations is 652 // 1/5 of the whole spectrum 497 653 // A minimum number of consequtive channels, which should satisfy 498 654 // the detection criterion, to be a detection … … 577 733 lines.resize(0); // search from the scratch 578 734 Vector<Bool> temp_mask(mask); 579 735 736 Bool first_pass=True; 580 737 while (true) { 581 // line find algorithm582 LFRunningMean lfalg(spectrum,temp_mask,edge,max_box_nchan,min_nchan,583 threshold);584 SignAccumulator sacc(spectrum.nelements(),spectrum);585 586 738 // a buffer for new lines found at this iteration 587 739 std::list<pair<int,int> > new_lines; 588 589 lfalg.findLines(new_lines,&sacc); 740 741 // line find algorithm 742 LFAboveThreshold lfalg(new_lines,min_nchan, threshold); 743 744 SignAccumulator sacc(spectrum.nelements(),spectrum); 745 746 747 try { 748 lfalg.findLines(spectrum,temp_mask,edge,max_box_nchan,&sacc); 749 } 750 catch(const AipsError &ae) { 751 if (first_pass) throw; 752 break; // nothing new 753 } 754 first_pass=False; 590 755 if (!new_lines.size()) break; // nothing new 591 756
Note:
See TracChangeset
for help on using the changeset viewer.