26 #include "boost/tuple/tuple.hpp"
38 namespace pexPolicy = lsst::pex::policy;
39 namespace pexExceptions = lsst::pex::exceptions;
40 namespace pexLogging = lsst::pex::logging;
41 namespace afwDet = lsst::afw::detection;
43 namespace afwGeom = lsst::afw::geom;
51 typedef Eigen::Matrix<double,4,4,Eigen::DontAlign> Matrix4d;
54 double computeFluxScale(SdssShapeResult
const & result) {
61 double const Mxx = result.xx;
62 double const Mxy = result.xy;
63 double const Myy = result.yy;
65 double const Muu_p_Mvv = Mxx + Myy;
66 double const Muu_m_Mvv = ::sqrt(::pow(Mxx - Myy, 2) + 4*::pow(Mxy, 2));
67 double const Muu = 0.5*(Muu_p_Mvv + Muu_m_Mvv);
68 double const Mvv = 0.5*(Muu_p_Mvv - Muu_m_Mvv);
86 calc_fisher(SdssShapeResult
const& shape,
89 float const A = shape.flux;
90 float const sigma11W = shape.xx;
91 float const sigma12W = shape.xy;
92 float const sigma22W = shape.yy;
94 double const D = sigma11W*sigma22W - sigma12W*sigma12W;
96 if (D <= std::numeric_limits<double>::epsilon()) {
97 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
98 "Determinant is too small calculating Fisher matrix");
103 if (bkgd_var <= 0.0) {
104 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
105 (
boost::format(
"Background variance must be positive (saw %g)") % bkgd_var).str());
113 double fac = F*A/(4.0*D);
115 fisher(0, 1) = fac*sigma22W;
116 fisher(1, 0) = fisher(0, 1);
117 fisher(0, 2) = fac*sigma11W;
118 fisher(2, 0) = fisher(0, 2);
119 fisher(0, 3) = -fac*2*sigma12W;
120 fisher(3, 0) = fisher(0, 3);
122 fac = 3.0*F*A*A/(16.0*D*D);
123 fisher(1, 1) = fac*sigma22W*sigma22W;
124 fisher(2, 2) = fac*sigma11W*sigma11W;
125 fisher(3, 3) = fac*4.0*(sigma12W*sigma12W + D/3.0);
127 fisher(1, 2) = fisher(3, 3)/4.0;
128 fisher(2, 1) = fisher(1, 2);
129 fisher(1, 3) = fac*(-2*sigma22W*sigma12W);
130 fisher(3, 1) = fisher(1, 3);
131 fisher(2, 3) = fac*(-2*sigma11W*sigma12W);
132 fisher(3, 2) = fisher(2, 3);
139 template<
typename ImageT>
140 struct ImageAdaptor {
141 typedef ImageT Image;
143 static bool const hasVariance =
false;
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> > {
158 static bool const hasVariance =
true;
165 return mimage.
at(ix, iy).variance();
170 boost::tuple<std::pair<bool, double>, double, double,
double>
171 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 return boost::make_tuple(std::make_pair(
false, det), NaN, NaN, NaN);
180 return boost::make_tuple(std::make_pair(
true, det), sigma22/det, -sigma12/det, sigma11/det);
184 bool shouldInterp(
double sigma11,
double sigma22,
double det) {
185 float const xinterp = 0.25;
186 return (sigma11 < xinterp || sigma22 < xinterp || det < xinterp*xinterp);
192 afw::geom::Box2I computeAdaptiveMomentsBBox(
193 afw::geom::Box2I
const & bbox,
198 double maxRadius = 1000
200 double radius = std::min(4*std::sqrt(std::max(sigma11_w, sigma22_w)), maxRadius);
202 afw::geom::Box2I result(afw::geom::Box2D(center - offset, center + offset));
211 template<
bool fluxOnly,
typename ImageT>
213 calcmom(ImageT
const&
image,
214 float xcen,
float ycen,
218 double w11,
double w12,
double w22,
221 double *psumx,
double *psumy,
222 double *psumxx,
double *psumxy,
double *psumyy,
231 double sum, sumx, sumy, sumxx, sumyy, sumxy, sums4;
232 #define RECALC_W 0 // estimate sigmaXX_w within BBox?
234 double wsum, wsumxx, wsumxy, wsumyy;
236 wsum = wsumxx = wsumxy = wsumyy = 0;
240 if (fabs(w11) > 1e6 || fabs(w12) > 1e6 || fabs(w22) > 1e6) {
244 sum = sumx = sumy = sumxx = sumxy = sumyy = sums4 = 0;
246 int const ix0 = bbox.
getMinX();
247 int const ix1 = bbox.
getMaxX();
248 int const iy0 = bbox.
getMinY();
249 int const iy1 = bbox.
getMaxY();
251 if (ix0 < 0 || ix1 >= image.getWidth() || iy0 < 0 || iy1 >= image.getHeight()) {
255 for (
int i = iy0; i <= iy1; ++i) {
256 typename ImageT::x_iterator ptr = image.x_at(ix0, i);
257 float const y = i - ycen;
258 float const y2 = y*
y;
259 float const yl = y - 0.375;
260 float const yh = y + 0.375;
261 for (
int j = ix0; j <= ix1; ++j, ++ptr) {
264 float const xl = x - 0.375;
265 float const xh = x + 0.375;
267 float expon = xl*xl*w11 + yl*yl*w22 + 2.0*xl*yl*w12;
268 tmp = xh*xh*w11 + yh*yh*w22 + 2.0*xh*yh*w12;
269 expon = (expon > tmp) ? expon : tmp;
270 tmp = xl*xl*w11 + yh*yh*w22 + 2.0*xl*yh*w12;
271 expon = (expon > tmp) ? expon : tmp;
272 tmp = xh*xh*w11 + yl*yl*w22 + 2.0*xh*yl*w12;
273 expon = (expon > tmp) ? expon : tmp;
277 for (Y = yl; Y <= yh; Y += 0.25) {
278 double const interpY2 = Y*
Y;
279 for (X = xl; X <= xh; X += 0.25) {
280 double const interpX2 = X*
X;
281 double const interpXy = X*
Y;
282 expon = interpX2*w11 + 2*interpXy*w12 + interpY2*w22;
283 weight = std::exp(-0.5*expon);
288 sumx += ymod*(X + xcen);
289 sumy += ymod*(Y + ycen);
305 sumxx += interpX2*ymod;
306 sumxy += interpXy*ymod;
307 sumyy += interpY2*ymod;
309 sums4 += expon*expon*ymod;
317 float expon = x2*w11 + 2*xy*w12 + y2*w22;
320 weight = std::exp(-0.5*expon);
346 sums4 += expon*expon*ymod;
354 boost::tuple<std::pair<bool, double>, double, double,
double>
const weights = getWeights(w11, w12, w22);
355 double const detW = weights.get<1>()*weights.get<3>() - std::pow(weights.get<2>(), 2);
366 if (psums4 != NULL) {
372 if (wsum > 0 && !fluxOnly) {
373 double det = w11*w22 - w12*w12;
377 printf(
"%g %g %g %g %g %g\n", w22/det, -w12/det, w11/det, wsumxx, wsumxy, wsumyy);
381 return (fluxOnly || (sum > 0 && sumxx > 0 && sumyy > 0)) ? 0 : -1;
389 template<
typename ImageT>
390 bool getAdaptiveMoments(ImageT
const& mimage,
double bkgd,
double xcen,
double ycen,
double shiftmax,
391 SdssShapeResult *shape,
int maxIter,
float tol1,
float tol2)
396 double sumxx, sumxy, sumyy;
398 float const xcen0 = xcen;
399 float const ycen0 = ycen;
401 double sigma11W = 1.5;
402 double sigma12W = 0.0;
403 double sigma22W = 1.5;
405 double w11 = -1, w12 = -1, w22 = -1;
406 float e1_old = 1e6, e2_old = 1e6;
407 float sigma11_ow_old = 1e6;
409 typename ImageAdaptor<ImageT>::Image
const &image = ImageAdaptor<ImageT>().getImage(mimage);
417 bool interpflag =
false;
420 for (; iter < maxIter; iter++) {
422 sigma11W, sigma12W, sigma22W);
423 boost::tuple<std::pair<bool, double>, double, double,
double> weights =
424 getWeights(sigma11W, sigma12W, sigma22W);
425 if (!weights.get<0>().first) {
430 double const detW = weights.get<0>().second;
432 #if 0 // this form was numerically unstable on my G4 powerbook
435 assert(sigma11W*sigma22W >= sigma12W*sigma12W - std::numeric_limits<float>::epsilon());
439 const double ow11 = w11;
440 const double ow12 = w12;
441 const double ow22 = w22;
443 w11 = weights.get<1>();
444 w12 = weights.get<2>();
445 w22 = weights.get<3>();
447 if (shouldInterp(sigma11W, sigma22W, detW)) {
451 sigma11_ow_old = 1.e6;
461 if (calcmom<false>(image, xcen, ycen, bbox, bkgd, interpflag, w11, w12, w22,
462 &I0, &sum, &sumx, &sumy, &sumxx, &sumxy, &sumyy, &sums4) < 0) {
478 if (fabs(shape->x - xcen0) > shiftmax || fabs(shape->y - ycen0) > shiftmax) {
484 float const sigma11_ow = sumxx/
sum;
485 float const sigma22_ow = sumyy/
sum;
486 float const sigma12_ow = sumxy/
sum;
488 if (sigma11_ow <= 0 || sigma22_ow <= 0) {
493 float const d = sigma11_ow + sigma22_ow;
494 float const e1 = (sigma11_ow - sigma22_ow)/d;
495 float const e2 = 2.0*sigma12_ow/d;
500 fabs(e1 - e1_old) < tol1 && fabs(e2 - e2_old) < tol1 &&
501 fabs(sigma11_ow/sigma11_ow_old - 1.0) < tol2 ) {
507 sigma11_ow_old = sigma11_ow;
532 float ow11, ow12, ow22;
534 boost::tuple<std::pair<bool, double>, double, double,
double> weights =
535 getWeights(sigma11_ow, sigma12_ow, sigma22_ow);
536 if (!weights.get<0>().first) {
541 ow11 = weights.get<1>();
542 ow12 = weights.get<2>();
543 ow22 = weights.get<3>();
549 weights = getWeights(n11, n12, n22);
550 if (!weights.get<0>().first) {
556 sigma11W = weights.get<1>();
557 sigma12W = weights.get<2>();
558 sigma22W = weights.get<3>();
561 if (sigma11W <= 0 || sigma22W <= 0) {
567 if (iter == maxIter) {
572 if (sumxx + sumyy == 0.0) {
580 if (calcmom<false>(image, xcen, ycen, bbox, bkgd, interpflag, w11, w12, w22,
581 &I0, &sum, &sumx, &sumy, &sumxx, &sumxy, &sumyy, NULL) < 0 || sum <= 0) {
594 sigma11W = sumxx/
sum;
595 sigma12W = sumxy/
sum;
596 sigma22W = sumyy/
sum;
600 shape->xx = sigma11W;
601 shape->xy = sigma12W;
602 shape->yy = sigma22W;
604 if (shape->xx + shape->yy != 0.0) {
608 if (ix >= 0 && ix < mimage.getWidth() && iy >= 0 && iy < mimage.getHeight()) {
609 float const bkgd_var =
610 ImageAdaptor<ImageT>().getVariance(mimage, ix, iy);
612 if (bkgd_var > 0.0) {
614 Matrix4d fisher = calc_fisher(*shape, bkgd_var);
615 Matrix4d cov = fisher.inverse();
619 shape->fluxSigma = std::sqrt(cov(0, 0));
620 shape->xxSigma = std::sqrt(cov(1, 1));
621 shape->xySigma = std::sqrt(cov(2, 2));
622 shape->yySigma = std::sqrt(cov(3, 3));
623 shape->flux_xx_Cov = cov(0, 1);
624 shape->flux_xy_Cov = cov(0, 2);
625 shape->flux_yy_Cov = cov(0, 3);
626 shape->xx_yy_Cov = cov(1, 3);
627 shape->xx_xy_Cov = cov(1, 2);
628 shape->yy_xy_Cov = cov(2, 3);
641 flux_xx_Cov(std::numeric_limits<
ErrElement>::quiet_NaN()),
642 flux_yy_Cov(std::numeric_limits<
ErrElement>::quiet_NaN()),
643 flux_xy_Cov(std::numeric_limits<
ErrElement>::quiet_NaN())
646 static boost::array<FlagDefinition,SdssShapeAlgorithm::N_FLAGS>
const flagDefs = {{
647 {
"flag",
"general failure flag, set if anything went wrong"},
648 {
"flag_unweightedBad",
"Both weighted and unweighted moments were invalid"},
649 {
"flag_unweighted",
"Weighted moments converged to an invalid value; using unweighted moments"},
650 {
"flag_shift",
"centroid shifted by more than the maximum allowed amount"},
651 {
"flag_maxIter",
"Too many iterations in adaptive moments"}
656 std::string
const &
name
665 schema.
join(name,
"flux",
"xx",
"Cov"),
667 % schema.
join(name,
"flux") % schema.
join(name,
"xx")).str(),
671 schema.
join(name,
"flux",
"yy",
"Cov"),
673 % schema.
join(name,
"flux") % schema.
join(name,
"yy")).str(),
677 schema.
join(name,
"flux",
"xy",
"Cov"),
679 % schema.
join(name,
"flux") % schema.
join(name,
"xy")).str(),
690 _flux_xx_Cov(s[
"flux"][
"xx"][
"Cov"]),
691 _flux_yy_Cov(s[
"flux"][
"yy"][
"Cov"]),
692 _flux_xy_Cov(s[
"flux"][
"xy"][
"Cov"]),
693 _flagHandler(s, flagDefs.begin(), flagDefs.end())
744 std::string
const &
name,
748 _resultKey(
ResultKey::addFields(schema, name)),
749 _centroidExtractor(schema, name)
752 template <
typename ImageT>
754 ImageT
const & image,
758 double xcen = center.getX();
759 double ycen = center.getY();
761 xcen -= image.getX0();
762 ycen -= image.getY0();
767 }
else if (shiftmax > 10) {
774 image, control.
background, xcen, ycen, shiftmax, &result,
788 pex::exceptions::LogicError,
789 "Should not get singular moments unless a flag is set");
796 double fluxScale = computeFluxScale(result);
798 result.
flux *= fluxScale;
800 result.
x += image.getX0();
801 result.
y += image.getY0();
803 if (ImageAdaptor<ImageT>::hasVariance) {
812 template <
typename ImageT>
814 ImageT
const & image,
825 boost::tuple<std::pair<bool, double>, double, double,
double> weights =
830 if (!weights.get<0>().first) {
831 throw pex::exceptions::InvalidParameterError(
"Input shape is singular");
834 double const w11 = weights.get<1>();
835 double const w12 = weights.get<2>();
836 double const w22 = weights.get<3>();
837 bool const interp = shouldInterp(shape.
getIxx(), shape.
getIyy(), weights.get<0>().second);
840 if (calcmom<true>(ImageAdaptor<ImageT>().getImage(image), localCenter.getX(), localCenter.getY(),
841 bbox, 0.0, interp, w11, w12, w22, &i0, NULL, NULL, NULL, NULL, NULL, NULL, NULL)< 0) {
842 throw LSST_EXCEPT(pex::exceptions::RuntimeError,
"Error from calcmom");
847 result.flux = i0*2*wArea;
849 if (ImageAdaptor<ImageT>::hasVariance) {
850 int ix =
static_cast<int>(center.getX() - image.getX0());
851 int iy =
static_cast<int>(center.getY() - image.getY0());
855 ix % iy % image.getWidth() % image.getHeight()).str());
857 double var = ImageAdaptor<ImageT>().getVariance(image, ix, iy);
858 double i0Err = std::sqrt(var/wArea);
859 result.fluxSigma = i0Err*2*wArea;
884 #define INSTANTIATE_IMAGE(IMAGE) \
885 template SdssShapeResult SdssShapeAlgorithm::computeAdaptiveMoments( \
887 afw::geom::Point2D const &, \
890 template FluxResult SdssShapeAlgorithm::computeFixedMomentsFlux( \
892 afw::geom::ellipses::Quadrupole const &, \
893 afw::geom::Point2D const & \
896 #define INSTANTIATE_PIXEL(PIXEL) \
897 INSTANTIATE_IMAGE(lsst::afw::image::Image<PIXEL>); \
898 INSTANTIATE_IMAGE(lsst::afw::image::MaskedImage<PIXEL>);
906 std::string
const &
name,
910 _fluxTransform{
name, mapper},
911 _centroidTransform{
name, mapper}
913 for (
auto flag = flagDefs.begin() + 1; flag < flagDefs.end(); flag++) {
914 mapper.addMapping(mapper.getInputSchema().find<afw::table::Flag>(
915 mapper.getInputSchema().join(
name, flag->name)).key);
938 for (; inSrc != inputCatalog.
end(); ++inSrc, ++outSrc) {
An ellipse core with quadrupole moments as parameters.
Defines the fields and offsets for a table.
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...
MaskedImageT getMaskedImage()
Return the MaskedImage.
bool getValue(afw::table::BaseRecord const &record, int i) const
ImagePtr getImage(bool const noThrow=false) const
Return a (Ptr to) the MaskedImage's image.
A proxy type for name lookups in a Schema.
afw::table::Key< ErrElement > _flux_yy_Cov
ShapeTrMatrix makeShapeTransformMatrix(afw::geom::LinearTransform const &xform)
Construct a matrix suitable for transforming second moments.
SafeCentroidExtractor _centroidExtractor
table::Key< std::string > name
Public header class for ellipse library.
geom::AffineTransform linearizePixelToSky(coord::Coord const &coord, geom::AngleUnit skyUnit=geom::degrees) const
Return the local linear approximation to Wcs::pixelToSky at a point given in sky coordinates.
tbl::Key< double > weight
static FluxResult computeFixedMomentsFlux(ImageT const &image, afw::geom::ellipses::Quadrupole const &shape, afw::geom::Point2D const &position)
float tol2
"Convergence tolerance for FWHM" ;
int positionToIndex(double pos)
Convert image position to nearest integer index.
FlagHandler const & getFlagHandler() const
AngleUnit const radians
constant with units of radians
A custom container class for records, based on std::vector.
iterator at(int const x, int const y) const
Return an iterator at the point (x, y)
A mapping between the keys of two Schemas, used to copy data between them.
Only the diagonal elements of the covariance matrix are provided.
Extent< double, 2 > Extent2D
A reusable struct for centroid measurements.
afw::geom::ellipses::Quadrupole getQuadrupole()
double background
"Additional value to add to background" ;
definition of the Trace messaging facilities
The full covariance matrix is provided.
bool isValid() const
Return true if the key was initialized to valid offset.
CentroidElement x
x (column) coordinate of the measured position
int maxIter
"Maximum number of iterations" ;
Implementation of the WCS standard for a any projection.
bool isValid() const
Return True if the centroid key is valid.
double const getIyy() const
Exception to be thrown when a measurement algorithm experiences a known failure mode.
static FluxResultKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc)
float tol1
"Convergence tolerance for e1,e2" ;
boost::enable_if< typename ExpressionTraits< Scalar >::IsScalar, Scalar >::type sum(Scalar const &scalar)
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.
Shape const getShape() const
Return an afw::geom::ellipses object corresponding to xx, yy, xy.
An integer coordinate rectangle.
ShapeResultKey _shapeResult
A FunctorKey for ShapeResult.
table::Key< table::Array< Kernel::Pixel > > image
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
void setShapeErr(ShapeCov const &matrix)
Set the struct uncertainty elements from the given matrix, with rows and columns ordered (xx...
An include file to include the header files for lsst::afw::image.
A reusable struct for moments-based shape measurements.
virtual void set(afw::table::BaseRecord &record, ShapeResult const &value) const
Set a ShapeResult in the given record.
Result object SdssShapeAlgorithm.
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Schema getSchema() const
Return the schema associated with the catalog's table.
static Result computeAdaptiveMoments(ImageT const &image, afw::geom::Point2D const &position, Control const &ctrl=Control())
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)
Iterator class for CatalogT.
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
Flux flux
Measured flux in DN.
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.
double const getIxx() const
virtual void set(afw::table::BaseRecord &record, SdssShapeResult const &value) const
Set a CentroidResult in the given record.
std::bitset< SdssShapeAlgorithm::N_FLAGS > flags
Status flags (see SdssShapeAlgorithm).
#define LSST_EXCEPT(type,...)
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
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.
std::string join(std::string const &a, std::string const &b) const
Join strings using the field delimiter appropriate for this Schema.
A FunctorKey that maps SdssShapeResult to afw::table Records.
Transformer transform(LinearTransform const &transform)
Algorithm provides no uncertainy information at all.
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
double getDeterminant() const
Return the determinant of the matrix representation.
A FunctorKey for CentroidResult.
CentroidElement y
y (row) coordinate of the measured position
void handleFailure(afw::table::BaseRecord &record, MeasurementError const *error=NULL) const
Point< double, 2 > Point2D
double const getIxy() const
Record class that contains measurements made on a single exposure.
afw::table::Key< ErrElement > _flux_xy_Cov
double const PI
The ratio of a circle's circumference to diameter.
SdssShapeResultKey()
Default constructor; instance will not be usuable unless subsequently assigned to.
afw::table::Key< ErrElement > _flux_xx_Cov
#define INSTANTIATE_PIXEL(PIXEL)
ErrElement flux_xx_Cov
flux, xx term in the uncertainty covariance matrix
void setShape(Shape const &shape)
Set struct elements from the given Quadrupole object.
FluxErrElement fluxSigma
1-Sigma error (sqrt of variance) on flux in DN.
Eigen::Matrix< ShapeElement, 3, 3, Eigen::DontAlign > ShapeTrMatrix
ShapeCov const getShapeErr() const
Return the 3x3 symmetric covariance matrix, with rows and columns ordered (xx, yy, xy)
static FlagHandler addFields(afw::table::Schema &schema, std::string const &prefix, FlagDefinition const *begin, FlagDefinition const *end)
FluxResultKey _fluxResult
A reusable result struct for flux measurements.
ErrElement flux_xy_Cov
flux, xy term in the uncertainty covariance matrix
static ShapeResultKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc, UncertaintyEnum uncertainty, afw::table::CoordinateType coordType=afw::table::CoordinateType::PIXEL)
Add the appropriate fields to a Schema, and return a ShapeResultKey that manages them.