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.
CentroidChecker _centroidChecker
CentroidElement y
y (row) coordinate of the measured position
double indexToPosition(double ind)
Convert image index to image position.
table::Key< std::string > name
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.
ErrElement ySigma
1-Sigma uncertainty on y (sqrt of variance)
double peakMin
"if the peak's less than this insist on binning at least once" ;
int binmax
"maximum allowed binning" ;
afw::table::Schema schema
A mapping between the keys of two Schemas, used to copy data between them.
A reusable struct for centroid measurements.
virtual void fail(afw::table::SourceRecord &measRecord, MeasurementError *error=NULL) const
A C++ control class to handle SdssCentroidAlgorithm's configuration.
std::array< FlagDefinition, BlendednessAlgorithm::N_FLAGS > const & getFlagDefinitions()
Exception to be thrown when a measurement algorithm experiences a known failure mode.
double getDeterminantRadius() const
Return the radius defined as the 4th root of the determinant of the quadrupole matrix.
double wfac
"fiddle factor for adjusting the binning" ;
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
double const PI
The ratio of a circle's circumference to diameter.
MaskedImageT getMaskedImage()
Return the MaskedImage.
CentroidResultKey _centroidKey
ErrElement xSigma
1-Sigma uncertainty on x (sqrt of variance)
SafeCentroidExtractor _centroidExtractor
Convolve and convolveAtAPoint functions for Image and Kernel.
boost::shared_ptr< lsst::afw::detection::Psf > getPsf()
Return the Exposure's Psf object.
#define LSST_EXCEPT(type,...)
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...
CentroidElement x
x (column) coordinate of the measured position
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.
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Record class that contains measurements made on a single exposure.
SdssCentroidAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
Include files required for standard LSST Exception handling.
A polymorphic base class for representing an image's Point Spread Function.
Only the diagonal elements of the covariance matrix are provided.
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
afw::table::Key< double > sigma2
boost::shared_ptr< ImageT > binImage(ImageT const &in, int const binsize, lsst::afw::math::Property const flags)
Schema getSchema() const
Return the Schema that holds this record's fields and keys.