Changeset 331 for trunk


Ignore:
Timestamp:
01/31/05 15:48:36 (20 years ago)
Author:
vor010
Message:

Line searching algorithm is now in the separate
class LFRunningMean. It allows to run the same code several passes for different
masks, criteria, etc. In the future this interface may be more readily modified
to have multiple algorithms

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/python/asaplinefind.py

    r302 r331  
    5353              regardless on the invert option
    5454        """
    55         return self.finder._getmask(invert)
     55        return self.finder.getmask(invert)
    5656    def get_ranges(self,defunits=True):
    5757        """
  • trunk/src/SDLineFinder.cc

    r297 r331  
    4242using namespace boost::python;
    4343
     44namespace asap {
     45
     46// An auxiliary class implementing one pass of the line search algorithm,
     47// which uses a running mean. We define this class here because it is
     48// used in SDLineFinder only. The incapsulation of this code into a separate
     49// class will provide a possibility to add new algorithms with minor changes
     50class LFRunningMean {
     51   // The input data to work with. Use reference symantics to avoid
     52   // an unnecessary copying   
     53   const casa::Vector<casa::Float>  &spectrum; // a buffer for the spectrum
     54   const casa::Vector<casa::Bool>   &mask; // associated mask
     55   const std::pair<int,int>         &edge; // start and stop+1 channels
     56                                           // to work with
     57   
     58   // statistics for running mean filtering
     59   casa::Float sum;       // sum of fluxes
     60   casa::Float sumsq;     // sum of squares of fluxes
     61   int box_chan_cntr;     // actual number of channels in the box
     62   int max_box_nchan;     // maximum allowed number of channels in the box
     63                          // (calculated from boxsize and actual spectrum size)
     64
     65   // temporary line edge channels and flag, which is True if the line
     66   // was detected in the previous channels.
     67   std::pair<int,int> cur_line;
     68   casa::Bool is_detected_before;
     69   int  min_nchan;                         // A minimum number of consequtive
     70                                           // channels, which should satisfy
     71                                           // the detection criterion, to be
     72                                           // a detection
     73   casa::Float threshold;                  // detection threshold - the
     74                                           // minimal signal to noise ratio
     75public:
     76   // set up the object with the references to actual data
     77   // as well as the detection criterion (min_nchan and threshold, see above)
     78   // and the number of channels in the running box
     79   LFRunningMean(const casa::Vector<casa::Float>  &in_spectrum,
     80                 const casa::Vector<casa::Bool>   &in_mask,
     81                 const std::pair<int,int>         &in_edge,
     82                 int in_max_box_nchan,
     83                 int in_min_nchan = 3,
     84                 casa::Float in_threshold = 5);
     85                 
     86   // replace the detection criterion
     87   void setCriterion(int in_min_nchan, casa::Float in_threshold) throw();
     88
     89   // find spectral lines and add them into list
     90   void findLines(std::list<pair<int,int> > &lines) throw(casa::AipsError);
     91   
     92protected:
     93   // supplementary function to control running mean calculations.
     94   // It adds a specified channel to the running mean box and
     95   // removes (ch-maxboxnchan+1)'th channel from there
     96   // Channels, for which the mask is false or index is beyond the
     97   // allowed range, are ignored
     98   void advanceRunningBox(int ch) throw(casa::AipsError);
     99   
     100
     101   // test a channel against current running mean & rms
     102   // if channel specified is masked out or beyond the allowed indexes,
     103   // false is returned
     104   casa::Bool testChannel(int ch) const
     105                     throw(std::exception, casa::AipsError);
     106
     107   // process a channel: update curline and is_detected before and
     108   // add a new line to the list, if necessary using processCurLine()
     109   void processChannel(std::list<pair<int,int> > &lines,
     110                       int ch) throw(casa::AipsError);
     111
     112   // process the interval of channels stored in curline
     113   // if it satisfies the criterion, add this interval as a new line
     114   void processCurLine(std::list<pair<int,int> > &lines) throw(casa::AipsError);
     115
     116};
     117} // namespace asap
     118
     119///////////////////////////////////////////////////////////////////////////////
     120//
     121// LFRunningMean - a running mean algorithm for line detection
     122//
     123//
     124
     125// set up the object with the references to actual data
     126// as well as the detection criterion (min_nchan and threshold, see above)
     127// and the number of channels in the running box
     128LFRunningMean::LFRunningMean(const casa::Vector<casa::Float>  &in_spectrum,
     129                             const casa::Vector<casa::Bool>   &in_mask,
     130                             const std::pair<int,int>         &in_edge,
     131                             int in_max_box_nchan,
     132                             int in_min_nchan, casa::Float in_threshold) :
     133        spectrum(in_spectrum), mask(in_mask), edge(in_edge),
     134        max_box_nchan(in_max_box_nchan),
     135        min_nchan(in_min_nchan),threshold(in_threshold) {}
     136
     137// replace the detection criterion
     138void LFRunningMean::setCriterion(int in_min_nchan, casa::Float in_threshold)
     139                                 throw()
     140{
     141  min_nchan=in_min_nchan;
     142  threshold=in_threshold;
     143}
     144
     145
     146// supplementary function to control running mean calculations.
     147// It adds a specified channel to the running mean box and
     148// removes (ch-max_box_nchan+1)'th channel from there
     149// Channels, for which the mask is false or index is beyond the
     150// allowed range, are ignored
     151void LFRunningMean::advanceRunningBox(int ch) throw(AipsError)
     152{
     153  if (ch>=edge.first && ch<edge.second)
     154      if (mask[ch]) { // ch is a valid channel
     155          ++box_chan_cntr;
     156          sum+=spectrum[ch];
     157          sumsq+=square(spectrum[ch]);         
     158      }
     159  int ch2remove=ch-max_box_nchan;
     160  if (ch2remove>=edge.first && ch2remove<edge.second)
     161      if (mask[ch2remove]) { // ch2remove is a valid channel
     162          --box_chan_cntr;
     163          sum-=spectrum[ch2remove];
     164          sumsq-=square(spectrum[ch2remove]); 
     165      }
     166}
     167
     168// test a channel against current running mean & rms
     169// if channel specified is masked out or beyond the allowed indexes,
     170// false is returned
     171Bool LFRunningMean::testChannel(int ch) const throw(exception, AipsError)
     172{
     173  if (ch<edge.first || ch>=edge.second) return False;
     174  if (!mask[ch]) return False;
     175  DebugAssert(box_chan_cntr, AipsError);
     176  Float mean=sum/Float(box_chan_cntr);
     177  Float variance=sqrt(sumsq/Float(box_chan_cntr)-square(mean));
     178  /*
     179  if (ch>3900 && ch<4100)
     180    cout<<"Tested "<<ch<<" mean="<<mean<<" variance="<<variance<<" sp-mean="<<spectrum[ch]-mean<<endl;
     181  */
     182  return fabs(spectrum[ch]-mean)>=threshold*variance;
     183}
     184
     185// process a channel: update cur_line and is_detected before and
     186// add a new line to the list, if necessary
     187void LFRunningMean::processChannel(std::list<pair<int,int> > &lines,
     188                                   int ch) throw(casa::AipsError)
     189{
     190  try {
     191       if (testChannel(ch)) {
     192           if (is_detected_before)
     193               cur_line.second=ch+1;
     194           else {
     195               is_detected_before=True;
     196               cur_line.first=ch;
     197               cur_line.second=ch+1;
     198           }
     199       } else processCurLine(lines);   
     200  }
     201  catch (const AipsError &ae) {
     202      throw;
     203  } 
     204  catch (const exception &ex) {
     205      throw AipsError(String("SDLineFinder::processChannel - STL error: ")+ex.what());
     206  }
     207}
     208
     209// process the interval of channels stored in cur_line
     210// if it satisfies the criterion, add this interval as a new line
     211void LFRunningMean::processCurLine(std::list<pair<int,int> > &lines)
     212                                   throw(casa::AipsError)
     213{
     214  try {
     215       if (is_detected_before) {                     
     216           if (cur_line.second-cur_line.first>min_nchan) {
     217               // it was a detection. We need to change the list
     218               Bool add_new_line=False;
     219               if (lines.size()) {
     220                   for (int i=lines.back().second;i<cur_line.first;++i)
     221                        if (mask[i]) { // one valid channel in between
     222                                //  means that we deal with a separate line
     223                            add_new_line=True;
     224                            break;
     225                        }
     226               } else add_new_line=True;
     227               if (add_new_line)
     228                   lines.push_back(cur_line);
     229               else lines.back().second=cur_line.second;                   
     230           }
     231           is_detected_before=False;
     232       }     
     233  }
     234  catch (const AipsError &ae) {
     235      throw;
     236  } 
     237  catch (const exception &ex) {
     238      throw AipsError(String("SDLineFinder::processCurLine - STL error: ")+ex.what());
     239  }
     240}
     241
     242// find spectral lines and add them into list
     243void LFRunningMean::findLines(std::list<pair<int,int> > &lines)
     244                        throw(casa::AipsError)
     245{
     246  const int minboxnchan=4;
     247
     248  // fill statistics for initial box
     249  box_chan_cntr=0; // no channels are currently in the box
     250  sum=0;           // initialize statistics
     251  sumsq=0;
     252  int initial_box_ch=edge.first;
     253  for (;initial_box_ch<edge.second && box_chan_cntr<max_box_nchan;
     254        ++initial_box_ch)
     255       advanceRunningBox(initial_box_ch);
     256   
     257  if (initial_box_ch==edge.second)       
     258      throw AipsError("LFRunningMean::findLines - too much channels are masked");
     259
     260  // actual search algorithm
     261  is_detected_before=False;
     262
     263  if (box_chan_cntr>=minboxnchan)
     264      // there is a minimum amount of data. We can search in the
     265      // half of the initial box   
     266      for (int n=edge.first;n<initial_box_ch-max_box_nchan/2;++n)
     267           processChannel(lines,n);               
     268 
     269  // now the box can be moved. n+max_box_nchan/2 is a new index which haven't
     270  // yet been included in the running mean.
     271  for (int n=initial_box_ch-max_box_nchan/2;n<edge.second;++n) {
     272      advanceRunningBox(n+max_box_nchan/2); // update running mean & variance
     273      if (box_chan_cntr>=minboxnchan) // have enough data to process
     274          processChannel(lines,n);
     275      else processCurLine(lines); // just finish what was accumulated before
     276  } 
     277}
     278
     279//
     280///////////////////////////////////////////////////////////////////////////////
     281
     282
    44283// SDLineFinder  -  a class for automated spectral line search
    45284
     
    123362                      " use set_scan");
    124363  DebugAssert(mask.nelements()==scan->nChan(), AipsError);
    125   lines.resize(0); // search from the scratch
    126  
    127   max_box_nchan=int(scan->nChan()*box_size); // number of channels in running
    128                                               // box
    129 
     364  int max_box_nchan=int(scan->nChan()*box_size); // number of channels in running
     365                                                 // box
    130366  if (max_box_nchan<2)
    131367      throw AipsError("SDLineFinder::findLines - box_size is too small");
    132368
    133369  scan->getSpectrum(spectrum);
    134  
    135   // fill statistics for initial box
    136   box_chan_cntr=0; // no channels are currently in the box
    137   sum=0;           // initialize statistics
    138   sumsq=0;
    139   int initial_box_ch=edge.first;
    140   for (;initial_box_ch<edge.second && box_chan_cntr<max_box_nchan;
    141         ++initial_box_ch)
    142        advanceRunningBox(initial_box_ch);
    143    
    144   if (initial_box_ch==edge.second)       
    145       throw AipsError("SDLineFinder::findLines - too much channels are masked");
    146 
    147   // actual search algorithm
    148   is_detected_before=False;
    149 
    150   if (box_chan_cntr>=minboxnchan)
    151       // there is a minimum amount of data. We can search in the
    152       // half of the initial box   
    153       for (int n=edge.first;n<initial_box_ch-max_box_nchan/2;++n)
    154            processChannel(n);                     
    155  
    156   // now the box can be moved. n+max_box_nchan/2 is a new index which haven't
    157   // yet been included in the running mean.
    158   for (int n=initial_box_ch-max_box_nchan/2;n<edge.second;++n) {
    159       advanceRunningBox(n+max_box_nchan/2); // update running mean & variance
    160       if (box_chan_cntr>=minboxnchan) // have enough data to process
    161           processChannel(n);
    162       else processCurLine(); // just finish what was accumulated before
    163   }
     370
     371  lines.resize(0); // search from the scratch
     372  Vector<Bool> temp_mask(mask);
     373  size_t cursz;
     374  do {
     375     cursz=lines.size();
     376     // line find algorithm
     377     LFRunningMean lfalg(spectrum,temp_mask,edge,max_box_nchan,min_nchan,threshold);
     378     lfalg.findLines(lines);
     379     temp_mask=getMask();
     380  } while (cursz!=lines.size());
    164381  return int(lines.size());
    165382}
    166383
    167 // supplementary function to control running mean calculations.
    168 // It adds a specified channel to the running mean box and
    169 // removes (ch-max_box_nchan+1)'th channel from there
    170 // Channels, for which the mask is false or index is beyond the
    171 // allowed range, are ignored
    172 void SDLineFinder::advanceRunningBox(int ch) throw(AipsError)
    173 {
    174   if (ch>=edge.first && ch<edge.second)
    175       if (mask[ch]) { // ch is a valid channel
    176           ++box_chan_cntr;
    177           sum+=spectrum[ch];
    178           sumsq+=square(spectrum[ch]);         
    179       }
    180   int ch2remove=ch-max_box_nchan;
    181   if (ch2remove>=edge.first && ch2remove<edge.second)
    182       if (mask[ch2remove]) { // ch2remove is a valid channel
    183           --box_chan_cntr;
    184           sum-=spectrum[ch2remove];
    185           sumsq-=square(spectrum[ch2remove]); 
    186       }
    187 }
    188 
    189 // test a channel against current running mean & rms
    190 // if channel specified is masked out or beyond the allowed indexes,
    191 // false is returned
    192 Bool SDLineFinder::testChannel(int ch) throw(exception, AipsError)
    193 {
    194   if (ch<edge.first || ch>=edge.second) return False;
    195   if (!mask[ch]) return False;
    196   DebugAssert(box_chan_cntr, AipsError);
    197   Float mean=sum/Float(box_chan_cntr);
    198   Float variance=sqrt(sumsq/Float(box_chan_cntr)-square(mean));
    199   /*
    200   if (ch>3900 && ch<4100)
    201     cout<<"Tested "<<ch<<" mean="<<mean<<" variance="<<variance<<" sp-mean="<<spectrum[ch]-mean<<endl;
    202   */
    203   return fabs(spectrum[ch]-mean)>=threshold*variance;
    204 }
    205 
    206 // process a channel: update cur_line and is_detected before and
    207 // add a new line to the list, if necessary
    208 void SDLineFinder::processChannel(int ch) throw(casa::AipsError)
    209 {
    210   try {
    211        if (testChannel(ch)) {
    212            if (is_detected_before)
    213                cur_line.second=ch+1;
    214            else {
    215                is_detected_before=True;
    216                cur_line.first=ch;
    217                cur_line.second=ch+1;
    218            }
    219        } else processCurLine();   
    220   }
    221   catch (const AipsError &ae) {
    222       throw;
    223   } 
    224   catch (const exception &ex) {
    225       throw AipsError(String("SDLineFinder::processChannel - STL error: ")+ex.what());
    226   }
    227 }
    228 
    229 // process the interval of channels stored in cur_line
    230 // if it satisfies the criterion, add this interval as a new line
    231 void SDLineFinder::processCurLine() throw(casa::AipsError)
    232 {
    233   try {
    234        if (is_detected_before) {                     
    235            if (cur_line.second-cur_line.first>min_nchan) {
    236                // it was a detection. We need to change the list
    237                Bool add_new_line=False;
    238                if (lines.size()) {
    239                    for (int i=lines.back().second;i<cur_line.first;++i)
    240                         if (mask[i]) { // one valid channel in between
    241                                 //  means that we deal with a separate line
    242                             add_new_line=True;
    243                             break;
    244                         }
    245                } else add_new_line=True;
    246                if (add_new_line)
    247                    lines.push_back(cur_line);
    248                else lines.back().second=cur_line.second;                   
    249            }
    250            is_detected_before=False;
    251        }     
    252   }
    253   catch (const AipsError &ae) {
    254       throw;
    255   } 
    256   catch (const exception &ex) {
    257       throw AipsError(String("SDLineFinder::processCurLine - STL error: ")+ex.what());
    258   }
    259 }
    260384
    261385// get the mask to mask out all lines that have been found (default)
     
    341465  }
    342466}
     467
     468// concatenate two lists preserving the order. If two lines appear to
     469// be adjacent, they are joined into the new one
     470void SDLineFinder::addNewSearchResult(const std::list<pair<int, int> > &newlines)
     471                        throw(AipsError)
     472{
     473  try {
     474      for (std::list<pair<int,int> >::const_iterator cli=newlines.begin();
     475           cli!=newlines.end();++cli) {
     476           // search for a right place for the new line
     477           //TODO
     478      }
     479  }
     480  catch (const AipsError &ae) {
     481      throw;
     482  } 
     483  catch (const exception &ex) {
     484      throw AipsError(String("SDLineFinder::addNewSearchResult - STL error: ")+ex.what());
     485  }
     486
     487}
  • trunk/src/SDLineFinder.h

    r297 r331  
    8585   std::vector<int>   getLineRanges(bool defunits=true)
    8686                                const throw(casa::AipsError);
    87    
    8887protected:
    89    // supplementary function to control running mean calculations.
    90    // It adds a specified channel to the running mean box and
    91    // removes (ch-maxboxnchan+1)'th channel from there
    92    // Channels, for which the mask is false or index is beyond the
    93    // allowed range, are ignored
    94    void advanceRunningBox(int ch) throw(casa::AipsError);
    95    
    96 
    97    // test a channel against current running mean & rms
    98    // if channel specified is masked out or beyond the allowed indexes,
    99    // false is returned
    100    casa::Bool testChannel(int ch) throw(std::exception, casa::AipsError);
    101 
    102    // process a channel: update curline and is_detected before and
    103    // add a new line to the list, if necessary using processCurLine()
    104    void processChannel(int ch) throw(casa::AipsError);
    105 
    106    // process the interval of channels stored in curline
    107    // if it satisfies the criterion, add this interval as a new line
    108    void processCurLine() throw(casa::AipsError);
    109    
     88   // concatenate two lists preserving the order. If two lines appear to
     89   // be adjacent, they are joined into the new one
     90   void addNewSearchResult(const std::list<std::pair<int, int> > &newlines)
     91                           throw(casa::AipsError);
    11092private:
    11193   casa::CountedConstPtr<SDMemTable> scan; // the scan to work with
     
    124106   std::list<std::pair<int, int> > lines;  // container of start and stop+1
    125107                                           // channels of the spectral lines
    126    // statistics for running mean filtering
    127    casa::Float sum;       // sum of fluxes
    128    casa::Float sumsq;     // sum of squares of fluxes
    129    int box_chan_cntr;     // actual number of channels in the box
    130    int max_box_nchan;     // maximum allowed number of channels in the box
    131                           // (calculated from boxsize and actual spectrum size)
    132108   // a buffer for the spectrum
    133109   mutable casa::Vector<casa::Float>  spectrum;
    134110
    135    // temporary line edge channels and flag, which is True if the line
    136    // was detected in the previous channels.
    137    std::pair<int,int> cur_line;
    138    casa::Bool is_detected_before;
    139111};
    140112} // namespace asap
Note: See TracChangeset for help on using the changeset viewer.