LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
SizeMagnitudeStarSelectorAlgo.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
25 #include <algorithm>
26 #include <fstream>
27 #include <iostream>
28 #include <functional>
29 #include <vector>
30 #include <sstream>
31 #include <string>
32 #include <string>
33 #include <valarray>
34 // #include "unistd.h" // this doesn't appear to be used, fortunately
35 
43 
44 namespace lsst {
45 namespace meas {
46 namespace algorithms {
47 namespace shapelet {
48 
49 #define SFKeyAssign(var,key) \
50  do { \
51  if (mustexist || params.keyExists(keyPrefix + key)) { \
52  var = params[ keyPrefix + key ];\
53  } \
54  xdbg << "SFAssign " key " = " << var; \
55  xdbg << " from parameter "<<(keyPrefix + key)<<std::endl; \
56  } while (false)
57 
59  const ConfigFile& params, std::string keyPrefix, bool mustexist)
60  {
61  SFKeyAssign(_minSize,"minsize");
62  SFKeyAssign(_maxSize,"maxsize");
63  SFKeyAssign(_isSizeLog,"logsize");
64 
65  SFKeyAssign(_minMag,"minmag");
66  SFKeyAssign(_maxMag,"maxmag");
67  SFKeyAssign(_maxOutMag,"maxoutmag");
68  SFKeyAssign(_nDivX,"ndivx");
69  SFKeyAssign(_nDivY,"ndivy");
70 
71  SFKeyAssign(_nStart1,"startn1");
72  SFKeyAssign(_starFrac,"starfrac");
73  SFKeyAssign(_magStep1,"magstep1");
74  SFKeyAssign(_reject1,"reject1");
75  SFKeyAssign(_maxRatio1,"maxratio1");
76  SFKeyAssign(_binSize1,"binsize1");
77  SFKeyAssign(_okValCount,"okvalcount");
78  SFKeyAssign(_minIter1,"miniter1");
79  SFKeyAssign(_maxRms,"maxrms");
80 
81  SFKeyAssign(_nStart2,"startn2");
82  SFKeyAssign(_magStep2,"magstep2");
83  SFKeyAssign(_minBinSize,"minbinsize");
84  SFKeyAssign(_reject2,"reject2");
85  SFKeyAssign(_purityRatio,"purityratio");
86  SFKeyAssign(_minIter2,"miniter2");
87 
88  SFKeyAssign(_starsPerBin,"starsperbin");
89  SFKeyAssign(_fitOrder,"fitorder");
90  SFKeyAssign(_fitSigClip,"fitsigclip");
91  SFKeyAssign(_maxRefitIter,"maxrefititer");
92  }
93 
95  const ConfigFile& params, std::string keyPrefix)
96  {
97  std::string fspd((const char*)findstars_params_default,
99  std::istringstream is(fspd);
100  ConfigFile defaultParams;
101  defaultParams.setDelimiter("\t");
102  defaultParams.setComment("#");
103  defaultParams.read(is);
104  setParams(defaultParams,"",true);
105 
106  setParams(params,keyPrefix);
107 
108  _shouldOutputDesQa = params.read("des_qa",false);
109  }
110 
111 #undef SFKeyAssign
112 
113  std::vector<PotentialStar*> SizeMagnitudeStarSelectorAlgo::findStars(
114  std::vector<PotentialStar*>& allObj)
115  {
116  dbg<<"starting FindStars, allobj.size() = "<<allObj.size()<<std::endl;
117 
118  // sort allObj by magnitude
119  std::sort(allObj.begin(),allObj.end(),std::mem_fun(
121  dbg<<"sorted allobj\n";
122 
123  // First stage:
124  // Split area into sections
125  // For each secion find as many stars as possible.
126  // Use these stars to fit the variation in rsq across the image.
127 
128  // Make bounds of whole region called totalBounds
129  Bounds totalBounds;
130  const int nObj = allObj.size();
131  for(int k=0;k<nObj;++k) totalBounds += allObj[k]->getPos();
132  dbg<<"totalbounds = \n"<<totalBounds<<std::endl;
133 
134  // boundsar is the 3x3 (nDivX x ndivy) array of quadrant bounds
135  // call it qBounds even though it won't be quadrants unless 2x2
136  std::vector<Bounds> qBounds = totalBounds.divide(_nDivX,_nDivY);
137  xdbg<<"made qbounds\n";
138 
139  // probStars will be our first pass list of probable stars
140  std::vector<PotentialStar*> probStars;
141 
142  // For each section, find the stars and add to probStars
143  const int nSection = qBounds.size();
144  for(int i=0;i<nSection;++i) {
145  dbg<<"i = "<<i<<": bounds = "<<qBounds[i]<<std::endl;
146 
147  // someObj are the objects in this section
148  // Note that someObj is automatically sorted by magnitude, since
149  // allObj was sorted.
150  std::vector<PotentialStar*> someObj;
151  for(int k=0;k<nObj;++k) {
152  if (qBounds[i].includes(allObj[k]->getPos()))
153  someObj.push_back(allObj[k]);
154  }
155  dbg<<"added "<<someObj.size()<<" obj\n";
156 
157  // Does a really quick and dirty fit to the bright stars
158  // Basically it takes the 10 smallest of the 50 brightest objects,
159  // finds the peakiest 5, then fits their sizes to a 1st order function.
160  // It also gives us a rough value for the sigma
161  Legendre2D fLinear(qBounds[i]);
162  double sigma;
163  roughlyFitBrightStars(someObj,&fLinear,&sigma);
164  dbg<<"fit bright stars: sigma = "<<sigma<<std::endl;
165 
166  // Calculate the min and max values of the (adjusted) sizes
167  double minSize,maxSize;
168  findMinMax(someObj,&minSize,&maxSize,fLinear);
169  dbg<<"min,max = "<<minSize<<','<<maxSize<<std::endl;
170 
171  // Find the objects clustered around the stellar peak.
172  std::vector<PotentialStar*> qPeakList =
173  getPeakList(
174  someObj,_binSize1,minSize,maxSize,
175  int(_nStart1*someObj.size()),_minIter1,_magStep1,_maxRatio1,
176  true,fLinear);
177  const int nPeak = qPeakList.size();
178  dbg<<"peaklist has "<<nPeak<<" stars\n";
179 
180  // Remove outliers using a median,percentile rejection scheme.
181  // _binSize1/2. is the minimum value of "sigma".
182  rejectOutliers(qPeakList,_reject1,_binSize1/2.,fLinear);
183  dbg<<"rejected outliers, now have "<<nPeak<<" stars\n";
184 
185  // Use at most 10 (starsPerBin) stars per region to prevent one region
186  // from dominating the fit. Use the 10 brightest stars to prevent being
187  // position biased (as one would if it were based on size
188  int nStarsExpected = int(_starFrac * someObj.size());
189  if (nPeak < nStarsExpected) {
190  if (nPeak < int(0.2 * nStarsExpected)) {
191  if (_shouldOutputDesQa) {
192  std::cout<<"STATUS3BEG Warning: Only "<<
193  qPeakList.size()<<" stars found in section "<<
194  i<<". STATUS3END"<<std::endl;
195  }
196  dbg<<"Warning: only "<<qPeakList.size()<<
197  " stars found in section "<<i<<
198  " "<<qBounds[i]<<std::endl;
199  }
200  probStars.insert(probStars.end(),qPeakList.begin(),qPeakList.end());
201  } else {
202  std::sort(qPeakList.begin(),qPeakList.end(),
203  std::mem_fun(&PotentialStar::isBrighterThan));
204  probStars.insert(probStars.end(),qPeakList.begin(),
205  qPeakList.begin()+nStarsExpected);
206  }
207  dbg<<"added to probstars\n";
208  }
209  xdbg<<"done qbounds loop\n";
210  int nStars = probStars.size();
211  dbg<<"nstars = "<<nStars<<std::endl;
212 
213  // Now we have a first estimate of which objects are stars.
214  // Fit a quadratic function to them to characterize the variation in size
215  Legendre2D f(totalBounds);
216  double sigma;
217  fitStellarSizes(&f,_fitOrder,_fitSigClip,probStars,&sigma);
218 
219  // Second stage:
220  // Use the fitted function for the size we just got to adjust
221  // the measured sizes according to their position in the image.
222  // Make the histogram using measured - predicted sizes (still log
223  // size actually) which should then have the peak very close to 0.
224  // Set the bin size for this new histogram to be the rms scatter of the
225  // stellar peak from pass 1.
226  // This time step by 0.25 mag (magStep2).
227 
228  // Find the values of minSize,maxSize for the whole thing with the new f
229  double minSize,maxSize;
230  findMinMax(allObj,&minSize,&maxSize,f);
231  dbg<<"new minmax = "<<minSize<<','<<maxSize<<std::endl;
232 
233  // Find the objects clustered around the stellar peak.
234  // Note that the binSize is a set fraction of sigma (0.5), so this
235  // should make the stellar peak clearer.
236  // Also, the f at the end means the functional fitted size will be
237  // subtracted off before adding to the histogram.
238  probStars = getPeakList(
239  allObj,0.5*sigma,minSize,maxSize,
240  int(_nStart2*allObj.size()),_minIter2,_magStep2,_purityRatio,false,f);
241  nStars = probStars.size();
242  dbg<<"probstars has "<<nStars<<" stars\n";
243 
244  // Remove outliers using a median,percentile rejection scheme.
245  rejectOutliers(probStars,_reject2,sigma,f);
246  dbg<<"rejected outliers, now have "<<probStars.size()<<" stars\n";
247  // Worth doing twice, since first pass usually throws out a lot of junk.
248  rejectOutliers(probStars,_reject2,sigma,f);
249  dbg<<"rejected outliers, now have "<<probStars.size()<<" stars\n";
250 
251  // If you get a bad fit the first time through, it can take a
252  // few passes to fix it.
253  // And always do at least one refit.
254  bool shouldRefit = true;
255  for(int iter=0;shouldRefit && iter<_maxRefitIter;++iter) {
256  dbg<<"starting refit\n"<<std::endl;
257  shouldRefit = false;
258  std::vector<std::vector<PotentialStar*> > starsList(nSection);
259 
260  // Add each star to the appropriate sublist
261  for(int k=0;k<nStars;++k) {
262  for(int i=0;i<nSection;++i) {
263  if(qBounds[i].includes(probStars[k]->getPos()))
264  starsList[i].push_back(probStars[k]);
265  }
266  }
267 
268  // Make fitList, the new list of the 10 brightest stars per section
269  std::vector<PotentialStar*> fitList;
270  for(int i=0;i<nSection;++i) {
271  // if there are still < 10 stars, give a warning, and
272  // just add all the stars to fitList
273  if (int(starsList[i].size()) < _starsPerBin) {
274  if (_shouldOutputDesQa) {
275  std::cout<<"STATUS3BEG Warning: Only "<<
276  starsList[i].size()<<" stars in section "<<i<<
277  ". STATUS3END"<<std::endl;
278  }
279  dbg<<"Warning: only "<<starsList[i].size()<<
280  " stars in section ";
281  dbg<<i<<" "<<qBounds[i]<<std::endl;
282  fitList.insert(fitList.end(),starsList[i].begin(),
283  starsList[i].end());
284  shouldRefit = true;
285  } else {
286  // sort the sublist by magnitude
287  std::sort(starsList[i].begin(),starsList[i].end(),
288  std::mem_fun(&PotentialStar::isBrighterThan));
289 
290  // add the brightest 10 to fitList
291  fitList.insert(fitList.end(),starsList[i].begin(),
292  starsList[i].begin()+_starsPerBin);
293  }
294  }
295  // Do all the same stuff as before (refit f, get new min,max,
296  // find the peakList again, and reject the outliers)
297  nStars = fitList.size();
298  fitStellarSizes(&f,_fitOrder,_fitSigClip,fitList,&sigma);
299  findMinMax(allObj,&minSize,&maxSize,f);
300  dbg<<"new minmax = "<<minSize<<','<<maxSize<<std::endl;
301  probStars = getPeakList(
302  allObj,0.5*sigma,minSize,maxSize,
303  int(_nStart2*allObj.size()),_minIter2,_magStep2,
304  _purityRatio,false,f);
305  dbg<<"probstars has "<<probStars.size()<<" stars\n";
306  rejectOutliers(probStars,_reject2,sigma,f);
307  rejectOutliers(probStars,_reject2,sigma,f);
308  dbg<<"rejected outliers, now have "<<probStars.size()<<" stars\n";
309  if (int(probStars.size()) < nStars) {
310  shouldRefit = true;
311  dbg<<"fewer than "<<nStars<<" - so refit\n";
312  }
313  nStars = probStars.size();
314  }
315 
316  dbg<<"done FindStars\n";
317 
318  for(int i=0;i<nStars;++i) {
319  xdbg<<"stars["<<i<<"]: size = "<<probStars[i]->getSize();
320  xdbg<<", size - f(pos) = ";
321  xdbg<<probStars[i]->getSize()-f(probStars[i]->getPos())<<std::endl;
322  xdbg<<probStars[i]->getLine()<<std::endl;
323  }
324  return probStars;
325  }
326 
328  const std::vector<PotentialStar*>& list,
329  double *min, double *max, const Function2D& f)
330  {
331  double min1,max1;
332  min1 = max1 = list[0]->getSize()-f(list[0]->getPos());
333  const int nStars = list.size();
334  for(int k=1;k<nStars;++k) {
335  double size = list[k]->getSize() - f(list[k]->getPos());
336  if (size > max1) max1 = size;
337  if (size < min1) min1 = size;
338  }
339  *min = min1;
340  *max = max1;
341  }
342 
344  std::vector<PotentialStar*>& list,
345  double nSigma, double minSigma, const Function2D& f)
346  {
347  // This rejects outliers by finding the median and the quartile
348  // values, and calls sigma the average of the two quartile deviations.
349  // "nSigma" is then how many of these "sigma" away from the median
350  // to consider something an outlier.
351 
352  const int nStars = list.size();
353  if (nStars <= 4) return;
354  // Find median, 1st and 3rd quartile stars;
355  std::vector<PotentialStar*> modifList(nStars);
356  for(int k=0;k<nStars;++k) {
357  modifList[k] = new PotentialStar(*list[k]);
358  double newSize = list[k]->getSize() - f(list[k]->getPos());
359  modifList[k]->setSize(newSize);
360  }
361  std::sort(modifList.begin(),modifList.end(),
362  std::mem_fun(&PotentialStar::isSmallerThan));
363  PotentialStar* mStar = modifList[modifList.size()/2];
364  PotentialStar* q1Star = modifList[modifList.size()/4];
365  PotentialStar* q3Star = modifList[modifList.size()*3/4];
366  double median = mStar->getSize();
367  double q1 = q1Star->getSize();
368  double q3 = q3Star->getSize();
369  double sigma = std::max(q3-median,median-q1);
370  sigma = std::max(sigma,minSigma);
371  xdbg<<"q1,m,q3 = "<<q1<<" , "<<median<<" , "<<q3<<std::endl;
372  xdbg<<"sigma = "<<sigma<<", nsig * sigma = "<<nSigma*sigma<<std::endl;
373  // Remove elements which std::abs(x->getSize()-median) > nSigma*sigma
374  int j=0;
375  for(int k=0; k<nStars;++k) {
376  if (std::abs(modifList[k]->getSize()-median)>nSigma*sigma) {
377  xdbg<<"k = "<<k<<", size = "<<modifList[k]->getSize();
378  xdbg<<" might be an outlier (median = "<<median<<")\n";
379  }
380  list[j] = list[k]; // Note: update original list, not modified version.
381  ++j;
382  }
383  if (j<nStars) list.erase(list.begin()+j,list.end());
384  for(int k=0;k<nStars;++k) delete modifList[k];
385  }
386 
387  std::vector<PotentialStar*> SizeMagnitudeStarSelectorAlgo::getPeakList(
388  const std::vector<PotentialStar*>& objList,
389  double binSize, double minSize, double maxSize,
390  int nStart, int minIter, double magStep, double maxSignifRatio,
391  bool isFirstPass, const Function2D& f)
392  {
393  if (binSize < _minBinSize) binSize = _minBinSize;
394 
395  // Make a histogram to find the stellar peak
396  Histogram<PotentialStar*> hist(binSize,minSize,maxSize);
397 
398  // Start with the nStart brightest objects, then step up in
399  // magnitude by magStep at a time until histogram peak
400  // gets dilluted.
401  int k;
402  Assert(nStart<=int(objList.size()));
403  for(k=0;k<nStart;++k) {
404  hist.add(objList[k]->getSize()-f(objList[k]->getPos()),objList[k]);
405  }
406  xdbg<<"added "<<nStart<<" objects\n";
407  if(XDEBUG && dbgout) hist.print(*dbgout,minSize,maxSize);
408 
409  // Get first estimates of the stellar and (first) galaxy peaks
410  // along with the corresponding valleys.
411  double peak1 = hist.findFirstPeakAfter(minSize);
412  xdbg<<"peak1 = "<<peak1<<" ("<<hist[peak1]<<")\n";
413  double valley1 = hist.findFirstValleyAfter(peak1);
414  xdbg<<"valley1 = "<<valley1<<" ("<<hist[valley1]<<")\n";
415  while (valley1 < 0.0) {
416  // Since we subtract the fit, stars should be close to 0
417  peak1 = hist.findFirstPeakAfter(valley1);
418  xdbg<<"new peak1 = "<<peak1<<" ("<<hist[peak1]<<")\n";
419 
420  // Old code, did not do well with stupid no-galaxy images created
421  // by DES pipeline.
422  //valley1 = hist.findFirstValleyAfter(peak1);
423  //xdbg<<"new valley1 = "<<valley1<<" ("<<hist[valley1]<<")\n";
424 
425  double newValley1 = hist.findFirstValleyAfter(peak1);
426  if (!(newValley1 > valley1)) {
427  std::ostringstream err;
428  err<<"Couldn't find a stellar peak. ";
429  err<<"All the objects seem to be basically the same size.";
430  throw SizeMagnitudeStarSelectorException(err.str());
431  }
432  valley1 = newValley1;
433  xdbg<<"new valley1 = "<<valley1<<" ("<<hist[valley1]<<")\n";
434 
435 
436  }
437  double peak2 = hist.findFirstPeakAfter(valley1);
438  xdbg<<"peak2 = "<<peak2<<" ("<<hist[peak2]<<")\n";
439  // Sometimes the stellar peak is horned (eg. 212), in which case, find
440  // the next peak
441  double nBinsForHorn = isFirstPass ? 2.5 : 4.5;
442  if (peak2-peak1 < nBinsForHorn*binSize && peak2+binSize < maxSize) {
443  valley1 = hist.findFirstValleyAfter(peak2);
444  peak2 = hist.findFirstPeakAfter(peak2+binSize);
445  xdbg<<"horn pattern: next peak2 = "<<peak2<<" ("<<hist[peak2]<<")\n";
446  }
447  double valley2 = hist.findFirstValleyAfter(peak2);
448  xdbg<<"valley2 = "<<valley2<<" ("<<hist[valley2]<<")\n";
449  double valley0 = hist.findFirstValleyBefore(peak1);
450  if (!isFirstPass && valley0 > 0.) valley0 = hist.findFirstValleyBefore(0.);
451  xdbg<<"valley0 = "<<valley0<<" ("<<hist[valley0]<<")\n";
452 
453  // Get the stars in the first peak for the initial value of peakList
454  // When we're all done it will be the list of objects at the stellar peak.
455  std::vector<PotentialStar*> peakList=hist.getRefsInRange(valley0,valley1);
456 
457  // Define the highest mag so far in list
458  double highMag = objList[nStart-1]->getMag();
459 
460  // Loop at least minIter times. Done is set to false whenever
461  // we've just found a new best peak.
462  bool done=false;
463  double prevValley=valley1;
464  double prevPeak=0.;
465  for(int count = 0; count < minIter || !done; ++count) {
466 
467  // Increment highMag
468  highMag += magStep;
469  xdbg<<"count = "<<count<<", highmag = "<<highMag<<std::endl;
470 
471  // Add new objects to histogram.
472  const int nObj = objList.size();
473  for(;k<nObj && objList[k]->getMag()<highMag;++k) {
474  hist.add(objList[k]->getSize()-f(objList[k]->getPos()),objList[k]);
475  }
476  xdbg<<"hist has "<<k<<" objects\n";
477  if(XDEBUG && dbgout) hist.print(*dbgout,valley0,valley2);
478 
479  xdbg<<"valley0 = "<<valley0<<std::endl;
480  // Find the peak on the stellar side
481  double peak = hist.findFirstPeakAfter(valley0,!isFirstPass);
482  valley0 = hist.findFirstValleyBefore(peak); // for next pass
483  xdbg<<"done findpeak: "<<peak<<std::endl;
484 
485  // Find the valley
486  // The !isFirstPass allows poisson noise fluctuations for the final pass
487  double valley = hist.findFirstValleyAfter(peak,!isFirstPass);
488  xdbg<<"done findvalley: "<<valley<<std::endl;
489 
490  // If valley ends up negative, find next peak and valley
491  if (valley < 0.0) {
492  double nextPeak = hist.findFirstPeakAfter(valley,!isFirstPass);
493  // if next peak is closer to 0 than current one, use new values.
494  while (std::abs(nextPeak) < std::abs(peak)) {
495  peak = nextPeak;
496  xdbg<<"negative valley - new peak: "<<peak<<std::endl;
497  valley = hist.findFirstValleyAfter(peak,!isFirstPass);
498  xdbg<<"new valley: "<<valley<<std::endl;
499  if (valley < 0.0) {
500  nextPeak = hist.findFirstPeakAfter(valley,!isFirstPass);
501  } else {
502  break;
503  }
504  }
505  }
506 
507  // For the first pass, be extra conservative and don't let the valley
508  // position get much larger than a previous good value.
509  // If the peak is also larger than prevValley, something weird is
510  // going on, so bail on this pass.
511  if (isFirstPass && valley > prevValley+binSize) {
512  valley = prevValley;
513  xdbg<<"moved valley to "<<valley<<std::endl;
514  if (peak >= valley) {
515  done = true;
516  continue;
517  }
518  }
519 
520  // If new peak is larger than the previous valley, and the new peak
521  // isn't closer to 0, then reject it (probably added enough objects to
522  // the valley to fill it in completely).
523  if (peak > prevValley && std::abs(peak) > std::abs(prevPeak)) {
524  xdbg<<"New peak passed previous valley.\n";
525  done = true;
526  continue;
527  }
528 
529  // The refined counts allow the bins to be centered anywhere near
530  // the respective peak and valley and finds how many objects
531  // would fall in the optimally centered bin.
532  int peakCount = hist.getRefinedPeakCount(&peak);
533  xdbg<<"peakcount = "<<peakCount<<" centered at "<<peak<<std::endl;
534  xdbg<<"before refined: valley = "<<valley<<std::endl;
535  int valleyCount = hist.getRefinedValleyCount(&valley);
536  xdbg<<"valleycount = "<<valleyCount<<" centered at "<<valley<<std::endl;
537 
538  // Check for the horned pattern and use the next valley if it is there.
539  // But only if the new valley is at least as good as the first one.
540  //double nBinsForHorn = isFirstPass ? 1.5 : 3.5;
541  nBinsForHorn = 1.5;
542  if ((valley-peak < nBinsForHorn*binSize) &&
543  (hist.findFirstPeakAfter(valley) - valley) < nBinsForHorn*binSize) {
544  double newValley = hist.findFirstValleyAfter(
545  hist.findFirstPeakAfter(valley));
546  int newValleyCount = hist.getRefinedValleyCount(&newValley);
547  if (((isFirstPass && newValleyCount <= valleyCount) ||
548  (!isFirstPass && valleyCount > 0 && newValleyCount == 0)) &&
549  newValley <= prevValley + nBinsForHorn*binSize) {
550 
551  xdbg<<"horn pattern detected. new valley = "<<
552  newValley<<std::endl;
553  xdbg<<"new valley count = "<<newValleyCount<<std::endl;
554  valley = newValley;
555  valleyCount = newValleyCount;
556  }
557  }
558 
559  // The significance of the peak is the ratio of the number in the
560  // valley to the number in the peak
561  // So _small_ significances are good.
562  // Has to be this way, since valleyCount can be 0,
563  // so peakCount/valleyCount can be undefined.
564  double signif = (double)valleyCount/(double)peakCount;
565  dbg<<"signif = "<<valleyCount<<"/"<<peakCount<<" = "<<signif<<std::endl;
566 
567  // If the valley count is <= okValCount then drop the significance
568  // to 0 regardless of the peak count, since sometimes the peak is fairly
569  // broad so peakCount isn't high enough to get a good ratio.
570  if (isFirstPass && valleyCount <= _okValCount) {
571  signif = 0.;
572  dbg<<"reset signif to 0, since valleycount "<<valleyCount<<" <= ";
573  dbg<<_okValCount<<std::endl;
574  }
575 
576  // If we have a new good peak, get all the stars in it for peakList
577  // Also make sure we repeat the loop by setting done = false
578  if (signif <= maxSignifRatio) {
579  dbg<<"signif = "<<signif<<" <= "<<maxSignifRatio<<std::endl;
580  xdbg<<"good signif value\n";
581  // Range is symmetrical about peakvalue with upper limit at valley
582  double peakStart = std::min(2.*peak-valley,peak-binSize*0.5);
583  std::vector<PotentialStar*> temp =
584  hist.getRefsInRange(peakStart,valley);
585  xdbg<<"get refs from "<<peakStart<<" to "<<valley<<std::endl;
586  xdbg<<"got refs: "<<temp.size()<<std::endl;
587  // If this list has fewer objects than a previous list,
588  // and it doesn't just have a tighter range,
589  // don't take it. Surprisingly, this is actually possible, and it
590  // means something weird happened
591  if (temp.size() >= peakList.size() ||
592  (valley<prevValley && valley>prevPeak) ||
593  (peak > prevPeak) ) {
594 
595  if (temp.size() >= peakList.size()) {
596  xdbg<<"tempsize = "<<temp.size()<<" >= "<<peakList.size();
597  } else if ((valley<prevValley && valley>prevPeak)) {
598  xdbg<<"valley = "<<valley<<" < "<<prevValley;
599  } else {
600  xdbg<<"peak = "<<peak<<" > "<<prevPeak;
601  }
602  xdbg<<" so keep adding...\n";
603  peakList = temp;
604  done = false;
605  prevValley = valley;
606  prevPeak = peak;
607  } else {
608  xdbg<<"tempsize < "<<peakList.size();
609  xdbg<<" and peak,valley didn't contract, so stop adding now.\n";
610  done=true;
611  }
612  } else {
613  dbg<<"signif = "<<signif<<" > "<<maxSignifRatio<<std::endl;
614  done=true; // maybe. still loop if count < minIter
615  }
616 
617  // If we've already used all the stars, don't loop anymore
618  if (k == nObj) {
619  xdbg<<"no more to add\n";
620  count = minIter; done = true;
621  }
622  } // for count
623  xdbg<<"done finding peaklist\n";
624  return peakList;
625  }
626 
628  Function2D *f, int order, double sigClip,
629  const std::vector<PotentialStar*>& starList, double *outSigma)
630  {
631  // Make list of positions
632  std::vector<Position> posList(starList.size());
633  std::transform(starList.begin(),starList.end(),posList.begin(),
634  std::mem_fun(&PotentialStar::getPos));
635 
636  // Make list of sizes
637  std::vector<double> sizelist(starList.size());
638  std::transform(starList.begin(),starList.end(),sizelist.begin(),
639  std::mem_fun(&PotentialStar::getSize));
640 
641  // Use all stars in list (to start with anyway), so all true
642  std::vector<bool> useList(starList.size(),true);
643 
644  if (int(starList.size()) <= (order+1)*(order+2)/2) {
645  std::ostringstream err;
646  err<<"Not enough stars to do fit. ";
647  err<<"Increase starsPerBin or decrease fitOrder.";
648  throw SizeMagnitudeStarSelectorException(err.str());
649  }
650 
651  // Do the fit.
652  double chisq;
653  int dof;
654  xdbg<<"before outlier fit\n";
655  f->outlierFit(order,sigClip,posList,sizelist,&useList,0,&chisq,&dof,0);
656  xdbg<<"after outlier fit\n";
657 
658  xdbg<<"chisq,dof,sigma = "<<chisq<<','<<dof<<','<<
659  sqrt(chisq/dof)<<std::endl;
660  *outSigma = sqrt(chisq/dof);
661  }
662 
664  const std::vector<PotentialStar*>& objList,
665  Function2D *f,double *outSigma)
666  {
667  // objList is already sorted by magnitude
668  // make a new list with just the 50 brightest objects:
669  std::vector<PotentialStar*> brightList(
670  objList.begin(),
671  objList.begin()+int(_nStart1*objList.size()));
672 
673  // now sort this list by size
674  std::sort(brightList.begin(),brightList.end(),
675  std::mem_fun(&PotentialStar::isSmallerThan));
676 
677  xdbg<<"brightlist is:\n";
678  const int twenty = std::min(20,int(brightList.size()));
679  for(int i=0; i<twenty; ++i) {
680  xdbg<<brightList[i]->getMag()<<" , "<<
681  brightList[i]->getSize()<<std::endl;
682  }
683  if (brightList.size() > 20) {
684  xdbg<<"... (total of "<<brightList.size()<<" elements)\n";
685  }
686  // Of the smallest 20, find the 5 that form the tightest peak
687  // Originally I hardcoded this number as 5, but now it is calculated
688  // from _starFrac.
689  int five = int(0.5*_starFrac*brightList.size());
690  xdbg<<"'five' = "<<five<<std::endl;
691  if (five < 5) {
692  five = 5;
693  xdbg<<"'five' => "<<five<<std::endl;
694  }
695  if (3*five-1 >= int(brightList.size())) {
696  std::ostringstream err;
697  err<<"Too few objects in brightList. Increase startn1.";
698  throw SizeMagnitudeStarSelectorException(err.str());
699  }
700  int peakStart = 0;
701  double peakWidth = brightList[five-1]->getSize()-brightList[0]->getSize();
702  for(int k=1;k<=2*five;++k) {
703  Assert(k+five-1<int(brightList.size()));
704  double sizeRange = brightList[k+five-1]->getSize() -
705  brightList[k]->getSize();
706  if (sizeRange < peakWidth) {
707  peakWidth = brightList[k+five-1]->getSize() -
708  brightList[k]->getSize();
709  peakStart = k;
710  }
711  }
712  xdbg<<"found peak starting at "<<peakStart<<
713  " with width "<<peakWidth<<std::endl;
714 
715  int k1=peakStart, k2=peakStart+five;
716  // Now expand list to be 2*binSize1 wide
717  while (
718  k1 > 0 && brightList[peakStart+2]->getSize() -
719  brightList[k1-1]->getSize() < _binSize1) {
720  --k1;
721  }
722  const int nBright = brightList.size();
723  while (
724  k2 < nBright &&
725  brightList[k2]->getSize() - brightList[k1]->getSize() < 2.*_binSize1) {
726  ++k2;
727  }
728  xdbg<<"expanded to "<<k1<<','<<k2<<std::endl;
729 
730  // Call these objects in the expanded peak stars
731  std::vector<PotentialStar*> starList(
732  brightList.begin()+k1, brightList.begin()+k2);
733 
734  // Fit f using a linear fit with 2 sigma clipping
735  fitStellarSizes(f,0,2.,starList,outSigma);
736 
737  // if sigma is too big, drop either the first or last "star" and try again.
738  while(*outSigma > _maxRms && starList.size() > 4) {
739  double diff1 = starList.front()->getSize() -
740  (*f)(starList.front()->getPos());
741  double diff2 = starList.back()->getSize() -
742  (*f)(starList.back()->getPos());
743 
744  if (std::abs(diff1) > std::abs(diff2)) {
745  starList.erase(starList.begin());
746  xdbg<<"Erased first star. New size = "<<starList.size()<<std::endl;
747  } else {
748  starList.erase(starList.end()-1);
749  xdbg<<"Erased last star. New size = "<<starList.size()<<std::endl;
750  }
751  fitStellarSizes(f,0,2.,starList,outSigma);
752  }
753  }
754 
755 }}}}
int iter
const bool XDEBUG
Definition: dbg.h:63
std::vector< Bounds > divide(int nx, int ny) const
Definition: Bounds.cc:152
void setParams(const ConfigFile &params, std::string keyPrefix, bool mustExist=false)
void fitStellarSizes(Function2D *f, int order, double sigClip, const std::vector< PotentialStar * > &starList, double *outSigma)
std::vector< PotentialStar * > findStars(std::vector< PotentialStar * > &allObj)
SizeMagnitudeStarSelectorAlgo(const ConfigFile &params, std::string keyPrefix)
std::vector< PotentialStar * > getPeakList(const std::vector< PotentialStar * > &objList, double binSize, double minSize, double maxSize, int nStart, int minIter, double magStep, double maxSignifRatio, bool isFirstPass, const Function2D &f)
unsigned int findstars_params_default_len
Definition: fspd.h:4
void rejectOutliers(std::vector< PotentialStar * > &list, double nSigma, double minSigma, const Function2D &f)
virtual void outlierFit(int order, double nsig, const std::vector< Position > &pos, const std::vector< double > &v, std::vector< bool > *shouldUse, const std::vector< double > *sigList=0, double *chisqout=0, int *dofout=0, DMatrix *cov=0)
Definition: Function2D.cc:534
double findFirstValleyAfter(double val1, bool hasPoissonNoise=false) const
Definition: Histogram.cc:140
std::string setComment(const std::string &s)
Definition: ConfigFile.h:317
afw::table::Key< double > sigma
Definition: GaussianPsf.cc:43
double findFirstPeakAfter(double val1, bool hasPoissonNoise=false) const
Definition: Histogram.cc:204
void print(std::ostream &fout, double val1=-1.e10, double val2=1.e10) const
Definition: Histogram.cc:517
#define SFKeyAssign(var, key)
char findstars_params_default[]
Definition: fspd.h:2
double chisq
bool isBrighterThan(const PotentialStar *rhs) const
Definition: PotentialStar.h:34
bool isSmallerThan(const PotentialStar *rhs) const
Definition: PotentialStar.h:37
void add(double value, const T &ref)
Definition: Histogram.cc:61
#define xdbg
Definition: dbg.h:70
#define dbg
Definition: dbg.h:69
std::string setDelimiter(const std::string &s)
Definition: ConfigFile.h:315
void roughlyFitBrightStars(const std::vector< PotentialStar * > &objList, Function2D *f, double *outSigma)
std::ostream *const dbgout
Definition: dbg.h:64
#define Assert(x)
Definition: dbg.h:73
void findMinMax(const std::vector< PotentialStar * > &list, double *min, double *max, const Function2D &f)