26 #include "lsst/afw/geom/ellipses/PixelRegion.h" 28 #include "lsst/afw/math.h" 30 namespace lsst {
namespace meas {
namespace extensions {
namespace simpleShape {
32 base::FlagDefinitionList flagDefinitions;
38 return flagDefinitions;
52 using afw::geom::Span;
54 Span clipSpan(Span
const & span, afw::geom::Box2I
const & box) {
55 if (span.getY() < box.getMinY() || span.getY() > box.getMaxY())
return Span();
56 return Span(span.getY(),
57 std::min(std::max(span.getMinX(), box.getMinX()), box.getMaxX()),
58 std::max(std::min(span.getMaxX(), box.getMaxX()), box.getMinX())
62 template <
typename Function,
typename Iterator>
63 void 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);
73 template <
typename Function,
typename Image,
typename Region>
74 void iterateRegion(Function &
function, Image
const & image, Region
const & region) {
75 afw::geom::Box2I bbox = image.getBBox(afw::image::PARENT);
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()),
118 enum { Q0=0, QXX, QYY, QXY, QX, QY, N_Q };
119 enum { MXX=0, MYY, MXY, MX, MY, N_M };
121 typedef Eigen::Matrix<double,N_Q,1> VectorQ;
122 typedef Eigen::Matrix<double,N_Q,N_Q> MatrixQ;
123 typedef Eigen::Matrix<double,N_M,1> VectorM;
124 typedef Eigen::Matrix<double,N_M,N_M> MatrixM;
125 typedef Eigen::Matrix<double,N_M,N_Q> MatrixMQ;
127 struct RawMomentAccumulator {
129 template <
typename PixelT>
130 void operator()(afw::geom::Point2I
const & pos, PixelT
const & pixel) {
131 afw::geom::Extent2D d = afw::geom::Point2D(pos) - _center;
132 afw::geom::Extent2D gtd = _gt(d);
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())
154 afw::geom::Point2D _center;
155 afw::geom::LinearTransform _gt;
160 template <
typename T>
162 afw::geom::ellipses::Ellipse
const & weight,
163 afw::image::MaskedImage<T>
const & image,
167 afw::geom::ellipses::Ellipse regionEllipse(weight);
168 regionEllipse.getCore().scale(nSigmaRegion);
169 afw::geom::ellipses::PixelRegion region(regionEllipse);
178 RawMomentAccumulator functor(weight);
179 iterateRegion(functor, image, region);
184 MatrixM mCov = dm_dq * functor.covariance.selfadjointView<Eigen::Lower>() * dm_dq.adjoint();
190 weight.getCore().as<afw::geom::ellipses::Quadrupole>(),
194 result.
covariance = dc_dm * mCov.selfadjointView<Eigen::Lower>() * dc_dm.adjoint();
197 result.
center += afw::geom::Extent2D(weight.getCenter());
205 afw::geom::ellipses::Quadrupole & ellipse,
206 afw::geom::Point2D & center
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];
225 ellipse.setIxx(m[MXX]);
226 ellipse.setIyy(m[MYY]);
227 ellipse.setIxy(m[MXY]);
235 afw::geom::ellipses::Quadrupole
const & weight,
236 afw::geom::ellipses::Quadrupole & ellipse,
237 afw::geom::Point2D & center
239 Eigen::Matrix2d wMat = weight.getMatrix();
240 Eigen::Vector2d mVec = center.asEigen();
241 Eigen::Matrix2d mMat = ellipse.getMatrix();
242 if (wMat.determinant() <= 0.0) {
244 base::MeasurementError,
245 "Weight moments matrix is singular",
249 if (mMat.determinant() <= 0.0) {
251 base::MeasurementError,
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) {
260 base::MeasurementError,
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()))
320 afw::table::Schema & schema,
321 std::string
const &
name 324 r._shapeResult = lsst::afw::table::QuadrupoleKey::addFields(schema,
325 name,
"elliptical Gaussian moments");
326 r._centroidResult = lsst::afw::table::Point2DKey::addFields(schema,
327 name,
"elliptical Gaussian moments",
"pixel");
328 r._uncertantyResult = lsst::afw::table::CovarianceMatrixKey<double, 5>::addFields(schema,name,
329 std::vector<std::string> ({
"Ixx",
"Iyy",
"Ixy",
330 "Ix",
"Iy"}),
"pixel");
331 r._flagHandler = lsst::meas::base::FlagHandler::addFields(schema,
339 _uncertantyResult(s,
std::vector<
std::string> ({
"Ixx",
"Iyy",
"Ixy",
"Ix",
"Iy"})),
345 result.
ellipse = record.get(_shapeResult);
346 result.
center = record.get(_centroidResult);
347 result.
covariance = record.get(_uncertantyResult);
349 result.
flags[n] = _flagHandler.getValue(record, n);
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() &&
372 _centroidResult.isValid() &&
373 _uncertantyResult.isValid();
380 std::string
const &
name,
381 afw::table::Schema & schema
385 _centroidExtractor(schema, name)
389 afw::table::SourceRecord & source,
390 afw::image::Exposure<float>
const & exposure
392 afw::geom::Point2D center = _centroidExtractor(source, _resultKey.
getFlagHandler());
393 afw::geom::ellipses::Ellipse weight(afw::geom::ellipses::Axes(_ctrl.
sigma), center);
396 source.set(_resultKey, result);
400 afw::table::SourceRecord & measRecord,
401 lsst::meas::base::MeasurementError * error
406 #define INSTANTIATE_IMAGE(IMAGE) \ 407 template SimpleShapeResult SimpleShape::computeMoments(\ 408 afw::geom::ellipses::Ellipse const &, \ Struct to hold the results of SimpleShape when we don't run it as a plugin.
Eigen::Matrix< double, 5, 5 > covariance
Matrix of uncertainties; ordered Ixx, Iyy, Ixy, Ix, Iy.
bool isValid() const
Return True if the key is valid.
static Eigen::Matrix< double, 5, 5 > correctWeightedMoments(afw::geom::ellipses::Quadrupole const &weight, afw::geom::ellipses::Quadrupole &ellipse, afw::geom::Point2D ¢er)
Correct moments measured with a Gaussian weight function by assuming the data was also an elliptical ...
static Eigen::Matrix< double, 5, 6 > convertRawMoments(Eigen::Matrix< double, 6, 1 > const &q, afw::geom::ellipses::Quadrupole &quadrupole, afw::geom::Point2D ¢er)
Convert linear raw moments into an ellipse and centroid, and return the derivative of the conversion...
static base::FlagDefinition const FAILURE
SimpleShapeResult()
Constructor; initializes everything to Nan.
SimpleShape(Control const &ctrl, std::string const &name, afw::table::Schema &schema)
virtual void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
A C++ control class to handle SdssShapeAlgorithm's configuration.
#define INSTANTIATE_IMAGE(IMAGE)
static unsigned int const N_FLAGS
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.
std::bitset< SimpleShape::N_FLAGS > flags
virtual void fail(afw::table::SourceRecord &measRecord, lsst::meas::base::MeasurementError *error=NULL) const
double nSigmaRegion
"Maximum radius for pixels to include, in units of sigma" ;
static SimpleShapeResultKey addFields(afw::table::Schema &schema, std::string const &name)
virtual void set(afw::table::BaseRecord &record, SimpleShapeResult const &value) const
virtual SimpleShapeResult get(afw::table::BaseRecord const &record) const
bool operator==(SimpleShapeResultKey const &other) const
Compare the FunctorKey for equality with another, using the underlying Keys.
lsst::meas::base::FlagHandler const & getFlagHandler() const
afw::geom::Point2D center
Measured first moments, or the input center if !recentroid.
double sigma
"Sigma of circular Gaussian used as weight function, in pixels" ;
static base::FlagDefinitionList const & getFlagDefinitions()
afw::geom::ellipses::Quadrupole ellipse
Measured second moments.