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 double const k = quarticBad ? 0 : AMPAST4;
96 double 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,
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) {
153 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.doc,
154 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.number
157 if (d2x < 0.0 || d2y < 0.0) {
160 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.doc +
161 (boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
162 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.number
166 double const dx0 = sx/d2x;
167 double const dy0 = sy/d2y;
169 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
172 SdssCentroidAlgorithm::ALMOST_NO_SECOND_DERIVATIVE.doc +
173 (boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
174 SdssCentroidAlgorithm::ALMOST_NO_SECOND_DERIVATIVE.number
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,
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) {
275 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.doc,
276 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.number
279 if ((!negative && (d2x < 0.0 || d2y < 0.0)) ||
280 ( negative && (d2x > 0.0 || d2y > 0.0))) {
283 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.doc +
284 (boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
285 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.number
289 double const dx0 = sx/d2x;
290 double const dy0 = sy/d2y;
292 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
295 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.doc +
296 (boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
297 SdssCentroidAlgorithm::ALMOST_NO_SECOND_DERIVATIVE.number
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,
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) {
408 SdssCentroidAlgorithm::EDGE.doc,
409 SdssCentroidAlgorithm::EDGE.number
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
439 _centroidExtractor(schema, name, true),
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);
552 std::string
const & name,
553 afw::table::SchemaMapper & mapper
559 if (flag == SdssCentroidAlgorithm::FAILURE)
continue;
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);
std::size_t size() const
return the current size (number of defined elements) of the collection
SdssCentroidAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
ErrElement ySigma
1-Sigma uncertainty on y (sqrt of variance)
static FlagDefinitionList const & getFlagDefinitions()
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
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.
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...
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" ;
void handleFailure(afw::table::BaseRecord &record, MeasurementError const *error=nullptr) const
Handle an expected or unexpected Exception thrown by a measurement algorithm.
A FunctorKey for CentroidResult.
CentroidElement y
y (row) coordinate of the measured position
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Called to measure a single child source in an image.
This implements the SdssCentroid algorithm within the meas_base measurement framework.
vector-type utility class to build a collection of FlagDefinitions
static FlagDefinition const FAILURE