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) {
312 MaskedImageT
const &mimage = exposure.getMaskedImage();
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,...)
Create an exception with a given type.
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.
MaskedImage< ImageT, MaskT, VarianceT > MaskedImageT
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.
std::string join(std::string const &a, std::string const &b) const
Join strings using the field delimiter appropriate for this Schema.
std::set< std::string > getNames(bool topOnly=false) const
Return a set of field names in the schema.
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.
Key< T > addMapping(Key< T > const &inputKey, bool doReplace=false)
Add a new field to the output Schema that is a copy of a field in the input Schema.
Schema const getInputSchema() const
Return the input schema (copy-on-write).
Record class that contains measurements made on a single exposure.
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)
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)
Point< double, 2 > Point2D
double constexpr PI
The ratio of a circle's circumference to diameter.
Extent< int, 2 > Extent2I
@ 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...