33 # include "Minuit2/FCNBase.h"
34 # include "Minuit2/FunctionMinimum.h"
35 # include "Minuit2/MnMigrad.h"
36 # include "Minuit2/MnMinos.h"
37 # include "Minuit2/MnPrint.h"
41 #include "lsst/pex/exceptions.h"
42 #include "lsst/afw/image.h"
43 #include "lsst/afw/detection.h"
44 #include "lsst/afw/table.h"
45 #include "lsst/afw/math.h"
46 #include "lsst/afw/geom.h"
48 #include "ndarray/eigen.h"
50 namespace pexExceptions = lsst::pex::exceptions;
51 namespace afwDet = lsst::afw::detection;
52 namespace afwImage = lsst::afw::image;
53 namespace afwMath = lsst::afw::math;
54 namespace afwGeom = lsst::afw::geom;
56 namespace lsst {
namespace ip {
namespace diffim {
59 meas::base::FlagDefinitionList dipoleFluxFlagDefinitions;
62 meas::base::FlagDefinition
const DipoleFluxAlgorithm::FAILURE = dipoleFluxFlagDefinitions.addFailureFlag(
"general failure flag, set if anything went wrong");
63 meas::base::FlagDefinition
const DipoleFluxAlgorithm::POS_FLAG = dipoleFluxFlagDefinitions.add(
"pos_flag",
"failure flag for positive, set if anything went wrong");
64 meas::base::FlagDefinition
const DipoleFluxAlgorithm::NEG_FLAG = dipoleFluxFlagDefinitions.add(
"neg_flag",
"failure flag for negative, set if anything went wrong");
67 return dipoleFluxFlagDefinitions;
71 meas::base::FlagDefinitionList dipoleCentroidFlagDefinitions;
74 meas::base::FlagDefinition
const DipoleCentroidAlgorithm::FAILURE = dipoleCentroidFlagDefinitions.addFailureFlag(
"general failure flag, set if anything went wrong");
75 meas::base::FlagDefinition
const DipoleCentroidAlgorithm::POS_FLAG = dipoleCentroidFlagDefinitions.add(
"pos_flag",
"failure flag for positive, set if anything went wrong");
76 meas::base::FlagDefinition
const DipoleCentroidAlgorithm::NEG_FLAG = dipoleCentroidFlagDefinitions.add(
"neg_flag",
"failure flag for negative, set if anything went wrong");
79 return dipoleCentroidFlagDefinitions;
93 afw::table::SourceRecord & source,
94 afw::image::Exposure<float>
const& exposure,
95 afw::geom::Point2I
const & center,
96 meas::base::CentroidResultKey
const & keys
99 typedef afw::image::Image<float> ImageT;
100 ImageT
const& image = *exposure.getMaskedImage().getImage();
102 source.set(keys.getX(), center.getX());
103 source.set(keys.getY(), center.getY());
105 int x = center.getX() - image.getX0();
106 int y = center.getY() - image.getY0();
108 if (x < 1 || x >= image.getWidth() - 1 || y < 1 || y >= image.getHeight() - 1) {
109 throw LSST_EXCEPT(pex::exceptions::LengthError,
110 (boost::format(
"Object at (%d, %d) is too close to the edge")
114 ImageT::xy_locator im = image.xy_at(x, y);
117 (im(-1, 1) + im( 0, 1) + im( 1, 1) +
118 im(-1, 0) + im( 0, 0) + im( 1, 0) +
119 im(-1, -1) + im( 0, -1) + im( 1, -1));
123 throw LSST_EXCEPT(pexExceptions::RuntimeError,
124 (boost::format(
"Object at (%d, %d) has no counts") %
129 -im(-1, 1) + im( 1, 1) +
130 -im(-1, 0) + im( 1, 0) +
131 -im(-1, -1) + im( 1, -1);
133 (im(-1, 1) + im( 0, 1) + im( 1, 1)) -
134 (im(-1, -1) + im( 0, -1) + im( 1, -1));
136 float xx = afw::image::indexToPosition(x + image.getX0()) + sum_x / sum;
137 float yy = afw::image::indexToPosition(y + image.getY0()) + sum_y / sum;
138 source.set(keys.getX(), xx);
139 source.set(keys.getY(), yy);
147 std::string
const & name,
148 afw::table::Schema & schema
157 afw::table::SourceRecord & source,
158 afw::image::Exposure<float>
const & exposure
160 afw::detection::PeakCatalog
const& peaks = source.getFootprint()->getPeaks();
163 double posValue = peaks[posInd].getPeakValue(), negValue = 0;
165 posInd = peaks.size() - 1;
166 posValue = peaks[posInd].getPeakValue();
168 naiveCentroid(source, exposure, peaks[posInd].getI(),
171 if (posValue > 0. && posInd == 0 && peaks.size() > 1) {
172 int negInd = peaks.size() - 1;
173 negValue = peaks[negInd].getPeakValue();
174 if (posValue > 0. && negValue < 0.) {
175 naiveCentroid(source, exposure, peaks[negInd].getI(),
185 double posValue,
double negValue)
const {
187 double pos_x, pos_y, pos_f;
188 double neg_x, neg_y, neg_f;
198 if(std::isfinite(pos_x) && std::isfinite(pos_y) &&
199 std::isfinite(neg_x) && std::isfinite(neg_y)) {
200 source.set(
getCenterKeys().getX(), (pos_x * pos_f + neg_x * neg_f) / (pos_f + neg_f));
201 source.set(
getCenterKeys().getY(), (pos_y * pos_f + neg_y * neg_f) / (pos_f + neg_f));
202 }
else if (std::isfinite(pos_x) && std::isfinite(pos_y)) {
212 meas::base::MeasurementError * error)
const {
219 class NaiveDipoleFootprinter {
229 void reset(afwDet::Footprint
const&) {}
232 void operator() (afw::geom::Point2I
const & pos,
233 afw::image::MaskedImage<float>::Image::Pixel
const & ival,
234 afw::image::MaskedImage<float>::Image::Pixel
const & vval) {
269 afw::table::SourceRecord & source,
270 afw::image::Exposure<float>
const & exposure
272 typedef afw::image::Exposure<float>::MaskedImageT MaskedImageT;
274 NaiveDipoleFootprinter functor;
275 source.getFootprint()->getSpans()->applyFunctor(functor, *(exposure.getMaskedImage().getImage()),
276 *(exposure.getMaskedImage().getVariance()));
279 source.set(
getPositiveKeys().getFluxSigma(), ::sqrt(functor.getVarPositive()));
283 source.set(
getNegativeKeys().getFluxSigma(), ::sqrt(functor.getVarNegative()));
299 afw::table::SourceRecord & source,
300 afw::image::Exposure<float>
const& exposure
316 virtual double operator()(std::vector<double>
const & params)
const {
325 if ((negFlux > 0.0) || (posFlux < 0.0)) {
330 posCenterX, posCenterY, posFlux);
331 double chi2 = fit.first;
332 int nPix = fit.second;
353 afw::table::SourceRecord & source,
354 afw::image::Exposure<float>
const& exposure,
355 double negCenterX,
double negCenterY,
double negFlux,
356 double posCenterX,
double posCenterY,
double posFlux
359 afw::geom::Point2D negCenter(negCenterX, negCenterY);
360 afw::geom::Point2D posCenter(posCenterX, posCenterY);
362 CONST_PTR(afw::detection::Footprint) footprint = source.getFootprint();
367 CONST_PTR(afwDet::Psf) psf = exposure.getPsf();
368 PTR(afwImage::Image<afwMath::Kernel::Pixel>) negPsf = psf->computeImage(negCenter);
369 PTR(afwImage::Image<afwMath::Kernel::Pixel>) posPsf = psf->computeImage(posCenter);
371 afwImage::Image<double> negModel(footprint->getBBox());
372 afwImage::Image<double> posModel(footprint->getBBox());
373 afwImage::Image<float> data(*(exposure.getMaskedImage().getImage()),footprint->getBBox());
374 afwImage::Image<afwImage::VariancePixel> var(*(exposure.getMaskedImage().getVariance()),
375 footprint->getBBox());
377 afwGeom::Box2I negPsfBBox = negPsf->getBBox();
378 afwGeom::Box2I posPsfBBox = posPsf->getBBox();
379 afwGeom::Box2I negModelBBox = negModel.getBBox();
380 afwGeom::Box2I posModelBBox = posModel.getBBox();
383 int negXmin = std::max(negPsfBBox.getMinX(), negModelBBox.getMinX());
384 int negYmin = std::max(negPsfBBox.getMinY(), negModelBBox.getMinY());
385 int negXmax = std::min(negPsfBBox.getMaxX(), negModelBBox.getMaxX());
386 int negYmax = std::min(negPsfBBox.getMaxY(), negModelBBox.getMaxY());
387 afwGeom::Box2I negBBox = afwGeom::Box2I(afwGeom::Point2I(negXmin, negYmin),
388 afwGeom::Point2I(negXmax, negYmax));
389 afwImage::Image<afwMath::Kernel::Pixel> negSubim(*negPsf, negBBox);
390 afwImage::Image<double> negModelSubim(negModel, negBBox);
391 negModelSubim += negSubim;
394 int posXmin = std::max(posPsfBBox.getMinX(), posModelBBox.getMinX());
395 int posYmin = std::max(posPsfBBox.getMinY(), posModelBBox.getMinY());
396 int posXmax = std::min(posPsfBBox.getMaxX(), posModelBBox.getMaxX());
397 int posYmax = std::min(posPsfBBox.getMaxY(), posModelBBox.getMaxY());
398 afwGeom::Box2I posBBox = afwGeom::Box2I(afwGeom::Point2I(posXmin, posYmin),
399 afwGeom::Point2I(posXmax, posYmax));
400 afwImage::Image<afwMath::Kernel::Pixel> posSubim(*posPsf, posBBox);
401 afwImage::Image<double> posModelSubim(posModel, posBBox);
402 posModelSubim += posSubim;
406 afwImage::Image<double> residuals(negModel,
true);
407 residuals += posModel;
409 residuals *= residuals;
411 afwMath::Statistics stats = afwMath::makeStatistics(residuals, afwMath::SUM | afwMath::NPOINT);
412 double chi2 = stats.getValue(afwMath::SUM);
413 int nPix = stats.getValue(afwMath::NPOINT);
414 return std::pair<double,int>(
chi2, nPix);
418 afw::table::SourceRecord & source,
419 afw::image::Exposure<float>
const & exposure
422 typedef afw::image::Exposure<float>::MaskedImageT MaskedImageT;
424 CONST_PTR(afw::detection::Footprint) footprint = source.getFootprint();
426 throw LSST_EXCEPT(pex::exceptions::RuntimeError,
427 (boost::format(
"No footprint for source %d") % source.getId()).str());
430 afw::detection::PeakCatalog peakCatalog = afw::detection::PeakCatalog(footprint->getPeaks());
432 if (peakCatalog.size() == 0) {
433 throw LSST_EXCEPT(pex::exceptions::RuntimeError,
434 (boost::format(
"No peak for source %d") % source.getId()).str());
436 else if (peakCatalog.size() == 1) {
444 afw::detection::PeakRecord
const& positivePeak = peakCatalog.front();
445 afw::detection::PeakRecord
const& negativePeak = peakCatalog.back();
448 ROOT::Minuit2::MnUserParameters fitPar;
465 ROOT::Minuit2::MnMigrad migrad(minimizerFunc, fitPar);
472 float minChi2 = min.Fval();
473 bool const isValid = min.IsValid() && std::isfinite(minChi2);
475 if (
true || isValid) {
482 std::pair<double,int> fit =
chi2(source, exposure,
489 double evalChi2 = fit.first;
490 int nPix = fit.second;
492 PTR(afw::geom::Point2D) minNegCentroid(
new afw::geom::Point2D(min.UserState().Value(
NEGCENTXPAR),
497 PTR(afw::geom::Point2D) minPosCentroid(
new afw::geom::Point2D(min.UserState().Value(
POSCENTXPAR),
503 source.set(
_negCentroid.getX(), minNegCentroid->getX());
504 source.set(
_negCentroid.getY(), minNegCentroid->getY());
505 source.set(
_posCentroid.getX(), minPosCentroid->getX());
506 source.set(
_posCentroid.getY(), minPosCentroid->getY());
507 source.set(
_avgCentroid.getX(), 0.5*(minNegCentroid->getX() + minPosCentroid->getX()));
508 source.set(
_avgCentroid.getY(), 0.5*(minNegCentroid->getY() + minPosCentroid->getY()));
513 void PsfDipoleFlux::fail(afw::table::SourceRecord & measRecord, meas::base::MeasurementError * error)
const {
afw::table::Key< float > _chi2dofKey
float stepSizeFlux
"Default initial step size for flux in non-linear fitter" ;
meas::base::CentroidResultKey _avgCentroid
Implementation of Psf dipole flux.
void mergeCentroids(afw::table::SourceRecord &source, double posValue, double negValue) const
afw::table::Key< int > _numNegativeKey
ResultKey const & getCenterKeys() const
Return the standard centroid keys registered by this algorithm.
MinimizeDipoleChi2(PsfDipoleFlux const &psfDipoleFlux, afw::table::SourceRecord &source, afw::image::Exposure< float > const &exposure)
static meas::base::FlagDefinition const FAILURE
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
meas::base::CentroidResultKey _negCentroid
PsfDipoleFlux const & _psfDipoleFlux
ResultKey const & getPositiveKeys() const
Return the standard flux keys registered by this algorithm.
static meas::base::FlagDefinition const POS_FLAG
ResultKey const & getNegativeKeys() 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...
afw::image::Exposure< float > const & _exposure
meas::base::CentroidResultKey _posCentroid
void setErrorDef(double def)
static meas::base::FlagDefinitionList const & getFlagDefinitions()
void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Class to minimize PsfDipoleFlux; this is the object that Minuit minimizes.
meas::base::FlagHandler _flagHandler
ResultKey const & getNegativeKeys() const
virtual double operator()(std::vector< double > const ¶ms) const
NaiveDipoleCentroid(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
static meas::base::FlagDefinition const POS_FLAG
static meas::base::FlagDefinition const FAILURE
double errorDef
"How many sigma the error bars of the non-linear fitter represent" ;
void setMaxPix(int maxPix)
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
Intermediate base class for algorithms that compute a centroid.
int maxFnCalls
"Maximum function calls for non-linear fitter; 0 = unlimited" ;
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
meas::base::FlagHandler _flagHandler
float stepSizeCoord
"Default initial step size for coordinates in non-linear fitter" ;
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
ResultKey const & getPositiveKeys() const
afw::table::SourceRecord & _source
afw::table::Key< int > _numPositiveKey
static meas::base::FlagDefinition const NEG_FLAG
static meas::base::FlagDefinitionList const & getFlagDefinitions()
static meas::base::FlagDefinition const NEG_FLAG
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...