26#include "ndarray/eigen.h"
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;
100 double const sigma2 = sigma * sigma;
112 sVar += 0.5 * sourceVar * exp(-1 / (2 * tau2));
113 dVar += sourceVar * (4 * exp(-1 / (2 * tau2)) + 2 * exp(-1 / (2 * tau2)));
115 sVar = skyVar / (8 *
geom::PI * sigma2) * (1 -
exp(-1 / sigma2));
116 dVar = skyVar / (2 *
geom::PI * sigma2) * (3 - 4 *
exp(-1 / (4 * sigma2)) +
exp(-1 / sigma2));
118 sVar += sourceVar / (12 *
geom::PI * sigma2) * (
exp(-1 / (3 * sigma2)) -
exp(-1 / sigma2));
119 dVar += sourceVar / (3 *
geom::PI * sigma2) * (2 - 3 *
exp(-1 / (3 * sigma2)) +
exp(-1 / sigma2));
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 MaskedImageXy_locatorT>
134int doMeasureCentroidImpl(
double *xCenter,
138 double *sizeX2,
double *sizeY2,
140 MaskedImageXy_locatorT mim,
141 double smoothingSigma,
146 double const d2x = 2 * mim.image(0, 0) - mim.image(-1, 0) - mim.image(1, 0);
147 double const d2y = 2 * mim.image(0, 0) - mim.image(0, -1) - mim.image(0, 1);
148 double const sx = 0.5 * (mim.image(1, 0) - mim.image(-1, 0));
149 double const sy = 0.5 * (mim.image(0, 1) - mim.image(0, -1));
151 if (d2x == 0.0 || d2y == 0.0) {
154 if ((!negative && (d2x < 0.0 || d2y < 0.0)) || (negative && (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 = mim.image(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(mim.image(-1, -1), mim.image(0, -1), mim.image(1, -1), &m0x, negative);
177 quarticBad += inter4(mim.image(-1, 0), mim.image(0, 0), mim.image(1, 0), &m1x, negative);
178 quarticBad += inter4(mim.image(-1, 1), mim.image(0, 1), mim.image(1, 1), &m2x, negative);
180 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y, negative);
181 quarticBad += inter4(mim.image(0, -1), mim.image(0, 0), mim.image(0, 1), &m1y, negative);
182 quarticBad += inter4(mim.image(1, -1), mim.image(1, 0), mim.image(1, 1), &m2y, negative);
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;
223 (mim.variance(-1, -1) + mim.variance(0, -1) + mim.variance(1, -1) + mim.variance(-1, 0) +
224 mim.variance(1, 0) + mim.variance(-1, 1) + mim.variance(0, 1) + mim.variance(1, 1)) /
226 float const sourceVar = mim.variance(0, 0);
227 float const A = vpk *
sqrt((sigmaX2 / tauX2) * (sigmaY2 / tauY2));
232 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x,
fabs(smoothingSigma), quarticBad);
233 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y,
fabs(smoothingSigma), quarticBad);
242template <
typename MaskedImageT>
243std::tuple<MaskedImageT, double, int> smoothAndBinImage(std::shared_ptr<afw::detection::Psf const> psf,
int const x,
244 const int y, MaskedImageT
const &mimage,
int binX,
int binY,
246 geom::Point2D const center(x + mimage.getX0(), y + mimage.getY0());
247 afw::geom::ellipses::Quadrupole
const &shape = psf->computeShape(center);
248 double const smoothingSigma = shape.getDeterminantRadius();
250 double const nEffective = psf->computeEffectiveArea();
252 double const nEffective = 4 * M_PI * smoothingSigma * smoothingSigma;
255 std::shared_ptr<afw::math::Kernel const> kernel = psf->getLocalKernel(center);
256 int const kWidth = kernel->getWidth();
257 int const kHeight = kernel->getHeight();
260 geom::ExtentI(binX * (3 + kWidth + 1), binY * (3 + kHeight + 1)));
263 std::shared_ptr<MaskedImageT> subImage;
269 binnedImage->setXY0(subImage->getXY0());
271 MaskedImageT smoothedImage = MaskedImageT(*binnedImage,
true);
272 if(smoothedImage.getWidth() / 2 != kWidth / 2 + 2 ||
273 smoothedImage.getHeight() / 2 != kHeight / 2 + 2) {
274 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
"invalid image dimensions");
277 afw::math::convolve(smoothedImage, *binnedImage, *kernel, afw::math::ConvolutionControl());
278 *smoothedImage.getVariance() *= binX * binY * nEffective;
289 _centroidKey(
CentroidResultKey::addFields(schema, name,
"centroid from Sdss Centroid algorithm",
292 _centroidExtractor(schema, name, true),
293 _centroidChecker(schema, name, ctrl.doFootprintCheck, ctrl.maxDistToPeak) {}
297 geom::Point2D center = _centroidExtractor(measRecord, _flagHandler);
299 result.
x = center.getX();
300 result.
y = center.getY();
301 measRecord.
set(_centroidKey, result);
304 typedef MaskedImageT::Image ImageT;
305 typedef MaskedImageT::Variance VarianceT;
306 bool negative =
false;
308 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"is_negative").key);
309 }
catch (pexExcept::Exception &e) {
313 ImageT
const &
image = *mimage.getImage();
320 _flagHandler.setValue(measRecord,
EDGE.number,
true);
333 double xc = 0., yc = 0., dxc = 0., dyc = 0.;
334 bool stopBinning =
false;
335 for (
int binsize = 1; binsize <= _ctrl.binmax; binsize *= 2) {
337 smoothAndBinImage(psf, x, y, mimage, binX, binY, _flagHandler);
338 int errorFlag = std::get<2>(smoothResult);
339 if (errorFlag ==
static_cast<int>(
EDGE.number)) {
341 smoothResult = smoothAndBinImage(psf, x, y, mimage, binX, binY, _flagHandler);
343 errorFlag = std::get<2>(smoothResult);
344 if (errorFlag == 0) {
349 _flagHandler.setValue(measRecord, errorFlag,
true);
352 if (errorFlag !=
static_cast<int>(
NEAR_EDGE.number)) {
356 MaskedImageT
const smoothedImage = std::get<0>(smoothResult);
357 double const smoothingSigma = std::get<1>(smoothResult);
359 MaskedImageT::xy_locator mim =
360 smoothedImage.xy_at(smoothedImage.getWidth() / 2, smoothedImage.getHeight() / 2);
362 double sizeX2, sizeY2;
365 errorFlag = doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim, smoothingSigma, negative,
368 _flagHandler.setValue(measRecord, errorFlag,
true);
375 xc = (xc + 0.5) * binX - 0.5;
377 sizeX2 *= binX * binX;
379 yc = (yc + 0.5) * binY - 0.5;
381 sizeY2 *= binY * binY;
391 double const fac = _ctrl.wfac * (1 + smoothingSigma * smoothingSigma);
392 double const facX2 = fac * binX * binX;
393 double const facY2 = fac * binY * binY;
395 if (sizeX2 < facX2 && ::pow(xc - x, 2) < facX2 && sizeY2 < facY2 && ::pow(yc - y, 2) < facY2) {
396 if (binsize > 1 || _ctrl.peakMin < 0.0 || peakVal > _ctrl.peakMin) {
401 if (sizeX2 >= facX2 || ::pow(xc - x, 2) >= facX2) {
404 if (sizeY2 >= facY2 || ::pow(yc - y, 2) >= facY2) {
411 result.
xErr = sqrt(dxc * dxc);
412 result.
yErr = sqrt(dyc * dyc);
413 measRecord.
set(_centroidKey, result);
414 _centroidChecker(measRecord);
418 _flagHandler.handleFailure(measRecord, error);
#define LSST_EXCEPT(type,...)
This implements the SdssCentroid algorithm within the meas_base measurement framework.
MaskedImageT getMaskedImage()
std::shared_ptr< lsst::afw::detection::Psf const > getPsf() const
MaskedImage< ImageT, MaskT, VarianceT > MaskedImageT
Field< T >::Value get(Key< T > const &key) const
void set(Key< T > const &key, U const &value)
std::string join(std::string const &a, std::string const &b) const
std::set< std::string > getNames(bool topOnly=false) const
SchemaItem< T > find(std::string const &name) const
Key< T > addMapping(Key< T > const &inputKey, bool doReplace=false)
Schema const getInputSchema() const
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
SdssCentroidControl Control
A typedef to the Control object for this algorithm, defined above.
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
double indexToPosition(double ind)
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
Point< double, 2 > Point2D
Extent< int, 2 > Extent2I
@ SIGMA_ONLY
Only the diagonal elements of the covariance matrix are provided.
A reusable struct for centroid measurements.
CentroidElement y
y (row) coordinate of the measured position
CentroidElement x
x (column) coordinate of the measured position
ErrElement yErr
standard deviation of y
ErrElement xErr
standard deviation of x
Simple class used to define and document flags The name and doc constitute the identity of the FlagDe...