lsst.meas.base  14.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Groups Pages
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 
30 #include "lsst/afw/math/offsetImage.h"
31 #include "lsst/afw/geom/ellipses/PixelRegion.h"
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 
59 std::string ApertureFluxAlgorithm::makeFieldPrefix(std::string const & name, double radius) {
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
70  FlagHandler::addFields(
71  schema, prefix,
72  ApertureFluxAlgorithm::getFlagDefinitions(),
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]);
90  std::string prefix = ApertureFluxAlgorithm::makeFieldPrefix(name, 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 
96 void ApertureFluxAlgorithm::fail(afw::table::SourceRecord & measRecord, MeasurementError * error) const {
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;
185  afw::image::MaskedImage<T> subImage(image, cImage->getBBox(afw::image::PARENT), afw::image::PARENT);
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) {
330  for (std::size_t j = 0; j < ApertureFluxAlgorithm::getFlagDefinitions().size(); j++) {
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);
351  std::vector<FluxResultKey> fluxKeys;
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 
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
void setFlag(unsigned int index, bool value=true)
Set the flag value associated with the given bit.
Definition: ApertureFlux.h:262
#define INSTANTIATE(T)
void checkCatalogSize(afw::table::BaseCatalog const &cat1, afw::table::BaseCatalog const &cat2) const
Ensure that catalogs have the same size.
Definition: Transform.h:101
Simple class used to define and document flags The name and doc constitute the identity of the FlagDe...
Definition: FlagHandler.h:38
virtual void operator()(afw::table::SourceCatalog const &inputCatalog, afw::table::BaseCatalog &outputCatalog, afw::image::Wcs const &wcs, afw::image::Calib const &calib) const
Temporarily replace negative fluxes with NaNs.
static FlagDefinitionList const & getFlagDefinitions()
Definition: ApertureFlux.cc:45
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.
Exception to be thrown when a measurement algorithm experiences a known failure mode.
Definition: exceptions.h:48
A singleton to calculate and cache the coefficients for sinc photometry.
Definition: SincCoeffs.h:43
void copyResultToRecord(Result const &result, afw::table::SourceRecord &record, int index) const
Return the flag definitions which apply to aperture flux measurements.
Configuration object for multiple-aperture flux algorithms.
Definition: ApertureFlux.h:43
Utility class for handling flag fields that indicate the failure modes of an algorithm.
Definition: FlagHandler.h:156
static FlagDefinition const FAILURE
Definition: ApertureFlux.h:89
static FlagDefinition const APERTURE_TRUNCATED
Definition: ApertureFlux.h:90
Base class for multiple-aperture photometry algorithms.
Definition: ApertureFlux.h:83
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
&quot;Maximum radius (in pixels) for which the sinc algorithm should be used instead of the &quot; &quot;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.
_centroidExtractor(schema, name)
Definition: ApertureFlux.cc:85
Flux flux
Measured flux in DN.
Definition: FluxUtilities.h:38
A FunctorKey for FluxResult.
Definition: FluxUtilities.h:56
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...
bool getFlag(unsigned int index) const
Return the flag value associated with the given bit.
Definition: ApertureFlux.h:254
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.
std::vector< double > radii
&quot;Radius (in pixels) of apertures.&quot; ;
Definition: ApertureFlux.h:51
vector-type utility class to build a collection of FlagDefinitions
Definition: FlagHandler.h:63
FluxErrElement fluxSigma
1-Sigma error (sqrt of variance) on flux in DN.
Definition: FluxUtilities.h:39
std::size_t size() const
return the current size (number of defined elements) of the collection
Definition: FlagHandler.h:133
ApertureFluxTransform(Control const &ctrl, std::string const &name, afw::table::SchemaMapper &mapper)
A reusable result struct for flux measurements.
Definition: FluxUtilities.h:37
A Result struct for running an aperture flux algorithm with a single radius.
Definition: ApertureFlux.h:251