lsst.meas.base  14.0-12-g233aa8e+6
ApertureFlux.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 /*
3  * LSST Data Management System
4  * Copyright 2008-2016 AURA/LSST.
5  *
6  * This product includes software developed by the
7  * LSST Project (http://www.lsst.org/).
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the LSST License Statement and
20  * the GNU General Public License along with this program. If not,
21  * see <http://www.lsstcorp.org/LegalNotices/>.
22  */
23 
24 #include <numeric>
25 
26 #include "boost/algorithm/string/replace.hpp"
27 
28 #include "ndarray/eigen.h"
29 
32 #include "lsst/afw/table/Source.h"
35 
36 namespace lsst { namespace meas { namespace base {
37 namespace {
38 FlagDefinitionList flagDefinitions;
39 } // end anonymous
40 
41 FlagDefinition const ApertureFluxAlgorithm::FAILURE = flagDefinitions.addFailureFlag();
42 FlagDefinition const ApertureFluxAlgorithm::APERTURE_TRUNCATED = flagDefinitions.add("flag_apertureTruncated", "aperture did not fit within measurement image");
43 FlagDefinition const ApertureFluxAlgorithm::SINC_COEFFS_TRUNCATED = flagDefinitions.add("flag_sincCoeffsTruncated", "full sinc coefficient image did not fit within measurement image");
44 
46  return flagDefinitions;
47 }
48 
49 
50 ApertureFluxControl::ApertureFluxControl() : radii(10), maxSincRadius(10.0), shiftKernel("lanczos5") {
51  // defaults here stolen from HSC pipeline defaults
52  static std::array<double,10> defaultRadii = {{
53  3.0, 4.5, 6.0, 9.0, 12.0, 17.0, 25.0, 35.0, 50.0, 70.0
54  }};
55  std::copy(defaultRadii.begin(), defaultRadii.end(), radii.begin());
56 }
57 
58 
60  std::string prefix = (boost::format("%s_%.1f") % name % radius).str();
61  return boost::replace_all_copy(prefix, ".", "_");
62 }
63 
64 ApertureFluxAlgorithm::Keys::Keys(
65  afw::table::Schema & schema, std::string const & prefix, std::string const & doc, bool isSinc
66 ) :
67  fluxKey(FluxResultKey::addFields(schema, prefix, doc)),
68  flags(
69  // The exclusion List can either be empty, or constain the sinc coeffs flag
71  schema, prefix,
73  isSinc ? FlagDefinitionList() : FlagDefinitionList({{SINC_COEFFS_TRUNCATED}})
74  )
75  )
76 {}
77 
79  Control const & ctrl,
80  std::string const & name,
81  afw::table::Schema & schema,
82  daf::base::PropertySet & metadata
83 
84 ) : _ctrl(ctrl),
85  _centroidExtractor(schema, name)
86 {
87  _keys.reserve(ctrl.radii.size());
88  for (std::size_t i = 0; i < ctrl.radii.size(); ++i) {
89  metadata.add(name + "_radii", ctrl.radii[i]);
91  std::string doc = (boost::format("flux within %f-pixel aperture") % ctrl.radii[i]).str();
92  _keys.push_back(Keys(schema, prefix, doc, ctrl.radii[i] <= ctrl.maxSincRadius));
93  }
94 }
95 
97  // This should only get called in the case of completely unexpected failures, so it's not terrible
98  // that we just set the general failure flags for all radii here instead of trying to figure out
99  // which ones we've already done. Any known failure modes are handled inside measure().
100  for (std::size_t i = 0; i < _ctrl.radii.size(); ++i) {
101  _keys[i].flags.handleFailure(measRecord, error);
102  }
103 }
104 
106  Result const & result,
107  afw::table::SourceRecord & record,
108  int index
109 ) const {
110  record.set(_keys[index].fluxKey, result);
111  if (result.getFlag(FAILURE.number)) {
112  _keys[index].flags.setValue(record, FAILURE.number, true);
113  }
114  if (result.getFlag(APERTURE_TRUNCATED.number)) {
115  _keys[index].flags.setValue(record, APERTURE_TRUNCATED.number, true);
116  }
117  if (result.getFlag(SINC_COEFFS_TRUNCATED.number)) {
118  _keys[index].flags.setValue(record, SINC_COEFFS_TRUNCATED.number, true);
119  }
120 }
121 
122 namespace {
123 
124 // Helper function for computeSincFlux get Sinc flux coefficients, and handle cases where the coeff
125 // image needs to be clipped to fit in the measurement image
126 template <typename T>
127 CONST_PTR(afw::image::Image<T>) getSincCoeffs(
128  afw::geom::Box2I const & bbox, // measurement image bbox we need to fit inside
129  afw::geom::ellipses::Ellipse const & ellipse, // ellipse that defines the aperture
130  ApertureFluxAlgorithm::Result & result, // result object where we set flags if we do clip
131  ApertureFluxAlgorithm::Control const & ctrl // configuration
132 ) {
133  CONST_PTR(afw::image::Image<T>) cImage = SincCoeffs<T>::get(ellipse.getCore(), 0.0);
134  cImage = afw::math::offsetImage(
135  *cImage,
136  ellipse.getCenter().getX(),
137  ellipse.getCenter().getY(),
138  ctrl.shiftKernel
139  );
140  if (!bbox.contains(cImage->getBBox())) {
141  // We had to clip out at least part part of the coeff image,
142  // but since that's much larger than the aperture (and close
143  // to zero outside the aperture), it may not be a serious
144  // problem.
145  result.setFlag(ApertureFluxAlgorithm::SINC_COEFFS_TRUNCATED.number);
146  afw::geom::Box2I overlap = cImage->getBBox();
147  overlap.clip(bbox);
148  if (!overlap.contains(afw::geom::Box2I(ellipse.computeBBox()))) {
149  // The clipping was indeed serious, as we we did have to clip within
150  // the aperture; can't expect any decent answer at this point.
151  result.setFlag(ApertureFluxAlgorithm::APERTURE_TRUNCATED.number);
152  result.setFlag(ApertureFluxAlgorithm::FAILURE.number);
153  }
154  cImage = std::make_shared< afw::image::Image<T> >(*cImage, overlap);
155  }
156  return cImage;
157 }
158 
159 } // anonymous
160 
161 template <typename T>
163  afw::image::Image<T> const & image,
164  afw::geom::ellipses::Ellipse const & ellipse,
165  Control const & ctrl
166 ) {
167  Result result;
168  CONST_PTR(afw::image::Image<T>) cImage = getSincCoeffs<T>(image.getBBox(), ellipse, result, ctrl);
169  if (result.getFlag(APERTURE_TRUNCATED.number)) return result;
170  afw::image::Image<T> subImage(image, cImage->getBBox());
171  result.flux = (subImage.getArray().template asEigen<Eigen::ArrayXpr>()
172  * cImage->getArray().template asEigen<Eigen::ArrayXpr>()).sum();
173  return result;
174 }
175 
176 template <typename T>
178  afw::image::MaskedImage<T> const & image,
179  afw::geom::ellipses::Ellipse const & ellipse,
180  Control const & ctrl
181 ) {
182  Result result;
183  CONST_PTR(afw::image::Image<T>) cImage = getSincCoeffs<T>(image.getBBox(), ellipse, result, ctrl);
184  if (result.getFlag(APERTURE_TRUNCATED.number)) return result;
186  result.flux = (subImage.getImage()->getArray().template asEigen<Eigen::ArrayXpr>()
187  * cImage->getArray().template asEigen<Eigen::ArrayXpr>()).sum();
188  result.fluxSigma = std::sqrt(
189  (subImage.getVariance()->getArray().template asEigen<Eigen::ArrayXpr>().template cast<T>()
190  * cImage->getArray().template asEigen<Eigen::ArrayXpr>().square()).sum()
191  );
192  return result;
193 }
194 
195 template <typename T>
197  afw::image::Image<T> const & image,
198  afw::geom::ellipses::Ellipse const & ellipse,
199  Control const & ctrl
200 ) {
201  Result result;
202  afw::geom::ellipses::PixelRegion region(ellipse); // behaves mostly like a Footprint
203  if (!image.getBBox().contains(region.getBBox())) {
205  result.setFlag(FAILURE.number);
206  return result;
207  }
208  result.flux = 0;
209  for (
210  afw::geom::ellipses::PixelRegion::Iterator spanIter = region.begin(), spanEnd = region.end();
211  spanIter != spanEnd;
212  ++spanIter
213  ) {
214  typename afw::image::Image<T>::x_iterator pixIter = image.x_at(
215  spanIter->getBeginX() - image.getX0(),
216  spanIter->getY() - image.getY0()
217  );
218  result.flux += std::accumulate(pixIter, pixIter + spanIter->getWidth(), 0.0);
219  }
220  return result;
221 }
222 
223 template <typename T>
225  afw::image::MaskedImage<T> const & image,
226  afw::geom::ellipses::Ellipse const & ellipse,
227  Control const & ctrl
228 ) {
229  Result result;
230  afw::geom::ellipses::PixelRegion region(ellipse); // behaves mostly like a Footprint
231  if (!image.getBBox().contains(region.getBBox())) {
233  result.setFlag(FAILURE.number);
234  return result;
235  }
236  result.flux = 0.0;
237  result.fluxSigma = 0.0;
238  for (
239  afw::geom::ellipses::PixelRegion::Iterator spanIter = region.begin(), spanEnd = region.end();
240  spanIter != spanEnd;
241  ++spanIter
242  ) {
243  typename afw::image::MaskedImage<T>::Image::x_iterator pixIter = image.getImage()->x_at(
244  spanIter->getBeginX() - image.getX0(),
245  spanIter->getY() - image.getY0()
246  );
247  typename afw::image::MaskedImage<T>::Variance::x_iterator varIter = image.getVariance()->x_at(
248  spanIter->getBeginX() - image.getX0(),
249  spanIter->getY() - image.getY0()
250  );
251  result.flux += std::accumulate(pixIter, pixIter + spanIter->getWidth(), 0.0);
252  // we use this to hold variance as we accumulate...
253  result.fluxSigma += std::accumulate(varIter, varIter + spanIter->getWidth(), 0.0);
254  }
255  result.fluxSigma = std::sqrt(result.fluxSigma); // ...and switch back to sigma here.
256  return result;
257 }
258 
259 template <typename T>
261  afw::image::Image<T> const & image,
262  afw::geom::ellipses::Ellipse const & ellipse,
263  Control const & ctrl
264 ) {
265  return (afw::geom::ellipses::Axes(ellipse.getCore()).getB() <= ctrl.maxSincRadius)
266  ? computeSincFlux(image, ellipse, ctrl)
267  : computeNaiveFlux(image, ellipse, ctrl);
268 }
269 
270 template <typename T>
272  afw::image::MaskedImage<T> const & image,
273  afw::geom::ellipses::Ellipse const & ellipse,
274  Control const & ctrl
275 ) {
276  return (afw::geom::ellipses::Axes(ellipse.getCore()).getB() <= ctrl.maxSincRadius)
277  ? computeSincFlux(image, ellipse, ctrl)
278  : computeNaiveFlux(image, ellipse, ctrl);
279 }
280 #define INSTANTIATE(T) \
281  template \
282  ApertureFluxAlgorithm::Result ApertureFluxAlgorithm::computeFlux( \
283  afw::image::Image<T> const &, \
284  afw::geom::ellipses::Ellipse const &, \
285  Control const & \
286  ); \
287  template \
288  ApertureFluxAlgorithm::Result ApertureFluxAlgorithm::computeFlux( \
289  afw::image::MaskedImage<T> const &, \
290  afw::geom::ellipses::Ellipse const &, \
291  Control const & \
292  ); \
293  template \
294  ApertureFluxAlgorithm::Result ApertureFluxAlgorithm::computeSincFlux( \
295  afw::image::Image<T> const &, \
296  afw::geom::ellipses::Ellipse const &, \
297  Control const & \
298  ); \
299  template \
300  ApertureFluxAlgorithm::Result ApertureFluxAlgorithm::computeSincFlux( \
301  afw::image::MaskedImage<T> const &, \
302  afw::geom::ellipses::Ellipse const &, \
303  Control const & \
304  ); \
305  template \
306  ApertureFluxAlgorithm::Result ApertureFluxAlgorithm::computeNaiveFlux( \
307  afw::image::Image<T> const &, \
308  afw::geom::ellipses::Ellipse const &, \
309  Control const & \
310  ); \
311  template \
312  ApertureFluxAlgorithm::Result ApertureFluxAlgorithm::computeNaiveFlux( \
313  afw::image::MaskedImage<T> const &, \
314  afw::geom::ellipses::Ellipse const &, \
315  Control const & \
316  )
317 
318 INSTANTIATE(float);
319 INSTANTIATE(double);
320 
322  Control const & ctrl,
323  std::string const & name,
324  afw::table::SchemaMapper & mapper
325 ) :
326  BaseTransform(name),
327  _ctrl(ctrl)
328 {
329  for (std::size_t i = 0; i < _ctrl.radii.size(); ++i) {
332  if (_ctrl.radii[i] > _ctrl.maxSincRadius && flag == ApertureFluxAlgorithm::SINC_COEFFS_TRUNCATED) {
333  continue;
334  }
335  mapper.addMapping(mapper.getInputSchema().find<afw::table::Flag>((boost::format("%s_%s") %
337  flag.name).str()).key);
338  }
339  _magKeys.push_back(MagResultKey::addFields(mapper.editOutputSchema(),
341  }
342 }
343 
345  afw::table::SourceCatalog const & inputCatalog,
346  afw::table::BaseCatalog & outputCatalog,
347  afw::image::Wcs const & wcs,
348  afw::image::Calib const & calib
349 ) const {
350  checkCatalogSize(inputCatalog, outputCatalog);
352  for (std::size_t i = 0; i < _ctrl.radii.size(); ++i) {
353  fluxKeys.push_back(FluxResultKey(inputCatalog.getSchema()[
355  }
356  afw::table::SourceCatalog::const_iterator inSrc = inputCatalog.begin();
357  afw::table::BaseCatalog::iterator outSrc = outputCatalog.begin();
358  {
359  // While noThrow is in scope, converting a negative flux to a magnitude
360  // returns NaN rather than throwing.
362  for (; inSrc != inputCatalog.end() && outSrc != outputCatalog.end(); ++inSrc, ++outSrc) {
363  for (std::size_t i = 0; i < _ctrl.radii.size(); ++i) {
364  FluxResult fluxResult = fluxKeys[i].get(*inSrc);
365  _magKeys[i].set(*outSrc, calib.getMagnitude(fluxResult.flux, fluxResult.fluxSigma));
366  }
367  }
368  }
369 }
370 
371 }}} // namespace lsst::meas::base
372 
std::size_t size() const
return the current size (number of defined elements) of the collection
Definition: FlagHandler.h:133
VariancePtr getVariance() const
static boost::shared_ptr< CoeffT const > get(afw::geom::ellipses::Axes const &outerEllipse, float const innerRadiusFactor=0.0)
Get the coefficients for an aperture.
Definition: SincCoeffs.cc:532
T copy(T... args)
virtual void fail(afw::table::SourceRecord &measRecord, MeasurementError *error=nullptr) const
Handle an exception thrown by the current algorithm by setting flags in the given record...
Definition: ApertureFlux.cc:96
bool getFlag(unsigned int index) const
Return the flag value associated with the given bit.
Definition: ApertureFlux.h:254
Key< T > addMapping(Key< T > const &inputKey, bool doReplace=false)
void setFlag(unsigned int index, bool value=true)
Set the flag value associated with the given bit.
Definition: ApertureFlux.h:262
geom::Box2I getBBox(ImageOrigin origin=PARENT) const
Schema const getInputSchema() const
afw::table::Schema schema
static FlagDefinitionList const & getFlagDefinitions()
Definition: ApertureFlux.cc:45
Simple class used to define and document flags The name and doc constitute the identity of the FlagDe...
Definition: FlagHandler.h:38
_view_t::x_iterator x_iterator
Temporarily replace negative fluxes with NaNs.
BaseCore const & getCore() const
ApertureFluxAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema, daf::base::PropertySet &metadata)
Construct the algorithm and add its fields to the given Schema.
Definition: ApertureFlux.cc:78
T end(T... args)
SchemaItem< T > find(std::string const &name) const
void copyResultToRecord(Result const &result, afw::table::SourceRecord &record, int index) const
Return the flag definitions which apply to aperture flux measurements.
Exception to be thrown when a measurement algorithm experiences a known failure mode.
Definition: exceptions.h:48
static FluxResultKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc)
Add a pair of _flux, _fluxSigma fields to a Schema, and return a FluxResultKey that points to them...
geom::Box2I getBBox(ImageOrigin const origin=PARENT) const
#define CONST_PTR(...)
Configuration object for multiple-aperture flux algorithms.
Definition: ApertureFlux.h:43
STL class.
T push_back(T... args)
static FlagDefinition const FAILURE
Definition: ApertureFlux.h:89
static FlagDefinition const APERTURE_TRUNCATED
Definition: ApertureFlux.h:90
static Result computeFlux(afw::image::Image< T > const &image, afw::geom::ellipses::Ellipse const &ellipse, Control const &ctrl=Control())
Compute the flux (and optionally, uncertanties) within an aperture using the algorithm determined by ...
double maxSincRadius
"Maximum radius (in pixels) for which the sinc algorithm should be used instead of the " "faster naiv...
Definition: ApertureFlux.h:57
static std::string makeFieldPrefix(std::string const &name, double radius)
Construct an appropriate prefix for table fields.
Definition: ApertureFlux.cc:59
static FlagDefinition const SINC_COEFFS_TRUNCATED
Definition: ApertureFlux.h:91
static Result computeNaiveFlux(afw::image::Image< T > const &image, afw::geom::ellipses::Ellipse const &ellipse, Control const &ctrl=Control())
Compute the flux (and optionally, uncertanties) within an aperture using naive photometry.
Flux flux
Measured flux in DN.
Definition: FluxUtilities.h:38
bool contains(Point2I const &point) const
A FunctorKey for FluxResult.
Definition: FluxUtilities.h:56
double getMagnitude(double const flux) const
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
static MagResultKey addFields(afw::table::Schema &schema, std::string const &name)
Add a pair of _mag, _magErr fields to a Schema, and return a MagResultKey that points to them...
T size(T... args)
STL class.
#define INSTANTIATE(T)
Point2D const & getCenter() const
std::shared_ptr< ImageT > offsetImage(ImageT const &image, float dx, float dy, std::string const &algorithmName="lanczos5", unsigned int buffer=0)
Base::const_iterator const_iterator
T begin(T... args)
Abstract base class for all C++ measurement transformations.
Definition: Transform.h:82
static Result computeSincFlux(afw::image::Image< T > const &image, afw::geom::ellipses::Ellipse const &ellipse, Control const &ctrl=Control())
Compute the flux (and optionally, uncertanties) within an aperture using Sinc photometry.
STL class.
x_iterator x_at(int x, int y) const
void set(Key< T > const &key, U const &value)
void checkCatalogSize(afw::table::BaseCatalog const &cat1, afw::table::BaseCatalog const &cat2) const
Ensure that catalogs have the same size.
Definition: Transform.h:101
void clip(Box2I const &other)
T sqrt(T... args)
std::string shiftKernel
"Warping kernel used to shift Sinc photometry coefficients to different center positions" ; ...
Definition: ApertureFlux.h:62
T accumulate(T... args)
std::vector< double > radii
"Radius (in pixels) of apertures." ;
Definition: ApertureFlux.h:51
vector-type utility class to build a collection of FlagDefinitions
Definition: FlagHandler.h:63
void add(std::string const &name, T const &value)
FluxErrElement fluxSigma
1-Sigma error (sqrt of variance) on flux in DN.
Definition: FluxUtilities.h:39
virtual void operator()(afw::table::SourceCatalog const &inputCatalog, afw::table::BaseCatalog &outputCatalog, afw::image::Wcs const &wcs, afw::image::Calib const &calib) const
ApertureFluxTransform(Control const &ctrl, std::string const &name, afw::table::SchemaMapper &mapper)
A reusable result struct for flux measurements.
Definition: FluxUtilities.h:37
T reserve(T... args)
A Result struct for running an aperture flux algorithm with a single radius.
Definition: ApertureFlux.h:251