30namespace lsst {
namespace meas {
namespace extensions {
namespace simpleShape {
32base::FlagDefinitionList flagDefinitions;
38 return flagDefinitions;
54Span clipSpan(Span
const & span,
geom::Box2I const & box) {
55 if (span.getY() < box.
getMinY() || span.getY() > box.
getMaxY())
return Span();
56 return Span(span.getY(),
62template <
typename Function,
typename Iterator>
63void iterateSpan(Function & function, Iterator pixIter, Span
const & span) {
65 Span::Iterator pointIter = span.begin(), pointEnd = span.end();
66 pointIter != pointEnd;
67 ++pointIter, ++pixIter
69 function(*pointIter, *pixIter);
73template <
typename Function,
typename Image,
typename Region>
74void iterateRegion(Function & function,
Image const &
image, Region
const & region) {
76 if (
bbox.contains(region.getBBox())) {
80 typename Region::Iterator spanIter = region.begin(), spanEnd = region.end();
86 image.x_at(spanIter->getMinX() -
image.getX0(), spanIter->getY() -
image.getY0()),
92 typename Region::Iterator spanIter = region.begin(), spanEnd = region.end();
96 Span span = clipSpan(*spanIter,
bbox);
99 image.x_at(span.getMinX() -
image.getX0(), span.getY() -
image.getY0()),
118enum { Q0=0, QXX, QYY, QXY, QX, QY, N_Q };
119enum { MXX=0, MYY, MXY, MX, MY, N_M };
121typedef Eigen::Matrix<double,N_Q,1> VectorQ;
122typedef Eigen::Matrix<double,N_Q,N_Q> MatrixQ;
123typedef Eigen::Matrix<double,N_M,1> VectorM;
124typedef Eigen::Matrix<double,N_M,N_M> MatrixM;
125typedef Eigen::Matrix<double,N_M,N_Q> MatrixMQ;
127struct RawMomentAccumulator {
129 template <
typename PixelT>
130 void operator()(
geom::Point2I const & pos, PixelT
const & pixel) {
133 double w =
std::exp(-0.5 * (gtd.getX()*gtd.getX() + gtd.getY()*gtd.getY()));
134 VectorQ q = VectorQ::Constant(
w);
135 q[QXX] *= d.getX() * d.getX();
136 q[QYY] *= d.getY() * d.getY();
137 q[QXY] *= d.getX() * d.getY();
141 covariance.selfadjointView<Eigen::Lower>().rankUpdate(q, pixel.variance());
144 explicit RawMomentAccumulator(afw::geom::ellipses::Ellipse
const & weightEllipse) :
147 _center(weightEllipse.getCenter()),
148 _gt(weightEllipse.getCore().getGridTransform())
178 RawMomentAccumulator functor(
weight);
179 iterateRegion(functor,
image, region);
184 MatrixM mCov = dm_dq * functor.covariance.selfadjointView<Eigen::Lower>() * dm_dq.adjoint();
194 result.covariance = dc_dm * mCov.selfadjointView<Eigen::Lower>() * dc_dm.adjoint();
208 VectorM
m = q.segment<N_M>(1) / q[Q0];
209 MatrixMQ dm_dq = MatrixMQ::Zero();;
210 dm_dq.block<N_M,N_M>(0,1) = MatrixM::Identity() / q[Q0];
211 dm_dq.col(Q0) = -
m / q[Q0];
213 m[MXX] -=
m[MX] *
m[MX];
214 m[MYY] -=
m[MY] *
m[MY];
215 m[MXY] -=
m[MX] *
m[MY];
216 dm_dq(MXX, QX) = -2.0 *
m[MX] / q[Q0];
217 dm_dq(MYY, QY) = -2.0 *
m[MY] / q[Q0];
218 dm_dq(MXY, QX) =
m[MY] / q[Q0];
219 dm_dq(MXY, QY) =
m[MX] / q[Q0];
220 double tmp = 2.0 / (q[Q0] * q[Q0] * q[Q0]);
221 dm_dq(MXX, Q0) += tmp * q[QX] * q[QX];
222 dm_dq(MYY, Q0) += tmp * q[QY] * q[QY];
223 dm_dq(MXY, Q0) += tmp * q[QX] * q[QY];
239 Eigen::Matrix2d wMat =
weight.getMatrix();
240 Eigen::Vector2d mVec = center.
asEigen();
241 Eigen::Matrix2d mMat = ellipse.
getMatrix();
242 if (wMat.determinant() <= 0.0) {
245 "Weight moments matrix is singular",
249 if (mMat.determinant() <= 0.0) {
252 "Measured moments matrix is singular",
256 Eigen::Matrix2d mInv = mMat.inverse();
257 Eigen::Matrix2d cInv = mInv - wMat.inverse();
258 if (cInv.determinant() <= 0.0) {
261 "Corrected moments matrix is singular",
265 Eigen::Matrix2d cMat = cInv.inverse();
266 ellipse.
setIxx(cMat(0, 0));
267 ellipse.
setIyy(cMat(1, 1));
268 ellipse.
setIxy(cMat(0, 1));
269 Eigen::Matrix2d cMat_mInv = cMat * mInv;
270 Eigen::Vector2d mInv_mVec = mInv * mVec;
271 Eigen::Vector2d cVec = cMat_mInv * mVec;
272 center.setX(cVec[0]);
273 center.setY(cVec[1]);
274 Eigen::Matrix2d dcMat_dmxx = cMat_mInv.col(0) * cMat_mInv.col(0).adjoint();
275 Eigen::Matrix2d dcMat_dmyy = cMat_mInv.col(1) * cMat_mInv.col(1).adjoint();
276 Eigen::Matrix2d dcMat_dmxy = cMat_mInv.col(0) * cMat_mInv.col(1).adjoint()
277 + cMat_mInv.col(1) * cMat_mInv.col(0).adjoint();
278 Eigen::Vector2d dcVec_dmxx = cMat_mInv.col(0) * mInv_mVec[0];
279 Eigen::Vector2d dcVec_dmyy = cMat_mInv.col(1) * mInv_mVec[1];
280 Eigen::Vector2d dcVec_dmxy = cMat_mInv.col(0) * mInv_mVec[1] + cMat_mInv.col(1) * mInv_mVec[0];
281 Eigen::Matrix2d
const & dcVec_dmVec = cMat_mInv;
285 MatrixM dc_dm = MatrixM::Zero();
286 dc_dm(MXX, MXX) = dcMat_dmxx(0, 0);
287 dc_dm(MYY, MXX) = dcMat_dmxx(1, 1);
288 dc_dm(MXY, MXX) = dcMat_dmxx(0, 1);
289 dc_dm(MXX, MYY) = dcMat_dmyy(0, 0);
290 dc_dm(MYY, MYY) = dcMat_dmyy(1, 1);
291 dc_dm(MXY, MYY) = dcMat_dmyy(0, 1);
292 dc_dm(MXX, MXY) = dcMat_dmxy(0, 0);
293 dc_dm(MYY, MXY) = dcMat_dmxy(1, 1);
294 dc_dm(MXY, MXY) = dcMat_dmxy(0, 1);
295 dc_dm(MX, MXX) = dcVec_dmxx[0];
296 dc_dm(MY, MXX) = dcVec_dmxx[1];
297 dc_dm(MX, MYY) = dcVec_dmyy[0];
298 dc_dm(MY, MYY) = dcVec_dmyy[1];
299 dc_dm(MX, MXY) = dcVec_dmxy[0];
300 dc_dm(MY, MXY) = dcVec_dmxy[1];
301 dc_dm(MX, MX) = dcVec_dmVec(0, 0);
302 dc_dm(MX, MY) = dcVec_dmVec(0, 1);
303 dc_dm(MY, MX) = dcVec_dmVec(1, 0);
304 dc_dm(MY, MY) = dcVec_dmVec(1, 1);
311 std::numeric_limits<
lsst::meas::base::ErrElement>::quiet_NaN(),
312 std::numeric_limits<
lsst::meas::base::ErrElement>::quiet_NaN()),
313 center(
std::numeric_limits<
lsst::meas::base::ErrElement>::quiet_NaN(),
314 std::numeric_limits<
lsst::meas::base::ErrElement>::quiet_NaN()),
315 covariance(Eigen::Matrix<double,5,5>::Constant(
std::numeric_limits<
lsst::meas::base::ErrElement>::quiet_NaN()))
325 name,
"elliptical Gaussian moments");
327 name,
"elliptical Gaussian moments",
"pixel");
330 "Ix",
"Iy"}),
"pixel");
339 _uncertantyResult(s,
std::vector<
std::string> ({
"Ixx",
"Iyy",
"Ixy",
"Ix",
"Iy"})),
346 result.center = record.
get(_centroidResult);
347 result.covariance = record.
get(_uncertantyResult);
355 record.
set(_shapeResult, value.ellipse);
356 record.
set(_centroidResult, value.center);
357 record.
set(_uncertantyResult, value.covariance);
359 _flagHandler.
setValue(record, n, value.flags[n]);
364 return _shapeResult == other._shapeResult &&
365 _centroidResult == other._centroidResult &&
366 _uncertantyResult == other._uncertantyResult;
371 return _shapeResult.
isValid() &&
385 _centroidExtractor(
schema, name)
396 source.set(_resultKey,
result);
406#define INSTANTIATE_IMAGE(IMAGE) \
407 template SimpleShapeResult SimpleShape::computeMoments(\
408 afw::geom::ellipses::Ellipse const &, \
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
A range of pixels within one row of an Image.
An ellipse core for the semimajor/semiminor axis and position angle parametrization (a,...
void scale(double factor)
Scale the size of the ellipse core by the given factor.
An ellipse defined by an arbitrary BaseCore and a center point.
BaseCore const & getCore() const
Return the ellipse core.
A pixelized region containing all pixels whose centers are within an Ellipse.
An ellipse core with quadrupole moments as parameters.
Matrix const & getMatrix() const
Return a 2x2 symmetric matrix of the parameters.
A class to contain the data, WCS, and other information needed to describe an image of the sky.
A class to manipulate images, masks, and variance as a single object.
Base class for all records.
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
static CovarianceMatrixKey addFields(Schema &schema, std::string const &prefix, NameArray const &names, std::string const &unit, bool diagonalOnly=false)
Add covariance matrix fields to a Schema, and return a CovarianceMatrixKey to manage them.
bool isValid() const noexcept
Return True if all the constituent error Keys are valid.
static PointKey addFields(Schema &schema, std::string const &name, std::string const &doc, std::string const &unit)
Add a pair of _x, _y fields to a Schema, and return a PointKey that points to them.
bool isValid() const noexcept
Return True if both the x and y Keys are valid.
static QuadrupoleKey addFields(Schema &schema, std::string const &name, std::string const &doc, CoordinateType coordType=CoordinateType::PIXEL)
Add a set of quadrupole subfields to a schema and return a QuadrupoleKey that points to them.
bool isValid() const noexcept
Return True if all the constituent Keys are valid.
Defines the fields and offsets for a table.
Record class that contains measurements made on a single exposure.
A proxy type for name lookups in a Schema.
An integer coordinate rectangle.
int getMinY() const noexcept
int getMinX() const noexcept
int getMaxX() const noexcept
int getMaxY() const noexcept
EigenVector const & asEigen() const noexcept(IS_ELEMENT_NOTHROW_COPYABLE)
Return a fixed-size Eigen representation of the coordinate object.
vector-type utility class to build a collection of FlagDefinitions
void handleFailure(afw::table::BaseRecord &record, MeasurementError const *error=nullptr) const
Handle an expected or unexpected Exception thrown by a measurement algorithm.
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.
void setValue(afw::table::BaseRecord &record, std::size_t i, bool value) const
Set the flag field corresponding to the given flag index.
bool getValue(afw::table::BaseRecord const &record, std::size_t i) const
Return the value of the flag field corresponding to the given flag index.
Exception to be thrown when a measurement algorithm experiences a known failure mode.
A C++ control class to handle SdssShapeAlgorithm's configuration.
double sigma
"Sigma of circular Gaussian used as weight function, in pixels" ;
double nSigmaRegion
"Maximum radius for pixels to include, in units of sigma" ;
SimpleShape(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
static Eigen::Matrix< double, 5, 5 > correctWeightedMoments(afw::geom::ellipses::Quadrupole const &weight, afw::geom::ellipses::Quadrupole &ellipse, geom::Point2D ¢er)
Correct moments measured with a Gaussian weight function by assuming the data was also an elliptical ...
static SimpleShapeResult computeMoments(afw::geom::ellipses::Ellipse const &weight, afw::image::MaskedImage< T > const &image, double nSigmaRegion=3.0)
Compute the Gaussian-weighted moments of an image.
virtual void fail(afw::table::SourceRecord &measRecord, lsst::meas::base::MeasurementError *error=NULL) const
Handle an exception thrown by the current algorithm by setting flags in the given record.
static unsigned int const N_FLAGS
static base::FlagDefinitionList const & getFlagDefinitions()
static base::FlagDefinition const FAILURE
static Eigen::Matrix< double, 5, 6 > convertRawMoments(Eigen::Matrix< double, 6, 1 > const &q, afw::geom::ellipses::Quadrupole &quadrupole, geom::Point2D ¢er)
Convert linear raw moments into an ellipse and centroid, and return the derivative of the conversion.
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
Called to measure a single child source in an image.
Struct to hold the results of SimpleShape when we don't run it as a plugin.
SimpleShapeResult()
Constructor; initializes everything to Nan.
afw::geom::ellipses::Quadrupole ellipse
Measured second moments.
virtual void set(afw::table::BaseRecord &record, SimpleShapeResult const &value) const
virtual SimpleShapeResult get(afw::table::BaseRecord const &record) const
bool isValid() const
Return True if the key is valid.
bool operator==(SimpleShapeResultKey const &other) const
Compare the FunctorKey for equality with another, using the underlying Keys.
lsst::meas::base::FlagHandler const & getFlagHandler() const
static SimpleShapeResultKey addFields(afw::table::Schema &schema, std::string const &name)
#define INSTANTIATE_IMAGE(IMAGE)
Extent< double, 2 > Extent2D
Point< double, 2 > Point2D
afw::table::Key< double > weight
g2d::python::Image< double > Image