LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
Blendedness.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 /*
3  * This file is part of meas_base.
4  *
5  * Developed for the LSST Data Management System.
6  * This product includes software developed by the LSST Project
7  * (https://www.lsst.org).
8  * See the COPYRIGHT file at the top-level directory of this distribution
9  * for details of code ownership.
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <https://www.gnu.org/licenses/>.
23  */
24 
25 #include <cmath>
26 
27 #include "boost/math/constants/constants.hpp"
28 
35 
36 namespace lsst {
37 namespace meas {
38 namespace base {
39 namespace {
40 FlagDefinitionList flagDefinitions;
41 } // namespace
42 
43 FlagDefinition const BlendednessAlgorithm::FAILURE = flagDefinitions.addFailureFlag();
44 FlagDefinition const BlendednessAlgorithm::NO_CENTROID =
45  flagDefinitions.add("flag_noCentroid", "Object has no centroid");
46 FlagDefinition const BlendednessAlgorithm::NO_SHAPE =
47  flagDefinitions.add("flag_noShape", "Object has no shape");
48 
50 
51 namespace {
52 
53 double computeOldBlendedness(PTR(afw::detection::Footprint const) childFootprint,
54  afw::image::Image<float> const& parentImage) {
55  if (!childFootprint) {
56  throw LSST_EXCEPT(pex::exceptions::LogicError, "blendedness_old requires a Footprint.");
57  }
58 
60  childHeavy = std::dynamic_pointer_cast<afw::detection::HeavyFootprint<float> const>(childFootprint);
61 
62  if (!childHeavy) {
63  return 0.0; // if it's not a HeavyFootprint, it's not blended.
64  }
65 
66  if (!parentImage.getBBox(afw::image::PARENT).contains(childHeavy->getBBox())) {
67  throw LSST_EXCEPT(pex::exceptions::LogicError, "Child footprint extends beyond image.");
68  }
69 
70  // Iterate over all the spans in the child HeavyFootprint,
71  // along with iterators for the child pixels (from the HeavyFootprint)
72  // and parent pixels (from the Exposure).
73  typedef afw::image::Image<float>::const_x_iterator ParentPixIter;
75  auto spanIter = childHeavy->getSpans()->begin();
76  auto const spanEnd = childHeavy->getSpans()->end();
77  ChildPixIter childPixIter = childHeavy->getImageArray().begin();
78  double cp = 0.0; // child.dot(parent)
79  double cc = 0.0; // child.dot(child)
80  while (spanIter != spanEnd) {
81  afw::geom::Span const& span = *spanIter;
82  ParentPixIter parentPixIter =
83  parentImage.x_at(span.getBeginX() - parentImage.getX0(), span.getY() - parentImage.getY0());
84  int const width = span.getWidth();
85  // Iterate over the pixels within the span, updating the dot products.
86  for (int x = 0; x < width; ++parentPixIter, ++childPixIter, ++x) {
87  cp += (*childPixIter) * ((*parentPixIter) - (*childPixIter));
88  cc += (*childPixIter) * (*childPixIter);
89  }
90  ++spanIter;
91  }
92  if (cc > 0.0) {
93  return cp / cc;
94  }
95  return 0.0;
96 }
97 
98 class FluxAccumulator {
99 public:
100  FluxAccumulator() : _w(0.0), _ww(0.0), _wd(0.0) {}
101 
102  void operator()(double, double, float weight, float data) {
103  _w += weight;
104  _ww += weight * weight;
105  _wd += weight * data;
106  }
107 
108  double getFlux() const { return _w * _wd / _ww; }
109 
110 protected:
111  double _w;
112  double _ww;
113  double _wd;
114 };
115 
116 class ShapeAccumulator : public FluxAccumulator {
117 public:
118  ShapeAccumulator() : FluxAccumulator(), _wdxx(0.0), _wdyy(0.0), _wdxy(0.0) {}
119 
120  void operator()(double x, double y, float weight, float data) {
121  FluxAccumulator::operator()(x, y, weight, data);
122  _wdxx += x * x * weight * data;
123  _wdyy += y * y * weight * data;
124  _wdxy += x * y * weight * data;
125  }
126 
127  ShapeResult getShape() const {
128  // Factor of 2 corrects for bias from weight function (correct is exact for an object
129  // with a Gaussian profile.)
130  ShapeResult result;
131  result.xx = 2.0 * _wdxx / _wd;
132  result.yy = 2.0 * _wdyy / _wd;
133  result.xy = 2.0 * _wdxy / _wd;
134  return result;
135  }
136 
137 private:
138  double _wdxx;
139  double _wdyy;
140  double _wdxy;
141 };
142 
143 template <typename Accumulator>
144 void computeMoments(afw::image::MaskedImage<float> const& image, geom::Point2D const& centroid,
145  afw::geom::ellipses::Quadrupole const& shape, double nSigmaWeightMax,
146  Accumulator& accumulatorRaw, Accumulator& accumulatorAbs) {
147  geom::Box2I bbox = image.getBBox(afw::image::PARENT);
148 
149  afw::geom::ellipses::Ellipse ellipse(shape, centroid);
150  ellipse.getCore().scale(nSigmaWeightMax);
151 
152  // To evaluate an elliptically-symmetric function, we transform points
153  // by the following transform, then evaluate a circularly-symmetric function
154  // at the transformed positions.
155  geom::LinearTransform transform = shape.getGridTransform();
156 
157  typedef afw::geom::ellipses::PixelRegion::Iterator SpanIter; // yields Spans
158  typedef afw::geom::Span::Iterator PointIter; // yields Point2I positions
159  typedef afw::image::MaskedImage<float>::const_x_iterator PixelIter; // yields pixel values
160 
161  afw::geom::ellipses::PixelRegion region(ellipse);
162  bool isContained = bbox.contains(region.getBBox());
163  SpanIter const spanEnd = region.end();
164  for (SpanIter spanIter = region.begin(); spanIter != spanEnd; ++spanIter) {
165  afw::geom::Span span = *spanIter;
166  if (!isContained) {
167  if (span.getY() < bbox.getMinY() || span.getY() > bbox.getMaxY()) {
168  continue;
169  }
170  span = afw::geom::Span(span.getY(), std::max(span.getMinX(), bbox.getMinX()),
171  std::min(span.getMaxX(), bbox.getMaxX()));
172  if (span.getMinX() > span.getMaxX()) {
173  continue;
174  }
175  }
176  PixelIter pixelIter = image.x_at(span.getBeginX() - image.getX0(), span.getY() - image.getY0());
177  PointIter const pointEnd = span.end();
178  for (PointIter pointIter = span.begin(); pointIter != pointEnd; ++pointIter, ++pixelIter) {
179  geom::Extent2D d = geom::Point2D(*pointIter) - centroid;
180  geom::Extent2D td = transform(d);
181  // use single precision for faster exp, erf
182  float weight = std::exp(static_cast<float>(-0.5 * td.computeSquaredNorm()));
183  float data = pixelIter.image();
184  accumulatorRaw(d.getX(), d.getY(), weight, data);
185  float variance = pixelIter.variance();
186  float mu = BlendednessAlgorithm::computeAbsExpectation(data, variance);
187  float bias = BlendednessAlgorithm::computeAbsBias(mu, variance);
188  accumulatorAbs(d.getX(), d.getY(), weight, std::abs(data) - bias);
189  }
190  }
191 }
192 
193 } // namespace
194 
197  : _ctrl(ctrl) {
198  if (_ctrl.doOld) {
199  _old = schema.addField<double>(
200  schema.join(name, "old"),
201  "Blendedness from dot products: (child.dot(parent)/child.dot(child) - 1)");
202  }
203  if (_ctrl.doFlux) {
204  _raw = schema.addField<double>(
205  schema.join(name, "raw"),
206  "Measure of how much the flux is affected by neighbors: "
207  "(1 - child_instFlux/parent_instFlux). Operates on the \"raw\" pixel values.");
208  _instFluxChildRaw = schema.addField<double>(
209  schema.join(name, "raw_child_instFlux"),
210  "Instrumental flux of the child, measured with a Gaussian weight matched to the child. "
211  "Operates on the \"raw\" pixel values.", "count");
212  _instFluxParentRaw = schema.addField<double>(
213  schema.join(name, "raw_parent_instFlux"),
214  "Instrumental flux of the parent, measured with a Gaussian weight matched to the child. "
215  "Operates on the \"raw\" pixel values.", "count");
216  _abs = schema.addField<double>(
217  schema.join(name, "abs"),
218  "Measure of how much the flux is affected by neighbors: "
219  "(1 - child_instFlux/parent_instFlux). "
220  "Operates on the absolute value of the pixels to try to obtain a \"de-noised\" value. "
221  "See section 4.9.11 of Bosch et al. 2018, PASJ, 70, S5 for details.");
222  _instFluxChildAbs = schema.addField<double>(
223  schema.join(name, "abs_child_instFlux"),
224  "Instrumental flux of the child, measured with a Gaussian weight matched to the child. "
225  "Operates on the absolute value of the pixels to try to obtain a \"de-noised\" value. "
226  "See section 4.9.11 of Bosch et al. 2018, PASJ, 70, S5 for details.", "count");
227  _instFluxParentAbs = schema.addField<double>(
228  schema.join(name, "abs_parent_instFlux"),
229  "Instrumental flux of the parent, measured with a Gaussian weight matched to the child. "
230  "Operates on the absolute value of the pixels to try to obtain a \"de-noised\" value. "
231  "See section 4.9.11 of Bosch et al. 2018, PASJ, 70, S5 for details.", "count");
232  }
233  if (_ctrl.doShape) {
234  _shapeChildRaw = ShapeResultKey::addFields(
235  schema, schema.join(name, "raw_child"),
236  "Shape of the child, measured with a Gaussian weight matched to the child. "
237  "Operates on the \"raw\" pixel values.", NO_UNCERTAINTY);
238  _shapeParentRaw = ShapeResultKey::addFields(
239  schema, schema.join(name, "raw_parent"),
240  "Shape of the parent, measured with a Gaussian weight matched to the child. "
241  "Operates on the \"raw\" pixel values.", NO_UNCERTAINTY);
242  _shapeChildAbs = ShapeResultKey::addFields(
243  schema, schema.join(name, "abs_child"),
244  "Shape of the child, measured with a Gaussian weight matched to the child. "
245  "Operates on the absolute value of the pixels to try to obtain a \"de-noised\" value. "
246  "See section 4.9.11 of Bosch et al. 2018, PASJ, 70, S5 for details.",
248  _shapeParentAbs = ShapeResultKey::addFields(
249  schema, schema.join(name, "abs_parent"),
250  "Shape of the parent, measured with a Gaussian weight matched to the child. "
251  "Operates on the absolute value of the pixels to try to obtain a \"de-noised\" value. "
252  "See section 4.9.11 of Bosch et al. 2018, PASJ, 70, S5 for details.",
254  }
255  if (_ctrl.doShape || _ctrl.doFlux) {
256  _flagHandler = FlagHandler::addFields(schema, name, getFlagDefinitions());
257  }
258 }
259 
261  float normalization = 0.5f * std::erfc(-data / std::sqrt(2.0f * variance));
262  if (!(normalization > 0)) {
263  // avoid division by zero; we know the limit at data << -sigma -> 0.
264  return 0.0;
265  }
266  return data + (std::sqrt(0.5f * variance / boost::math::constants::pi<float>()) *
267  std::exp(-0.5f * (data * data) / variance) / normalization);
268 }
269 
271  return (std::sqrt(2.0f * variance / boost::math::constants::pi<float>()) *
272  std::exp(-0.5f * (mu * mu) / variance)) -
273  mu * std::erfc(mu / std::sqrt(2.0f * variance));
274 }
275 
276 void BlendednessAlgorithm::_measureMoments(afw::image::MaskedImage<float> const& image,
278  afw::table::Key<double> const& instFluxRawKey,
279  afw::table::Key<double> const& instFluxAbsKey,
280  ShapeResultKey const& _shapeRawKey,
281  ShapeResultKey const& _shapeAbsKey) const {
282  if (_ctrl.doFlux || _ctrl.doShape) {
283  if (!child.getTable()->getCentroidKey().isValid()) {
285  "Centroid Key must be defined to measure the blendedness instFlux");
286  }
287  }
288  if (_ctrl.doShape) {
289  if (!child.getTable()->getShapeKey().isValid()) {
291  "Shape Key must be defined to measure the blendedness shape");
292  }
293  }
294  if (_ctrl.doShape || _ctrl.doFlux) {
295  bool fatal = false;
296  if (child.getTable()->getCentroidFlagKey().isValid()) {
297  if (child.getCentroidFlag()) {
298  // don't set general flag, because even a failed centroid should
299  // just fall back to the peak, and that should be fine for this
300  // measurement.
301  _flagHandler.setValue(child, NO_CENTROID.number, true);
302  }
303  }
304  if (child.getTable()->getShapeFlagKey().isValid()) {
305  if (child.getShapeFlag()) {
306  _flagHandler.setValue(child, NO_SHAPE.number, true);
307  _flagHandler.setValue(child, FAILURE.number, true);
308  }
309  }
310  if (!(child.getShape().getDeterminant() >= 0.0)) {
311  // shape flag should have been set already, but we're paranoid
312  _flagHandler.setValue(child, NO_SHAPE.number, true);
313  _flagHandler.setValue(child, FAILURE.number, true);
314  fatal = true;
315  }
316  if (!(std::isfinite(child.getX()) && std::isfinite(child.getY()))) {
317  // shape flag should have been set already, but we're paranoid
318  _flagHandler.setValue(child, NO_CENTROID.number, true);
319  _flagHandler.setValue(child, FAILURE.number, true);
320  fatal = true;
321  }
322  if (fatal) return;
323  }
324 
325  if (_ctrl.doShape) {
326  ShapeAccumulator accumulatorRaw;
327  ShapeAccumulator accumulatorAbs;
328  computeMoments(image, child.getCentroid(), child.getShape(), _ctrl.nSigmaWeightMax, accumulatorRaw,
329  accumulatorAbs);
330  if (_ctrl.doFlux) {
331  child.set(instFluxRawKey, accumulatorRaw.getFlux());
332  child.set(instFluxAbsKey, std::max(accumulatorAbs.getFlux(), 0.0));
333  }
334  _shapeRawKey.set(child, accumulatorRaw.getShape());
335  _shapeAbsKey.set(child, accumulatorAbs.getShape());
336  } else if (_ctrl.doFlux) {
337  FluxAccumulator accumulatorRaw;
338  FluxAccumulator accumulatorAbs;
339  computeMoments(image, child.getCentroid(), child.getShape(), _ctrl.nSigmaWeightMax, accumulatorRaw,
340  accumulatorAbs);
341  child.set(instFluxRawKey, accumulatorRaw.getFlux());
342  child.set(instFluxAbsKey, std::max(accumulatorAbs.getFlux(), 0.0));
343  }
344 }
345 
347  afw::table::SourceRecord& child) const {
348  _measureMoments(image, child, _instFluxChildRaw, _instFluxChildAbs, _shapeChildRaw, _shapeChildAbs);
349 }
350 
352  afw::table::SourceRecord& child) const {
353  if (_ctrl.doOld) {
354  child.set(_old, computeOldBlendedness(child.getFootprint(), *image.getImage()));
355  }
356  _measureMoments(image, child, _instFluxParentRaw, _instFluxParentAbs, _shapeParentRaw, _shapeParentAbs);
357  if (_ctrl.doFlux) {
358  child.set(_raw, 1.0 - child.get(_instFluxChildRaw) / child.get(_instFluxParentRaw));
359  child.set(_abs, 1.0 - child.get(_instFluxChildAbs) / child.get(_instFluxParentAbs));
360  if (child.get(_instFluxParentAbs) == 0.0) {
361  // We can get NaNs in the absolute measure if both parent and child have only negative
362  // biased-corrected instFluxes (which we clip to zero). We can't really recover from this,
363  // so we should set the flag.
364  _flagHandler.setValue(child, FAILURE.number, true);
365  }
366  }
367 }
368 
369 } // namespace base
370 } // namespace meas
371 } // namespace lsst
int getBeginX() const noexcept
Begin (inclusive) x-value.
Definition: Span.h:81
Defines the fields and offsets for a table.
Definition: Schema.h:50
virtual void set(afw::table::BaseRecord &record, ShapeResult const &value) const
Set a ShapeResult in the given record.
Angle abs(Angle const &a)
Definition: Angle.h:106
char * data
Definition: BaseTable.cc:205
static FlagDefinition const NO_CENTROID
Definition: Blendedness.h:71
ShapeSlotDefinition::MeasValue getShape() const
Get the value of the Shape slot measurement.
Definition: Source.h:656
double _w
Definition: Blendedness.cc:111
afw::table::Key< afw::table::Array< VariancePixelT > > variance
T exp(T... args)
std::string join(std::string const &a, std::string const &b) const
Join strings using the field delimiter appropriate for this Schema.
Definition: Schema.cc:641
A range of pixels within one row of an Image.
Definition: Span.h:47
_const_view_t::x_iterator const_x_iterator
A const iterator for traversing the pixels in a row.
Definition: ImageBase.h:142
py::object result
Definition: schema.cc:284
bool getShapeFlag() const
Return true if the measurement in the Shape slot failed.
Definition: Source.h:664
int y
Definition: SpanSet.cc:49
std::shared_ptr< Footprint > getFootprint() const
Definition: Source.h:89
T erfc(T... args)
int getX0() const
Return the image&#39;s column-origin.
Definition: ImageBase.h:324
void setValue(afw::table::BaseRecord &record, std::size_t i, bool value) const
Set the flag field corresponding to the given flag index.
Definition: FlagHandler.h:262
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Definition: BaseRecord.h:125
static FlagDefinition const FAILURE
Definition: Blendedness.h:70
Point< double, 2 > Point2D
Definition: Point.h:324
A set of pixels in an Image, including those pixels&#39; actual values.
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage&#39;s image.
Definition: MaskedImage.h:1058
A FunctorKey for ShapeResult.
STL class.
static float computeAbsExpectation(float data, float variance)
Compute the posterior expectation value of the true instrumental flux in a pixel from its (Gaussian) ...
Definition: Blendedness.cc:260
T min(T... args)
FastFinder::Iterator Iterator
Definition: FastFinder.cc:179
static FlagDefinition const NO_SHAPE
Definition: Blendedness.h:72
A base class for image defects.
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
Definition: ImageBase.h:463
std::shared_ptr< SourceTable const > getTable() const
Definition: Source.h:93
UnitVector3d centroid(VertexIterator const begin, VertexIterator const end)
#define PTR(...)
Definition: base.h:41
void measureChildPixels(afw::image::MaskedImage< float > const &image, afw::table::SourceRecord &child) const
Definition: Blendedness.cc:346
int getWidth() const noexcept
Return the number of pixels.
Definition: Span.h:78
double getX() const
Return the centroid slot x coordinate.
Definition: Source.h:668
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:74
bool getCentroidFlag() const
Return true if the measurement in the Centroid slot failed.
Definition: Source.h:652
T isfinite(T... args)
table::Schema schema
Definition: Camera.cc:161
double nSigmaWeightMax
"Radius factor that sets the maximum extent of the weight function (and hence the " "flux measurement...
Definition: Blendedness.h:52
bool doShape
"Whether to compute quantities related to the Gaussian-weighted shape" ;
Definition: Blendedness.h:48
T dynamic_pointer_cast(T... args)
Reports errors in the logical structure of the program.
Definition: Runtime.h:46
table::Box2IKey bbox
Definition: Detector.cc:166
T max(T... args)
Class to describe the properties of a detected object from an image.
Definition: Footprint.h:62
double x
static FlagHandler addFields(afw::table::Schema &schema, std::string const &prefix, FlagDefinitionList const &flagDefs, FlagDefinitionList const &exclDefs=FlagDefinitionList::getEmptyList())
Add Flag fields to a schema, creating a FlagHandler object to manage them.
Definition: FlagHandler.cc:37
afw::table::Key< double > weight
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
int getY0() const
Return the image&#39;s row-origin.
Definition: ImageBase.h:332
bool contains(Point2I const &point) const noexcept
Return true if the box contains the point.
Definition: Box.cc:113
Algorithm provides no uncertainy information at all.
Definition: constants.h:44
double _ww
Definition: Blendedness.cc:112
static float computeAbsBias(float mu, float variance)
Compute the bias induced by using the absolute value of a pixel instead of its value.
Definition: Blendedness.cc:270
double getY() const
Return the centroid slot y coordinate.
Definition: Source.h:669
x_iterator x_at(int x, int y) const
Return an x_iterator to the point (x, y) in the image.
Definition: ImageBase.h:425
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
Definition: BaseRecord.h:138
Record class that contains measurements made on a single exposure.
Definition: Source.h:82
int getY() const noexcept
Return the y-value.
Definition: Span.h:76
T sqrt(T... args)
table::Key< int > transform
double _wd
Definition: Blendedness.cc:113
SpanPixelIterator Iterator
An iterator over points in the Span.
Definition: Span.h:50
Extent< double, 2 > Extent2D
Definition: Extent.h:400
vector-type utility class to build a collection of FlagDefinitions
Definition: FlagHandler.h:60
double getDeterminant() const
Return the determinant of the matrix representation.
Definition: Quadrupole.h:83
static FlagDefinitionList const & getFlagDefinitions()
Definition: Blendedness.cc:49
bool doFlux
"Whether to compute quantities related to the Gaussian-weighted flux" ;
Definition: Blendedness.h:46
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:59
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
Definition: Schema.cc:668
bool doOld
"Whether to compute HeavyFootprint dot products (the old deblend.blendedness parameter)" ; ...
Definition: Blendedness.h:44
void measureParentPixels(afw::image::MaskedImage< float > const &image, afw::table::SourceRecord &child) const
Definition: Blendedness.cc:351
const_MaskedImageIterator< typename Image::x_iterator, typename Mask::x_iterator, typename Variance::x_iterator > const_x_iterator
A const_iterator to a row of a MaskedImage.
Definition: MaskedImage.h:561
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
Definition: Source.h:644
BlendednessAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
Definition: Blendedness.cc:195
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.