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;
333 double const detW = std::get<1>(weights) * std::get<3>(weights) -
std::pow(std::get<2>(weights), 2);
344 if (psums4 != NULL) {
350 if (wsum > 0 && !instFluxOnly) {
351 double det = w11 * w22 - w12 * w12;
355 printf(
"%g %g %g %g %g %g\n", w22 /
det, -w12 /
det, w11 /
det, wsumxx, wsumxy, wsumyy);
360 return (instFluxOnly || (sum < 0 && sumxx < 0 && sumyy < 0)) ? 0 : -1;
362 return (instFluxOnly || (sum > 0 && sumxx > 0 && sumyy > 0)) ? 0 : -1;
371 template <
typename ImageT>
372 bool getAdaptiveMoments(ImageT
const &mimage,
double bkgd,
double xcen,
double ycen,
double shiftmax,
373 SdssShapeResult *shape,
int maxIter,
float tol1,
float tol2,
bool negative) {
377 double sumxx, sumxy, sumyy;
379 float const xcen0 = xcen;
380 float const ycen0 = ycen;
382 double sigma11W = 1.5;
383 double sigma12W = 0.0;
384 double sigma22W = 1.5;
386 double w11 = -1, w12 = -1, w22 = -1;
387 float e1_old = 1e6, e2_old = 1e6;
388 float sigma11_ow_old = 1e6;
390 typename ImageAdaptor<ImageT>::Image
const &
image = ImageAdaptor<ImageT>().getImage(mimage);
398 bool interpflag =
false;
403 sigma11W, sigma12W, sigma22W);
405 getWeights(sigma11W, sigma12W, sigma22W);
406 if (!std::get<0>(weights).
first) {
411 double const detW = std::get<0>(weights).second;
413 #if 0 // this form was numerically unstable on my G4 powerbook
420 const double ow11 = w11;
421 const double ow12 = w12;
422 const double ow22 = w22;
424 w11 = std::get<1>(weights);
425 w12 = std::get<2>(weights);
426 w22 = std::get<3>(weights);
428 if (shouldInterp(sigma11W, sigma22W, detW)) {
432 sigma11_ow_old = 1.e6;
442 if (calcmom<false>(
image, xcen, ycen,
bbox, bkgd, interpflag, w11, w12, w22, &I0, &sum, &sumx, &sumy,
443 &sumxx, &sumxy, &sumyy, &sums4, negative) < 0) {
457 shape->x = sumx / sum;
458 shape->y = sumy / sum;
460 if (
fabs(shape->x - xcen0) > shiftmax ||
fabs(shape->y - ycen0) > shiftmax) {
466 float const sigma11_ow = sumxx / sum;
467 float const sigma22_ow = sumyy / sum;
468 float const sigma12_ow = sumxy / sum;
470 if (sigma11_ow <= 0 || sigma22_ow <= 0) {
475 float const d = sigma11_ow + sigma22_ow;
476 float const e1 = (sigma11_ow - sigma22_ow) / d;
477 float const e2 = 2.0 * sigma12_ow / d;
481 if (
iter > 0 &&
fabs(e1 - e1_old) < tol1 &&
fabs(e2 - e2_old) < tol1 &&
482 fabs(sigma11_ow / sigma11_ow_old - 1.0) < tol2) {
488 sigma11_ow_old = sigma11_ow;
513 float ow11, ow12, ow22;
516 getWeights(sigma11_ow, sigma12_ow, sigma22_ow);
517 if (!std::get<0>(weights).
first) {
522 ow11 = std::get<1>(weights);
523 ow12 = std::get<2>(weights);
524 ow22 = std::get<3>(weights);
530 weights = getWeights(n11, n12, n22);
531 if (!std::get<0>(weights).
first) {
537 sigma11W = std::get<1>(weights);
538 sigma12W = std::get<2>(weights);
539 sigma22W = std::get<3>(weights);
542 if (sigma11W <= 0 || sigma22W <= 0) {
548 if (
iter == maxIter) {
553 if (sumxx + sumyy == 0.0) {
561 if (calcmom<false>(
image, xcen, ycen,
bbox, bkgd, interpflag, w11, w12, w22, &I0, &sum, &sumx, &sumy,
562 &sumxx, &sumxy, &sumyy, NULL, negative) < 0 ||
563 (!negative && sum <= 0) || (negative && sum >= 0)) {
568 shape->xx = 1 / 12.0;
570 shape->yy = 1 / 12.0;
576 sigma11W = sumxx / sum;
577 sigma12W = sumxy / sum;
578 sigma22W = sumyy / sum;
581 shape->instFlux = I0;
582 shape->xx = sigma11W;
583 shape->xy = sigma12W;
584 shape->yy = sigma22W;
586 if (shape->xx + shape->yy != 0.0) {
590 if (ix >= 0 && ix < mimage.getWidth() && iy >= 0 && iy < mimage.getHeight()) {
591 float const bkgd_var = ImageAdaptor<ImageT>().getVariance(
594 if (bkgd_var > 0.0) {
596 Matrix4d fisher = calc_fisher(*shape, bkgd_var);
597 Matrix4d cov = fisher.inverse();
601 shape->instFluxErr =
std::sqrt(cov(0, 0));
605 shape->instFlux_xx_Cov = cov(0, 1);
606 shape->instFlux_xy_Cov = cov(0, 2);
607 shape->instFlux_yy_Cov = cov(0, 3);
608 shape->xx_yy_Cov = cov(1, 3);
609 shape->xx_xy_Cov = cov(1, 2);
610 shape->yy_xy_Cov = cov(2, 3);
622 : instFlux_xx_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()),
623 instFlux_yy_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()),
624 instFlux_xy_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()) {}
637 r._includePsf =
true;
639 schema,
schema.join(
name,
"psf"),
"adaptive moments of the PSF model at the object position");
641 r._includePsf =
false;
677 _instFlux_xx_Cov(s[
"instFlux"][
"xx"][
"Cov"]),
678 _instFlux_yy_Cov(s[
"instFlux"][
"yy"][
"Cov"]),
679 _instFlux_xy_Cov(s[
"instFlux"][
"xy"][
"Cov"]) {
697 result.instFlux_xx_Cov = record.
get(_instFlux_xx_Cov);
698 result.instFlux_yy_Cov = record.
get(_instFlux_yy_Cov);
699 result.instFlux_xy_Cov = record.
get(_instFlux_xy_Cov);
708 return record.
get(_psfShapeResult);
712 record.
set(_shapeResult, value);
713 record.
set(_centroidResult, value);
714 record.
set(_instFluxResult, value);
726 record.
set(_psfShapeResult, value);
730 return _shapeResult ==
other._shapeResult && _centroidResult ==
other._centroidResult &&
731 _instFluxResult ==
other._instFluxResult && _psfShapeResult ==
other._psfShapeResult &&
732 _instFlux_xx_Cov ==
other._instFlux_xx_Cov && _instFlux_yy_Cov ==
other._instFlux_yy_Cov &&
733 _instFlux_xy_Cov ==
other._instFlux_xy_Cov;
750 template <
typename ImageT>
752 bool negative,
Control const &control) {
753 double xcen = center.getX();
754 double ycen = center.getY();
756 xcen -=
image.getX0();
757 ycen -=
image.getY0();
762 }
else if (shiftmax > 10) {
770 control.
tol1, control.
tol2, negative);
779 double IxxIyy =
result.getQuadrupole().getIxx() *
result.getQuadrupole().getIyy();
780 double Ixy_sq =
result.getQuadrupole().getIxy();
782 double epsilon = 1.0e-6;
783 if (IxxIyy < (1.0 + epsilon) * Ixy_sq)
789 (
boost::format(
"computeAdaptiveMoments IxxIxy %d < (1 + eps=%d)*(Ixy^2=%d);"
790 " implying singular moments without any flag set")
791 % IxxIyy % epsilon % Ixy_sq).str());
804 result.instFlux *= instFluxScale;
805 result.instFluxErr *= instFluxScale;
809 if (ImageAdaptor<ImageT>::hasVariance) {
810 result.instFlux_xx_Cov *= instFluxScale;
811 result.instFlux_yy_Cov *= instFluxScale;
812 result.instFlux_xy_Cov *= instFluxScale;
818 template <
typename ImageT>
833 if (!std::get<0>(weights).
first) {
837 double const w11 = std::get<1>(weights);
838 double const w12 = std::get<2>(weights);
839 double const w22 = std::get<3>(weights);
840 bool const interp = shouldInterp(shape.
getIxx(), shape.
getIyy(), std::get<0>(weights).second);
843 if (calcmom<true>(ImageAdaptor<ImageT>().getImage(
image), localCenter.getX(), localCenter.getY(),
bbox,
844 0.0, interp, w11, w12, w22, &i0, NULL, NULL, NULL, NULL, NULL, NULL, NULL) < 0) {
850 result.instFlux = i0 * 2 * wArea;
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);
871 bool negative =
false;
874 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"flags_negative").key);
908 #define INSTANTIATE_IMAGE(IMAGE) \
909 template SdssShapeResult SdssShapeAlgorithm::computeAdaptiveMoments( \
910 IMAGE const &, geom::Point2D const &, bool, Control const &); \
911 template FluxResult SdssShapeAlgorithm::computeFixedMomentsFlux( \
912 IMAGE const &, afw::geom::ellipses::Quadrupole const &, geom::Point2D const &)
914 #define INSTANTIATE_PIXEL(PIXEL) \
915 INSTANTIATE_IMAGE(afw::image::Image<PIXEL>); \
916 INSTANTIATE_IMAGE(afw::image::MaskedImage<PIXEL>);
926 _transformPsf =
mapper.getInputSchema().getNames().count(
"sdssShape_flag_psf") ? true :
false;
932 if (
mapper.getInputSchema().getNames().count(
mapper.getInputSchema().join(
name, flag.name)) == 0)
935 mapper.getInputSchema().find<afw::table::Flag>(
name +
"_" + flag.name).
key;
943 "PSF shape in celestial moments",
953 _instFluxTransform(inputCatalog, outputCatalog,
wcs,
photoCalib);
954 _centroidTransform(inputCatalog, outputCatalog,
wcs,
photoCalib);
961 inputCatalog.getSchema()[inputCatalog.getSchema().join(
_name,
"psf")]);
966 for (; inSrc != inputCatalog.end(); ++inSrc, ++outSrc) {
979 _outShapeKey.
set(*outSrc, outShape);