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;
72 double const Mxx =
result.xx;
73 double const Mxy =
result.xy;
74 double const Myy =
result.yy;
76 double const Muu_p_Mvv = Mxx + Myy;
77 double const Muu_m_Mvv = ::sqrt(::pow(Mxx - Myy, 2) + 4 * ::pow(Mxy, 2));
78 double const Muu = 0.5 * (Muu_p_Mvv + Muu_m_Mvv);
79 double const Mvv = 0.5 * (Muu_p_Mvv - Muu_m_Mvv);
96 Matrix4d calc_fisher(SdssShapeResult
const &shape,
99 float const A = shape.instFlux;
100 float const sigma11W = shape.xx;
101 float const sigma12W = shape.xy;
102 float const sigma22W = shape.yy;
104 double const D = sigma11W * sigma22W - sigma12W * sigma12W;
112 if (bkgd_var <= 0.0) {
114 (
boost::format(
"Background variance must be positive (saw %g)") % bkgd_var).str());
116 double const F =
geom::PI * sqrt(D) / bkgd_var;
122 double fac = F * A / (4.0 * D);
124 fisher(0, 1) = fac * sigma22W;
125 fisher(1, 0) = fisher(0, 1);
126 fisher(0, 2) = fac * sigma11W;
127 fisher(2, 0) = fisher(0, 2);
128 fisher(0, 3) = -fac * 2 * sigma12W;
129 fisher(3, 0) = fisher(0, 3);
131 fac = 3.0 * F * A * A / (16.0 * D * D);
132 fisher(1, 1) = fac * sigma22W * sigma22W;
133 fisher(2, 2) = fac * sigma11W * sigma11W;
134 fisher(3, 3) = fac * 4.0 * (sigma12W * sigma12W + D / 3.0);
136 fisher(1, 2) = fisher(3, 3) / 4.0;
137 fisher(2, 1) = fisher(1, 2);
138 fisher(1, 3) = fac * (-2 * sigma22W * sigma12W);
139 fisher(3, 1) = fisher(1, 3);
140 fisher(2, 3) = fac * (-2 * sigma11W * sigma12W);
141 fisher(3, 2) = fisher(2, 3);
148 template <
typename ImageT>
149 struct ImageAdaptor {
150 typedef ImageT Image;
152 static bool const hasVariance =
false;
154 Image
const &getImage(ImageT
const &
image)
const {
return image; }
159 template <
typename T>
163 static bool const hasVariance =
true;
165 Image
const &getImage(afw::image::MaskedImage<T>
const &mimage)
const {
return *mimage.getImage(); }
167 double getVariance(afw::image::MaskedImage<T>
const &mimage,
int ix,
int iy) {
168 return mimage.at(ix, iy).variance();
179 double const det = sigma11 * sigma22 - sigma12 * sigma12;
187 bool shouldInterp(
double sigma11,
double sigma22,
double det) {
188 float const xinterp = 0.25;
189 return (sigma11 < xinterp || sigma22 < xinterp ||
det < xinterp * xinterp);
200 double maxRadius = 1000
213 template <
bool instFluxOnly,
typename ImageT>
214 static int calcmom(ImageT
const &
image,
215 float xcen,
float ycen,
219 double w11,
double w12,
double w22,
222 double *psumx,
double *psumy,
223 double *psumxx,
double *psumxy,
double *psumyy,
225 bool negative =
false) {
230 double sum, sumx, sumy, sumxx, sumyy, sumxy, sums4;
231 #define RECALC_W 0 // estimate sigmaXX_w within BBox?
233 double wsum, wsumxx, wsumxy, wsumyy;
235 wsum = wsumxx = wsumxy = wsumyy = 0;
239 if (
fabs(w11) > 1e6 ||
fabs(w12) > 1e6 ||
fabs(w22) > 1e6) {
243 sum = sumx = sumy = sumxx = sumxy = sumyy = sums4 = 0;
245 int const ix0 =
bbox.getMinX();
246 int const ix1 =
bbox.getMaxX();
247 int const iy0 =
bbox.getMinY();
248 int const iy1 =
bbox.getMaxY();
250 if (ix0 < 0 || ix1 >=
image.getWidth() || iy0 < 0 || iy1 >=
image.getHeight()) {
254 for (
int i = iy0; i <= iy1; ++i) {
255 typename ImageT::x_iterator
ptr =
image.x_at(ix0, i);
256 float const y = i - ycen;
257 float const y2 =
y *
y;
258 float const yl =
y - 0.375;
259 float const yh =
y + 0.375;
260 for (
int j = ix0; j <= ix1; ++j, ++
ptr) {
263 float const xl =
x - 0.375;
264 float const xh =
x + 0.375;
266 float expon = xl * xl * w11 + yl * yl * w22 + 2.0 * xl * yl * w12;
267 tmp = xh * xh * w11 + yh * yh * w22 + 2.0 * xh * yh * w12;
268 expon = (expon > tmp) ? expon : tmp;
269 tmp = xl * xl * w11 + yh * yh * w22 + 2.0 * xl * yh * w12;
270 expon = (expon > tmp) ? expon : tmp;
271 tmp = xh * xh * w11 + yl * yl * w22 + 2.0 * xh * yl * w12;
272 expon = (expon > tmp) ? expon : tmp;
276 for (
Y = yl;
Y <= yh;
Y += 0.25) {
277 double const interpY2 =
Y *
Y;
278 for (
X = xl;
X <= xh;
X += 0.25) {
279 double const interpX2 =
X *
X;
280 double const interpXy =
X *
Y;
281 expon = interpX2 * w11 + 2 * interpXy * w12 + interpY2 * w22;
287 sumx += ymod * (
X + xcen);
288 sumy += ymod * (
Y + ycen);
304 sumxx += interpX2 * ymod;
305 sumxy += interpXy * ymod;
306 sumyy += interpY2 * ymod;
308 sums4 += expon * expon * ymod;
316 float expon = x2 * w11 + 2 * xy * w12 + y2 * w22;
345 sums4 += expon * expon * ymod;
353 double const detW = std::get<1>(weights) * std::get<3>(weights) -
std::pow(std::get<2>(weights), 2);
364 if (psums4 != NULL) {
370 if (wsum > 0 && !instFluxOnly) {
371 double det = w11 * w22 - w12 * w12;
375 printf(
"%g %g %g %g %g %g\n", w22 /
det, -w12 /
det, w11 /
det, wsumxx, wsumxy, wsumyy);
380 return (instFluxOnly || (sum < 0 && sumxx < 0 && sumyy < 0)) ? 0 : -1;
382 return (instFluxOnly || (sum > 0 && sumxx > 0 && sumyy > 0)) ? 0 : -1;
391 template <
typename ImageT>
392 bool getAdaptiveMoments(ImageT
const &mimage,
double bkgd,
double xcen,
double ycen,
double shiftmax,
393 SdssShapeResult *shape,
int maxIter,
float tol1,
float tol2,
bool negative) {
397 double sumxx, sumxy, sumyy;
399 float const xcen0 = xcen;
400 float const ycen0 = ycen;
402 double sigma11W = 1.5;
403 double sigma12W = 0.0;
404 double sigma22W = 1.5;
406 double w11 = -1, w12 = -1, w22 = -1;
407 float e1_old = 1e6, e2_old = 1e6;
408 float sigma11_ow_old = 1e6;
410 typename ImageAdaptor<ImageT>::Image
const &
image = ImageAdaptor<ImageT>().getImage(mimage);
418 bool interpflag =
false;
423 sigma11W, sigma12W, sigma22W);
425 getWeights(sigma11W, sigma12W, sigma22W);
426 if (!std::get<0>(weights).
first) {
431 double const detW = std::get<0>(weights).second;
433 #if 0 // this form was numerically unstable on my G4 powerbook
440 const double ow11 = w11;
441 const double ow12 = w12;
442 const double ow22 = w22;
444 w11 = std::get<1>(weights);
445 w12 = std::get<2>(weights);
446 w22 = std::get<3>(weights);
448 if (shouldInterp(sigma11W, sigma22W, detW)) {
452 sigma11_ow_old = 1.e6;
462 if (calcmom<false>(
image, xcen, ycen,
bbox, bkgd, interpflag, w11, w12, w22, &I0, &sum, &sumx, &sumy,
463 &sumxx, &sumxy, &sumyy, &sums4, negative) < 0) {
477 shape->x = sumx / sum;
478 shape->y = sumy / sum;
480 if (
fabs(shape->x - xcen0) > shiftmax ||
fabs(shape->y - ycen0) > shiftmax) {
486 float const sigma11_ow = sumxx / sum;
487 float const sigma22_ow = sumyy / sum;
488 float const sigma12_ow = sumxy / sum;
490 if (sigma11_ow <= 0 || sigma22_ow <= 0) {
495 float const d = sigma11_ow + sigma22_ow;
496 float const e1 = (sigma11_ow - sigma22_ow) / d;
497 float const e2 = 2.0 * sigma12_ow / d;
501 if (
iter > 0 &&
fabs(e1 - e1_old) < tol1 &&
fabs(e2 - e2_old) < tol1 &&
502 fabs(sigma11_ow / sigma11_ow_old - 1.0) < tol2) {
508 sigma11_ow_old = sigma11_ow;
533 float ow11, ow12, ow22;
536 getWeights(sigma11_ow, sigma12_ow, sigma22_ow);
537 if (!std::get<0>(weights).
first) {
542 ow11 = std::get<1>(weights);
543 ow12 = std::get<2>(weights);
544 ow22 = std::get<3>(weights);
550 weights = getWeights(n11, n12, n22);
551 if (!std::get<0>(weights).
first) {
557 sigma11W = std::get<1>(weights);
558 sigma12W = std::get<2>(weights);
559 sigma22W = std::get<3>(weights);
562 if (sigma11W <= 0 || sigma22W <= 0) {
568 if (
iter == maxIter) {
573 if (sumxx + sumyy == 0.0) {
581 if (calcmom<false>(
image, xcen, ycen,
bbox, bkgd, interpflag, w11, w12, w22, &I0, &sum, &sumx, &sumy,
582 &sumxx, &sumxy, &sumyy, NULL, negative) < 0 ||
583 (!negative && sum <= 0) || (negative && sum >= 0)) {
588 shape->xx = 1 / 12.0;
590 shape->yy = 1 / 12.0;
596 sigma11W = sumxx / sum;
597 sigma12W = sumxy / sum;
598 sigma22W = sumyy / sum;
601 shape->instFlux = I0;
602 shape->xx = sigma11W;
603 shape->xy = sigma12W;
604 shape->yy = sigma22W;
606 if (shape->xx + shape->yy != 0.0) {
610 if (ix >= 0 && ix < mimage.getWidth() && iy >= 0 && iy < mimage.getHeight()) {
611 float const bkgd_var = ImageAdaptor<ImageT>().getVariance(
614 if (bkgd_var > 0.0) {
616 Matrix4d fisher = calc_fisher(*shape, bkgd_var);
617 Matrix4d cov = fisher.inverse();
621 shape->instFluxErr =
std::sqrt(cov(0, 0));
625 shape->instFlux_xx_Cov = cov(0, 1);
626 shape->instFlux_xy_Cov = cov(0, 2);
627 shape->instFlux_yy_Cov = cov(0, 3);
628 shape->xx_yy_Cov = cov(1, 3);
629 shape->xx_xy_Cov = cov(1, 2);
630 shape->yy_xy_Cov = cov(2, 3);
642 : instFlux_xx_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()),
643 instFlux_yy_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()),
644 instFlux_xy_Cov(
std::numeric_limits<
ErrElement>::quiet_NaN()) {}
657 r._includePsf =
true;
659 schema,
schema.join(
name,
"psf"),
"adaptive moments of the PSF model at the object position");
661 r._includePsf =
false;
697 _instFlux_xx_Cov(s[
"instFlux"][
"xx"][
"Cov"]),
698 _instFlux_yy_Cov(s[
"instFlux"][
"yy"][
"Cov"]),
699 _instFlux_xy_Cov(s[
"instFlux"][
"xy"][
"Cov"]) {
717 result.instFlux_xx_Cov = record.
get(_instFlux_xx_Cov);
718 result.instFlux_yy_Cov = record.
get(_instFlux_yy_Cov);
719 result.instFlux_xy_Cov = record.
get(_instFlux_xy_Cov);
728 return record.
get(_psfShapeResult);
732 record.
set(_shapeResult, value);
733 record.
set(_centroidResult, value);
734 record.
set(_instFluxResult, value);
746 record.
set(_psfShapeResult, value);
750 return _shapeResult ==
other._shapeResult && _centroidResult ==
other._centroidResult &&
751 _instFluxResult ==
other._instFluxResult && _psfShapeResult ==
other._psfShapeResult &&
752 _instFlux_xx_Cov ==
other._instFlux_xx_Cov && _instFlux_yy_Cov ==
other._instFlux_yy_Cov &&
753 _instFlux_xy_Cov ==
other._instFlux_xy_Cov;
770 template <
typename ImageT>
772 bool negative,
Control const &control) {
773 double xcen = center.getX();
774 double ycen = center.getY();
776 xcen -=
image.getX0();
777 ycen -=
image.getY0();
782 }
else if (shiftmax > 10) {
790 control.
tol1, control.
tol2, negative);
799 if (
result.getQuadrupole().getIxx() *
result.getQuadrupole().getIyy() <
800 (1.0 + 1.0e-6) *
result.getQuadrupole().getIxy() *
result.getQuadrupole().getIxy())
807 "Should not get singular moments unless a flag is set");
814 double instFluxScale = computeFluxScale(
result);
816 result.instFlux *= instFluxScale;
817 result.instFluxErr *= instFluxScale;
821 if (ImageAdaptor<ImageT>::hasVariance) {
822 result.instFlux_xx_Cov *= instFluxScale;
823 result.instFlux_yy_Cov *= instFluxScale;
824 result.instFlux_xy_Cov *= instFluxScale;
830 template <
typename ImageT>
845 if (!std::get<0>(weights).
first) {
849 double const w11 = std::get<1>(weights);
850 double const w12 = std::get<2>(weights);
851 double const w22 = std::get<3>(weights);
852 bool const interp = shouldInterp(shape.
getIxx(), shape.
getIyy(), std::get<0>(weights).second);
855 if (calcmom<true>(ImageAdaptor<ImageT>().getImage(
image), localCenter.getX(), localCenter.getY(),
bbox,
856 0.0, interp, w11, w12, w22, &i0, NULL, NULL, NULL, NULL, NULL, NULL, NULL) < 0) {
862 result.instFlux = i0 * 2 * wArea;
864 if (ImageAdaptor<ImageT>::hasVariance) {
865 int ix =
static_cast<int>(center.getX() -
image.getX0());
866 int iy =
static_cast<int>(center.getY() -
image.getY0());
869 (
boost::format(
"Center (%d,%d) not in image (%dx%d)") % ix % iy %
873 double var = ImageAdaptor<ImageT>().getVariance(
image, ix, iy);
875 result.instFluxErr = i0Err * 2 * wArea;
883 bool negative =
false;
886 negative = measRecord.
get(measRecord.
getSchema().
find<afw::table::Flag>(
"flags_negative").key);
920 #define INSTANTIATE_IMAGE(IMAGE) \
921 template SdssShapeResult SdssShapeAlgorithm::computeAdaptiveMoments( \
922 IMAGE const &, geom::Point2D const &, bool, Control const &); \
923 template FluxResult SdssShapeAlgorithm::computeFixedMomentsFlux( \
924 IMAGE const &, afw::geom::ellipses::Quadrupole const &, geom::Point2D const &)
926 #define INSTANTIATE_PIXEL(PIXEL) \
927 INSTANTIATE_IMAGE(afw::image::Image<PIXEL>); \
928 INSTANTIATE_IMAGE(afw::image::MaskedImage<PIXEL>);
938 _transformPsf =
mapper.getInputSchema().getNames().count(
"sdssShape_flag_psf") ? true :
false;
944 if (
mapper.getInputSchema().getNames().count(
mapper.getInputSchema().join(
name, flag.name)) == 0)
947 mapper.getInputSchema().find<afw::table::Flag>(
name +
"_" + flag.name).
key;
955 "PSF shape in celestial moments",
965 _instFluxTransform(inputCatalog, outputCatalog,
wcs,
photoCalib);
966 _centroidTransform(inputCatalog, outputCatalog,
wcs,
photoCalib);
973 inputCatalog.getSchema()[inputCatalog.getSchema().join(
_name,
"psf")]);
978 for (; inSrc != inputCatalog.end(); ++inSrc, ++outSrc) {
991 _outShapeKey.
set(*outSrc, outShape);