54 double bad_pixel_max_fraction,
55 double bad_pixel_exclusion_radius) {
59 auto area =
spans.getArea();
60 auto bbox =
spans.getBBox();
62 result->spans =
spans.intersectNot(*bad_spans);
63 if ((area - result->spans->getArea()) > (bad_pixel_max_fraction * area)) {
64 result->too_many_bad_pixels =
true;
68 result->variance = 0.0;
69 result->spans->applyFunctor(
70 [&result](
geom::Point2I const& point,
float data) { result->variance += data; },
76 result->spans->applyFunctor(
79 qx += point.getX() * data;
80 qy += point.getY() * data;
83 result->center.setX(qx / result->flux);
84 result->center.setY(qy / result->flux);
88 result->center_out_of_bounds =
true;
92 for (
auto const& span : *bad_spans) {
93 for (
auto point : span) {
94 min_bad_pixel_center_distance_squared =
96 min_bad_pixel_center_distance_squared);
99 if (min_bad_pixel_center_distance_squared < bad_pixel_exclusion_radius * bad_pixel_exclusion_radius) {
100 result->bad_pixel_in_center =
true;
107 result->spans->applyFunctor(
108 [&result, &qxx, &qyy, &qxy](
geom::Point2I const& point,
float data) {
109 double dx = point.getX() - result->center.getX();
110 double dy = point.getY() - result->center.getY();
111 qxx += dx * dx * data;
112 qyy += dy * dy * data;
113 qxy += dx * dy * data;
116 result->shape.setIxx(qxx / result->flux);
117 result->shape.setIyy(qyy / result->flux);
118 result->shape.setIxy(qxy / result->flux);
119 if (result->shape.getDeterminant() <= 0.0 || !
std::isfinite(result->shape.getTraceRadius())) {
120 result->singular_second_moments =
true;
131 return n + source->spans->getArea();
134 ndarray::Array<float, 1, 1> data_array = ndarray::allocate(n_pixels);
135 data_array.deep() = 0.0;
139 ndarray::Array<float, 2, -2> design_matrix =
140 ndarray::Array<float, 2, 2>(ndarray::allocate(n_coeff, n_pixels)).transpose();
141 design_matrix.deep() = 0.0;
143 for (
auto const& source : sources) {
146 auto source_data_array = data_array[ndarray::view(start, stop)].shallow();
147 ndarray::Array<float, 1, 1> source_weight_array = ndarray::allocate(builder.
getDataSize());
148 source->spans->applyFunctor(
149 [](
geom::Point2I const& point,
float& data,
float& weight,
float img,
float var) {
153 ndFlat(source_data_array), ndFlat(source_weight_array), *masked_image.
getImage(),
155 auto source_design_matrix = design_matrix[ndarray::view(start, stop)()].shallow();
157 ellipse.
scale(scale);
158 builder(source_design_matrix, ellipse);
159 asEigenArray(source_design_matrix) *= source->flux;
160 asEigenArray(source_design_matrix).colwise() *= asEigenArray(source_weight_array);
165 .bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV)
166 .solve(asEigenMatrix(data_array)).cast<
double>();
details::FlatNdGetter< T, inA, inB > ndFlat(ndarray::Array< T, inA, inB > const &array)
static shapelet::ShapeletFunction fit_shapelets(afw::image::MaskedImage< float > const &masked_image, std::vector< std::shared_ptr< SpanSetMoments > > const &moments, int order, double scale)
Fit a common shapelet expansion to multiple sources whose moments have already been computed.
static std::shared_ptr< SpanSetMoments > compute(afw::geom::SpanSet const &spans, afw::image::MaskedImage< float > const &masked_image, afw::image::MaskPixel bad_bitmask, double bad_pixel_max_fraction, double bad_pixel_exclusion_radius)
Compute the unweighted moments of an image within a SpanSet.