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) {
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.
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
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
A custom container class for records, based on std::vector.
afw::table::Schema schema
A mapping between the keys of two Schemas, used to copy data between them.
Only the diagonal elements of the covariance matrix are provided.
A reusable struct for centroid measurements.
double const getIxy() const
iterator at(int const x, int const y) const
Return an iterator at the point (x, y)
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.
Point< double, 2 > Point2D
CentroidElement x
x (column) coordinate of the measured position
ImagePtr getImage(bool const noThrow=false) const
Return a (Ptr to) the MaskedImage's image.
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.
Transformer transform(LinearTransform const &transform)
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.
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
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.
double const PI
The ratio of a circle's circumference to diameter.
MaskedImageT getMaskedImage()
Return the MaskedImage.
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.
static Result computeAdaptiveMoments(ImageT const &image, afw::geom::Point2D const &position, Control const &ctrl=Control())
AngleUnit const radians
constant with units of radians
SdssShapeResult()
Constructor; initializes everything to NaN.
double const getIyy() const
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.
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,...)
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.
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.
Algorithm provides no uncertainy information at all.
A FunctorKey for CentroidResult.
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.
double getDeterminant() const
Return the determinant of the matrix representation.
Extent< double, 2 > Extent2D
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
#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
Schema getSchema() const
Return the schema associated with the catalog's table.
double const getIxx() const
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.