27 #include "boost/tuple/tuple.hpp"
43 FlagDefinitionList flagDefinitions;
48 flagDefinitions.add(
"flag_unweightedBad",
"Both weighted and unweighted moments were invalid");
50 "flag_unweighted",
"Weighted moments converged to an invalid value; using unweighted moments");
52 flagDefinitions.add(
"flag_shift",
"centroid shifted by more than the maximum allowed amount");
54 flagDefinitions.add(
"flag_maxIter",
"Too many iterations in adaptive moments");
56 flagDefinitions.add(
"flag_psf",
"Failure in measuring PSF model shape");
62 typedef Eigen::Matrix<double, 4, 4, Eigen::DontAlign> Matrix4d;
80 float const sigma11W = shape.
xx;
81 float const sigma12W = shape.
xy;
82 float const sigma22W = shape.
yy;
84 double const D = sigma11W * sigma22W - sigma12W * sigma12W;
92 if (bkgd_var <= 0.0) {
94 (
boost::format(
"Background variance must be positive (saw %g)") % bkgd_var).
str());
96 double const F =
geom::PI * sqrt(D) / bkgd_var;
102 double fac = F * A / (4.0 * D);
104 fisher(0, 1) = fac * sigma22W;
105 fisher(1, 0) = fisher(0, 1);
106 fisher(0, 2) = fac * sigma11W;
107 fisher(2, 0) = fisher(0, 2);
108 fisher(0, 3) = -fac * 2 * sigma12W;
109 fisher(3, 0) = fisher(0, 3);
111 fac = 3.0 * F * A * A / (16.0 * D * D);
112 fisher(1, 1) = fac * sigma22W * sigma22W;
113 fisher(2, 2) = fac * sigma11W * sigma11W;
114 fisher(3, 3) = fac * 4.0 * (sigma12W * sigma12W + D / 3.0);
116 fisher(1, 2) = fisher(3, 3) / 4.0;
117 fisher(2, 1) = fisher(1, 2);
118 fisher(1, 3) = fac * (-2 * sigma22W * sigma12W);
119 fisher(3, 1) = fisher(1, 3);
120 fisher(2, 3) = fac * (-2 * sigma11W * sigma12W);
121 fisher(3, 2) = fisher(2, 3);
128 template <
typename ImageT>
129 struct ImageAdaptor {
130 typedef ImageT Image;
132 static bool const hasVariance =
false;
134 Image
const &getImage(ImageT
const &
image)
const {
return image; }
139 template <
typename T>
143 static bool const hasVariance =
true;
145 Image
const &getImage(afw::image::MaskedImage<T>
const &mimage)
const {
return *mimage.getImage(); }
147 double getVariance(afw::image::MaskedImage<T>
const &mimage,
int ix,
int iy) {
148 return mimage.at(ix, iy).variance();
159 double const det = sigma11 * sigma22 - sigma12 * sigma12;
167 bool shouldInterp(
double sigma11,
double sigma22,
double det) {
168 float const xinterp = 0.25;
169 return (sigma11 < xinterp || sigma22 < xinterp ||
det < xinterp * xinterp);
180 double maxRadius = 1000
193 template <
bool instFluxOnly,
typename ImageT>
194 static int calcmom(ImageT
const &
image,
195 float xcen,
float ycen,
199 double w11,
double w12,
double w22,
202 double *psumx,
double *psumy,
203 double *psumxx,
double *psumxy,
double *psumyy,
205 bool negative =
false) {
210 double sum, sumx, sumy, sumxx, sumyy, sumxy, sums4;
211 #define RECALC_W 0 // estimate sigmaXX_w within BBox?
213 double wsum, wsumxx, wsumxy, wsumyy;
215 wsum = wsumxx = wsumxy = wsumyy = 0;
219 if (
fabs(w11) > 1e6 ||
fabs(w12) > 1e6 ||
fabs(w22) > 1e6) {
223 sum = sumx = sumy = sumxx = sumxy = sumyy = sums4 = 0;
225 int const ix0 =
bbox.getMinX();
226 int const ix1 =
bbox.getMaxX();
227 int const iy0 =
bbox.getMinY();
228 int const iy1 =
bbox.getMaxY();
230 if (ix0 < 0 || ix1 >=
image.getWidth() || iy0 < 0 || iy1 >=
image.getHeight()) {
234 for (
int i = iy0; i <= iy1; ++i) {
235 typename ImageT::x_iterator
ptr =
image.x_at(ix0, i);
236 float const y = i - ycen;
237 float const y2 =
y *
y;
238 float const yl =
y - 0.375;
239 float const yh =
y + 0.375;
240 for (
int j = ix0; j <= ix1; ++j, ++
ptr) {
243 float const xl =
x - 0.375;
244 float const xh =
x + 0.375;
246 float expon = xl * xl * w11 + yl * yl * w22 + 2.0 * xl * yl * w12;
247 tmp = xh * xh * w11 + yh * yh * w22 + 2.0 * xh * yh * w12;
248 expon = (expon > tmp) ? expon : tmp;
249 tmp = xl * xl * w11 + yh * yh * w22 + 2.0 * xl * yh * w12;
250 expon = (expon > tmp) ? expon : tmp;
251 tmp = xh * xh * w11 + yl * yl * w22 + 2.0 * xh * yl * w12;
252 expon = (expon > tmp) ? expon : tmp;
256 for (
Y = yl;
Y <= yh;
Y += 0.25) {
257 double const interpY2 =
Y *
Y;
258 for (
X = xl;
X <= xh;
X += 0.25) {
259 double const interpX2 =
X *
X;
260 double const interpXy =
X *
Y;
261 expon = interpX2 * w11 + 2 * interpXy * w12 + interpY2 * w22;
267 sumx += ymod * (
X + xcen);
268 sumy += ymod * (
Y + ycen);
284 sumxx += interpX2 * ymod;
285 sumxy += interpXy * ymod;
286 sumyy += interpY2 * ymod;
288 sums4 += expon * expon * ymod;
296 float expon = x2 * w11 + 2 * xy * w12 + y2 * w22;
325 sums4 += expon * expon * ymod;
334 double const detW = std::get<1>(weights) * std::get<3>(weights) -
std::pow(std::get<2>(weights), 2);
346 if (psums4 != NULL) {
352 if (wsum > 0 && !instFluxOnly) {
353 double det = w11 * w22 - w12 * w12;
357 printf(
"%g %g %g %g %g %g\n", w22 /
det, -w12 /
det, w11 /
det, wsumxx, wsumxy, wsumyy);
362 return (instFluxOnly || (sum < 0 && sumxx < 0 && sumyy < 0)) ? 0 : -1;
364 return (instFluxOnly || (sum > 0 && sumxx > 0 && sumyy > 0)) ? 0 : -1;
373 template <
typename ImageT>
374 bool getAdaptiveMoments(ImageT
const &mimage,
double bkgd,
double xcen,
double ycen,
double shiftmax,
375 SdssShapeResult *shape,
int maxIter,
float tol1,
float tol2,
bool negative) {
379 double sumxx, sumxy, sumyy;
381 float const xcen0 = xcen;
382 float const ycen0 = ycen;
384 double sigma11W = 1.5;
385 double sigma12W = 0.0;
386 double sigma22W = 1.5;
388 double w11 = -1, w12 = -1, w22 = -1;
389 float e1_old = 1e6, e2_old = 1e6;
390 float sigma11_ow_old = 1e6;
392 typename ImageAdaptor<ImageT>::Image
const &
image = ImageAdaptor<ImageT>().getImage(mimage);
400 bool interpflag =
false;
405 sigma11W, sigma12W, sigma22W);
407 getWeights(sigma11W, sigma12W, sigma22W);
408 if (!std::get<0>(weights).
first) {
413 double const detW = std::get<0>(weights).second;
415 #if 0 // this form was numerically unstable on my G4 powerbook
422 const double ow11 = w11;
423 const double ow12 = w12;
424 const double ow22 = w22;
426 w11 = std::get<1>(weights);
427 w12 = std::get<2>(weights);
428 w22 = std::get<3>(weights);
430 if (shouldInterp(sigma11W, sigma22W, detW)) {
434 sigma11_ow_old = 1.e6;
444 if (calcmom<false>(
image, xcen, ycen,
bbox, bkgd, interpflag, w11, w12, w22, &I0, &sum, &sumx, &sumy,
445 &sumxx, &sumxy, &sumyy, &sums4, negative) < 0) {
459 shape->x = sumx / sum;
460 shape->y = sumy / sum;
462 if (
fabs(shape->x - xcen0) > shiftmax ||
fabs(shape->y - ycen0) > shiftmax) {
468 float const sigma11_ow = sumxx / sum;
469 float const sigma22_ow = sumyy / sum;
470 float const sigma12_ow = sumxy / sum;
472 if (sigma11_ow <= 0 || sigma22_ow <= 0) {
477 float const d = sigma11_ow + sigma22_ow;
478 float const e1 = (sigma11_ow - sigma22_ow) / d;
479 float const e2 = 2.0 * sigma12_ow / d;
483 if (
iter > 0 &&
fabs(e1 - e1_old) < tol1 &&
fabs(e2 - e2_old) < tol1 &&
484 fabs(sigma11_ow / sigma11_ow_old - 1.0) < tol2) {
490 sigma11_ow_old = sigma11_ow;
515 float ow11, ow12, ow22;
518 getWeights(sigma11_ow, sigma12_ow, sigma22_ow);
519 if (!std::get<0>(weights).
first) {
524 ow11 = std::get<1>(weights);
525 ow12 = std::get<2>(weights);
526 ow22 = std::get<3>(weights);
532 weights = getWeights(n11, n12, n22);
533 if (!std::get<0>(weights).
first) {
539 sigma11W = std::get<1>(weights);
540 sigma12W = std::get<2>(weights);
541 sigma22W = std::get<3>(weights);
544 if (sigma11W <= 0 || sigma22W <= 0) {
550 if (
iter == maxIter) {
555 if (sumxx + sumyy == 0.0) {
563 if (calcmom<false>(
image, xcen, ycen,
bbox, bkgd, interpflag, w11, w12, w22, &I0, &sum, &sumx, &sumy,
564 &sumxx, &sumxy, &sumyy, NULL, negative) < 0 ||
565 (!negative && sum <= 0) || (negative && sum >= 0)) {
570 shape->xx = 1 / 12.0;
572 shape->yy = 1 / 12.0;
578 sigma11W = sumxx / sum;
579 sigma12W = sumxy / sum;
580 sigma22W = sumyy / sum;
583 shape->instFlux = I0;
584 shape->xx = sigma11W;
585 shape->xy = sigma12W;
586 shape->yy = sigma22W;
588 if (shape->xx + shape->yy != 0.0) {
592 if (ix >= 0 && ix < mimage.getWidth() && iy >= 0 && iy < mimage.getHeight()) {
593 float const bkgd_var = ImageAdaptor<ImageT>().getVariance(
596 if (bkgd_var > 0.0) {
598 Matrix4d fisher = calc_fisher(*shape, bkgd_var);
599 Matrix4d cov = fisher.inverse();
603 shape->instFluxErr =
std::sqrt(cov(0, 0));
607 shape->instFlux_xx_Cov = cov(0, 1);
608 shape->instFlux_xy_Cov = cov(0, 2);
609 shape->instFlux_yy_Cov = cov(0, 3);
610 shape->xx_yy_Cov = cov(1, 3);
611 shape->xx_xy_Cov = cov(1, 2);
612 shape->yy_xy_Cov = cov(2, 3);
624 : instFlux_xx_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()),
625 instFlux_yy_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()),
626 instFlux_xy_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()) {}
639 r._includePsf =
true;
641 schema,
schema.join(
name,
"psf"),
"adaptive moments of the PSF model at the object position");
643 r._includePsf =
false;
679 _instFlux_xx_Cov(s[
"instFlux"][
"xx"][
"Cov"]),
680 _instFlux_yy_Cov(s[
"instFlux"][
"yy"][
"Cov"]),
681 _instFlux_xy_Cov(s[
"instFlux"][
"xy"][
"Cov"]) {
699 result.instFlux_xx_Cov = record.
get(_instFlux_xx_Cov);
700 result.instFlux_yy_Cov = record.
get(_instFlux_yy_Cov);
701 result.instFlux_xy_Cov = record.
get(_instFlux_xy_Cov);
710 return record.
get(_psfShapeResult);
714 record.
set(_shapeResult, value);
715 record.
set(_centroidResult, value);
716 record.
set(_instFluxResult, value);
728 record.
set(_psfShapeResult, value);
732 return _shapeResult ==
other._shapeResult && _centroidResult ==
other._centroidResult &&
733 _instFluxResult ==
other._instFluxResult && _psfShapeResult ==
other._psfShapeResult &&
734 _instFlux_xx_Cov ==
other._instFlux_xx_Cov && _instFlux_yy_Cov ==
other._instFlux_yy_Cov &&
735 _instFlux_xy_Cov ==
other._instFlux_xy_Cov;
752 template <
typename ImageT>
754 bool negative,
Control const &control) {
755 double xcen = center.getX();
756 double ycen = center.getY();
758 xcen -=
image.getX0();
759 ycen -=
image.getY0();
764 }
else if (shiftmax > 10) {
772 control.
tol1, control.
tol2, negative);
781 double IxxIyy =
result.getQuadrupole().getIxx() *
result.getQuadrupole().getIyy();
782 double Ixy_sq =
result.getQuadrupole().getIxy();
784 double epsilon = 1.0e-6;
785 if (IxxIyy < (1.0 + epsilon) * Ixy_sq)
791 (
boost::format(
"computeAdaptiveMoments IxxIxy %d < (1 + eps=%d)*(Ixy^2=%d);"
792 " implying singular moments without any flag set")
793 % IxxIyy % epsilon % Ixy_sq).
str());
806 result.instFlux *= instFluxScale;
807 result.instFluxErr *= instFluxScale;
811 if (ImageAdaptor<ImageT>::hasVariance) {
812 result.instFlux_xx_Cov *= instFluxScale;
813 result.instFlux_yy_Cov *= instFluxScale;
814 result.instFlux_xy_Cov *= instFluxScale;
820 template <
typename ImageT>
835 if (!std::get<0>(weights).
first) {
839 double const w11 = std::get<1>(weights);
840 double const w12 = std::get<2>(weights);
841 double const w22 = std::get<3>(weights);
842 bool const interp = shouldInterp(shape.
getIxx(), shape.
getIyy(), std::get<0>(weights).second);
845 if (calcmom<true>(ImageAdaptor<ImageT>().getImage(
image), localCenter.getX(), localCenter.getY(),
bbox,
846 0.0, interp, w11, w12, w22, NULL, &sum0, NULL, NULL, NULL, NULL, NULL, NULL) < 0) {
850 result.instFlux = sum0 * 2.0;
852 if (ImageAdaptor<ImageT>::hasVariance) {
853 int ix =
static_cast<int>(center.getX() -
image.getX0());
854 int iy =
static_cast<int>(center.getY() -
image.getY0());
857 (
boost::format(
"Center (%d,%d) not in image (%dx%d)") % ix % iy %
861 double var = ImageAdaptor<ImageT>().getVariance(
image, ix, iy);
872 bool negative =
false;
875 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"flags_negative").key);
909 #define INSTANTIATE_IMAGE(IMAGE) \
910 template SdssShapeResult SdssShapeAlgorithm::computeAdaptiveMoments( \
911 IMAGE const &, geom::Point2D const &, bool, Control const &); \
912 template FluxResult SdssShapeAlgorithm::computeFixedMomentsFlux( \
913 IMAGE const &, afw::geom::ellipses::Quadrupole const &, geom::Point2D const &)
915 #define INSTANTIATE_PIXEL(PIXEL) \
916 INSTANTIATE_IMAGE(afw::image::Image<PIXEL>); \
917 INSTANTIATE_IMAGE(afw::image::MaskedImage<PIXEL>);
927 _transformPsf =
mapper.getInputSchema().getNames().count(
"sdssShape_flag_psf") ? true :
false;
933 if (
mapper.getInputSchema().getNames().count(
mapper.getInputSchema().join(
name, flag.name)) == 0)
936 mapper.getInputSchema().find<afw::table::Flag>(
name +
"_" + flag.name).
key;
944 "PSF shape in celestial moments",
954 _instFluxTransform(inputCatalog, outputCatalog,
wcs,
photoCalib);
955 _centroidTransform(inputCatalog, outputCatalog,
wcs,
photoCalib);
962 inputCatalog.getSchema()[inputCatalog.getSchema().join(
_name,
"psf")]);
967 for (; inSrc != inputCatalog.end(); ++inSrc, ++outSrc) {
980 _outShapeKey.
set(*outSrc, outShape);