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;
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
130 double const d2x = 2*im(0, 0) - im(-1, 0) - im(1, 0);
131 double const d2y = 2*im(0, 0) - im( 0, -1) - im(0, 1);
132 double const sx = 0.5*(im(1, 0) - im(-1, 0));
133 double const sy = 0.5*(im(0, 1) - im( 0, -1));
135 if (d2x == 0.0 || d2y == 0.0) {
136 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
"Object has a vanishing 2nd derivative");
138 if (d2x < 0.0 || d2y < 0.0) {
139 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
140 (
boost::format(
"Object is not at a maximum: d2I/dx2, d2I/dy2 = %g %g")
144 double const dx0 = sx/d2x;
145 double const dy0 = sy/d2y;
147 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
148 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
149 (
boost::format(
"Object has an almost vanishing 2nd derivative:"
150 " sx, d2x, sy, d2y = %f %f %f %f")
151 % sx % d2x % sy % d2y).str());
154 double vpk = im(0, 0) + 0.5*(sx*dx0 + sy*dy0);
161 float m0x = 0, m1x = 0, m2x = 0;
162 float m0y = 0, m1y = 0, m2y = 0;
165 quarticBad += inter4(im(-1, -1), im( 0, -1), im( 1, -1), &m0x);
166 quarticBad += inter4(im(-1, 0), im( 0, 0), im( 1, 0), &m1x);
167 quarticBad += inter4(im(-1, 1), im( 0, 1), im( 1, 1), &m2x);
169 quarticBad += inter4(im(-1, -1), im(-1, 0), im(-1, 1), &m0y);
170 quarticBad += inter4(im( 0, -1), im( 0, 0), im( 0, 1), &m1y);
171 quarticBad += inter4(im( 1, -1), im( 1, 0), im( 1, 1), &m2y);
174 double sigmaX2, sigmaY2;
182 double const smx = 0.5*(m2x - m0x);
183 double const smy = 0.5*(m2y - m0y);
184 double const dm2x = m1x - 0.5*(m0x + m2x);
185 double const dm2y = m1y - 0.5*(m0y + m2y);
186 double const dx = m1x + dy0*(smx - dy0*dm2x);
187 double const dy = m1y + dx0*(smy - dx0*dm2y);
188 double const dx4 = m1x + dy*(smx - dy*dm2x);
189 double const dy4 = m1y + dx*(smy - dx*dm2y);
193 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
194 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
199 float tauX2 = sigmaX2;
200 float tauY2 = sigmaY2;
201 tauX2 -= smoothingSigma*smoothingSigma;
202 tauY2 -= smoothingSigma*smoothingSigma;
204 if (tauX2 <= smoothingSigma*smoothingSigma) {
205 tauX2 = smoothingSigma*smoothingSigma;
207 if (tauY2 <= smoothingSigma*smoothingSigma) {
208 tauY2 = smoothingSigma*smoothingSigma;
211 float const skyVar = (vim(-1, -1) + vim( 0, -1) + vim( 1, -1) +
212 vim(-1, 0) + vim( 1, 0) +
213 vim(-1, 1) + vim( 0, 1) + vim( 1, 1))/8.0;
214 float const sourceVar = vim(0, 0);
215 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
220 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
221 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
227 template<
typename MaskedImageXy_locatorT>
228 void doMeasureCentroidImpl(
double *xCenter,
232 double *sizeX2,
double *sizeY2,
234 MaskedImageXy_locatorT mim,
235 double smoothingSigma
241 double const d2x = 2*mim.image(0, 0) - mim.image(-1, 0) - mim.image(1, 0);
242 double const d2y = 2*mim.image(0, 0) - mim.image( 0, -1) - mim.image(0, 1);
243 double const sx = 0.5*(mim.image(1, 0) - mim.image(-1, 0));
244 double const sy = 0.5*(mim.image(0, 1) - mim.image( 0, -1));
246 if (d2x == 0.0 || d2y == 0.0) {
247 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
"Object has a vanishing 2nd derivative");
249 if (d2x < 0.0 || d2y < 0.0) {
250 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
251 (
boost::format(
"Object is not at a maximum: d2I/dx2, d2I/dy2 = %g %g")
255 double const dx0 = sx/d2x;
256 double const dy0 = sy/d2y;
258 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
259 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
260 (
boost::format(
"Object has an almost vanishing 2nd derivative:"
261 " sx, d2x, sy, d2y = %f %f %f %f")
262 % sx % d2x % sy % d2y).str());
265 double vpk = mim.image(0, 0) + 0.5*(sx*dx0 + sy*dy0);
272 float m0x = 0, m1x = 0, m2x = 0;
273 float m0y = 0, m1y = 0, m2y = 0;
276 quarticBad += inter4(mim.image(-1, -1), mim.image( 0, -1), mim.image( 1, -1), &m0x);
277 quarticBad += inter4(mim.image(-1, 0), mim.image( 0, 0), mim.image( 1, 0), &m1x);
278 quarticBad += inter4(mim.image(-1, 1), mim.image( 0, 1), mim.image( 1, 1), &m2x);
280 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y);
281 quarticBad += inter4(mim.image( 0, -1), mim.image( 0, 0), mim.image( 0, 1), &m1y);
282 quarticBad += inter4(mim.image( 1, -1), mim.image( 1, 0), mim.image( 1, 1), &m2y);
285 double sigmaX2, sigmaY2;
293 double const smx = 0.5*(m2x - m0x);
294 double const smy = 0.5*(m2y - m0y);
295 double const dm2x = m1x - 0.5*(m0x + m2x);
296 double const dm2y = m1y - 0.5*(m0y + m2y);
297 double const dx = m1x + dy0*(smx - dy0*dm2x);
298 double const dy = m1y + dx0*(smy - dx0*dm2y);
299 double const dx4 = m1x + dy*(smx - dy*dm2x);
300 double const dy4 = m1y + dx*(smy - dx*dm2y);
304 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
305 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
310 float tauX2 = sigmaX2;
311 float tauY2 = sigmaY2;
312 tauX2 -= smoothingSigma*smoothingSigma;
313 tauY2 -= smoothingSigma*smoothingSigma;
315 if (tauX2 <= smoothingSigma*smoothingSigma) {
316 tauX2 = smoothingSigma*smoothingSigma;
318 if (tauY2 <= smoothingSigma*smoothingSigma) {
319 tauY2 = smoothingSigma*smoothingSigma;
322 float const skyVar = (mim.variance(-1, -1) + mim.variance( 0, -1) + mim.variance( 1, -1) +
323 mim.variance(-1, 0) + mim.variance( 1, 0) +
324 mim.variance(-1, 1) + mim.variance( 0, 1) + mim.variance( 1, 1)
326 float const sourceVar = mim.variance(0, 0);
327 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
332 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
333 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
341 template<
typename MaskedImageT>
342 std::pair<MaskedImageT, double>
344 int const x,
const int y,
345 MaskedImageT
const& mimage,
347 FlagHandler _flagHandler)
353 double const nEffective = psf->computeEffectiveArea();
355 double const nEffective = 4*M_PI*smoothingSigma*smoothingSigma;
359 int const kWidth = kernel->getWidth();
360 int const kHeight = kernel->getHeight();
366 PTR(MaskedImageT) subImage;
369 }
catch (pex::exceptions::LengthError & err) {
376 PTR(MaskedImageT) binnedImage = lsst::afw::math::
binImage(*subImage, binX, binY, lsst::afw::math::
MEAN);
377 binnedImage->setXY0(subImage->getXY0());
379 MaskedImageT smoothedImage = MaskedImageT(*binnedImage, true);
380 assert(smoothedImage.getWidth()/2 == kWidth/2 + 2);
381 assert(smoothedImage.getHeight()/2 == kHeight/2 + 2);
383 lsst::afw::math::
convolve(smoothedImage, *binnedImage, *kernel, lsst::afw::math::ConvolutionControl());
384 *smoothedImage.getVariance() *= binX*binY*nEffective;
387 return std::make_pair(smoothedImage, smoothingSigma);
394 std::
string const & name,
395 afw::table::Schema &
schema
400 _centroidExtractor(schema, name, true)
402 static boost::array<FlagDefinition,N_FLAGS>
const flagDefs = {{
403 {
"flag",
"general failure flag, set if anything went wrong"},
404 {
"flag_edge",
"Object too close to edge"},
405 {
"flag_badData",
"Algorithm could not measure this data"}
417 result.
x = center.getX();
418 result.
y = center.getY();
422 typedef MaskedImageT::Image ImageT;
423 typedef MaskedImageT::Variance VarianceT;
426 ImageT
const&
image = *mimage.getImage();
435 _flagHandler.getDefinition(
EDGE).doc,
447 "SdssCentroid algorithm requires a Psf with every exposure"
453 double xc=0., yc=0., dxc=0., dyc=0.;
454 for(
int binsize = 1; binsize <=
_ctrl.
binmax; binsize *= 2) {
455 std::pair<MaskedImageT, double> result = smoothAndBinImage(psf, x, y, mimage, binX, binY, _flagHandler);
456 MaskedImageT
const smoothedImage = result.first;
457 double const smoothingSigma = result.second;
459 MaskedImageT::xy_locator mim = smoothedImage.xy_at(smoothedImage.getWidth()/2,
460 smoothedImage.getHeight()/2);
463 double sizeX2, sizeY2;
466 doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim, smoothingSigma);
470 xc = (xc + 0.5)*binX - 0.5;
474 yc = (yc + 0.5)*binY - 0.5;
482 double const fac =
_ctrl.
wfac*(1 + smoothingSigma*smoothingSigma);
483 double const facX2 = fac*binX*binX;
484 double const facY2 = fac*binY*binY;
486 if (sizeX2 < facX2 && ::pow(xc - x, 2) < facX2 &&
487 sizeY2 < facY2 && ::pow(yc - y, 2) < facY2) {
493 if (sizeX2 >= facX2 || ::pow(xc - x, 2) >= facX2) {
496 if (sizeY2 >= facY2 || ::pow(yc - y, 2) >= facY2) {
503 _flagHandler.getDefinition(
BAD_DATA).doc,
511 result.
xSigma = sqrt(dxc*dxc);
512 result.
ySigma = sqrt(dyc*dyc);
514 _flagHandler.setValue(measRecord,
FAILURE,
false);
520 _flagHandler.handleFailure(measRecord, error);
An ellipse core with quadrupole moments as parameters.
The Sdss Centroid Algorithm.
ErrElement ySigma
1-Sigma uncertainty on y (sqrt of variance)
double indexToPosition(double ind)
Convert image index to image position.
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)
A class to contain the data, WCS, and other information needed to describe an image of the sky...
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
Include files required for standard LSST Exception handling.
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.
afw::table::Centroid::MeasKey _centroidKey
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)