26#include "ndarray/eigen.h"
40FlagDefinitionList flagDefinitions;
45 flagDefinitions.add(
"flag_edge",
"Object too close to edge; peak used.");
47 flagDefinitions.add(
"flag_noSecondDerivative",
"Vanishing second derivative");
49 flagDefinitions.add(
"flag_almostNoSecondDerivative",
"Almost vanishing second derivative");
51 flagDefinitions.add(
"flag_notAtMaximum",
"Object is not at a maximum");
53 flagDefinitions.add(
"flag_near_edge",
"Object close to edge; fallback kernel used.");
61float const AMPAST4 = 1.33;
70static int inter4(
float vm,
float v0,
float vp,
float *cen,
bool negative =
false) {
71 float const sp = v0 - vp;
72 float const sm = v0 - vm;
73 float const d2 = sp + sm;
74 float const s = 0.5 * (vp - vm);
76 if ((!negative && (d2 <= 0.0f || v0 <= 0.0f)) || (negative && (d2 >= 0.0f || v0 >= 0.0f))) {
80 *cen = s / d2 * (1.0 + AMPAST4 * sp * sm / (d2 * v0));
82 return fabs(*cen) < 1 ? 0 : 1;
89float astrom_errors(
float skyVar,
99 double const k = quarticBad ? 0 : AMPAST4;
112 sVar += 0.5 * sourceVar * exp(-1 / (2 * tau2));
113 dVar += sourceVar * (4 * exp(-1 / (2 * tau2)) + 2 * exp(-1 / (2 * tau2)));
122 xVar = sVar *
pow(1 / d + k / (4 * As) * (1 - 12 * s * s / (d * d)), 2) +
123 dVar *
pow(s / (d * d) - k / (4 * As) * 8 * s * s / (d * d * d), 2);
125 return (xVar >= 0 ?
sqrt(xVar) : NAN);
133template <
typename ImageXy_locatorT,
typename VarImageXy_locatorT>
134int doMeasureCentroidImpl(
double *xCenter,
138 double *sizeX2,
double *sizeY2,
140 VarImageXy_locatorT vim,
141 double smoothingSigma,
142 FlagHandler flagHandler) {
146 double const d2x = 2 * im(0, 0) - im(-1, 0) - im(1, 0);
147 double const d2y = 2 * im(0, 0) - im(0, -1) - im(0, 1);
148 double const sx = 0.5 * (im(1, 0) - im(-1, 0));
149 double const sy = 0.5 * (im(0, 1) - im(0, -1));
151 if (d2x == 0.0 || d2y == 0.0) {
154 if (d2x < 0.0 || d2y < 0.0) {
158 double const dx0 = sx / d2x;
159 double const dy0 = sy / d2y;
161 if (
fabs(dx0) > 10.0 ||
fabs(dy0) > 10.0) {
165 double vpk = im(0, 0) + 0.5 * (sx * dx0 + sy * dy0);
172 float m0x = 0, m1x = 0, m2x = 0;
173 float m0y = 0, m1y = 0, m2y = 0;
176 quarticBad += inter4(im(-1, -1), im(0, -1), im(1, -1), &m0x);
177 quarticBad += inter4(im(-1, 0), im(0, 0), im(1, 0), &m1x);
178 quarticBad += inter4(im(-1, 1), im(0, 1), im(1, 1), &m2x);
180 quarticBad += inter4(im(-1, -1), im(-1, 0), im(-1, 1), &m0y);
181 quarticBad += inter4(im(0, -1), im(0, 0), im(0, 1), &m1y);
182 quarticBad += inter4(im(1, -1), im(1, 0), im(1, 1), &m2y);
185 double sigmaX2, sigmaY2;
193 double const smx = 0.5 * (m2x - m0x);
194 double const smy = 0.5 * (m2y - m0y);
195 double const dm2x = m1x - 0.5 * (m0x + m2x);
196 double const dm2y = m1y - 0.5 * (m0y + m2y);
197 double const dx = m1x + dy0 * (smx - dy0 * dm2x);
198 double const dy = m1y + dx0 * (smy - dx0 * dm2y);
199 double const dx4 = m1x + dy * (smx - dy * dm2x);
200 double const dy4 = m1y + dx * (smy - dx * dm2y);
204 sigmaX2 = vpk / d2x - (1 + 6 * dx0 * dx0) / 4;
205 sigmaY2 = vpk / d2y - (1 + 6 * dy0 * dy0) / 4;
210 float tauX2 = sigmaX2;
211 float tauY2 = sigmaY2;
212 tauX2 -= smoothingSigma * smoothingSigma;
213 tauY2 -= smoothingSigma * smoothingSigma;
215 if (tauX2 <= smoothingSigma * smoothingSigma) {
216 tauX2 = smoothingSigma * smoothingSigma;
218 if (tauY2 <= smoothingSigma * smoothingSigma) {
219 tauY2 = smoothingSigma * smoothingSigma;
222 float const skyVar = (vim(-1, -1) + vim(0, -1) + vim(1, -1) + vim(-1, 0) + vim(1, 0) + vim(-1, 1) +
223 vim(0, 1) + vim(1, 1)) /
225 float const sourceVar = vim(0, 0);
226 float const A = vpk *
sqrt((sigmaX2 / tauX2) * (sigmaY2 / tauY2));
231 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x,
fabs(smoothingSigma), quarticBad);
232 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y,
fabs(smoothingSigma), quarticBad);
239template <
typename MaskedImageXy_locatorT>
240int doMeasureCentroidImpl(
double *xCenter,
244 double *sizeX2,
double *sizeY2,
246 MaskedImageXy_locatorT mim,
247 double smoothingSigma,
248 bool negative, FlagHandler flagHandler) {
252 double const d2x = 2 * mim.image(0, 0) - mim.image(-1, 0) - mim.image(1, 0);
253 double const d2y = 2 * mim.image(0, 0) - mim.image(0, -1) - mim.image(0, 1);
254 double const sx = 0.5 * (mim.image(1, 0) - mim.image(-1, 0));
255 double const sy = 0.5 * (mim.image(0, 1) - mim.image(0, -1));
257 if (d2x == 0.0 || d2y == 0.0) {
260 if ((!negative && (d2x < 0.0 || d2y < 0.0)) || (negative && (d2x > 0.0 || d2y > 0.0))) {
264 double const dx0 = sx / d2x;
265 double const dy0 = sy / d2y;
267 if (
fabs(dx0) > 10.0 ||
fabs(dy0) > 10.0) {
271 double vpk = mim.image(0, 0) + 0.5 * (sx * dx0 + sy * dy0);
278 float m0x = 0, m1x = 0, m2x = 0;
279 float m0y = 0, m1y = 0, m2y = 0;
282 quarticBad += inter4(mim.image(-1, -1), mim.image(0, -1), mim.image(1, -1), &m0x, negative);
283 quarticBad += inter4(mim.image(-1, 0), mim.image(0, 0), mim.image(1, 0), &m1x, negative);
284 quarticBad += inter4(mim.image(-1, 1), mim.image(0, 1), mim.image(1, 1), &m2x, negative);
286 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y, negative);
287 quarticBad += inter4(mim.image(0, -1), mim.image(0, 0), mim.image(0, 1), &m1y, negative);
288 quarticBad += inter4(mim.image(1, -1), mim.image(1, 0), mim.image(1, 1), &m2y, negative);
291 double sigmaX2, sigmaY2;
299 double const smx = 0.5 * (m2x - m0x);
300 double const smy = 0.5 * (m2y - m0y);
301 double const dm2x = m1x - 0.5 * (m0x + m2x);
302 double const dm2y = m1y - 0.5 * (m0y + m2y);
303 double const dx = m1x + dy0 * (smx - dy0 * dm2x);
304 double const dy = m1y + dx0 * (smy - dx0 * dm2y);
305 double const dx4 = m1x + dy * (smx - dy * dm2x);
306 double const dy4 = m1y + dx * (smy - dx * dm2y);
310 sigmaX2 = vpk / d2x - (1 + 6 * dx0 * dx0) / 4;
311 sigmaY2 = vpk / d2y - (1 + 6 * dy0 * dy0) / 4;
316 float tauX2 = sigmaX2;
317 float tauY2 = sigmaY2;
318 tauX2 -= smoothingSigma * smoothingSigma;
319 tauY2 -= smoothingSigma * smoothingSigma;
321 if (tauX2 <= smoothingSigma * smoothingSigma) {
322 tauX2 = smoothingSigma * smoothingSigma;
324 if (tauY2 <= smoothingSigma * smoothingSigma) {
325 tauY2 = smoothingSigma * smoothingSigma;
329 (mim.variance(-1, -1) + mim.variance(0, -1) + mim.variance(1, -1) + mim.variance(-1, 0) +
330 mim.variance(1, 0) + mim.variance(-1, 1) + mim.variance(0, 1) + mim.variance(1, 1)) /
332 float const sourceVar = mim.variance(0, 0);
333 float const A = vpk *
sqrt((sigmaX2 / tauX2) * (sigmaY2 / tauY2));
338 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x,
fabs(smoothingSigma), quarticBad);
339 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y,
fabs(smoothingSigma), quarticBad);
348template <
typename MaskedImageT>
350 const int y, MaskedImageT
const &mimage,
int binX,
int binY,
351 FlagHandler _flagHandler) {
353 afw::geom::ellipses::Quadrupole
const &shape =
psf->computeShape(center);
354 double const smoothingSigma = shape.getDeterminantRadius();
356 double const nEffective =
psf->computeEffectiveArea();
358 double const nEffective = 4 *
M_PI * smoothingSigma * smoothingSigma;
362 int const kWidth = kernel->getWidth();
363 int const kHeight = kernel->getHeight();
366 geom::ExtentI(binX * (3 + kWidth + 1), binY * (3 + kHeight + 1)));
370 if (!mimage.getBBox(afw::image::LOCAL).contains(
bbox)) {
373 subImage.
reset(
new MaskedImageT(mimage,
bbox, afw::image::LOCAL));
375 binnedImage->setXY0(subImage->getXY0());
377 MaskedImageT smoothedImage = MaskedImageT(*binnedImage,
true);
378 if(smoothedImage.getWidth() / 2 != kWidth / 2 + 2 ||
379 smoothedImage.getHeight() / 2 != kHeight / 2 + 2) {
383 afw::math::convolve(smoothedImage, *binnedImage, *kernel, afw::math::ConvolutionControl());
384 *smoothedImage.getVariance() *= binX * binY * nEffective;
399 _centroidChecker(
schema,
name, ctrl.doFootprintCheck, ctrl.maxDistToPeak) {}
403 geom::Point2D center = _centroidExtractor(measRecord, _flagHandler);
410 typedef MaskedImageT::Image ImageT;
411 typedef MaskedImageT::Variance VarianceT;
412 bool negative =
false;
414 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"flags_negative").key);
418 MaskedImageT
const &mimage =
exposure.getMaskedImage();
419 ImageT
const &
image = *mimage.getImage();
426 _flagHandler.setValue(measRecord,
EDGE.
number,
true);
439 double xc = 0., yc = 0., dxc = 0., dyc = 0.;
440 bool stopBinning =
false;
441 for (
int binsize = 1; binsize <= _ctrl.
binmax; binsize *= 2) {
443 smoothAndBinImage(
psf,
x,
y, mimage, binX, binY, _flagHandler);
444 int errorFlag = std::get<2>(smoothResult);
445 if (errorFlag ==
static_cast<int>(
EDGE.
number)) {
446 psf = std::make_shared<afw::detection::GaussianPsf>(5, 5, 0.5);
447 smoothResult = smoothAndBinImage(
psf,
x,
y, mimage, binX, binY, _flagHandler);
449 errorFlag = std::get<2>(smoothResult);
450 if (errorFlag == 0) {
455 _flagHandler.setValue(measRecord, errorFlag,
true);
462 MaskedImageT
const smoothedImage = std::get<0>(smoothResult);
463 double const smoothingSigma = std::get<1>(smoothResult);
465 MaskedImageT::xy_locator mim =
466 smoothedImage.xy_at(smoothedImage.getWidth() / 2, smoothedImage.getHeight() / 2);
468 double sizeX2, sizeY2;
471 errorFlag = doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim, smoothingSigma, negative,
474 _flagHandler.setValue(measRecord, errorFlag,
true);
481 xc = (xc + 0.5) * binX - 0.5;
483 sizeX2 *= binX * binX;
485 yc = (yc + 0.5) * binY - 0.5;
487 sizeY2 *= binY * binY;
497 double const fac = _ctrl.
wfac * (1 + smoothingSigma * smoothingSigma);
498 double const facX2 = fac * binX * binX;
499 double const facY2 = fac * binY * binY;
501 if (sizeX2 < facX2 && ::pow(xc -
x, 2) < facX2 && sizeY2 < facY2 && ::pow(yc -
y, 2) < facY2) {
502 if (binsize > 1 || _ctrl.
peakMin < 0.0 || peakVal > _ctrl.
peakMin) {
507 if (sizeX2 >= facX2 || ::pow(xc -
x, 2) >= facX2) {
510 if (sizeY2 >= facY2 || ::pow(yc -
y, 2) >= facY2) {
517 result.xErr = sqrt(dxc * dxc);
518 result.yErr = sqrt(dyc * dyc);
520 _centroidChecker(measRecord);
524 _flagHandler.handleFailure(measRecord, error);
533 if (
mapper.getInputSchema().getNames().count(
mapper.getInputSchema().join(
name, flag.
name)) == 0)
536 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
static FlagDefinition const NEAR_EDGE
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...