26 #include "ndarray/eigen.h" 36 namespace lsst {
namespace meas {
namespace base {
38 FlagDefinitionList flagDefinitions;
48 return flagDefinitions;
56 float const AMPAST4 = 1.33;
65 static int inter4(
float vm,
float v0,
float vp,
float *cen,
bool negative=
false) {
66 float const sp = v0 - vp;
67 float const sm = v0 - vm;
68 float const d2 = sp + sm;
69 float const s = 0.5*(vp - vm);
71 if ((!negative && (d2 <= 0.0f || v0 <= 0.0f)) ||
72 ( negative && (d2 >= 0.0f || v0 >= 0.0f))) {
76 *cen = s/d2*(1.0 + AMPAST4*sp*sm/(d2*v0));
78 return fabs(*cen) < 1 ? 0 : 1;
85 float astrom_errors(
float skyVar,
95 double const k = quarticBad ? 0 : AMPAST4;
96 double const sigma2 = sigma*sigma;
109 sVar += 0.5*sourceVar*exp(-1/(2*tau2));
110 dVar += sourceVar*(4*exp(-1/(2*tau2)) + 2*exp(-1/(2*tau2)));
116 dVar += sourceVar/(3*
lsst::afw::geom::PI*sigma2)*(2 - 3*exp(-1/(3*sigma2)) + exp(-1/sigma2));
119 xVar = sVar*pow(1/d + k/(4*As)*(1 - 12*s*s/(d*d)), 2) +
120 dVar*pow(s/(d*d) - k/(4*As)*8*s*s/(d*d*d), 2);
122 return(xVar >= 0 ? sqrt(xVar) : NAN);
130 template<
typename ImageXy_locatorT,
typename VarImageXy_locatorT>
131 void doMeasureCentroidImpl(
double *xCenter,
135 double *sizeX2,
double *sizeY2,
137 VarImageXy_locatorT vim,
138 double smoothingSigma,
145 double const d2x = 2*im(0, 0) - im(-1, 0) - im(1, 0);
146 double const d2y = 2*im(0, 0) - im( 0, -1) - im(0, 1);
147 double const sx = 0.5*(im(1, 0) - im(-1, 0));
148 double const sy = 0.5*(im(0, 1) - im( 0, -1));
150 if (d2x == 0.0 || d2y == 0.0) {
153 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.doc,
154 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.number
157 if (d2x < 0.0 || d2y < 0.0) {
160 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.doc +
161 (boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
162 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.number
166 double const dx0 = sx/d2x;
167 double const dy0 = sy/d2y;
169 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
172 SdssCentroidAlgorithm::ALMOST_NO_SECOND_DERIVATIVE.doc +
173 (boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
174 SdssCentroidAlgorithm::ALMOST_NO_SECOND_DERIVATIVE.number
178 double vpk = im(0, 0) + 0.5*(sx*dx0 + sy*dy0);
185 float m0x = 0, m1x = 0, m2x = 0;
186 float m0y = 0, m1y = 0, m2y = 0;
189 quarticBad += inter4(im(-1, -1), im( 0, -1), im( 1, -1), &m0x);
190 quarticBad += inter4(im(-1, 0), im( 0, 0), im( 1, 0), &m1x);
191 quarticBad += inter4(im(-1, 1), im( 0, 1), im( 1, 1), &m2x);
193 quarticBad += inter4(im(-1, -1), im(-1, 0), im(-1, 1), &m0y);
194 quarticBad += inter4(im( 0, -1), im( 0, 0), im( 0, 1), &m1y);
195 quarticBad += inter4(im( 1, -1), im( 1, 0), im( 1, 1), &m2y);
198 double sigmaX2, sigmaY2;
206 double const smx = 0.5*(m2x - m0x);
207 double const smy = 0.5*(m2y - m0y);
208 double const dm2x = m1x - 0.5*(m0x + m2x);
209 double const dm2y = m1y - 0.5*(m0y + m2y);
210 double const dx = m1x + dy0*(smx - dy0*dm2x);
211 double const dy = m1y + dx0*(smy - dx0*dm2y);
212 double const dx4 = m1x + dy*(smx - dy*dm2x);
213 double const dy4 = m1y + dx*(smy - dx*dm2y);
217 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
218 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
223 float tauX2 = sigmaX2;
224 float tauY2 = sigmaY2;
225 tauX2 -= smoothingSigma*smoothingSigma;
226 tauY2 -= smoothingSigma*smoothingSigma;
228 if (tauX2 <= smoothingSigma*smoothingSigma) {
229 tauX2 = smoothingSigma*smoothingSigma;
231 if (tauY2 <= smoothingSigma*smoothingSigma) {
232 tauY2 = smoothingSigma*smoothingSigma;
235 float const skyVar = (vim(-1, -1) + vim( 0, -1) + vim( 1, -1) +
236 vim(-1, 0) + vim( 1, 0) +
237 vim(-1, 1) + vim( 0, 1) + vim( 1, 1))/8.0;
238 float const sourceVar = vim(0, 0);
239 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
244 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
245 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
251 template<
typename MaskedImageXy_locatorT>
252 void doMeasureCentroidImpl(
double *xCenter,
256 double *sizeX2,
double *sizeY2,
258 MaskedImageXy_locatorT mim,
259 double smoothingSigma,
267 double const d2x = 2*mim.image(0, 0) - mim.image(-1, 0) - mim.image(1, 0);
268 double const d2y = 2*mim.image(0, 0) - mim.image( 0, -1) - mim.image(0, 1);
269 double const sx = 0.5*(mim.image(1, 0) - mim.image(-1, 0));
270 double const sy = 0.5*(mim.image(0, 1) - mim.image( 0, -1));
272 if (d2x == 0.0 || d2y == 0.0) {
275 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.doc,
276 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.number
279 if ((!negative && (d2x < 0.0 || d2y < 0.0)) ||
280 ( negative && (d2x > 0.0 || d2y > 0.0))) {
283 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.doc +
284 (boost::format(
": d2I/dx2, d2I/dy2 = %g %g") % d2x % d2y).str(),
285 SdssCentroidAlgorithm::NOT_AT_MAXIMUM.number
289 double const dx0 = sx/d2x;
290 double const dy0 = sy/d2y;
292 if (fabs(dx0) > 10.0 || fabs(dy0) > 10.0) {
295 SdssCentroidAlgorithm::NO_SECOND_DERIVATIVE.doc +
296 (boost::format(
": sx, d2x, sy, d2y = %f %f %f %f") % sx % d2x % sy % d2y).str(),
297 SdssCentroidAlgorithm::ALMOST_NO_SECOND_DERIVATIVE.number
301 double vpk = mim.image(0, 0) + 0.5*(sx*dx0 + sy*dy0);
308 float m0x = 0, m1x = 0, m2x = 0;
309 float m0y = 0, m1y = 0, m2y = 0;
312 quarticBad += inter4(mim.image(-1, -1), mim.image( 0, -1), mim.image( 1, -1), &m0x, negative);
313 quarticBad += inter4(mim.image(-1, 0), mim.image( 0, 0), mim.image( 1, 0), &m1x, negative);
314 quarticBad += inter4(mim.image(-1, 1), mim.image( 0, 1), mim.image( 1, 1), &m2x, negative);
316 quarticBad += inter4(mim.image(-1, -1), mim.image(-1, 0), mim.image(-1, 1), &m0y, negative);
317 quarticBad += inter4(mim.image( 0, -1), mim.image( 0, 0), mim.image( 0, 1), &m1y, negative);
318 quarticBad += inter4(mim.image( 1, -1), mim.image( 1, 0), mim.image( 1, 1), &m2y, negative);
321 double sigmaX2, sigmaY2;
329 double const smx = 0.5*(m2x - m0x);
330 double const smy = 0.5*(m2y - m0y);
331 double const dm2x = m1x - 0.5*(m0x + m2x);
332 double const dm2y = m1y - 0.5*(m0y + m2y);
333 double const dx = m1x + dy0*(smx - dy0*dm2x);
334 double const dy = m1y + dx0*(smy - dx0*dm2y);
335 double const dx4 = m1x + dy*(smx - dy*dm2x);
336 double const dy4 = m1y + dx*(smy - dx*dm2y);
340 sigmaX2 = vpk/d2x - (1 + 6*dx0*dx0)/4;
341 sigmaY2 = vpk/d2y - (1 + 6*dy0*dy0)/4;
346 float tauX2 = sigmaX2;
347 float tauY2 = sigmaY2;
348 tauX2 -= smoothingSigma*smoothingSigma;
349 tauY2 -= smoothingSigma*smoothingSigma;
351 if (tauX2 <= smoothingSigma*smoothingSigma) {
352 tauX2 = smoothingSigma*smoothingSigma;
354 if (tauY2 <= smoothingSigma*smoothingSigma) {
355 tauY2 = smoothingSigma*smoothingSigma;
358 float const skyVar = (mim.variance(-1, -1) + mim.variance( 0, -1) + mim.variance( 1, -1) +
359 mim.variance(-1, 0) + mim.variance( 1, 0) +
360 mim.variance(-1, 1) + mim.variance( 0, 1) + mim.variance( 1, 1)
362 float const sourceVar = mim.variance(0, 0);
363 float const A = vpk*sqrt((sigmaX2/tauX2)*(sigmaY2/tauY2));
368 *dxc = astrom_errors(skyVar, sourceVar, A, tauX2, vpk, sx, d2x, fabs(smoothingSigma), quarticBad);
369 *dyc = astrom_errors(skyVar, sourceVar, A, tauY2, vpk, sy, d2y, fabs(smoothingSigma), quarticBad);
377 template<
typename MaskedImageT>
380 int const x,
const int y,
381 MaskedImageT
const& mimage,
389 double const nEffective = psf->computeEffectiveArea();
391 double const nEffective = 4*M_PI*smoothingSigma*smoothingSigma;
395 int const kWidth = kernel->getWidth();
396 int const kHeight = kernel->getHeight();
402 PTR(MaskedImageT) subImage;
408 SdssCentroidAlgorithm::EDGE.doc,
409 SdssCentroidAlgorithm::EDGE.number
413 binnedImage->setXY0(subImage->getXY0());
415 MaskedImageT smoothedImage = MaskedImageT(*binnedImage,
true);
416 assert(smoothedImage.getWidth()/2 == kWidth/2 + 2);
417 assert(smoothedImage.getHeight()/2 == kHeight/2 + 2);
420 *smoothedImage.getVariance() *= binX*binY*nEffective;
439 _centroidExtractor(schema, name, true),
440 _centroidChecker(schema, name, ctrl.doFootprintCheck, ctrl.maxDistToPeak)
451 result.
x = center.getX();
452 result.
y = center.getY();
453 measRecord.
set(_centroidKey, result);
456 typedef MaskedImageT::Image ImageT;
457 typedef MaskedImageT::Variance VarianceT;
458 bool negative =
false;
460 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"flags_negative").key);
461 }
catch(pexExcept::Exception &e) {}
464 ImageT
const&
image = *mimage.getImage();
483 "SdssCentroid algorithm requires a Psf with every exposure" 489 double xc=0., yc=0., dxc=0., dyc=0.;
490 for(
int binsize = 1; binsize <= _ctrl.
binmax; binsize *= 2) {
492 MaskedImageT
const smoothedImage = result.first;
493 double const smoothingSigma = result.second;
495 MaskedImageT::xy_locator mim = smoothedImage.xy_at(smoothedImage.getWidth()/2,
496 smoothedImage.getHeight()/2);
498 double sizeX2, sizeY2;
501 doMeasureCentroidImpl(&xc, &dxc, &yc, &dyc, &sizeX2, &sizeY2, &peakVal, mim,
502 smoothingSigma, negative, _flagHandler);
506 xc = (xc + 0.5)*binX - 0.5;
510 yc = (yc + 0.5)*binY - 0.5;
518 double const fac = _ctrl.
wfac*(1 + smoothingSigma*smoothingSigma);
519 double const facX2 = fac*binX*binX;
520 double const facY2 = fac*binY*binY;
522 if (sizeX2 < facX2 && ::pow(xc - x, 2) < facX2 &&
523 sizeY2 < facY2 && ::pow(yc - y, 2) < facY2) {
524 if (binsize > 1 || _ctrl.
peakMin < 0.0 || peakVal > _ctrl.
peakMin) {
529 if (sizeX2 >= facX2 || ::pow(xc - x, 2) >= facX2) {
532 if (sizeY2 >= facY2 || ::pow(yc - y, 2) >= facY2) {
539 result.
xSigma = sqrt(dxc*dxc);
540 result.
ySigma = sqrt(dyc*dyc);
541 measRecord.
set(_centroidKey, result);
542 _centroidChecker(measRecord);
559 if (flag == SdssCentroidAlgorithm::FAILURE)
continue;
560 if (mapper.getInputSchema().getNames().count(
561 mapper.getInputSchema().join(
name, flag.
name)) == 0)
continue;
564 mapper.addMapping(key);
std::size_t size() const
return the current size (number of defined elements) of the collection
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)
static FlagDefinitionList const & getFlagDefinitions()
Simple class used to define and document flags The name and doc constitute the identity of the FlagDe...
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.
int binmax
"maximum allowed binning" ;
SchemaItem< T > find(std::string const &name) const
static FlagDefinition const ALMOST_NO_SECOND_DERIVATIVE
Exception to be thrown when a measurement algorithm experiences a known failure mode.
static FlagDefinition const NOT_AT_MAXIMUM
Field< T >::Value get(Key< T > const &key) const
Exception to be thrown when a measurement algorithm experiences a fatal error.
afw::table::Key< double > sigma
Utility class for handling flag fields that indicate the failure modes of an algorithm.
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...
MaskedImageT getMaskedImage()
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
static FlagDefinition const EDGE
static FlagDefinition const NO_SECOND_DERIVATIVE
double peakMin
"if the peak's less than this insist on binning at least once" ;
#define LSST_EXCEPT(type,...)
double wfac
"fiddle factor for adjusting the binning" ;
geom::ellipses::Quadrupole computeShape(geom::Point2D position=makeNullPoint(), image::Color color=image::Color()) const
std::shared_ptr< lsst::afw::detection::Psf > getPsf()
void handleFailure(afw::table::BaseRecord &record, MeasurementError const *error=nullptr) const
Handle an expected or unexpected Exception thrown by a measurement algorithm.
A FunctorKey for CentroidResult.
void set(Key< T > const &key, U const &value)
CentroidElement y
y (row) coordinate of the measured position
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Called to measure a single child source in an image.
This implements the SdssCentroid algorithm within the meas_base measurement framework.
double getDeterminantRadius() const
vector-type utility class to build a collection of FlagDefinitions
static FlagDefinition const FAILURE
std::shared_ptr< math::Kernel const > getLocalKernel(geom::Point2D position=makeNullPoint(), image::Color color=image::Color()) const
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)