27 #include "boost/tuple/tuple.hpp"
40 namespace pexPolicy = lsst::pex::policy;
41 namespace pexExceptions = lsst::pex::exceptions;
42 namespace pexLogging = lsst::pex::logging;
43 namespace afwDet = lsst::afw::detection;
45 namespace afwGeom = lsst::afw::geom;
51 #define USE_APPROXIMATE_EXP 1
52 #if USE_APPROXIMATE_EXP
59 #if USE_APPROXIMATE_EXP
72 double sigma11_w,
double ,
double sigma22_w,
float maxRad);
87 calc_fisher(detail::SdssShapeImpl
const& shape,
91 float const A = shape.getI0();
92 float const sigma11W = shape.getIxx();
93 float const sigma12W = shape.getIxy();
94 float const sigma22W = shape.getIyy();
96 double const D = sigma11W*sigma22W - sigma12W*sigma12W;
98 if (D <= std::numeric_limits<double>::epsilon()) {
99 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
100 "Determinant is too small calculating Fisher matrix");
105 if (bkgd_var <= 0.0) {
106 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
107 (
boost::format(
"Background variance must be positive (saw %g)") % bkgd_var).str());
115 double fac = F*A/(4.0*D);
117 fisher(0, 1) = fac*sigma22W;
118 fisher(1, 0) = fisher(0, 1);
119 fisher(0, 2) = fac*sigma11W;
120 fisher(2, 0) = fisher(0, 2);
121 fisher(0, 3) = -fac*2*sigma12W;
122 fisher(3, 0) = fisher(0, 3);
124 fac = 3.0*F*A*A/(16.0*D*D);
125 fisher(1, 1) = fac*sigma22W*sigma22W;
126 fisher(2, 2) = fac*sigma11W*sigma11W;
127 fisher(3, 3) = fac*4.0*(sigma12W*sigma12W + D/3.0);
129 fisher(1, 2) = fisher(3, 3)/4.0;
130 fisher(2, 1) = fisher(1, 2);
131 fisher(1, 3) = fac*(-2*sigma22W*sigma12W);
132 fisher(3, 1) = fisher(1, 3);
133 fisher(2, 3) = fac*(-2*sigma11W*sigma12W);
134 fisher(3, 2) = fisher(2, 3);
141 template<
typename ImageT>
142 struct ImageAdaptor {
143 typedef ImageT Image;
145 Image
const& getImage(ImageT
const&
image)
const {
149 double getVariance(ImageT
const&,
int,
int) {
150 return std::numeric_limits<double>::quiet_NaN();
155 struct ImageAdaptor<afwImage::MaskedImage<T> > {
163 return mimage.
at(ix, iy).variance();
168 boost::tuple<std::pair<bool, double>, double, double,
double>
169 getWeights(
double sigma11,
double sigma12,
double sigma22,
172 double const NaN = std::numeric_limits<double>::quiet_NaN();
174 return boost::make_tuple(std::make_pair(
false, NaN), NaN, NaN, NaN);
176 double const det = sigma11*sigma22 - sigma12*sigma12;
178 double const nan = std::numeric_limits<double>::quiet_NaN();
179 return boost::make_tuple(std::make_pair(
false, nan), nan, nan, nan);
192 double const iMin = 1/12.0;
196 return boost::make_tuple(std::make_pair(
false, det), NaN, NaN, NaN);
202 axes.setA(::sqrt(::pow(axes.getA(), 2) + iMin));
203 axes.setB(::sqrt(::pow(axes.getB(), 2) + iMin));
209 return boost::make_tuple(std::make_pair(
true, q2.getDeterminant()), mat(0, 0), mat(1, 0), mat(1, 1));
212 assert(sigma11*sigma22 >= sigma12*sigma12 - std::numeric_limits<float>::epsilon());
214 return boost::make_tuple(std::make_pair(
true, det), sigma22/det, -sigma12/det, sigma11/det);
218 bool shouldInterp(
double sigma11,
double sigma22,
double det) {
219 float const xinterp = 0.25;
220 return (sigma11 < xinterp || sigma22 < xinterp || det < xinterp*xinterp);
230 float xcen,
float ycen,
237 float rad = 4*sqrt(((sigma11_w > sigma22_w) ? sigma11_w : sigma22_w));
243 int ix0 =
static_cast<int>(xcen - rad - 0.5);
244 ix0 = (ix0 < 0) ? 0 : ix0;
245 int iy0 =
static_cast<int>(ycen - rad - 0.5);
246 iy0 = (iy0 < 0) ? 0 : iy0;
249 int ix1 =
static_cast<int>(xcen + rad + 0.5);
253 int iy1 =
static_cast<int>(ycen + rad + 0.5);
266 template<
bool fluxOnly,
typename ImageT>
268 calcmom(ImageT
const&
image,
269 float xcen,
float ycen,
273 double w11,
double w12,
double w22,
276 double *psumx,
double *psumy,
277 double *psumxx,
double *psumxy,
double *psumyy,
286 double sum, sumx, sumy, sumxx, sumyy, sumxy, sums4;
287 #define RECALC_W 0 // estimate sigmaXX_w within BBox?
289 double wsum, wsumxx, wsumxy, wsumyy;
291 wsum = wsumxx = wsumxy = wsumyy = 0;
295 if (fabs(w11) > 1e6 || fabs(w12) > 1e6 || fabs(w22) > 1e6) {
299 sum = sumx = sumy = sumxx = sumxy = sumyy = sums4 = 0;
301 int const ix0 = bbox.
getMinX();
302 int const ix1 = bbox.
getMaxX();
303 int const iy0 = bbox.
getMinY();
304 int const iy1 = bbox.
getMaxY();
306 if (ix0 < 0 || ix1 >= image.getWidth() || iy0 < 0 || iy1 >= image.getHeight()) {
310 for (
int i = iy0; i <= iy1; ++i) {
311 typename ImageT::x_iterator ptr = image.x_at(ix0, i);
312 float const y = i - ycen;
313 float const y2 = y*
y;
314 float const yl = y - 0.375;
315 float const yh = y + 0.375;
316 for (
int j = ix0; j <= ix1; ++j, ++ptr) {
319 float const xl = x - 0.375;
320 float const xh = x + 0.375;
322 float expon = xl*xl*w11 + yl*yl*w22 + 2.0*xl*yl*w12;
323 tmp = xh*xh*w11 + yh*yh*w22 + 2.0*xh*yh*w12;
324 expon = (expon > tmp) ? expon : tmp;
325 tmp = xl*xl*w11 + yh*yh*w22 + 2.0*xl*yh*w12;
326 expon = (expon > tmp) ? expon : tmp;
327 tmp = xh*xh*w11 + yl*yl*w22 + 2.0*xh*yl*w12;
328 expon = (expon > tmp) ? expon : tmp;
332 for (Y = yl; Y <= yh; Y += 0.25) {
333 double const interpY2 = Y*
Y;
334 for (X = xl; X <= xh; X += 0.25) {
335 double const interpX2 = X*
X;
336 double const interpXy = X*
Y;
337 expon = interpX2*w11 + 2*interpXy*w12 + interpY2*w22;
343 sumx += ymod*(X + xcen);
344 sumy += ymod*(Y + ycen);
348 tmp = interpX2*weight;
352 tmp = interpXy*weight;
356 tmp = interpY2*weight;
360 sumxx += interpX2*ymod;
361 sumxy += interpXy*ymod;
362 sumyy += interpY2*ymod;
364 sums4 += expon*expon*ymod;
372 float expon = x2*w11 + 2*xy*w12 + y2*w22;
401 sums4 += expon*expon*ymod;
409 boost::tuple<std::pair<bool, double>, double, double,
double>
const weights = getWeights(w11, w12, w22);
410 double const detW = weights.get<1>()*weights.get<3>() - std::pow(weights.get<2>(), 2);
421 if (psums4 != NULL) {
427 if (wsum > 0 && !fluxOnly) {
428 double det = w11*w22 - w12*w12;
432 printf(
"%g %g %g %g %g %g\n", w22/det, -w12/det, w11/det, wsumxx, wsumxy, wsumyy);
436 return (fluxOnly || (sum > 0 && sumxx > 0 && sumyy > 0)) ? 0 : -1;
447 template<
typename ImageT>
448 bool getAdaptiveMoments(ImageT
const& mimage,
double bkgd,
double xcen,
double ycen,
double shiftmax,
454 double sumxx, sumxy, sumyy;
456 float const xcen0 = xcen;
457 float const ycen0 = ycen;
459 double sigma11W = 1.5;
460 double sigma12W = 0.0;
461 double sigma22W = 1.5;
463 double w11 = -1, w12 = -1, w22 = -1;
464 float e1_old = 1e6, e2_old = 1e6;
465 float sigma11_ow_old = 1e6;
467 typename ImageAdaptor<ImageT>::Image
const &image = ImageAdaptor<ImageT>().getImage(mimage);
475 bool interpflag =
false;
478 for (; iter < maxIter; iter++) {
479 bbox = set_amom_bbox(image.getWidth(), image.getHeight(), xcen, ycen, sigma11W, sigma12W, sigma22W);
480 boost::tuple<std::pair<bool, double>, double, double,
double> weights =
481 getWeights(sigma11W, sigma12W, sigma22W);
482 if (!weights.get<0>().first) {
487 double const detW = weights.get<0>().second;
489 #if 0 // this form was numerically unstable on my G4 powerbook
492 assert(sigma11W*sigma22W >= sigma12W*sigma12W - std::numeric_limits<float>::epsilon());
496 const double ow11 = w11;
497 const double ow12 = w12;
498 const double ow22 = w22;
500 w11 = weights.get<1>();
501 w12 = weights.get<2>();
502 w22 = weights.get<3>();
504 if (shouldInterp(sigma11W, sigma22W, detW)) {
508 sigma11_ow_old = 1.e6;
518 if (calcmom<false>(image, xcen, ycen, bbox, bkgd, interpflag, w11, w12, w22,
519 &I0, &sum, &sumx, &sumy, &sumxx, &sumxy, &sumyy, &sums4) < 0) {
532 shape->
setX(sumx/sum);
533 shape->
setY(sumy/sum);
535 if (fabs(shape->
getX() - xcen0) > shiftmax || fabs(shape->
getY() - ycen0) > shiftmax) {
541 float const sigma11_ow = sumxx/
sum;
542 float const sigma22_ow = sumyy/
sum;
543 float const sigma12_ow = sumxy/
sum;
545 if (sigma11_ow <= 0 || sigma22_ow <= 0) {
550 float const d = sigma11_ow + sigma22_ow;
551 float const e1 = (sigma11_ow - sigma22_ow)/d;
552 float const e2 = 2.0*sigma12_ow/
d;
557 fabs(e1 - e1_old) < tol1 && fabs(e2 - e2_old) < tol1 &&
558 fabs(sigma11_ow/sigma11_ow_old - 1.0) < tol2 ) {
564 sigma11_ow_old = sigma11_ow;
589 float ow11, ow12, ow22;
591 boost::tuple<std::pair<bool, double>, double, double,
double> weights =
592 getWeights(sigma11_ow, sigma12_ow, sigma22_ow);
593 if (!weights.get<0>().first) {
598 ow11 = weights.get<1>();
599 ow12 = weights.get<2>();
600 ow22 = weights.get<3>();
606 weights = getWeights(n11, n12, n22,
false);
607 if (!weights.get<0>().first) {
613 sigma11W = weights.get<1>();
614 sigma12W = weights.get<2>();
615 sigma22W = weights.get<3>();
618 if (sigma11W <= 0 || sigma22W <= 0) {
624 if (iter == maxIter) {
629 if (sumxx + sumyy == 0.0) {
637 if (calcmom<false>(image, xcen, ycen, bbox, bkgd, interpflag, w11, w12, w22,
638 &I0, &sum, &sumx, &sumy, &sumxx, &sumxy, &sumyy, NULL) < 0 || sum <= 0) {
651 sigma11W = sumxx/
sum;
652 sigma12W = sumxy/
sum;
653 sigma22W = sumyy/
sum;
666 if (ix >= 0 && ix < mimage.getWidth() && iy >= 0 && iy < mimage.getHeight()) {
667 float const bkgd_var =
668 ImageAdaptor<ImageT>().getVariance(mimage, ix, iy);
670 if (bkgd_var > 0.0) {
690 template<
typename ImageT>
691 std::pair<double, double>
701 afwGeom::BoxI const& bbox = set_amom_bbox(image.getWidth(), image.getHeight(), xcen, ycen,
704 boost::tuple<std::pair<bool, double>, double, double,
double> weights =
706 double const NaN = std::numeric_limits<double>::quiet_NaN();
707 if (!weights.get<0>().first) {
708 return std::make_pair(NaN, NaN);
711 double const w11 = weights.get<1>();
712 double const w12 = weights.get<2>();
713 double const w22 = weights.get<3>();
714 bool const interp = shouldInterp(shape.
getIxx(), shape.
getIyy(), weights.get<0>().second);
717 calcmom<true>(ImageAdaptor<ImageT>().getImage(image), xcen - image.getX0(), ycen - image.getY0(),
718 bbox, bkgd, interp, w11, w12, w22,
719 &I0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
726 int ix =
static_cast<int>(xcen - image.getX0());
727 int iy =
static_cast<int>(ycen - image.getY0());
729 ImageAdaptor<ImageT>().getVariance(image, ix, iy);
745 xy4Sigma(std::numeric_limits<
ShapeElement>::quiet_NaN()),
746 flux_xx_Cov(std::numeric_limits<
ErrElement>::quiet_NaN()),
747 flux_yy_Cov(std::numeric_limits<
ErrElement>::quiet_NaN()),
748 flux_xy_Cov(std::numeric_limits<
ErrElement>::quiet_NaN())
751 static boost::array<FlagDefinition,SdssShapeAlgorithm::N_FLAGS>
const flagDefs = {{
752 {
"flag",
"general failure flag, set if anything went wrong"},
753 {
"flag_unweightedBad",
"Both weighted and unweighted moments were invalid"},
754 {
"flag_unweighted",
"Weighted moments converged to an invalid value; using unweighted moments"},
755 {
"flag_shift",
"centroid shifted by more than the maximum allowed amount"},
756 {
"flag_maxIter",
"Too many iterations in adaptive moments"}
761 std::string
const & name
771 schema.
join(name,
"xy4"),
"4th moment used in certain shear-estimation algorithms",
"pixels^4"
774 schema.
join(name,
"xy4Sigma"),
775 "uncertainty on %s" + schema.
join(name,
"xy4"),
"pixels^4"
778 schema.
join(name,
"flux",
"xx",
"Cov"),
780 % schema.
join(name,
"flux") % schema.
join(name,
"xx")).str(),
784 schema.
join(name,
"flux",
"yy",
"Cov"),
786 % schema.
join(name,
"flux") % schema.
join(name,
"yy")).str(),
790 schema.
join(name,
"flux",
"xy",
"Cov"),
792 % schema.
join(name,
"flux") % schema.
join(name,
"xy")).str(),
804 _xy4Sigma(s[
"xy4Sigma"]),
805 _flux_xx_Cov(s[
"flux"][
"xx"][
"Cov"]),
806 _flux_yy_Cov(s[
"flux"][
"yy"][
"Cov"]),
807 _flux_xy_Cov(s[
"flux"][
"xy"][
"Cov"]),
808 _flagHandler(s, flagDefs.begin(), flagDefs.end())
867 std::string
const & name,
870 : _resultKey(
ResultKey::addFields(schema, name)),
871 _centroidExtractor(schema, name)
874 template <
typename T>
882 pex::exceptions::LogicError,
887 template <
typename T>
895 double xcen = center.getX();
896 double ycen = center.getY();
898 xcen -= mimage.
getX0();
899 ycen -= mimage.
getY0();
904 }
else if (shiftmax > 10) {
933 result.
flags[n + 1] =
true;
946 pex::exceptions::RuntimeError,
947 "No Footprint attached to SourceRecord"
967 #define INSTANTIATE_IMAGE(IMAGE) \
968 template bool detail::getAdaptiveMoments<IMAGE>( \
969 IMAGE const&, double, double, double, double, SdssShapeImpl*, int, float, float); \
970 template std::pair<double, double> detail::getFixedMomentsFlux<IMAGE>( \
971 IMAGE const&, double, double, double, detail::SdssShapeImpl const&); \
973 #define INSTANTIATE_PIXEL(PIXEL) \
974 INSTANTIATE_IMAGE(lsst::afw::image::Image<PIXEL>); \
975 INSTANTIATE_IMAGE(lsst::afw::image::MaskedImage<PIXEL>);
981 #define INSTANTIATE(T) \
982 template SdssShapeResult SdssShapeAlgorithm::apply( \
983 afw::image::MaskedImage<T> const & exposure, \
984 afw::detection::Footprint const & footprint, \
985 afw::geom::Point2D const & position, \
986 Control const & ctrl \
988 template SdssShapeResult SdssShapeAlgorithm::apply( \
989 afw::image::Image<T> const & exposure, \
990 afw::detection::Footprint const & footprint, \
991 afw::geom::Point2D const & position, \
992 Control const & ctrl \
bool isValid() const
Return true if the key was initialized to valid offset.
An ellipse core with quadrupole moments as parameters.
Defines the fields and offsets for a table.
ShapeElement xy4
A fourth moment used in lensing (RHL needs to clarify; not in the old docs)
static SdssShapeResultKey addFields(afw::table::Schema &schema, std::string const &name)
Add the appropriate fields to a Schema, and return a SdssShapeResultKey that manages them...
bool getValue(afw::table::BaseRecord const &record, int i) const
afw::table::Key< ErrElement > _xy4Sigma
Eigen::Matrix< double, 2, 2, Eigen::DontAlign > Matrix
Matrix type for the matrix representation of Quadrupole parameters.
A proxy type for name lookups in a Schema.
ErrElement ySigma
1-Sigma uncertainty on y (sqrt of variance)
afw::table::Key< ErrElement > _flux_yy_Cov
static Result apply(afw::image::MaskedImage< T > const &image, afw::detection::Footprint const &footprint, afw::geom::Point2D const &position, Control const &ctrl=Control())
SafeCentroidExtractor _centroidExtractor
Public header class for ellipse library.
void setX(double const x)
#define INSTANTIATE_PIXEL(PIXEL)
afw::table::Key< ShapeElement > _xy4
float tol2
"Convergence tolerance for FWHM" ;
int positionToIndex(double pos)
Convert image position to nearest integer index.
FlagHandler const & getFlagHandler() const
void resetFlag(Flag flag)
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.
iterator at(int const x, int const y) const
Return an iterator at the point (x, y)
ErrElement xy4Sigma
1-Sigma uncertainty on xy4
double background
"Additional value to add to background" ;
definition of the Trace messaging facilities
ErrElement xSigma
1-Sigma uncertainty on x (sqrt of variance)
void setY(double const y)
CentroidElement x
x (column) coordinate of the measured position
void setIxy4(double ixy4)
boost::shared_ptr< Footprint > getFootprint() const
ImagePtr getImage(bool const noThrow=false) const
Return a (Ptr to) the MaskedImage's image.
int maxIter
"Maximum number of iterations" ;
bool isValid() const
Return True if the centroid key is valid.
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
Exception to be thrown when a measurement algorithm experiences a known failure mode.
ErrElement xySigma
1-Sigma uncertainty on xy (sqrt of variance)
static FluxResultKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc)
float tol1
"Convergence tolerance for e1,e2" ;
double maxShift
"Maximum centroid shift, limited to 2-10" ;
bool isValid() const
Return True if the key is valid.
ErrElement flux_yy_Cov
flux, yy term in the uncertainty covariance matrix
bool operator==(SdssShapeResultKey const &other) const
Compare the FunctorKey for equality with another, using the underlying Keys.
An integer coordinate rectangle.
ShapeResultKey _shapeResult
table::Key< table::Array< Kernel::Pixel > > image
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
float exp(float x) const
Evaluate exp(x). (x must be in (-87.3ish, 88.7ish))
An include file to include the header files for lsst::afw::image.
A reusable struct for moments-based shape measurements.
double const PI
The ratio of a circle's circumference to diameter.
MaskedImageT getMaskedImage()
Return the MaskedImage.
Result object SdssShapeAlgorithm.
double getFluxScale() const
Return multiplier that transforms I0 to a total flux.
Eigen::Matrix< double, 4, 4, Eigen::DontAlign > Matrix4
SdssShapeResult()
Constructor; initializes everything to NaN.
bool isValid() const
Return True if both the flux and fluxSigma Keys are valid.
SdssShapeAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
bool isValid() const
Return True if the shape key is valid.
A class to manipulate images, masks, and variance as a single object.
CentroidResultKey _centroidResult
static CentroidResultKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc, UncertaintyEnum uncertainty)
Add the appropriate fields to a Schema, and return a CentroidResultKey that manages them...
void setValue(afw::table::BaseRecord &record, int i, bool value) const
A C++ control class to handle SdssShapeAlgorithm's configuration.
std::pair< double, double > getFixedMomentsFlux(ImageT const &image, double bkgd, double xcen, double ycen, SdssShapeImpl const &shape_)
Return the flux of an object, using the aperture described by the SdssShape object.
virtual void set(afw::table::BaseRecord &record, SdssShapeResult const &value) const
Set a CentroidResult in the given record.
bool getFlag(Flag flag) const
std::bitset< SdssShapeAlgorithm::N_FLAGS > flags
Status flags (see SdssShapeAlgorithm).
#define LSST_EXCEPT(type,...)
An ellipse core for the semimajor/semiminor axis and position angle parametrization (a...
virtual SdssShapeResult get(afw::table::BaseRecord const &record) const
Get a CentroidResult from the given record.
virtual void fail(afw::table::SourceRecord &measRecord, MeasurementError *error=NULL) const
Base class for all records.
lsst::utils::PowFast const & powFast
A FunctorKey that maps SdssShapeResult to afw::table Records.
std::string join(std::string const &a, std::string const &b) const
Join strings using the field delimiter appropriate for this Schema's version.
bool getAdaptiveMoments(ImageT const &mimage, double bkgd, double xcen, double ycen, double shiftmax, SdssShapeImpl *shape, int maxIter, float tol1, float tol2)
ErrElement yySigma
1-Sigma uncertainty on yy (sqrt of variance)
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
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
void handleFailure(afw::table::BaseRecord &record, MeasurementError const *error=NULL) const
Record class that contains measurements made on a single exposure.
ErrElement xxSigma
1-Sigma uncertainty on xx (sqrt of variance)
static ShapeResultKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc, UncertaintyEnum uncertainty)
Add the appropriate fields to a Schema, and return a ShapeResultKey that manages them.
afw::table::Key< ErrElement > _flux_xy_Cov
SdssShapeResultKey()
Default constructor; instance will not be usuable unless subsequently assigned to.
afw::table::Key< ErrElement > _flux_xx_Cov
ErrElement flux_xx_Cov
flux, xx term in the uncertainty covariance matrix
A class to represent a 2-dimensional array of pixels.
static FlagHandler addFields(afw::table::Schema &schema, std::string const &prefix, FlagDefinition const *begin, FlagDefinition const *end)
FluxResultKey _fluxResult
void setCovar(Matrix4 covar)
A reusable result struct for flux measurements.
ErrElement flux_xy_Cov
flux, xy term in the uncertainty covariance matrix
afw::table::SourceRecord * record