26 #include "ndarray/eigen.h"
36 namespace lsst {
namespace meas {
namespace base {
42 float const AMPAST4 = 1.33;
51 static int inter4(
float vm,
float v0,
float vp,
float *cen,
bool negative=
false) {
52 float const sp = v0 - vp;
53 float const sm = v0 - vm;
54 float const d2 = sp + sm;
55 float const s = 0.5*(vp - vm);
57 if ((!negative && (d2 <= 0.0f || v0 <= 0.0f)) ||
58 ( negative && (d2 >= 0.0f || v0 >= 0.0f))) {
62 *cen = s/d2*(1.0 + AMPAST4*sp*sm/(d2*v0));
64 return fabs(*cen) < 1 ? 0 : 1;
71 float astrom_errors(
float skyVar,
81 float const k = quarticBad ? 0 : AMPAST4;
86 if (fabs(As) < std::numeric_limits<float>::min() ||
87 fabs(d) < std::numeric_limits<float>::min()) {
95 sVar += 0.5*sourceVar*exp(-1/(2*tau2));
96 dVar += sourceVar*(4*exp(-1/(2*tau2)) + 2*exp(-1/(2*tau2)));
105 xVar = sVar*pow(1/d + k/(4*As)*(1 - 12*s*s/(d*d)), 2) +
106 dVar*pow(s/(d*d) - k/(4*As)*8*s*s/(d*d*d), 2);
108 return(xVar >= 0 ? sqrt(xVar) : NAN);
116 template<
typename ImageXy_locatorT,
typename VarImageXy_locatorT>
117 void doMeasureCentroidImpl(
double *xCenter,
121 double *sizeX2,
double *sizeY2,
123 VarImageXy_locatorT vim,
124 double smoothingSigma,
131 double const d2x = 2*im(0, 0) - im(-1, 0) - im(1, 0);
132 double const d2y = 2*im(0, 0) - im( 0, -1) - im(0, 1);
133 double const sx = 0.5*(im(1, 0) - im(-1, 0));
134 double const sy = 0.5*(im(0, 1) - im( 0, -1));
136 if (d2x == 0.0 || d2y == 0.0) {
143 if (d2x < 0.0 || d2y < 0.0) {
147 (
boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
152 double const dx0 = sx/d2x;
153 double const dy0 = sy/d2y;
155 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
159 (
boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
164 double vpk = im(0, 0) + 0.5*(sx*dx0 + sy*dy0);
171 float m0x = 0, m1x = 0, m2x = 0;
172 float m0y = 0, m1y = 0, m2y = 0;
175 quarticBad += inter4(im(-1, -1), im( 0, -1), im( 1, -1), &m0x);
176 quarticBad += inter4(im(-1, 0), im( 0, 0), im( 1, 0), &m1x);
177 quarticBad += inter4(im(-1, 1), im( 0, 1), im( 1, 1), &m2x);
179 quarticBad += inter4(im(-1, -1), im(-1, 0), im(-1, 1), &m0y);
180 quarticBad += inter4(im( 0, -1), im( 0, 0), im( 0, 1), &m1y);
181 quarticBad += inter4(im( 1, -1), im( 1, 0), im( 1, 1), &m2y);
184 double sigmaX2, sigmaY2;
192 double const smx = 0.5*(m2x - m0x);
193 double const smy = 0.5*(m2y - m0y);
194 double const dm2x = m1x - 0.5*(m0x + m2x);
195 double const dm2y = m1y - 0.5*(m0y + m2y);
196 double const dx = m1x + dy0*(smx - dy0*dm2x);
197 double const dy = m1y + dx0*(smy - dx0*dm2y);
198 double const dx4 = m1x + dy*(smx - dy*dm2x);
199 double const dy4 = m1y + dx*(smy - dx*dm2y);
203 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
204 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
209 float tauX2 = sigmaX2;
210 float tauY2 = sigmaY2;
211 tauX2 -= smoothingSigma*smoothingSigma;
212 tauY2 -= smoothingSigma*smoothingSigma;
214 if (tauX2 <= smoothingSigma*smoothingSigma) {
215 tauX2 = smoothingSigma*smoothingSigma;
217 if (tauY2 <= smoothingSigma*smoothingSigma) {
218 tauY2 = smoothingSigma*smoothingSigma;
221 float const skyVar = (vim(-1, -1) + vim( 0, -1) + vim( 1, -1) +
222 vim(-1, 0) + vim( 1, 0) +
223 vim(-1, 1) + vim( 0, 1) + vim( 1, 1))/8.0;
224 float const sourceVar = vim(0, 0);
225 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
230 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
231 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
237 template<
typename MaskedImageXy_locatorT>
238 void doMeasureCentroidImpl(
double *xCenter,
242 double *sizeX2,
double *sizeY2,
244 MaskedImageXy_locatorT mim,
245 double smoothingSigma,
247 FlagHandler flagHandler
253 double const d2x = 2*mim.image(0, 0) - mim.image(-1, 0) - mim.image(1, 0);
254 double const d2y = 2*mim.image(0, 0) - mim.image( 0, -1) - mim.image(0, 1);
255 double const sx = 0.5*(mim.image(1, 0) - mim.image(-1, 0));
256 double const sy = 0.5*(mim.image(0, 1) - mim.image( 0, -1));
258 if (d2x == 0.0 || d2y == 0.0) {
265 if ((!negative && (d2x < 0.0 || d2y < 0.0)) ||
266 ( negative && (d2x > 0.0 || d2y > 0.0))) {
270 (
boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
275 double const dx0 = sx/d2x;
276 double const dy0 = sy/d2y;
278 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
282 (
boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
287 double vpk = mim.image(0, 0) + 0.5*(sx*dx0 + sy*dy0);
294 float m0x = 0, m1x = 0, m2x = 0;
295 float m0y = 0, m1y = 0, m2y = 0;
298 quarticBad += inter4(mim.image(-1, -1), mim.image( 0, -1), mim.image( 1, -1), &m0x, negative);
299 quarticBad += inter4(mim.image(-1, 0), mim.image( 0, 0), mim.image( 1, 0), &m1x, negative);
300 quarticBad += inter4(mim.image(-1, 1), mim.image( 0, 1), mim.image( 1, 1), &m2x, negative);
302 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y, negative);
303 quarticBad += inter4(mim.image( 0, -1), mim.image( 0, 0), mim.image( 0, 1), &m1y, negative);
304 quarticBad += inter4(mim.image( 1, -1), mim.image( 1, 0), mim.image( 1, 1), &m2y, negative);
307 double sigmaX2, sigmaY2;
315 double const smx = 0.5*(m2x - m0x);
316 double const smy = 0.5*(m2y - m0y);
317 double const dm2x = m1x - 0.5*(m0x + m2x);
318 double const dm2y = m1y - 0.5*(m0y + m2y);
319 double const dx = m1x + dy0*(smx - dy0*dm2x);
320 double const dy = m1y + dx0*(smy - dx0*dm2y);
321 double const dx4 = m1x + dy*(smx - dy*dm2x);
322 double const dy4 = m1y + dx*(smy - dx*dm2y);
326 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
327 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
332 float tauX2 = sigmaX2;
333 float tauY2 = sigmaY2;
334 tauX2 -= smoothingSigma*smoothingSigma;
335 tauY2 -= smoothingSigma*smoothingSigma;
337 if (tauX2 <= smoothingSigma*smoothingSigma) {
338 tauX2 = smoothingSigma*smoothingSigma;
340 if (tauY2 <= smoothingSigma*smoothingSigma) {
341 tauY2 = smoothingSigma*smoothingSigma;
344 float const skyVar = (mim.variance(-1, -1) + mim.variance( 0, -1) + mim.variance( 1, -1) +
345 mim.variance(-1, 0) + mim.variance( 1, 0) +
346 mim.variance(-1, 1) + mim.variance( 0, 1) + mim.variance( 1, 1)
348 float const sourceVar = mim.variance(0, 0);
349 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
354 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
355 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
363 template<
typename MaskedImageT>
364 std::pair<MaskedImageT, double>
366 int const x,
const int y,
367 MaskedImageT
const& mimage,
369 FlagHandler _flagHandler)
375 double const nEffective = psf->computeEffectiveArea();
377 double const nEffective = 4*M_PI*smoothingSigma*smoothingSigma;
381 int const kWidth = kernel->getWidth();
382 int const kHeight = kernel->getHeight();
388 PTR(MaskedImageT) subImage;
391 }
catch (pex::exceptions::LengthError & err) {
398 PTR(MaskedImageT) binnedImage = lsst::afw::math::
binImage(*subImage, binX, binY, lsst::afw::math::
MEAN);
399 binnedImage->setXY0(subImage->getXY0());
401 MaskedImageT smoothedImage = MaskedImageT(*binnedImage, true);
402 assert(smoothedImage.getWidth()/2 == kWidth/2 + 2);
403 assert(smoothedImage.getHeight()/2 == kHeight/2 + 2);
405 lsst::afw::math::
convolve(smoothedImage, *binnedImage, *kernel, lsst::afw::math::ConvolutionControl());
406 *smoothedImage.getVariance() *= binX*binY*nEffective;
409 return std::make_pair(smoothedImage, smoothingSigma);
412 std::array<FlagDefinition,SdssCentroidAlgorithm::N_FLAGS> const &
getFlagDefinitions() {
413 static std::array<FlagDefinition,SdssCentroidAlgorithm::N_FLAGS>
const flagDefs = {{
414 {
"flag",
"general failure flag, set if anything went wrong"},
415 {
"flag_edge",
"Object too close to edge"},
416 {
"flag_noSecondDerivative",
"Vanishing second derivative"},
417 {
"flag_almostNoSecondDerivative",
"Almost vanishing second derivative"},
418 {
"flag_notAtMaximum",
"Object is not at a maximum"}
427 std::string
const &
name,
435 _centroidExtractor(schema, name, true),
436 _centroidChecker(schema, name, ctrl.doFootprintCheck, ctrl.maxDistToPeak)
447 result.
x = center.getX();
448 result.
y = center.getY();
452 typedef MaskedImageT::Image ImageT;
453 typedef MaskedImageT::Variance VarianceT;
454 bool negative =
false;
456 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"flags_negative").key);
460 ImageT
const&
image = *mimage.getImage();
469 _flagHandler.getDefinition(
EDGE).doc,
479 "SdssCentroid algorithm requires a Psf with every exposure"
485 double xc=0., yc=0., dxc=0., dyc=0.;
486 for(
int binsize = 1; binsize <=
_ctrl.
binmax; binsize *= 2) {
487 std::pair<MaskedImageT, double> result = smoothAndBinImage(psf, x, y, mimage, binX, binY, _flagHandler);
488 MaskedImageT
const smoothedImage = result.first;
489 double const smoothingSigma = result.second;
491 MaskedImageT::xy_locator mim = smoothedImage.xy_at(smoothedImage.getWidth()/2,
492 smoothedImage.getHeight()/2);
494 double sizeX2, sizeY2;
497 doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim,
498 smoothingSigma, negative, _flagHandler);
502 xc = (xc + 0.5)*binX - 0.5;
506 yc = (yc + 0.5)*binY - 0.5;
514 double const fac =
_ctrl.
wfac*(1 + smoothingSigma*smoothingSigma);
515 double const facX2 = fac*binX*binX;
516 double const facY2 = fac*binY*binY;
518 if (sizeX2 < facX2 && ::pow(xc - x, 2) < facX2 &&
519 sizeY2 < facY2 && ::pow(yc - y, 2) < facY2) {
525 if (sizeX2 >= facX2 || ::pow(xc - x, 2) >= facX2) {
528 if (sizeY2 >= facY2 || ::pow(yc - y, 2) >= facY2) {
535 result.
xSigma = sqrt(dxc*dxc);
536 result.
ySigma = sqrt(dyc*dyc);
543 _flagHandler.handleFailure(measRecord, error);
548 std::string
const &
name,
554 mapper.
addMapping(mapper.getInputSchema().find<afw::table::Flag>(
555 mapper.getInputSchema().join(
name, flag->name)).key);
An ellipse core with quadrupole moments as parameters.
Defines the fields and offsets for a table.
SdssCentroidAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
ErrElement ySigma
1-Sigma uncertainty on y (sqrt of variance)
double indexToPosition(double ind)
Convert image index to image position.
table::Key< std::string > name
afw::table::Schema schema
A mapping between the keys of two Schemas, used to copy data between them.
Include files required for standard LSST Exception handling.
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.
std::array< FlagDefinition, BlendednessAlgorithm::N_FLAGS > const & getFlagDefinitions()
int binmax
"maximum allowed binning" ;
Exception to be thrown when a measurement algorithm experiences a known failure mode.
CentroidResultKey _centroidKey
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
A coordinate class intended to represent absolute positions.
An integer coordinate rectangle.
afw::table::Key< double > sigma
table::Key< table::Array< Kernel::Pixel > > image
boost::shared_ptr< Kernel const > ConstPtr
Utility class for handling flag fields that indicate the failure modes of an algorithm.
CentroidChecker _centroidChecker
MaskedImageT getMaskedImage()
Return the MaskedImage.
SafeCentroidExtractor _centroidExtractor
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Convolve and convolveAtAPoint functions for Image and Kernel.
double peakMin
"if the peak's less than this insist on binning at least once" ;
boost::shared_ptr< lsst::afw::detection::Psf > getPsf()
Return the Exposure's Psf object.
#define LSST_EXCEPT(type,...)
Create an exception with a given type and message and optionally other arguments (dependent on the ty...
double wfac
"fiddle factor for adjusting the binning" ;
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...
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Called to measure a single child source in an image.
Schema getSchema() const
Return the Schema that holds this record's fields and keys.
double getDeterminantRadius() const
Return the radius defined as the 4th root of the determinant of the quadrupole matrix.
A FunctorKey for CentroidResult.
boost::shared_ptr< math::Kernel const > getLocalKernel(geom::Point2D position=makeNullPoint(), image::Color color=image::Color()) const
Return a FixedKernel corresponding to the Psf image at the given point.
geom::ellipses::Quadrupole computeShape(geom::Point2D position=makeNullPoint(), image::Color color=image::Color()) const
Compute the ellipse corresponding to the second moments of the Psf.
CentroidElement y
y (row) coordinate of the measured position
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
Record class that contains measurements made on a single exposure.
double const PI
The ratio of a circle's circumference to diameter.
This implements the SdssCentroid algorithm within the meas_base measurement framework.
#define CONST_PTR(...)
A shared pointer to a const object.
A polymorphic base class for representing an image's Point Spread Function.
afw::table::Key< double > sigma2
virtual void fail(afw::table::SourceRecord &measRecord, MeasurementError *error=NULL) const
Handle an exception thrown by the current algorithm by setting flags in the given record...
boost::shared_ptr< ImageT > binImage(ImageT const &in, int const binsize, lsst::afw::math::Property const flags)
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.