lsst.ip.diffim  21.0.0-12-g5009899+33d7f927f3
DipoleAlgorithms.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2008-2015 AURA/LSST
4  *
5  * This product includes software developed by the
6  * LSST Project (http://www.lsst.org/).
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the LSST License Statement and
19  * the GNU General Public License along with this program. If not,
20  * see <http://www.lsstcorp.org/LegalNotices/>.
21  */
22 
26 #include <iostream> // std::cout
27 #include <algorithm> // std::sort
28 #include <limits> // std::numeric_limits
29 #include <cmath> // std::sqrt
30 
31 #if !defined(DOXYGEN)
32 # include "Minuit2/FCNBase.h"
33 # include "Minuit2/FunctionMinimum.h"
34 # include "Minuit2/MnMigrad.h"
35 # include "Minuit2/MnMinos.h"
36 # include "Minuit2/MnPrint.h"
37 #endif
38 
39 #include <memory>
40 #include "lsst/pex/exceptions.h"
41 #include "lsst/afw/image.h"
42 #include "lsst/afw/detection.h"
43 #include "lsst/afw/table.h"
44 #include "lsst/afw/math.h"
45 #include "lsst/geom.h"
47 #include "ndarray/eigen.h"
48 
50 namespace afwDet = lsst::afw::detection;
51 namespace afwImage = lsst::afw::image;
52 namespace afwMath = lsst::afw::math;
53 namespace geom = lsst::geom;
54 
55 namespace lsst { namespace ip { namespace diffim {
56 
57 namespace {
58 meas::base::FlagDefinitionList dipoleFluxFlagDefinitions;
59 }
60 
61 meas::base::FlagDefinition const DipoleFluxAlgorithm::FAILURE = dipoleFluxFlagDefinitions.addFailureFlag("general failure flag, set if anything went wrong");
62 meas::base::FlagDefinition const DipoleFluxAlgorithm::POS_FLAG = dipoleFluxFlagDefinitions.add("pos_flag", "failure flag for positive, set if anything went wrong");
63 meas::base::FlagDefinition const DipoleFluxAlgorithm::NEG_FLAG = dipoleFluxFlagDefinitions.add("neg_flag", "failure flag for negative, set if anything went wrong");
64 
66  return dipoleFluxFlagDefinitions;
67 }
68 
69 namespace {
70 meas::base::FlagDefinitionList dipoleCentroidFlagDefinitions;
71 }
72 
73 meas::base::FlagDefinition const DipoleCentroidAlgorithm::FAILURE = dipoleCentroidFlagDefinitions.addFailureFlag("general failure flag, set if anything went wrong");
74 meas::base::FlagDefinition const DipoleCentroidAlgorithm::POS_FLAG = dipoleCentroidFlagDefinitions.add("pos_flag", "failure flag for positive, set if anything went wrong");
75 meas::base::FlagDefinition const DipoleCentroidAlgorithm::NEG_FLAG = dipoleCentroidFlagDefinitions.add("neg_flag", "failure flag for negative, set if anything went wrong");
76 
78  return dipoleCentroidFlagDefinitions;
79 }
80 
81  int const NEGCENTXPAR(0); // Parameter for the x-component of the negative lobe centroid
82  int const NEGCENTYPAR(1); // Parameter for the y-component of the negative lobe centroid
83  int const NEGFLUXPAR(2); // Parameter for the flux of the negative lobe
84  int const POSCENTXPAR(3); // Parameter for the x-component of the positive lobe centroid
85  int const POSCENTYPAR(4); // Parameter for the y-component of the positive lobe centroid
86  int const POSFLUXPAR(5); // Parameter for the flux of the positive lobe
87 
88 
89 namespace {
90 
91 void naiveCentroid(
92  afw::table::SourceRecord & source,
93  afw::image::Exposure<float> const& exposure,
94  geom::Point2I const & center,
96  )
97 {
98  typedef afw::image::Image<float> ImageT;
99  ImageT const& image = *exposure.getMaskedImage().getImage();
100  // set to the input centroid, just in case all else fails
101  source.set(keys.getX(), center.getX());
102  source.set(keys.getY(), center.getY());
103 
104  int x = center.getX() - image.getX0();
105  int y = center.getY() - image.getY0();
106 
107  if (x < 1 || x >= image.getWidth() - 1 || y < 1 || y >= image.getHeight() - 1) {
109  (boost::format("Object at (%d, %d) is too close to the edge")
110  % x % y).str());
111  }
112 
113  ImageT::xy_locator im = image.xy_at(x, y);
114 
115  double const sum =
116  (im(-1, 1) + im( 0, 1) + im( 1, 1) +
117  im(-1, 0) + im( 0, 0) + im( 1, 0) +
118  im(-1, -1) + im( 0, -1) + im( 1, -1));
119 
120 
121  if (sum == 0.0) {
123  (boost::format("Object at (%d, %d) has no counts") %
124  x % y).str());
125  }
126 
127  double const sum_x =
128  -im(-1, 1) + im( 1, 1) +
129  -im(-1, 0) + im( 1, 0) +
130  -im(-1, -1) + im( 1, -1);
131  double const sum_y =
132  (im(-1, 1) + im( 0, 1) + im( 1, 1)) -
133  (im(-1, -1) + im( 0, -1) + im( 1, -1));
134 
135  float xx = afw::image::indexToPosition(x + image.getX0()) + sum_x / sum;
136  float yy = afw::image::indexToPosition(y + image.getY0()) + sum_y / sum;
137  source.set(keys.getX(), xx);
138  source.set(keys.getY(), yy);
139 }
140 
141 } // anonymous namespace
142 
143 
145  Control const & ctrl,
146  std::string const & name,
147  afw::table::Schema & schema
148 ) : DipoleCentroidAlgorithm(ctrl, name, schema, "unweighted first moment centroid"),
149  _ctrl(ctrl)
150 { }
151 
156  afw::table::SourceRecord & source,
157  afw::image::Exposure<float> const & exposure
158 ) const {
159  afw::detection::PeakCatalog const& peaks = source.getFootprint()->getPeaks();
160 
161  int posInd = 0;
162  double posValue = peaks[posInd].getPeakValue(), negValue = 0;
163  if (posValue < 0.) { /* All peaks are negative so use the *most* negative value */
164  posInd = peaks.size() - 1;
165  posValue = peaks[posInd].getPeakValue();
166  }
167  naiveCentroid(source, exposure, peaks[posInd].getI(),
168  (posValue >= 0 ? getPositiveKeys() : getNegativeKeys()));
169 
170  if (posValue > 0. && posInd == 0 && peaks.size() > 1) { /* See if there's also a negative peak */
171  int negInd = peaks.size() - 1;
172  negValue = peaks[negInd].getPeakValue();
173  if (posValue > 0. && negValue < 0.) {
174  naiveCentroid(source, exposure, peaks[negInd].getI(),
175  (negValue >= 0 ? getPositiveKeys() : getNegativeKeys()));
176  }
177  }
178 
179  mergeCentroids(source, posValue, negValue);
180 
181 }
182 
184  double posValue, double negValue) const {
185 
186  double pos_x, pos_y, pos_f;
187  double neg_x, neg_y, neg_f;
188 
189  pos_x = source.get(getPositiveKeys().getX());
190  pos_y = source.get(getPositiveKeys().getY());
191  pos_f = posValue;
192 
193  neg_x = source.get(getNegativeKeys().getX());
194  neg_y = source.get(getNegativeKeys().getY());
195  neg_f = -negValue;
196 
197  if(std::isfinite(pos_x) && std::isfinite(pos_y) &&
198  std::isfinite(neg_x) && std::isfinite(neg_y)) {
199  source.set(getCenterKeys().getX(), (pos_x * pos_f + neg_x * neg_f) / (pos_f + neg_f));
200  source.set(getCenterKeys().getY(), (pos_y * pos_f + neg_y * neg_f) / (pos_f + neg_f));
201  } else if (std::isfinite(pos_x) && std::isfinite(pos_y)) {
202  source.set(getCenterKeys().getX(), pos_x);
203  source.set(getCenterKeys().getY(), pos_y);
204  } else {
205  source.set(getCenterKeys().getX(), neg_x);
206  source.set(getCenterKeys().getY(), neg_y);
207  }
208 }
209 
211  meas::base::MeasurementError * error) const {
212  _flagHandler.handleFailure(measRecord, error);
213 }
214 
215 
216 namespace {
217 
218 class NaiveDipoleFootprinter {
219 public:
220  explicit NaiveDipoleFootprinter() : _sumPositive(0.0), _sumNegative(0.0), _numPositive(0),
221  _numNegative(0) {}
222 
224  void reset() {
225  _sumPositive = _sumNegative = 0.0;
226  _numPositive = _numNegative = 0;
227  }
228  void reset(afwDet::Footprint const&) {}
229 
231  void operator() (geom::Point2I const & pos,
234  if (ival >= 0.0) {
235  _sumPositive += ival;
236  _varPositive += vval;
237  ++_numPositive;
238  } else {
239  _sumNegative += ival;
240  _varPositive += vval;
241  ++_numNegative;
242  }
243  }
244 
245  double getSumPositive() const { return _sumPositive; }
246  double getSumNegative() const { return _sumNegative; }
247  double getVarPositive() const { return _sumPositive; }
248  double getVarNegative() const { return _sumNegative; }
249  int getNumPositive() const { return _numPositive; }
250  int getNumNegative() const { return _numNegative; }
251 
252 private:
253  double _sumPositive;
254  double _sumNegative;
255  double _varPositive;
256  double _varNegative;
257  int _numPositive;
258  int _numNegative;
259 };
260 
261 } // anonymous namespace
262 
263 
268  afw::table::SourceRecord & source,
269  afw::image::Exposure<float> const & exposure
270 ) const {
271  typedef afw::image::Exposure<float>::MaskedImageT MaskedImageT;
272 
273  NaiveDipoleFootprinter functor;
274  source.getFootprint()->getSpans()->applyFunctor(functor, *(exposure.getMaskedImage().getImage()),
275  *(exposure.getMaskedImage().getVariance()));
276 
277  source.set(getPositiveKeys().getInstFlux(), functor.getSumPositive());
278  source.set(getPositiveKeys().getInstFluxErr(), ::sqrt(functor.getVarPositive()));
279  source.set(_numPositiveKey, functor.getNumPositive());
280 
281  source.set(getNegativeKeys().getInstFlux(), functor.getSumNegative());
282  source.set(getNegativeKeys().getInstFluxErr(), ::sqrt(functor.getVarNegative()));
283  source.set(_numNegativeKey, functor.getNumNegative());
284  functor.reset();
285 }
286 
288  _flagHandler.handleFailure(measRecord, error);
289 }
290 
291 
295 class MinimizeDipoleChi2 : public ROOT::Minuit2::FCNBase {
296 public:
297  explicit MinimizeDipoleChi2(PsfDipoleFlux const& psfDipoleFlux,
298  afw::table::SourceRecord & source,
299  afw::image::Exposure<float> const& exposure
300  ) : _errorDef(1.0),
301  _nPar(6),
302  _maxPix(1e4),
303  _bigChi2(1e10),
304  _psfDipoleFlux(psfDipoleFlux),
305  _source(source),
306  _exposure(exposure)
307  {}
308  double Up() const { return _errorDef; }
309  void setErrorDef(double def) { _errorDef = def; }
310  int getNpar() const { return _nPar; }
311  int getMaxPix() const { return _maxPix; }
312  void setMaxPix(int maxPix) { _maxPix = maxPix; }
313 
314  // Evaluate our cost function (in this case chi^2)
315  virtual double operator()(std::vector<double> const & params) const {
316  double negCenterX = params[NEGCENTXPAR];
317  double negCenterY = params[NEGCENTYPAR];
318  double negFlux = params[NEGFLUXPAR];
319  double posCenterX = params[POSCENTXPAR];
320  double posCenterY = params[POSCENTYPAR];
321  double posFlux = params[POSFLUXPAR];
322 
323  /* Restrict negative dipole to be negative; positive to be positive */
324  if ((negFlux > 0.0) || (posFlux < 0.0)) {
325  return _bigChi2;
326  }
327 
328  std::pair<double,int> fit = _psfDipoleFlux.chi2(_source, _exposure, negCenterX, negCenterY, negFlux,
329  posCenterX, posCenterY, posFlux);
330  double chi2 = fit.first;
331  int nPix = fit.second;
332  if (nPix > _maxPix) {
333  return _bigChi2;
334  }
335 
336  return chi2;
337  }
338 
339 private:
340  double _errorDef; // how much cost function has changed at the +- 1 error points
341  int _nPar; // number of parameters in the fit; hard coded for MinimizeDipoleChi2
342  int _maxPix; // maximum number of pixels that shoud be in the footprint;
343  // prevents too much centroid wander
344  double _bigChi2; // large value to tell fitter when it has gone into bad region of parameter space
345 
346  PsfDipoleFlux const& _psfDipoleFlux;
347  afw::table::SourceRecord & _source;
348  afw::image::Exposure<float> const& _exposure;
349 };
350 
352  afw::table::SourceRecord & source,
353  afw::image::Exposure<float> const& exposure,
354  double negCenterX, double negCenterY, double negFlux,
355  double posCenterX, double posCenterY, double posFlux
356 ) const {
357 
358  geom::Point2D negCenter(negCenterX, negCenterY);
359  geom::Point2D posCenter(posCenterX, posCenterY);
360 
361  CONST_PTR(afw::detection::Footprint) footprint = source.getFootprint();
362 
363  /*
364  * Fit for the superposition of Psfs at the two centroids.
365  */
366  CONST_PTR(afwDet::Psf) psf = exposure.getPsf();
367  PTR(afwImage::Image<afwMath::Kernel::Pixel>) negPsf = psf->computeImage(negCenter);
368  PTR(afwImage::Image<afwMath::Kernel::Pixel>) posPsf = psf->computeImage(posCenter);
369 
370  afwImage::Image<double> negModel(footprint->getBBox());
371  afwImage::Image<double> posModel(footprint->getBBox());
372  afwImage::Image<float> data(*(exposure.getMaskedImage().getImage()),footprint->getBBox());
374  footprint->getBBox());
375 
376  geom::Box2I negPsfBBox = negPsf->getBBox();
377  geom::Box2I posPsfBBox = posPsf->getBBox();
378  geom::Box2I negModelBBox = negModel.getBBox();
379  geom::Box2I posModelBBox = posModel.getBBox();
380 
381  // Portion of the negative Psf that overlaps the model
382  int negXmin = std::max(negPsfBBox.getMinX(), negModelBBox.getMinX());
383  int negYmin = std::max(negPsfBBox.getMinY(), negModelBBox.getMinY());
384  int negXmax = std::min(negPsfBBox.getMaxX(), negModelBBox.getMaxX());
385  int negYmax = std::min(negPsfBBox.getMaxY(), negModelBBox.getMaxY());
386  geom::Box2I negBBox = geom::Box2I(geom::Point2I(negXmin, negYmin),
387  geom::Point2I(negXmax, negYmax));
388  afwImage::Image<afwMath::Kernel::Pixel> negSubim(*negPsf, negBBox);
389  afwImage::Image<double> negModelSubim(negModel, negBBox);
390  negModelSubim += negSubim;
391 
392  // Portion of the positive Psf that overlaps the model
393  int posXmin = std::max(posPsfBBox.getMinX(), posModelBBox.getMinX());
394  int posYmin = std::max(posPsfBBox.getMinY(), posModelBBox.getMinY());
395  int posXmax = std::min(posPsfBBox.getMaxX(), posModelBBox.getMaxX());
396  int posYmax = std::min(posPsfBBox.getMaxY(), posModelBBox.getMaxY());
397  geom::Box2I posBBox = geom::Box2I(geom::Point2I(posXmin, posYmin),
398  geom::Point2I(posXmax, posYmax));
399  afwImage::Image<afwMath::Kernel::Pixel> posSubim(*posPsf, posBBox);
400  afwImage::Image<double> posModelSubim(posModel, posBBox);
401  posModelSubim += posSubim;
402 
403  negModel *= negFlux; // scale negative model to image
404  posModel *= posFlux; // scale positive model to image
405  afwImage::Image<double> residuals(negModel, true); // full model contains negative lobe...
406  residuals += posModel; // plus positive lobe...
407  residuals -= data; // minus the data...
408  residuals *= residuals; // squared...
409  residuals /= var; // divided by the variance : [(model-data)/sigma]**2
410  afwMath::Statistics stats = afwMath::makeStatistics(residuals, afwMath::SUM | afwMath::NPOINT);
411  double chi2 = stats.getValue(afwMath::SUM);
412  int nPix = stats.getValue(afwMath::NPOINT);
413  return std::pair<double,int>(chi2, nPix);
414 }
415 
417  afw::table::SourceRecord & source,
418  afw::image::Exposure<float> const & exposure
419 ) const {
420 
421  typedef afw::image::Exposure<float>::MaskedImageT MaskedImageT;
422 
423  CONST_PTR(afw::detection::Footprint) footprint = source.getFootprint();
424  if (!footprint) {
426  (boost::format("No footprint for source %d") % source.getId()).str());
427  }
428 
429  afw::detection::PeakCatalog peakCatalog = afw::detection::PeakCatalog(footprint->getPeaks());
430 
431  if (peakCatalog.size() == 0) {
433  (boost::format("No peak for source %d") % source.getId()).str());
434  }
435  else if (peakCatalog.size() == 1) {
436  // No deblending to do
437  return;
438  }
439 
440  // For N>=2, just measure the brightest-positive and brightest-negative
441  // peaks. peakCatalog is automatically ordered by peak flux, with the most
442  // positive one (brightest) being first
443  afw::detection::PeakRecord const& positivePeak = peakCatalog.front();
444  afw::detection::PeakRecord const& negativePeak = peakCatalog.back();
445 
446  // Set up fit parameters and param names
447  ROOT::Minuit2::MnUserParameters fitPar;
448 
449  fitPar.Add((boost::format("P%d")%NEGCENTXPAR).str(), negativePeak.getFx(), _ctrl.stepSizeCoord);
450  fitPar.Add((boost::format("P%d")%NEGCENTYPAR).str(), negativePeak.getFy(), _ctrl.stepSizeCoord);
451  fitPar.Add((boost::format("P%d")%NEGFLUXPAR).str(), negativePeak.getPeakValue(), _ctrl.stepSizeFlux);
452  fitPar.Add((boost::format("P%d")%POSCENTXPAR).str(), positivePeak.getFx(), _ctrl.stepSizeCoord);
453  fitPar.Add((boost::format("P%d")%POSCENTYPAR).str(), positivePeak.getFy(), _ctrl.stepSizeCoord);
454  fitPar.Add((boost::format("P%d")%POSFLUXPAR).str(), positivePeak.getPeakValue(), _ctrl.stepSizeFlux);
455 
456  // Create the minuit object that knows how to minimise our functor
457  //
458  MinimizeDipoleChi2 minimizerFunc(*this, source, exposure);
459  minimizerFunc.setErrorDef(_ctrl.errorDef);
460 
461  //
462  // tell minuit about it
463  //
464  ROOT::Minuit2::MnMigrad migrad(minimizerFunc, fitPar);
465 
466  //
467  // And let it loose
468  //
469  ROOT::Minuit2::FunctionMinimum min = migrad(_ctrl.maxFnCalls);
470 
471  float minChi2 = min.Fval();
472  bool const isValid = min.IsValid() && std::isfinite(minChi2);
473 
474  if (true || isValid) { // calculate coeffs even in minuit is unhappy
475 
476  /* I need to call chi2 one more time to grab nPix to calculate chi2/dof.
477  Turns out that the Minuit operator method has to be const, and the
478  measurement _apply method has to be const, so I can't store nPix as a
479  private member variable anywhere. Consted into a corner.
480  */
481  std::pair<double,int> fit = chi2(source, exposure,
482  min.UserState().Value(NEGCENTXPAR),
483  min.UserState().Value(NEGCENTYPAR),
484  min.UserState().Value(NEGFLUXPAR),
485  min.UserState().Value(POSCENTXPAR),
486  min.UserState().Value(POSCENTYPAR),
487  min.UserState().Value(POSFLUXPAR));
488  double evalChi2 = fit.first;
489  int nPix = fit.second;
490 
491  PTR(geom::Point2D) minNegCentroid(new geom::Point2D(min.UserState().Value(NEGCENTXPAR),
492  min.UserState().Value(NEGCENTYPAR)));
493  source.set(getNegativeKeys().getInstFlux(), min.UserState().Value(NEGFLUXPAR));
494  source.set(getNegativeKeys().getInstFluxErr(), min.UserState().Error(NEGFLUXPAR));
495 
496  PTR(geom::Point2D) minPosCentroid(new geom::Point2D(min.UserState().Value(POSCENTXPAR),
497  min.UserState().Value(POSCENTYPAR)));
498  source.set(getPositiveKeys().getInstFlux(), min.UserState().Value(POSFLUXPAR));
499  source.set(getPositiveKeys().getInstFluxErr(), min.UserState().Error(POSFLUXPAR));
500 
501  source.set(_chi2dofKey, evalChi2 / (nPix - minimizerFunc.getNpar()));
502  source.set(_negCentroid.getX(), minNegCentroid->getX());
503  source.set(_negCentroid.getY(), minNegCentroid->getY());
504  source.set(_posCentroid.getX(), minPosCentroid->getX());
505  source.set(_posCentroid.getY(), minPosCentroid->getY());
506  source.set(_avgCentroid.getX(), 0.5*(minNegCentroid->getX() + minPosCentroid->getX()));
507  source.set(_avgCentroid.getY(), 0.5*(minNegCentroid->getY() + minPosCentroid->getY()));
508 
509  }
510 }
511 
513  _flagHandler.handleFailure(measRecord, error);
514 }
515 }}} // namespace lsst::ip::diffim
table::Key< std::string > name
char * data
int min
double x
#define LSST_EXCEPT(type,...)
afw::table::Key< afw::table::Array< ImagePixelT > > image
int y
#define PTR(...)
#define CONST_PTR(...)
MaskedImageT getMaskedImage()
std::shared_ptr< lsst::afw::detection::Psf const > getPsf() const
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
VariancePtr getVariance() const
double getValue(Property const prop=NOTHING) const
size_type size() const
reference back() const
reference front() const
int getMinY() const noexcept
int getMinX() const noexcept
int getMaxX() const noexcept
int getMaxY() const noexcept
Intermediate base class for algorithms that compute a centroid.
static meas::base::FlagDefinition const FAILURE
static meas::base::FlagDefinition const POS_FLAG
static meas::base::FlagDefinition const NEG_FLAG
static meas::base::FlagDefinitionList const & getFlagDefinitions()
static meas::base::FlagDefinitionList const & getFlagDefinitions()
static meas::base::FlagDefinition const POS_FLAG
static meas::base::FlagDefinition const NEG_FLAG
ResultKey const & getNegativeKeys() const
static meas::base::FlagDefinition const FAILURE
ResultKey const & getPositiveKeys() const
Return the standard flux keys registered by this algorithm.
Class to minimize PsfDipoleFlux; this is the object that Minuit minimizes.
virtual double operator()(std::vector< double > const &params) const
MinimizeDipoleChi2(PsfDipoleFlux const &psfDipoleFlux, afw::table::SourceRecord &source, afw::image::Exposure< float > const &exposure)
ResultKey const & getNegativeKeys() const
NaiveDipoleCentroid(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
ResultKey const & getCenterKeys() const
Return the standard centroid keys registered by this algorithm.
ResultKey const & getPositiveKeys() const
void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Given an image and a pixel position, return a Centroid using a naive 3x3 weighted moment.
void mergeCentroids(afw::table::SourceRecord &source, double posValue, double negValue) const
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Given an image and a pixel position, return a Centroid using a naive 3x3 weighted moment.
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
float stepSizeCoord
"Default initial step size for coordinates in non-linear fitter" ;
double errorDef
"How many sigma the error bars of the non-linear fitter represent" ;
float stepSizeFlux
"Default initial step size for flux in non-linear fitter" ;
int maxFnCalls
"Maximum function calls for non-linear fitter; 0 = unlimited" ;
Implementation of Psf dipole flux.
std::pair< double, int > chi2(afw::table::SourceRecord &source, afw::image::Exposure< float > const &exposure, double negCenterX, double negCenterY, double negFlux, double posCenterX, double poCenterY, double posFlux) const
void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
afw::table::Key< CentroidElement > getY() const
afw::table::Key< CentroidElement > getX() const
void handleFailure(afw::table::BaseRecord &record, MeasurementError const *error=nullptr) const
bool isValid
T isfinite(T... args)
T max(T... args)
T min(T... args)
const char * source()
def keys(self)
afw::table::CatalogT< PeakRecord > PeakCatalog
double indexToPosition(double ind)
int const NEGFLUXPAR(2)
int const NEGCENTXPAR(0)
int const NEGCENTYPAR(1)
int const POSFLUXPAR(5)
int const POSCENTXPAR(3)
int const POSCENTYPAR(4)
Key< int > psf
table::Schema schema