26#include "ndarray/eigen.h"
39FlagDefinitionList flagDefinitions;
44 flagDefinitions.add(
"flag_edge",
"Object too close to edge");
46 flagDefinitions.add(
"flag_noSecondDerivative",
"Vanishing second derivative");
48 flagDefinitions.add(
"flag_almostNoSecondDerivative",
"Almost vanishing second derivative");
50 flagDefinitions.add(
"flag_notAtMaximum",
"Object is not at a maximum");
58float const AMPAST4 = 1.33;
67static int inter4(
float vm,
float v0,
float vp,
float *cen,
bool negative =
false) {
68 float const sp = v0 - vp;
69 float const sm = v0 - vm;
70 float const d2 = sp + sm;
71 float const s = 0.5 * (vp - vm);
73 if ((!negative && (d2 <= 0.0f || v0 <= 0.0f)) || (negative && (d2 >= 0.0f || v0 >= 0.0f))) {
77 *cen = s / d2 * (1.0 + AMPAST4 * sp * sm / (d2 * v0));
79 return fabs(*cen) < 1 ? 0 : 1;
86float astrom_errors(
float skyVar,
96 double const k = quarticBad ? 0 : AMPAST4;
109 sVar += 0.5 * sourceVar * exp(-1 / (2 * tau2));
110 dVar += sourceVar * (4 * exp(-1 / (2 * tau2)) + 2 * exp(-1 / (2 * tau2)));
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);
130template <
typename ImageXy_locatorT,
typename VarImageXy_locatorT>
131int doMeasureCentroidImpl(
double *xCenter,
135 double *sizeX2,
double *sizeY2,
137 VarImageXy_locatorT vim,
138 double smoothingSigma,
139 FlagHandler flagHandler) {
143 double const d2x = 2 * im(0, 0) - im(-1, 0) - im(1, 0);
144 double const d2y = 2 * im(0, 0) - im(0, -1) - im(0, 1);
145 double const sx = 0.5 * (im(1, 0) - im(-1, 0));
146 double const sy = 0.5 * (im(0, 1) - im(0, -1));
148 if (d2x == 0.0 || d2y == 0.0) {
151 if (d2x < 0.0 || d2y < 0.0) {
155 double const dx0 = sx / d2x;
156 double const dy0 = sy / d2y;
158 if (
fabs(dx0) > 10.0 ||
fabs(dy0) > 10.0) {
162 double vpk = im(0, 0) + 0.5 * (sx * dx0 + sy * dy0);
169 float m0x = 0, m1x = 0, m2x = 0;
170 float m0y = 0, m1y = 0, m2y = 0;
173 quarticBad += inter4(im(-1, -1), im(0, -1), im(1, -1), &m0x);
174 quarticBad += inter4(im(-1, 0), im(0, 0), im(1, 0), &m1x);
175 quarticBad += inter4(im(-1, 1), im(0, 1), im(1, 1), &m2x);
177 quarticBad += inter4(im(-1, -1), im(-1, 0), im(-1, 1), &m0y);
178 quarticBad += inter4(im(0, -1), im(0, 0), im(0, 1), &m1y);
179 quarticBad += inter4(im(1, -1), im(1, 0), im(1, 1), &m2y);
182 double sigmaX2, sigmaY2;
190 double const smx = 0.5 * (m2x - m0x);
191 double const smy = 0.5 * (m2y - m0y);
192 double const dm2x = m1x - 0.5 * (m0x + m2x);
193 double const dm2y = m1y - 0.5 * (m0y + m2y);
194 double const dx = m1x + dy0 * (smx - dy0 * dm2x);
195 double const dy = m1y + dx0 * (smy - dx0 * dm2y);
196 double const dx4 = m1x + dy * (smx - dy * dm2x);
197 double const dy4 = m1y + dx * (smy - dx * dm2y);
201 sigmaX2 = vpk / d2x - (1 + 6 * dx0 * dx0) / 4;
202 sigmaY2 = vpk / d2y - (1 + 6 * dy0 * dy0) / 4;
207 float tauX2 = sigmaX2;
208 float tauY2 = sigmaY2;
209 tauX2 -= smoothingSigma * smoothingSigma;
210 tauY2 -= smoothingSigma * smoothingSigma;
212 if (tauX2 <= smoothingSigma * smoothingSigma) {
213 tauX2 = smoothingSigma * smoothingSigma;
215 if (tauY2 <= smoothingSigma * smoothingSigma) {
216 tauY2 = smoothingSigma * smoothingSigma;
219 float const skyVar = (vim(-1, -1) + vim(0, -1) + vim(1, -1) + vim(-1, 0) + vim(1, 0) + vim(-1, 1) +
220 vim(0, 1) + vim(1, 1)) /
222 float const sourceVar = vim(0, 0);
223 float const A = vpk *
sqrt((sigmaX2 / tauX2) * (sigmaY2 / tauY2));
228 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x,
fabs(smoothingSigma), quarticBad);
229 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y,
fabs(smoothingSigma), quarticBad);
236template <
typename MaskedImageXy_locatorT>
237int doMeasureCentroidImpl(
double *xCenter,
241 double *sizeX2,
double *sizeY2,
243 MaskedImageXy_locatorT mim,
244 double smoothingSigma,
245 bool negative, FlagHandler flagHandler) {
249 double const d2x = 2 * mim.image(0, 0) - mim.image(-1, 0) - mim.image(1, 0);
250 double const d2y = 2 * mim.image(0, 0) - mim.image(0, -1) - mim.image(0, 1);
251 double const sx = 0.5 * (mim.image(1, 0) - mim.image(-1, 0));
252 double const sy = 0.5 * (mim.image(0, 1) - mim.image(0, -1));
254 if (d2x == 0.0 || d2y == 0.0) {
257 if ((!negative && (d2x < 0.0 || d2y < 0.0)) || (negative && (d2x > 0.0 || d2y > 0.0))) {
261 double const dx0 = sx / d2x;
262 double const dy0 = sy / d2y;
264 if (
fabs(dx0) > 10.0 ||
fabs(dy0) > 10.0) {
268 double vpk = mim.image(0, 0) + 0.5 * (sx * dx0 + sy * dy0);
275 float m0x = 0, m1x = 0, m2x = 0;
276 float m0y = 0, m1y = 0, m2y = 0;
279 quarticBad += inter4(mim.image(-1, -1), mim.image(0, -1), mim.image(1, -1), &m0x, negative);
280 quarticBad += inter4(mim.image(-1, 0), mim.image(0, 0), mim.image(1, 0), &m1x, negative);
281 quarticBad += inter4(mim.image(-1, 1), mim.image(0, 1), mim.image(1, 1), &m2x, negative);
283 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y, negative);
284 quarticBad += inter4(mim.image(0, -1), mim.image(0, 0), mim.image(0, 1), &m1y, negative);
285 quarticBad += inter4(mim.image(1, -1), mim.image(1, 0), mim.image(1, 1), &m2y, negative);
288 double sigmaX2, sigmaY2;
296 double const smx = 0.5 * (m2x - m0x);
297 double const smy = 0.5 * (m2y - m0y);
298 double const dm2x = m1x - 0.5 * (m0x + m2x);
299 double const dm2y = m1y - 0.5 * (m0y + m2y);
300 double const dx = m1x + dy0 * (smx - dy0 * dm2x);
301 double const dy = m1y + dx0 * (smy - dx0 * dm2y);
302 double const dx4 = m1x + dy * (smx - dy * dm2x);
303 double const dy4 = m1y + dx * (smy - dx * dm2y);
307 sigmaX2 = vpk / d2x - (1 + 6 * dx0 * dx0) / 4;
308 sigmaY2 = vpk / d2y - (1 + 6 * dy0 * dy0) / 4;
313 float tauX2 = sigmaX2;
314 float tauY2 = sigmaY2;
315 tauX2 -= smoothingSigma * smoothingSigma;
316 tauY2 -= smoothingSigma * smoothingSigma;
318 if (tauX2 <= smoothingSigma * smoothingSigma) {
319 tauX2 = smoothingSigma * smoothingSigma;
321 if (tauY2 <= smoothingSigma * smoothingSigma) {
322 tauY2 = smoothingSigma * smoothingSigma;
326 (mim.variance(-1, -1) + mim.variance(0, -1) + mim.variance(1, -1) + mim.variance(-1, 0) +
327 mim.variance(1, 0) + mim.variance(-1, 1) + mim.variance(0, 1) + mim.variance(1, 1)) /
329 float const sourceVar = mim.variance(0, 0);
330 float const A = vpk *
sqrt((sigmaX2 / tauX2) * (sigmaY2 / tauY2));
335 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x,
fabs(smoothingSigma), quarticBad);
336 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y,
fabs(smoothingSigma), quarticBad);
345template <
typename MaskedImageT>
347 const int y, MaskedImageT
const &mimage,
int binX,
int binY,
348 FlagHandler _flagHandler) {
350 afw::geom::ellipses::Quadrupole
const &shape =
psf->computeShape(center);
351 double const smoothingSigma = shape.getDeterminantRadius();
353 double const nEffective =
psf->computeEffectiveArea();
355 double const nEffective = 4 *
M_PI * smoothingSigma * smoothingSigma;
359 int const kWidth = kernel->getWidth();
360 int const kHeight = kernel->getHeight();
363 geom::ExtentI(binX * (3 + kWidth + 1), binY * (3 + kHeight + 1)));
367 if (!mimage.getBBox(afw::image::LOCAL).contains(
bbox)) {
370 subImage.
reset(
new MaskedImageT(mimage,
bbox, afw::image::LOCAL));
372 binnedImage->setXY0(subImage->getXY0());
374 MaskedImageT smoothedImage = MaskedImageT(*binnedImage,
true);
375 if(smoothedImage.getWidth() / 2 != kWidth / 2 + 2 ||
376 smoothedImage.getHeight() / 2 != kHeight / 2 + 2) {
380 afw::math::convolve(smoothedImage, *binnedImage, *kernel, afw::math::ConvolutionControl());
381 *smoothedImage.getVariance() *= binX * binY * nEffective;
396 _centroidChecker(
schema,
name, ctrl.doFootprintCheck, ctrl.maxDistToPeak) {}
400 geom::Point2D center = _centroidExtractor(measRecord, _flagHandler);
407 typedef MaskedImageT::Image ImageT;
408 typedef MaskedImageT::Variance VarianceT;
409 bool negative =
false;
411 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"flags_negative").key);
415 MaskedImageT
const &mimage = exposure.getMaskedImage();
416 ImageT
const &
image = *mimage.getImage();
423 _flagHandler.setValue(measRecord,
EDGE.
number,
true);
436 double xc = 0., yc = 0., dxc = 0., dyc = 0.;
437 for (
int binsize = 1; binsize <= _ctrl.
binmax; binsize *= 2) {
439 smoothAndBinImage(
psf,
x,
y, mimage, binX, binY, _flagHandler);
440 int errorFlag = std::get<2>(smoothResult);
442 _flagHandler.setValue(measRecord, errorFlag,
true);
446 MaskedImageT
const smoothedImage = std::get<0>(smoothResult);
447 double const smoothingSigma = std::get<1>(smoothResult);
449 MaskedImageT::xy_locator mim =
450 smoothedImage.xy_at(smoothedImage.getWidth() / 2, smoothedImage.getHeight() / 2);
452 double sizeX2, sizeY2;
455 errorFlag = doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim, smoothingSigma, negative,
458 _flagHandler.setValue(measRecord, errorFlag,
true);
465 xc = (xc + 0.5) * binX - 0.5;
467 sizeX2 *= binX * binX;
469 yc = (yc + 0.5) * binY - 0.5;
471 sizeY2 *= binY * binY;
477 double const fac = _ctrl.
wfac * (1 + smoothingSigma * smoothingSigma);
478 double const facX2 = fac * binX * binX;
479 double const facY2 = fac * binY * binY;
481 if (sizeX2 < facX2 && ::pow(xc -
x, 2) < facX2 && sizeY2 < facY2 && ::pow(yc -
y, 2) < facY2) {
482 if (binsize > 1 || _ctrl.
peakMin < 0.0 || peakVal > _ctrl.
peakMin) {
487 if (sizeX2 >= facX2 || ::pow(xc -
x, 2) >= facX2) {
490 if (sizeY2 >= facY2 || ::pow(yc -
y, 2) >= facY2) {
497 result.xErr = sqrt(dxc * dxc);
498 result.yErr = sqrt(dyc * dyc);
500 _centroidChecker(measRecord);
504 _flagHandler.handleFailure(measRecord, error);
513 if (
mapper.getInputSchema().getNames().count(
mapper.getInputSchema().join(
name, flag.
name)) == 0)
516 mapper.getInputSchema().find<afw::table::Flag>(
name +
"_" + flag.
name).key;
table::Key< std::string > name
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
table::Key< double > sigma2
afw::table::Key< double > sigma
This implements the SdssCentroid algorithm within the meas_base measurement framework.
A class to contain the data, WCS, and other information needed to describe an image of the sky.
A class to manipulate images, masks, and variance as a single object.
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Schema getSchema() const
Return the Schema that holds this record's fields and keys.
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
A class used as a handle to a particular field in a table.
Defines the fields and offsets for a table.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
A mapping between the keys of two Schemas, used to copy data between them.
Record class that contains measurements made on a single exposure.
An integer coordinate rectangle.
A FunctorKey for CentroidResult.
Exception to be thrown when a measurement algorithm experiences a fatal error.
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
Utility class for handling flag fields that indicate the failure modes of an algorithm.
Exception to be thrown when a measurement algorithm experiences a known failure mode.
static FlagDefinition const NOT_AT_MAXIMUM
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.
SdssCentroidAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
static FlagDefinition const NO_SECOND_DERIVATIVE
static FlagDefinition const FAILURE
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Called to measure a single child source in an image.
static FlagDefinitionList const & getFlagDefinitions()
static FlagDefinition const ALMOST_NO_SECOND_DERIVATIVE
static FlagDefinition const EDGE
A C++ control class to handle SdssCentroidAlgorithm's configuration.
double wfac
"fiddle factor for adjusting the binning" ;
int binmax
"maximum allowed binning" ;
double peakMin
"if the peak's less than this insist on binning at least once" ;
Provides consistent interface for LSST exceptions.
Reports attempts to exceed implementation-defined length limits for some classes.
double indexToPosition(double ind)
Convert image index to image position.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
Convolve an Image or MaskedImage with a Kernel, setting pixels of an existing output image.
@ MEAN
estimate sample mean
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
double constexpr PI
The ratio of a circle's circumference to diameter.
@ SIGMA_ONLY
Only the diagonal elements of the covariance matrix are provided.
A reusable struct for centroid measurements.
Simple class used to define and document flags The name and doc constitute the identity of the FlagDe...