26 #include "boost/math/constants/constants.hpp" 39 FlagDefinitionList flagDefinitions;
44 flagDefinitions.add(
"flag_noCentroid",
"Object has no centroid");
46 flagDefinitions.add(
"flag_noShape",
"Object has no shape");
54 if (!childFootprint) {
73 typedef ndarray::Array<float const, 1, 1>::Iterator ChildPixIter;
74 auto spanIter = childHeavy->getSpans()->begin();
75 auto const spanEnd = childHeavy->getSpans()->end();
76 ChildPixIter childPixIter = childHeavy->getImageArray().begin();
79 while (spanIter != spanEnd) {
81 ParentPixIter parentPixIter =
85 for (
int x = 0;
x < width; ++parentPixIter, ++childPixIter, ++
x) {
86 cp += (*childPixIter) * ((*parentPixIter) - (*childPixIter));
87 cc += (*childPixIter) * (*childPixIter);
97 class FluxAccumulator {
99 FluxAccumulator() :
_w(0.0),
_ww(0.0),
_wd(0.0) {}
101 void operator()(
double,
double,
float weight,
float data) {
103 _ww += weight * weight;
104 _wd += weight * data;
107 double getFlux()
const {
return _w *
_wd /
_ww; }
115 class ShapeAccumulator :
public FluxAccumulator {
117 ShapeAccumulator() : FluxAccumulator(), _wdxx(0.0), _wdyy(0.0), _wdxy(0.0) {}
119 void operator()(
double x,
double y,
float weight,
float data) {
120 FluxAccumulator::operator()(x, y, weight, data);
121 _wdxx += x * x * weight * data;
122 _wdyy += y * y * weight * data;
123 _wdxy += x * y * weight * data;
126 ShapeResult getShape()
const {
130 result.xx = 2.0 * _wdxx /
_wd;
131 result.yy = 2.0 * _wdyy /
_wd;
132 result.xy = 2.0 * _wdxy /
_wd;
142 template <
typename Accumulator>
143 void computeMoments(afw::image::MaskedImage<float>
const&
image,
geom::Point2D const& centroid,
144 afw::geom::ellipses::Quadrupole
const& shape,
double nSigmaWeightMax,
145 Accumulator& accumulatorRaw, Accumulator& accumulatorAbs) {
148 afw::geom::ellipses::Ellipse ellipse(shape, centroid);
149 ellipse.getCore().scale(nSigmaWeightMax);
154 geom::LinearTransform
transform = shape.getGridTransform();
156 typedef afw::geom::ellipses::PixelRegion::Iterator SpanIter;
160 afw::geom::ellipses::PixelRegion region(ellipse);
161 bool isContained = bbox.contains(region.getBBox());
162 SpanIter
const spanEnd = region.end();
163 for (SpanIter spanIter = region.begin(); spanIter != spanEnd; ++spanIter) {
164 afw::geom::Span span = *spanIter;
166 if (span.getY() < bbox.getMinY() || span.getY() > bbox.getMaxY()) {
169 span = afw::geom::Span(span.getY(),
std::max(span.getMinX(), bbox.getMinX()),
170 std::min(span.getMaxX(), bbox.getMaxX()));
171 if (span.getMinX() > span.getMaxX()) {
175 PixelIter pixelIter = image.x_at(span.getBeginX() - image.getX0(), span.getY() - image.getY0());
176 PointIter
const pointEnd = span.end();
177 for (PointIter pointIter = span.begin(); pointIter != pointEnd; ++pointIter, ++pixelIter) {
181 float weight =
std::exp(static_cast<float>(-0.5 * td.computeSquaredNorm()));
182 float data = pixelIter.image();
183 accumulatorRaw(d.getX(), d.getY(), weight, data);
184 float variance = pixelIter.variance();
187 accumulatorAbs(d.getX(), d.getY(), weight, std::abs(data) - bias);
199 schema.
join(name,
"old"),
200 "blendedness from dot products: (child.dot(parent)/child.dot(child) - 1)");
204 schema.
join(name,
"raw_flux"),
205 "measure of how flux is affected by neighbors: (1 - flux.child/flux.parent)");
206 _fluxChildRaw = schema.
addField<
double>(
207 schema.
join(name,
"raw_flux_child"),
208 "flux of the child, measured with a Gaussian weight matched to the child",
"count");
209 _fluxParentRaw = schema.
addField<
double>(
210 schema.
join(name,
"raw_flux_parent"),
211 "flux of the parent, measured with a Gaussian weight matched to the child",
"count");
213 schema.
join(name,
"abs_flux"),
214 "measure of how flux is affected by neighbors: (1 - flux.child/flux.parent)");
215 _fluxChildAbs = schema.
addField<
double>(
216 schema.
join(name,
"abs_flux_child"),
217 "flux of the child, measured with a Gaussian weight matched to the child",
"count");
218 _fluxParentAbs = schema.
addField<
double>(
219 schema.
join(name,
"abs_flux_parent"),
220 "flux of the parent, measured with a Gaussian weight matched to the child",
"count");
224 schema, schema.
join(name,
"raw_child"),
225 "shape of the child, measured with a Gaussian weight matched to the child",
NO_UNCERTAINTY);
227 schema, schema.
join(name,
"raw_parent"),
228 "shape of the parent, measured with a Gaussian weight matched to the child",
NO_UNCERTAINTY);
230 schema, schema.
join(name,
"abs_child"),
231 "shape of the child, measured with a Gaussian weight matched to the child",
NO_UNCERTAINTY);
233 schema, schema.
join(name,
"abs_parent"),
234 "shape of the parent, measured with a Gaussian weight matched to the child",
NO_UNCERTAINTY);
243 if (!(normalization > 0)) {
247 return data + (
std::sqrt(0.5f * variance / boost::math::constants::pi<float>()) *
248 std::exp(-0.5f * (data * data) / variance) / normalization);
252 return (
std::sqrt(2.0f * variance / boost::math::constants::pi<float>()) *
253 std::exp(-0.5f * (mu * mu) / variance)) -
264 if (!child.
getTable()->getCentroidKey().isValid()) {
266 "Centroid Key must be defined to measure the blendedness flux");
270 if (!child.
getTable()->getShapeKey().isValid()) {
272 "Shape Key must be defined to measure the blendedness shape");
277 if (child.
getTable()->getCentroidFlagKey().isValid()) {
285 if (child.
getTable()->getShapeFlagKey().isValid()) {
307 ShapeAccumulator accumulatorRaw;
308 ShapeAccumulator accumulatorAbs;
312 child.
set(fluxRawKey, accumulatorRaw.getFlux());
313 child.
set(fluxAbsKey,
std::max(accumulatorAbs.getFlux(), 0.0));
315 _shapeRawKey.
set(child, accumulatorRaw.getShape());
316 _shapeAbsKey.
set(child, accumulatorAbs.getShape());
317 }
else if (_ctrl.
doFlux) {
318 FluxAccumulator accumulatorRaw;
319 FluxAccumulator accumulatorAbs;
322 child.
set(fluxRawKey, accumulatorRaw.getFlux());
323 child.
set(fluxAbsKey,
std::max(accumulatorAbs.getFlux(), 0.0));
329 _measureMoments(image, child, _fluxChildRaw, _fluxChildAbs, _shapeChildRaw, _shapeChildAbs);
337 _measureMoments(image, child, _fluxParentRaw, _fluxParentAbs, _shapeParentRaw, _shapeParentAbs);
339 child.
set(_fluxRaw, 1.0 - child.
get(_fluxChildRaw) / child.
get(_fluxParentRaw));
340 child.
set(_fluxAbs, 1.0 - child.
get(_fluxChildAbs) / child.
get(_fluxParentAbs));
341 if (child.
get(_fluxParentAbs) == 0.0) {
int getBeginX() const noexcept
virtual void set(afw::table::BaseRecord &record, ShapeResult const &value) const
Set a ShapeResult in the given record.
static FlagDefinition const NO_CENTROID
ShapeSlotDefinition::MeasValue getShape() const
std::string join(std::string const &a, std::string const &b) const
_const_view_t::x_iterator const_x_iterator
bool getShapeFlag() const
std::shared_ptr< Footprint > getFootprint() const
void setValue(afw::table::BaseRecord &record, std::size_t i, bool value) const
Set the flag field corresponding to the given flag index.
Field< T >::Value get(Key< T > const &key) const
static FlagDefinition const FAILURE
Point< double, 2 > Point2D
ImagePtr getImage() const
A FunctorKey for ShapeResult.
static float computeAbsExpectation(float data, float variance)
Compute the posterior expectation value of the true flux in a pixel from its (Gaussian) likelihood an...
static FlagDefinition const NO_SHAPE
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
std::shared_ptr< SourceTable const > getTable() const
void measureChildPixels(afw::image::MaskedImage< float > const &image, afw::table::SourceRecord &child) const
int getWidth() const noexcept
bool getCentroidFlag() const
double nSigmaWeightMax
"Radius factor that sets the maximum extent of the weight function (and hence the " "flux measurement...
bool doShape
"Whether to compute quantities related to the Gaussian-weighted shape" ;
T dynamic_pointer_cast(T... args)
std::unique_ptr< SchemaItem< U > > result
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.
#define LSST_EXCEPT(type,...)
bool contains(Point2I const &point) const noexcept
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.
x_iterator x_at(int x, int y) const
void set(Key< T > const &key, U const &value)
int getY() const noexcept
SpanPixelIterator Iterator
Extent< double, 2 > Extent2D
vector-type utility class to build a collection of FlagDefinitions
double getDeterminant() const
static FlagDefinitionList const & getFlagDefinitions()
bool doFlux
"Whether to compute quantities related to the Gaussian-weighted flux" ;
Key< T > addField(Field< T > const &field, bool doReplace=false)
bool doOld
"Whether to compute HeavyFootprint dot products (the old deblend.blendedness parameter)" ; ...
void measureParentPixels(afw::image::MaskedImage< float > const &image, afw::table::SourceRecord &child) const
const_MaskedImageIterator< typename Image::x_iterator, typename Mask::x_iterator, typename Variance::x_iterator > const_x_iterator
CentroidSlotDefinition::MeasValue getCentroid() const
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.