26 #include "boost/math/constants/constants.hpp"
29 #include "lsst/afw/detection/HeavyFootprint.h"
31 #include "lsst/afw/geom/ellipses/Ellipse.h"
32 #include "lsst/afw/geom/ellipses/PixelRegion.h"
33 #include "lsst/afw/geom/ellipses/GridTransform.h"
35 namespace lsst {
namespace meas {
namespace base {
37 FlagDefinitionList flagDefinitions;
45 return flagDefinitions;
52 double computeOldBlendedness(
53 PTR(afw::detection::Footprint
const) childFootprint,
54 afw::image::Image<float>
const & parentImage
56 if (!childFootprint) {
58 pex::exceptions::LogicError,
59 "blendedness_old requires a Footprint."
63 PTR(afw::detection::HeavyFootprint<float>
const) childHeavy =
64 std::dynamic_pointer_cast<afw::detection::HeavyFootprint<
float> const>(childFootprint);
70 if (!parentImage.getBBox(afw::image::PARENT).contains(childHeavy->getBBox())) {
72 pex::exceptions::LogicError,
73 "Child footprint extends beyond image."
80 typedef afw::image::Image<float>::const_x_iterator ParentPixIter;
81 typedef ndarray::Array<float const,1,1>::Iterator ChildPixIter;
82 auto spanIter = childHeavy->getSpans()->begin();
83 auto const spanEnd = childHeavy->getSpans()->end();
84 ChildPixIter childPixIter = childHeavy->getImageArray().begin();
87 while (spanIter != spanEnd) {
88 afw::geom::Span
const & span = *spanIter;
89 ParentPixIter parentPixIter = parentImage.x_at(
90 span.getBeginX() - parentImage.getX0(),
91 span.getY() - parentImage.getY0()
93 int const width = span.getWidth();
95 for (
int x = 0;
x < width; ++parentPixIter, ++childPixIter, ++
x) {
96 cp += (*childPixIter) * ((*parentPixIter) - (*childPixIter));
97 cc += (*childPixIter) * (*childPixIter);
107 class FluxAccumulator {
110 FluxAccumulator() :
_w(0.0),
_ww(0.0),
_wd(0.0) {}
112 void operator()(
double,
double,
float weight,
float data) {
114 _ww += weight*weight;
118 double getFlux()
const {
return _w*
_wd/
_ww; }
127 class ShapeAccumulator :
public FluxAccumulator {
130 ShapeAccumulator() : FluxAccumulator(),
_wdxx(0.0),
_wdyy(0.0),
_wdxy(0.0) {}
132 void operator()(
double x,
double y,
float weight,
float data) {
133 FluxAccumulator::operator()(x, y, weight, data);
134 _wdxx += x*x*weight*data;
135 _wdyy += y*y*weight*data;
136 _wdxy += x*y*weight*data;
139 ShapeResult getShape()
const {
155 template <
typename Accumulator>
157 afw::image::MaskedImage<float>
const & image,
158 afw::geom::Point2D
const & centroid,
159 afw::geom::ellipses::Quadrupole
const & shape,
160 double nSigmaWeightMax,
161 Accumulator & accumulatorRaw,
162 Accumulator & accumulatorAbs
164 afw::geom::Box2I bbox = image.getBBox(lsst::afw::image::PARENT);
166 afw::geom::ellipses::Ellipse ellipse(shape, centroid);
167 ellipse.getCore().scale(nSigmaWeightMax);
172 afw::geom::LinearTransform transform = shape.getGridTransform();
174 typedef afw::geom::ellipses::PixelRegion::Iterator SpanIter;
175 typedef afw::geom::Span::Iterator PointIter;
176 typedef afw::image::MaskedImage<float>::const_x_iterator PixelIter;
178 afw::geom::ellipses::PixelRegion region(ellipse);
179 bool isContained = bbox.contains(region.getBBox());
180 SpanIter
const spanEnd = region.end();
181 for (SpanIter spanIter = region.begin(); spanIter != spanEnd; ++spanIter) {
182 afw::geom::Span span = *spanIter;
184 if (span.getY() < bbox.getMinY() || span.getY() > bbox.getMaxY()) {
187 span = afw::geom::Span(
189 std::max(span.getMinX(), bbox.getMinX()),
190 std::min(span.getMaxX(), bbox.getMaxX())
192 if (span.getMinX() > span.getMaxX()) {
196 PixelIter pixelIter = image.x_at(
197 span.getBeginX() - image.getX0(),
198 span.getY() - image.getY0()
200 PointIter
const pointEnd = span.end();
201 for (PointIter pointIter = span.begin(); pointIter != pointEnd; ++pointIter, ++pixelIter) {
202 afw::geom::Extent2D d = afw::geom::Point2D(*pointIter) - centroid;
203 afw::geom::Extent2D td = transform(d);
205 float weight = std::exp(static_cast<float>(-0.5*td.computeSquaredNorm()));
206 float data = pixelIter.image();
207 accumulatorRaw(d.getX(), d.getY(), weight, data);
208 float variance = pixelIter.variance();
211 accumulatorAbs(d.getX(), d.getY(), weight, std::abs(data) - bias);
225 _old = schema.addField<
double>(
226 schema.join(name,
"old"),
227 "blendedness from dot products: (child.dot(parent)/child.dot(child) - 1)"
231 _fluxRaw = schema.addField<
double>(
232 schema.join(name,
"raw_flux"),
233 "measure of how flux is affected by neighbors: (1 - flux.child/flux.parent)"
235 _fluxChildRaw = schema.addField<
double>(
236 schema.join(name,
"raw_flux_child"),
237 "flux of the child, measured with a Gaussian weight matched to the child",
240 _fluxParentRaw = schema.addField<
double>(
241 schema.join(name,
"raw_flux_parent"),
242 "flux of the parent, measured with a Gaussian weight matched to the child",
245 _fluxAbs = schema.addField<
double>(
246 schema.join(name,
"abs_flux"),
247 "measure of how flux is affected by neighbors: (1 - flux.child/flux.parent)"
249 _fluxChildAbs = schema.addField<
double>(
250 schema.join(name,
"abs_flux_child"),
251 "flux of the child, measured with a Gaussian weight matched to the child",
254 _fluxParentAbs = schema.addField<
double>(
255 schema.join(name,
"abs_flux_parent"),
256 "flux of the parent, measured with a Gaussian weight matched to the child",
262 "shape of the child, measured with a Gaussian weight matched to the child",
265 "shape of the parent, measured with a Gaussian weight matched to the child",
268 "shape of the child, measured with a Gaussian weight matched to the child",
271 "shape of the parent, measured with a Gaussian weight matched to the child",
281 float normalization = 0.5f*std::erfc(-data/std::sqrt(2.0f*variance));
282 if (!(normalization > 0)) {
286 return data + (std::sqrt(0.5f*variance/boost::math::constants::pi<float>()) *
287 std::exp(-0.5f*(data*data)/variance) / normalization);
291 return (std::sqrt(2.0f*variance/boost::math::constants::pi<float>()) *
292 std::exp(-0.5f*(mu*mu)/variance)) -
293 mu*std::erfc(mu/std::sqrt(2.0f*variance));
296 void BlendednessAlgorithm::_measureMoments(
297 afw::image::MaskedImage<float>
const & image,
298 afw::table::SourceRecord & child,
299 afw::table::Key<double>
const & fluxRawKey,
300 afw::table::Key<double>
const & fluxAbsKey,
306 if (!child.getTable()->getCentroidKey().isValid()) {
307 throw LSST_EXCEPT(pex::exceptions::LogicError,
308 "Centroid Key must be defined to measure the blendedness flux");
312 if (!child.getTable()->getShapeKey().isValid()) {
313 throw LSST_EXCEPT(pex::exceptions::LogicError,
314 "Shape Key must be defined to measure the blendedness shape");
319 if (child.getTable()->getCentroidFlagKey().isValid()) {
320 if (child.getCentroidFlag()) {
327 if (child.getTable()->getShapeFlagKey().isValid()) {
328 if (child.getShapeFlag()) {
333 if (!(child.getShape().getDeterminant() >= 0.0)) {
339 if (!(std::isfinite(child.getX()) && std::isfinite(child.getY()))) {
349 ShapeAccumulator accumulatorRaw;
350 ShapeAccumulator accumulatorAbs;
360 child.set(fluxRawKey, accumulatorRaw.getFlux());
361 child.set(fluxAbsKey, std::max(accumulatorAbs.getFlux(), 0.0));
363 _shapeRawKey.
set(child, accumulatorRaw.getShape());
364 _shapeAbsKey.
set(child, accumulatorAbs.getShape());
365 }
else if (_ctrl.
doFlux) {
366 FluxAccumulator accumulatorRaw;
367 FluxAccumulator accumulatorAbs;
376 child.set(fluxRawKey, accumulatorRaw.getFlux());
377 child.set(fluxAbsKey, std::max(accumulatorAbs.getFlux(), 0.0));
382 afw::image::MaskedImage<float>
const & image,
383 afw::table::SourceRecord & child
385 _measureMoments(image, child, _fluxChildRaw, _fluxChildAbs, _shapeChildRaw, _shapeChildAbs);
390 afw::image::MaskedImage<float>
const & image,
391 afw::table::SourceRecord & child
394 child.set(_old, computeOldBlendedness(child.getFootprint(), *image.getImage()));
396 _measureMoments(image, child, _fluxParentRaw, _fluxParentAbs, _shapeParentRaw, _shapeParentAbs);
398 child.set(_fluxRaw, 1.0 - child.get(_fluxChildRaw)/child.get(_fluxParentRaw));
399 child.set(_fluxAbs, 1.0 - child.get(_fluxChildAbs)/child.get(_fluxParentAbs));
400 if (child.get(_fluxParentAbs) == 0.0) {
static FlagDefinition const NO_CENTROID
static FlagDefinition const FAILURE
A FunctorKey for ShapeResult.
void measureParentPixels(afw::image::MaskedImage< float > const &image, afw::table::SourceRecord &child) const
static float computeAbsExpectation(float data, float variance)
Compute the posterior expectation value of the true flux in a pixel from its (Gaussian) likelihood an...
virtual void set(afw::table::BaseRecord &record, ShapeResult const &value) const
Set a ShapeResult in the given record.
static FlagDefinition const NO_SHAPE
void measureChildPixels(afw::image::MaskedImage< float > const &image, afw::table::SourceRecord &child) const
double nSigmaWeightMax
"Radius factor that sets the maximum extent of the weight function (and hence the flux measurements)"...
bool doShape
"Whether to compute quantities related to the Gaussian-weighted shape" ;
void setValue(afw::table::BaseRecord &record, std::size_t i, bool value) const
Set the flag field corresponding to the given flag index.
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.
Algorithm provides no uncertainy information at all.
static float computeAbsBias(float mu, float variance)
Compute the bias induced by using the absolute value of a pixel instead of its value.
static FlagDefinitionList const & getFlagDefinitions()
vector-type utility class to build a collection of FlagDefinitions
bool doFlux
"Whether to compute quantities related to the Gaussian-weighted flux" ;
bool doOld
"Whether to compute HeavyFootprint dot products (the old deblend.blendedness parameter)" ; ...
BlendednessAlgorithm(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
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.