26 #include "ndarray/eigen.h"
27 #include "lsst/afw/detection/Psf.h"
28 #include "lsst/pex/exceptions.h"
29 #include "lsst/afw/math/ConvolveImage.h"
30 #include "lsst/afw/math/offsetImage.h"
31 #include "lsst/afw/geom/Angle.h"
32 #include "lsst/afw/table/Source.h"
36 namespace lsst {
namespace meas {
namespace base {
38 FlagDefinitionList flagDefinitions;
48 return flagDefinitions;
56 float const AMPAST4 = 1.33;
65 static int inter4(
float vm,
float v0,
float vp,
float *cen,
bool negative=
false) {
66 float const sp = v0 - vp;
67 float const sm = v0 - vm;
68 float const d2 = sp + sm;
69 float const s = 0.5*(vp - vm);
71 if ((!negative && (d2 <= 0.0f || v0 <= 0.0f)) ||
72 ( negative && (d2 >= 0.0f || v0 >= 0.0f))) {
76 *cen = s/d2*(1.0 + AMPAST4*sp*sm/(d2*v0));
78 return fabs(*cen) < 1 ? 0 : 1;
85 float astrom_errors(
float skyVar,
95 float const k = quarticBad ? 0 : AMPAST4;
96 float const sigma2 = sigma*sigma;
100 if (fabs(As) < std::numeric_limits<float>::min() ||
101 fabs(d) < std::numeric_limits<float>::min()) {
109 sVar += 0.5*sourceVar*exp(-1/(2*tau2));
110 dVar += sourceVar*(4*exp(-1/(2*tau2)) + 2*exp(-1/(2*tau2)));
112 sVar = skyVar/(8*lsst::afw::geom::PI*sigma2)*(1 - exp(-1/sigma2));
113 dVar = skyVar/(2*lsst::afw::geom::PI*sigma2)*(3 - 4*exp(-1/(4*sigma2)) + exp(-1/sigma2));
115 sVar += sourceVar/(12*lsst::afw::geom::PI*sigma2)*(exp(-1/(3*sigma2)) - exp(-1/sigma2));
116 dVar += sourceVar/(3*lsst::afw::geom::PI*sigma2)*(2 - 3*exp(-1/(3*sigma2)) + exp(-1/sigma2));
119 xVar = sVar*pow(1/d + k/(4*As)*(1 - 12*s*s/(d*d)), 2) +
120 dVar*pow(s/(d*d) - k/(4*As)*8*s*s/(d*d*d), 2);
122 return(xVar >= 0 ? sqrt(xVar) : NAN);
130 template<
typename ImageXy_locatorT,
typename VarImageXy_locatorT>
131 void doMeasureCentroidImpl(
double *xCenter,
135 double *sizeX2,
double *sizeY2,
137 VarImageXy_locatorT vim,
138 double smoothingSigma,
139 FlagHandler flagHandler
145 double const d2x = 2*im(0, 0) - im(-1, 0) - im(1, 0);
146 double const d2y = 2*im(0, 0) - im( 0, -1) - im(0, 1);
147 double const sx = 0.5*(im(1, 0) - im(-1, 0));
148 double const sy = 0.5*(im(0, 1) - im( 0, -1));
150 if (d2x == 0.0 || d2y == 0.0) {
157 if (d2x < 0.0 || d2y < 0.0) {
161 (boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
166 double const dx0 = sx/d2x;
167 double const dy0 = sy/d2y;
169 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
173 (boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
178 double vpk = im(0, 0) + 0.5*(sx*dx0 + sy*dy0);
185 float m0x = 0, m1x = 0, m2x = 0;
186 float m0y = 0, m1y = 0, m2y = 0;
189 quarticBad += inter4(im(-1, -1), im( 0, -1), im( 1, -1), &m0x);
190 quarticBad += inter4(im(-1, 0), im( 0, 0), im( 1, 0), &m1x);
191 quarticBad += inter4(im(-1, 1), im( 0, 1), im( 1, 1), &m2x);
193 quarticBad += inter4(im(-1, -1), im(-1, 0), im(-1, 1), &m0y);
194 quarticBad += inter4(im( 0, -1), im( 0, 0), im( 0, 1), &m1y);
195 quarticBad += inter4(im( 1, -1), im( 1, 0), im( 1, 1), &m2y);
198 double sigmaX2, sigmaY2;
206 double const smx = 0.5*(m2x - m0x);
207 double const smy = 0.5*(m2y - m0y);
208 double const dm2x = m1x - 0.5*(m0x + m2x);
209 double const dm2y = m1y - 0.5*(m0y + m2y);
210 double const dx = m1x + dy0*(smx - dy0*dm2x);
211 double const dy = m1y + dx0*(smy - dx0*dm2y);
212 double const dx4 = m1x + dy*(smx - dy*dm2x);
213 double const dy4 = m1y + dx*(smy - dx*dm2y);
217 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
218 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
223 float tauX2 = sigmaX2;
224 float tauY2 = sigmaY2;
225 tauX2 -= smoothingSigma*smoothingSigma;
226 tauY2 -= smoothingSigma*smoothingSigma;
228 if (tauX2 <= smoothingSigma*smoothingSigma) {
229 tauX2 = smoothingSigma*smoothingSigma;
231 if (tauY2 <= smoothingSigma*smoothingSigma) {
232 tauY2 = smoothingSigma*smoothingSigma;
235 float const skyVar = (vim(-1, -1) + vim( 0, -1) + vim( 1, -1) +
236 vim(-1, 0) + vim( 1, 0) +
237 vim(-1, 1) + vim( 0, 1) + vim( 1, 1))/8.0;
238 float const sourceVar = vim(0, 0);
239 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
244 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
245 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
251 template<
typename MaskedImageXy_locatorT>
252 void doMeasureCentroidImpl(
double *xCenter,
256 double *sizeX2,
double *sizeY2,
258 MaskedImageXy_locatorT mim,
259 double smoothingSigma,
261 FlagHandler flagHandler
267 double const d2x = 2*mim.image(0, 0) - mim.image(-1, 0) - mim.image(1, 0);
268 double const d2y = 2*mim.image(0, 0) - mim.image( 0, -1) - mim.image(0, 1);
269 double const sx = 0.5*(mim.image(1, 0) - mim.image(-1, 0));
270 double const sy = 0.5*(mim.image(0, 1) - mim.image( 0, -1));
272 if (d2x == 0.0 || d2y == 0.0) {
279 if ((!negative && (d2x < 0.0 || d2y < 0.0)) ||
280 ( negative && (d2x > 0.0 || d2y > 0.0))) {
284 (boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
289 double const dx0 = sx/d2x;
290 double const dy0 = sy/d2y;
292 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
296 (boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
301 double vpk = mim.image(0, 0) + 0.5*(sx*dx0 + sy*dy0);
308 float m0x = 0, m1x = 0, m2x = 0;
309 float m0y = 0, m1y = 0, m2y = 0;
312 quarticBad += inter4(mim.image(-1, -1), mim.image( 0, -1), mim.image( 1, -1), &m0x, negative);
313 quarticBad += inter4(mim.image(-1, 0), mim.image( 0, 0), mim.image( 1, 0), &m1x, negative);
314 quarticBad += inter4(mim.image(-1, 1), mim.image( 0, 1), mim.image( 1, 1), &m2x, negative);
316 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y, negative);
317 quarticBad += inter4(mim.image( 0, -1), mim.image( 0, 0), mim.image( 0, 1), &m1y, negative);
318 quarticBad += inter4(mim.image( 1, -1), mim.image( 1, 0), mim.image( 1, 1), &m2y, negative);
321 double sigmaX2, sigmaY2;
329 double const smx = 0.5*(m2x - m0x);
330 double const smy = 0.5*(m2y - m0y);
331 double const dm2x = m1x - 0.5*(m0x + m2x);
332 double const dm2y = m1y - 0.5*(m0y + m2y);
333 double const dx = m1x + dy0*(smx - dy0*dm2x);
334 double const dy = m1y + dx0*(smy - dx0*dm2y);
335 double const dx4 = m1x + dy*(smx - dy*dm2x);
336 double const dy4 = m1y + dx*(smy - dx*dm2y);
340 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
341 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
346 float tauX2 = sigmaX2;
347 float tauY2 = sigmaY2;
348 tauX2 -= smoothingSigma*smoothingSigma;
349 tauY2 -= smoothingSigma*smoothingSigma;
351 if (tauX2 <= smoothingSigma*smoothingSigma) {
352 tauX2 = smoothingSigma*smoothingSigma;
354 if (tauY2 <= smoothingSigma*smoothingSigma) {
355 tauY2 = smoothingSigma*smoothingSigma;
358 float const skyVar = (mim.variance(-1, -1) + mim.variance( 0, -1) + mim.variance( 1, -1) +
359 mim.variance(-1, 0) + mim.variance( 1, 0) +
360 mim.variance(-1, 1) + mim.variance( 0, 1) + mim.variance( 1, 1)
362 float const sourceVar = mim.variance(0, 0);
363 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
368 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
369 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
377 template<
typename MaskedImageT>
378 std::pair<MaskedImageT, double>
379 smoothAndBinImage(CONST_PTR(lsst::afw::detection::Psf) psf,
380 int const x,
const int y,
381 MaskedImageT
const& mimage,
383 FlagHandler _flagHandler)
385 lsst::afw::geom::Point2D
const center(x + mimage.getX0(), y + mimage.getY0());
386 lsst::afw::geom::ellipses::Quadrupole
const& shape = psf->computeShape(center);
387 double const smoothingSigma = shape.getDeterminantRadius();
389 double const nEffective = psf->computeEffectiveArea();
391 double const nEffective = 4*M_PI*smoothingSigma*smoothingSigma;
394 std::shared_ptr<lsst::afw::math::Kernel const> kernel = psf->getLocalKernel(center);
395 int const kWidth = kernel->getWidth();
396 int const kHeight = kernel->getHeight();
398 lsst::afw::geom::BoxI bbox(lsst::afw::geom::Point2I(x - binX*(2 + kWidth/2), y - binY*(2 + kHeight/2)),
399 lsst::afw::geom::ExtentI(binX*(3 + kWidth + 1), binY*(3 + kHeight + 1)));
402 PTR(MaskedImageT) subImage;
404 subImage.reset(
new MaskedImageT(mimage, bbox, lsst::afw::image::LOCAL));
405 }
catch (pex::exceptions::LengthError & err) {
412 PTR(MaskedImageT) binnedImage = lsst::afw::math::binImage(*subImage, binX, binY, lsst::afw::math::MEAN);
413 binnedImage->setXY0(subImage->getXY0());
415 MaskedImageT smoothedImage = MaskedImageT(*binnedImage, true);
416 assert(smoothedImage.getWidth()/2 == kWidth/2 + 2);
417 assert(smoothedImage.getHeight()/2 == kHeight/2 + 2);
419 lsst::afw::math::convolve(smoothedImage, *binnedImage, *kernel, lsst::afw::math::ConvolutionControl());
420 *smoothedImage.getVariance() *= binX*binY*nEffective;
423 return std::make_pair(smoothedImage, smoothingSigma);
431 std::
string const & name,
432 afw::table::Schema & schema
438 getFlagDefinitions())),
440 _centroidChecker(schema, name, ctrl.doFootprintCheck, ctrl.maxDistToPeak)
444 afw::table::SourceRecord & measRecord,
445 afw::image::Exposure<float>
const & exposure
449 afw::geom::Point2D center = _centroidExtractor(measRecord, _flagHandler);
451 result.
x = center.getX();
452 result.
y = center.getY();
453 measRecord.set(_centroidKey, result);
455 typedef afw::image::Exposure<float>::MaskedImageT MaskedImageT;
456 typedef MaskedImageT::Image ImageT;
457 typedef MaskedImageT::Variance VarianceT;
458 bool negative =
false;
460 negative = measRecord.get(measRecord.getSchema().find<afw::table::Flag>(
"flags_negative").key);
461 }
catch(pexExcept::Exception &e) {}
463 MaskedImageT
const& mimage = exposure.getMaskedImage();
464 ImageT
const& image = *mimage.getImage();
465 CONST_PTR(lsst::afw::detection::Psf) psf = exposure.getPsf();
467 int const x = image.positionToIndex(center.getX(), lsst::afw::image::X).first;
468 int const y = image.positionToIndex(center.getY(), lsst::afw::image::Y).first;
470 if (!image.getBBox().contains(lsst::afw::geom::Extent2I(x,y) + image.getXY0())) {
483 "SdssCentroid algorithm requires a Psf with every exposure"
489 double xc=0., yc=0., dxc=0., dyc=0.;
490 for(
int binsize = 1; binsize <= _ctrl.
binmax; binsize *= 2) {
491 std::pair<MaskedImageT, double> result = smoothAndBinImage(psf, x, y, mimage, binX, binY, _flagHandler);
492 MaskedImageT
const smoothedImage = result.first;
493 double const smoothingSigma = result.second;
495 MaskedImageT::xy_locator mim = smoothedImage.xy_at(smoothedImage.getWidth()/2,
496 smoothedImage.getHeight()/2);
498 double sizeX2, sizeY2;
501 doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim,
502 smoothingSigma, negative, _flagHandler);
506 xc = (xc + 0.5)*binX - 0.5;
510 yc = (yc + 0.5)*binY - 0.5;
518 double const fac = _ctrl.
wfac*(1 + smoothingSigma*smoothingSigma);
519 double const facX2 = fac*binX*binX;
520 double const facY2 = fac*binY*binY;
522 if (sizeX2 < facX2 && ::pow(xc - x, 2) < facX2 &&
523 sizeY2 < facY2 && ::pow(yc - y, 2) < facY2) {
524 if (binsize > 1 || _ctrl.
peakMin < 0.0 || peakVal > _ctrl.
peakMin) {
529 if (sizeX2 >= facX2 || ::pow(xc - x, 2) >= facX2) {
532 if (sizeY2 >= facY2 || ::pow(yc - y, 2) >= facY2) {
536 result.
x = lsst::afw::image::indexToPosition(xc + image.getX0());
537 result.
y = lsst::afw::image::indexToPosition(yc + image.getY0());
539 result.
xSigma = sqrt(dxc*dxc);
540 result.
ySigma = sqrt(dyc*dyc);
541 measRecord.set(_centroidKey, result);
542 _centroidChecker(measRecord);
547 _flagHandler.handleFailure(measRecord, error);
552 std::string
const & name,
553 afw::table::SchemaMapper & mapper
560 if (mapper.getInputSchema().getNames().count(
561 mapper.getInputSchema().join(name, flag.
name)) == 0)
continue;
562 afw::table::Key<afw::table::Flag> key = mapper.getInputSchema().find<afw::table::Flag>(
563 name +
"_" + flag.
name).key;
564 mapper.addMapping(key);
The Sdss Centroid Algorithm.
ErrElement ySigma
1-Sigma uncertainty on y (sqrt of variance)
virtual void fail(afw::table::SourceRecord &measRecord, MeasurementError *error=nullptr) const
Handle an exception thrown by the current algorithm by setting flags in the given record...
Simple class used to define and document flags The name and doc constitute the identity of the FlagDe...
Only the diagonal elements of the covariance matrix are provided.
A reusable struct for centroid measurements.
ErrElement xSigma
1-Sigma uncertainty on x (sqrt of variance)
CentroidElement x
x (column) coordinate of the measured position
A C++ control class to handle SdssCentroidAlgorithm's configuration.
int binmax
"maximum allowed binning" ;
static FlagDefinition const ALMOST_NO_SECOND_DERIVATIVE
Exception to be thrown when a measurement algorithm experiences a known failure mode.
static FlagDefinition const NOT_AT_MAXIMUM
static FlagDefinitionList const & getFlagDefinitions()
Exception to be thrown when a measurement algorithm experiences a fatal error.
Utility class for handling flag fields that indicate the failure modes of an algorithm.
_centroidExtractor(schema, name)
static FlagDefinition const EDGE
static FlagDefinition const NO_SECOND_DERIVATIVE
double peakMin
"if the peak's less than this insist on binning at least once" ;
double wfac
"fiddle factor for adjusting the binning" ;
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Called to measure a single child source in an image.
A FunctorKey for CentroidResult.
CentroidElement y
y (row) coordinate of the measured position
afw::geom::Point< CentroidElement, 2 > Centroid
This implements the SdssCentroid algorithm within the meas_base measurement framework.
vector-type utility class to build a collection of FlagDefinitions
std::size_t size() const
return the current size (number of defined elements) of the collection
static FlagDefinition const FAILURE