37 namespace lsst {
namespace meas {
namespace base {
43 float const AMPAST4 = 1.33;
52 static int inter4(
float vm,
float v0,
float vp,
float *cen) {
53 float const sp = v0 - vp;
54 float const sm = v0 - vm;
55 float const d2 = sp + sm;
56 float const s = 0.5*(vp - vm);
58 if (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,
125 FlagHandler flagHandler
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,
246 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) {
264 if (d2x < 0.0 || d2y < 0.0) {
268 (
boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
273 double const dx0 = sx/d2x;
274 double const dy0 = sy/d2y;
276 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
280 (
boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
285 double vpk = mim.image(0, 0) + 0.5*(sx*dx0 + sy*dy0);
292 float m0x = 0, m1x = 0, m2x = 0;
293 float m0y = 0, m1y = 0, m2y = 0;
296 quarticBad += inter4(mim.image(-1, -1), mim.image( 0, -1), mim.image( 1, -1), &m0x);
297 quarticBad += inter4(mim.image(-1, 0), mim.image( 0, 0), mim.image( 1, 0), &m1x);
298 quarticBad += inter4(mim.image(-1, 1), mim.image( 0, 1), mim.image( 1, 1), &m2x);
300 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y);
301 quarticBad += inter4(mim.image( 0, -1), mim.image( 0, 0), mim.image( 0, 1), &m1y);
302 quarticBad += inter4(mim.image( 1, -1), mim.image( 1, 0), mim.image( 1, 1), &m2y);
305 double sigmaX2, sigmaY2;
313 double const smx = 0.5*(m2x - m0x);
314 double const smy = 0.5*(m2y - m0y);
315 double const dm2x = m1x - 0.5*(m0x + m2x);
316 double const dm2y = m1y - 0.5*(m0y + m2y);
317 double const dx = m1x + dy0*(smx - dy0*dm2x);
318 double const dy = m1y + dx0*(smy - dx0*dm2y);
319 double const dx4 = m1x + dy*(smx - dy*dm2x);
320 double const dy4 = m1y + dx*(smy - dx*dm2y);
324 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
325 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
330 float tauX2 = sigmaX2;
331 float tauY2 = sigmaY2;
332 tauX2 -= smoothingSigma*smoothingSigma;
333 tauY2 -= smoothingSigma*smoothingSigma;
335 if (tauX2 <= smoothingSigma*smoothingSigma) {
336 tauX2 = smoothingSigma*smoothingSigma;
338 if (tauY2 <= smoothingSigma*smoothingSigma) {
339 tauY2 = smoothingSigma*smoothingSigma;
342 float const skyVar = (mim.variance(-1, -1) + mim.variance( 0, -1) + mim.variance( 1, -1) +
343 mim.variance(-1, 0) + mim.variance( 1, 0) +
344 mim.variance(-1, 1) + mim.variance( 0, 1) + mim.variance( 1, 1)
346 float const sourceVar = mim.variance(0, 0);
347 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
352 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
353 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
361 template<
typename MaskedImageT>
362 std::pair<MaskedImageT, double>
364 int const x,
const int y,
365 MaskedImageT
const& mimage,
367 FlagHandler _flagHandler)
373 double const nEffective = psf->computeEffectiveArea();
375 double const nEffective = 4*M_PI*smoothingSigma*smoothingSigma;
379 int const kWidth = kernel->getWidth();
380 int const kHeight = kernel->getHeight();
386 PTR(MaskedImageT) subImage;
389 }
catch (pex::exceptions::LengthError & err) {
396 PTR(MaskedImageT) binnedImage = lsst::afw::math::
binImage(*subImage, binX, binY, lsst::afw::math::
MEAN);
397 binnedImage->setXY0(subImage->getXY0());
399 MaskedImageT smoothedImage = MaskedImageT(*binnedImage, true);
400 assert(smoothedImage.getWidth()/2 == kWidth/2 + 2);
401 assert(smoothedImage.getHeight()/2 == kHeight/2 + 2);
403 lsst::afw::math::
convolve(smoothedImage, *binnedImage, *kernel, lsst::afw::math::ConvolutionControl());
404 *smoothedImage.getVariance() *= binX*binY*nEffective;
407 return std::make_pair(smoothedImage, smoothingSigma);
410 boost::array<FlagDefinition,SdssCentroidAlgorithm::N_FLAGS> const & getFlagDefinitions() {
411 static boost::array<FlagDefinition,SdssCentroidAlgorithm::N_FLAGS>
const flagDefs = {{
412 {
"flag",
"general failure flag, set if anything went wrong"},
413 {
"flag_edge",
"Object too close to edge"},
414 {
"flag_noSecondDerivative",
"Vanishing second derivative"},
415 {
"flag_almostNoSecondDerivative",
"Almost vanishing second derivative"},
416 {
"flag_notAtMaximum",
"Object is not at a maximum"}
425 std::string
const &
name,
431 _centroidExtractor(schema, name, true)
434 getFlagDefinitions().begin(), getFlagDefinitions().end());
444 result.
x = center.getX();
445 result.
y = center.getY();
449 typedef MaskedImageT::Image ImageT;
450 typedef MaskedImageT::Variance VarianceT;
453 ImageT
const&
image = *mimage.getImage();
462 _flagHandler.getDefinition(
EDGE).doc,
472 "SdssCentroid algorithm requires a Psf with every exposure"
478 double xc=0., yc=0., dxc=0., dyc=0.;
479 for(
int binsize = 1; binsize <=
_ctrl.
binmax; binsize *= 2) {
480 std::pair<MaskedImageT, double> result = smoothAndBinImage(psf, x, y, mimage, binX, binY, _flagHandler);
481 MaskedImageT
const smoothedImage = result.first;
482 double const smoothingSigma = result.second;
484 MaskedImageT::xy_locator mim = smoothedImage.xy_at(smoothedImage.getWidth()/2,
485 smoothedImage.getHeight()/2);
487 double sizeX2, sizeY2;
490 doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim,
491 smoothingSigma, _flagHandler);
495 xc = (xc + 0.5)*binX - 0.5;
499 yc = (yc + 0.5)*binY - 0.5;
507 double const fac =
_ctrl.
wfac*(1 + smoothingSigma*smoothingSigma);
508 double const facX2 = fac*binX*binX;
509 double const facY2 = fac*binY*binY;
511 if (sizeX2 < facX2 && ::pow(xc - x, 2) < facX2 &&
512 sizeY2 < facY2 && ::pow(yc - y, 2) < facY2) {
518 if (sizeX2 >= facX2 || ::pow(xc - x, 2) >= facX2) {
521 if (sizeY2 >= facY2 || ::pow(yc - y, 2) >= facY2) {
528 result.
xSigma = sqrt(dxc*dxc);
529 result.
ySigma = sqrt(dyc*dyc);
531 _flagHandler.setValue(measRecord,
FAILURE,
false);
537 _flagHandler.handleFailure(measRecord, error);
542 std::string
const &
name,
547 for (
auto flag = getFlagDefinitions().begin() + 1; flag < getFlagDefinitions().end(); ++flag) {
548 mapper.
addMapping(mapper.getInputSchema().find<afw::table::Flag>(
549 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
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.
Eigen matrix objects that present a view into an ndarray::Array.
boost::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
afw::table::Schema schema
Include files required for standard LSST Exception handling.
A mapping between the keys of two Schemas, used to copy data between them.
Only the diagonal elements of the covariance matrix are provided.
A reusable struct for centroid measurements.
definition of the Trace messaging facilities
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.
int binmax
"maximum allowed binning" ;
Exception to be thrown when a measurement algorithm experiences a known failure mode.
CentroidResultKey _centroidKey
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.
SafeCentroidExtractor _centroidExtractor
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.
double getDeterminantRadius() const
Return the radius defined as the 4th root of the determinant of the quadrupole matrix.
#define LSST_EXCEPT(type,...)
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
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.
CentroidElement y
y (row) coordinate of the measured position
Record class that contains measurements made on a single exposure.
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
static FlagHandler addFields(afw::table::Schema &schema, std::string const &prefix, FlagDefinition const *begin, FlagDefinition const *end)