lsst.afw gf03f0b42f3+b1047159b2
Loading...
Searching...
No Matches
BackgroundMI.cc
Go to the documentation of this file.
1// -*- LSST-C++ -*-
2
3/*
4 * LSST Data Management System
5 * Copyright 2008-2015 AURA/LSST.
6 *
7 * This product includes software developed by the
8 * LSST Project (http://www.lsst.org/).
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the LSST License Statement and
21 * the GNU General Public License along with this program. If not,
22 * see <https://www.lsstcorp.org/LegalNotices/>.
23 */
24
25/*
26 * Background estimation class code
27 */
28#include <limits>
29#include <vector>
30#include <cmath>
36
37namespace lsst {
38namespace ex = pex::exceptions;
39
40namespace afw {
41namespace math {
42
43namespace {
44
45// Given two vectors x and y, with some nans in y we want vectors x' and y' that correspond to the data
46// without the nans basic idea is that 'x' is the values, and 'y' is the ref (where nan checking happens)
47// cullNan(x, y, x', y')
48void cullNan(std::vector<double> const& values, std::vector<double> const& refs,
49 std::vector<double>& culledValues, std::vector<double>& culledRefs,
50 double const defaultValue = std::numeric_limits<double>::quiet_NaN()) {
51 if (culledValues.capacity() == 0) {
52 culledValues.reserve(refs.size());
53 } else {
54 culledValues.clear();
55 }
56 if (culledRefs.capacity() == 0) {
57 culledRefs.reserve(refs.size());
58 } else {
59 culledRefs.clear();
60 }
61
62 bool const haveDefault = !std::isnan(defaultValue);
63
64 for (std::vector<double>::const_iterator pVal = values.begin(), pRef = refs.begin(); pRef != refs.end();
65 ++pRef, ++pVal) {
66 if (!std::isnan(*pRef)) {
67 culledValues.push_back(*pVal);
68 culledRefs.push_back(*pRef);
69 } else if (haveDefault) {
70 culledValues.push_back(*pVal);
71 culledRefs.push_back(defaultValue);
72 } else {
73 ; // drop a NaN
74 }
75 }
76}
77} // namespace
78
79template <typename ImageT>
80BackgroundMI::BackgroundMI(ImageT const& img, BackgroundControl const& bgCtrl)
81 : Background(img, bgCtrl), _statsImage(image::MaskedImage<InternalPixelT>()) {
82 // =============================================================
83 // Loop over the cells in the image, computing statistical properties
84 // of each cell in turn and using them to set _statsImage
85 int const nxSample = bgCtrl.getNxSample();
86 int const nySample = bgCtrl.getNySample();
87 _statsImage = image::MaskedImage<InternalPixelT>(nxSample, nySample);
88
89 image::MaskedImage<InternalPixelT>::Image& im = *_statsImage.getImage();
90 image::MaskedImage<InternalPixelT>::Variance& var = *_statsImage.getVariance();
91
92 for (int iX = 0; iX < nxSample; ++iX) {
93 for (int iY = 0; iY < nySample; ++iY) {
94 ImageT subimg = ImageT(img,
98
100 *bgCtrl.getStatisticsControl())
101 .getResult();
102 im(iX, iY) = res.first;
103 var(iX, iY) = res.second;
104 }
105 }
106}
108 image::MaskedImage<InternalPixelT> const& statsImage)
109 : Background(imageBBox, statsImage.getWidth(), statsImage.getHeight()), _statsImage(statsImage) {}
110
111void BackgroundMI::_setGridColumns(Interpolate::Style const interpStyle,
112 UndersampleStyle const undersampleStyle, int const iX,
113 std::vector<int> const& ypix) const {
115
116 int const height = _imgBBox.getHeight();
117 _gridColumns[iX].resize(height);
118
119 // Set _grid as a transitional measure
120 std::vector<double> _grid(_statsImage.getHeight());
121 std::copy(im.col_begin(iX), im.col_end(iX), _grid.begin());
122
123 // remove nan from the grid values before computing columns
124 // if we do it here (ie. in _setGridColumns), it should
125 // take care of all future occurrences, so we don't need to do this elsewhere
126 std::vector<double> ycenTmp, gridTmp;
127 cullNan(_ycen, _grid, ycenTmp, gridTmp);
128
130 try {
131 intobj = makeInterpolate(ycenTmp, gridTmp, interpStyle);
133 switch (undersampleStyle) {
134 case THROW_EXCEPTION:
135 LSST_EXCEPT_ADD(e, "setting _gridcolumns");
136 throw;
137 case REDUCE_INTERP_ORDER: {
138 if (gridTmp.empty()) {
139 // Set the column to NaN. We'll deal with this properly when interpolating in x
140 ycenTmp.push_back(0);
142
143 intobj = makeInterpolate(ycenTmp, gridTmp, Interpolate::CONSTANT);
144 break;
145 } else {
146 return _setGridColumns(lookupMaxInterpStyle(gridTmp.size()), undersampleStyle, iX, ypix);
147 }
148 }
151 e, "The BackgroundControl UndersampleStyle INCREASE_NXNYSAMPLE is not supported.");
152 throw;
153 default:
154 LSST_EXCEPT_ADD(e, str(boost::format("The selected BackgroundControl "
155 "UndersampleStyle %d is not defined.") %
156 undersampleStyle));
157 throw;
158 }
159 } catch (ex::Exception& e) {
160 LSST_EXCEPT_ADD(e, "setting _gridcolumns");
161 throw;
162 }
163
164 for (int iY = 0; iY < height; ++iY) {
165 _gridColumns[iX][iY] = intobj->interpolate(ypix[iY]);
166 }
167}
168
170 _statsImage += delta;
171 return *this;
172}
173
175 _statsImage -= delta;
176 return *this;
177}
178
179ndarray::Array<double, 1, 1> BackgroundMI::getBinCentersX() const {
180 ndarray::Array<double, 1, 1> result = ndarray::allocate(_xcen.size());
181 std::copy(_xcen.begin(), _xcen.end(), result.begin());
182 result.deep() += _imgBBox.getMinX();
183 return result;
184}
185
186ndarray::Array<double, 1, 1> BackgroundMI::getBinCentersY() const {
187 ndarray::Array<double, 1, 1> result = ndarray::allocate(_ycen.size());
188 std::copy(_ycen.begin(), _ycen.end(), result.begin());
189 result.deep() += _imgBBox.getMinY();
190 return result;
191}
192
193template <typename PixelT>
194std::shared_ptr<image::Image<PixelT>> BackgroundMI::doGetImage(
195 lsst::geom::Box2I const& bbox,
196 Interpolate::Style const interpStyle_, // Style of the interpolation
197 UndersampleStyle const undersampleStyle // Behaviour if there are too few points
198 ) const {
199 if (!_imgBBox.contains(bbox)) {
200 throw LSST_EXCEPT(
202 str(boost::format("BBox (%d:%d,%d:%d) out of range (%d:%d,%d:%d)") % bbox.getMinX() %
203 bbox.getMaxX() % bbox.getMinY() % bbox.getMaxY() % _imgBBox.getMinX() %
205 }
206 int const nxSample = _statsImage.getWidth();
207 int const nySample = _statsImage.getHeight();
208 Interpolate::Style interpStyle = interpStyle_; // not const -- may be modified if REDUCE_INTERP_ORDER
209
210 /*
211 * Save the as-used interpStyle and undersampleStyle.
212 *
213 * N.b. The undersampleStyle may actually be overridden for some columns of the statsImage if they
214 * have too few good values. This doesn't prevent you reproducing the results of getImage() by
215 * calling getImage(getInterpStyle(), getUndersampleStyle())
216 */
217 _asUsedInterpStyle = interpStyle;
218 _asUsedUndersampleStyle = undersampleStyle;
219 /*
220 * Check if the requested nx,ny are sufficient for the requested interpolation style,
221 * making suitable adjustments
222 */
223 bool const isXundersampled = (nxSample < lookupMinInterpPoints(interpStyle));
224 bool const isYundersampled = (nySample < lookupMinInterpPoints(interpStyle));
225
226 switch (undersampleStyle) {
227 case THROW_EXCEPTION:
228 if (isXundersampled && isYundersampled) {
229 throw LSST_EXCEPT(
231 "nxSample and nySample have too few points for requested interpolation style.");
232 } else if (isXundersampled) {
234 "nxSample has too few points for requested interpolation style.");
235 } else if (isYundersampled) {
237 "nySample has too few points for requested interpolation style.");
238 }
239 break;
241 if (isXundersampled || isYundersampled) {
242 Interpolate::Style const xStyle = lookupMaxInterpStyle(nxSample);
243 Interpolate::Style const yStyle = lookupMaxInterpStyle(nySample);
244 interpStyle = (nxSample < nySample) ? xStyle : yStyle;
245 _asUsedInterpStyle = interpStyle;
246 }
247 break;
249 if (isXundersampled || isYundersampled) {
250 throw LSST_EXCEPT(
251 ex::InvalidParameterError,
252 "The BackgroundControl UndersampleStyle INCREASE_NXNYSAMPLE is not supported.");
253 }
254 break;
255 default:
256 throw LSST_EXCEPT(ex::InvalidParameterError,
257 str(boost::format("The selected BackgroundControl "
258 "UndersampleStyle %d is not defined.") %
259 undersampleStyle));
260 }
261
262 // if we're approximating, don't bother with the rest of the interp-related work. Return from here.
263 if (_bctrl->getApproximateControl()->getStyle() != ApproximateControl::UNKNOWN) {
264 return doGetApproximate<PixelT>(*_bctrl->getApproximateControl(), _asUsedUndersampleStyle)
265 ->getImage();
266 }
267
268 // =============================================================
269 // --> We'll store nxSample fully-interpolated columns to interpolate the rows over
270 // make a vector containing the y pixel coords for the column
271 int const width = _imgBBox.getWidth();
272 int const height = _imgBBox.getHeight();
273 auto const bboxOff = bbox.getMin() - _imgBBox.getMin();
274
275 std::vector<int> ypix(height);
276 for (int iY = 0; iY < height; ++iY) {
277 ypix[iY] = iY;
278 }
279
280 _gridColumns.resize(width);
281 for (int iX = 0; iX < nxSample; ++iX) {
282 _setGridColumns(interpStyle, undersampleStyle, iX, ypix);
283 }
284
285 // create a shared_ptr to put the background image in and return to caller
286 // start with xy0 = 0 and set final xy0 later
287 std::shared_ptr<image::Image<PixelT>> bg =
288 std::shared_ptr<image::Image<PixelT>>(new image::Image<PixelT>(bbox.getDimensions()));
289
290 // go through row by row
291 // - interpolate on the gridcolumns that were pre-computed by the constructor
292 // - copy the values to an ImageT to return to the caller.
293 std::vector<double> xcenTmp, bgTmp;
294
295 // N.b. There's no API to set defaultValue to other than NaN (due to issues with persistence
296 // that I don't feel like fixing; #2825). If we want to address this, this is the place
297 // to start, but note that NaN is treated specially -- it means, "Interpolate" so to allow
298 // us to put a NaN into the outputs some changes will be needed
299 double defaultValue = std::numeric_limits<double>::quiet_NaN();
300
301 for (int y = 0, iY = bboxOff.getY(); y < bbox.getHeight(); ++y, ++iY) {
302 // build an interp object for this row
303 std::vector<double> bg_x(nxSample);
304 for (int iX = 0; iX < nxSample; iX++) {
305 bg_x[iX] = static_cast<double>(_gridColumns[iX][iY]);
306 }
307 cullNan(_xcen, bg_x, xcenTmp, bgTmp, defaultValue);
308
309 std::shared_ptr<Interpolate> intobj;
310 try {
311 intobj = makeInterpolate(xcenTmp, bgTmp, interpStyle);
312 } catch (pex::exceptions::OutOfRangeError& e) {
313 switch (undersampleStyle) {
314 case THROW_EXCEPTION:
315 LSST_EXCEPT_ADD(e, str(boost::format("Interpolating in y (iY = %d)") % iY));
316 throw;
317 case REDUCE_INTERP_ORDER: {
318 if (bgTmp.empty()) {
319 xcenTmp.push_back(0);
320 bgTmp.push_back(defaultValue);
321
322 intobj = makeInterpolate(xcenTmp, bgTmp, Interpolate::CONSTANT);
323 break;
324 } else {
325 intobj = makeInterpolate(xcenTmp, bgTmp, lookupMaxInterpStyle(bgTmp.size()));
326 }
327 } break;
330 e,
331 "The BackgroundControl UndersampleStyle INCREASE_NXNYSAMPLE is not supported.");
332 throw;
333 default:
334 LSST_EXCEPT_ADD(e, str(boost::format("The selected BackgroundControl "
335 "UndersampleStyle %d is not defined.") %
336 undersampleStyle));
337 throw;
338 }
339 } catch (ex::Exception& e) {
340 LSST_EXCEPT_ADD(e, str(boost::format("Interpolating in y (iY = %d)") % iY));
341 throw;
342 }
343
344 // fill the image with interpolated values
345 for (int iX = bboxOff.getX(), x = 0; x < bbox.getWidth(); ++iX, ++x) {
346 (*bg)(x, y) = static_cast<PixelT>(intobj->interpolate(iX));
347 }
348 }
349 bg->setXY0(bbox.getMin());
350
351 return bg;
352}
353
354template <typename PixelT>
355std::shared_ptr<Approximate<PixelT>> BackgroundMI::doGetApproximate(
356 ApproximateControl const& actrl, /* Approximation style */
357 UndersampleStyle const undersampleStyle /* Behaviour if there are too few points */
358 ) const {
359 auto const localBBox = lsst::geom::Box2I(lsst::geom::Point2I(0, 0), _imgBBox.getDimensions());
360 return makeApproximate(_xcen, _ycen, _statsImage, localBBox, actrl);
361}
362
364/*
365 * Create the versions we need of _get{Approximate,Image} and Explicit instantiations
366 *
367 */
368#define CREATE_BACKGROUND(m, v, TYPE) \
369 template BackgroundMI::BackgroundMI(image::Image<TYPE> const& img, BackgroundControl const& bgCtrl); \
370 template BackgroundMI::BackgroundMI(image::MaskedImage<TYPE> const& img, \
371 BackgroundControl const& bgCtrl); \
372 std::shared_ptr<image::Image<TYPE>> BackgroundMI::_getImage( \
373 lsst::geom::Box2I const& bbox, \
374 Interpolate::Style const interpStyle, /* Style of the interpolation */ \
375 UndersampleStyle const undersampleStyle, /* Behaviour if there are too few points */ \
376 TYPE /* disambiguate */ \
377 ) const { \
378 return BackgroundMI::doGetImage<TYPE>(bbox, interpStyle, undersampleStyle); \
379 }
380
381#define CREATE_getApproximate(m, v, TYPE) \
382 std::shared_ptr<Approximate<TYPE>> BackgroundMI::_getApproximate( \
383 ApproximateControl const& actrl, /* Approximation style */ \
384 UndersampleStyle const undersampleStyle, /* Behaviour if there are too few points */ \
385 TYPE /* disambiguate */ \
386 ) const { \
387 return BackgroundMI::doGetApproximate<TYPE>(actrl, undersampleStyle); \
388 }
389
392
393
394} // namespace math
395} // namespace afw
396} // namespace lsst
#define LSST_makeBackground_getApproximate_types
Definition Background.h:386
#define LSST_makeBackground_getImage_types
Definition Background.h:385
#define LSST_EXCEPT_ADD(e, m)
#define LSST_EXCEPT(type,...)
T begin(T... args)
T capacity(T... args)
y_iterator col_begin(int x) const
Return an y_iterator to the start of the y'th row.
Definition ImageBase.h:413
y_iterator col_end(int x) const
Return an y_iterator to the start of the y'th row.
Definition ImageBase.h:416
A class to manipulate images, masks, and variance as a single object.
Definition MaskedImage.h:74
lsst::afw::image::Image< VariancePixelT > Variance
Definition MaskedImage.h:85
int getHeight() const
Return the number of rows in the image.
int getWidth() const
Return the number of columns in the image.
lsst::afw::image::Image< ImagePixelT > Image
Definition MaskedImage.h:86
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage's image.
Control how to make an approximation.
Definition Approximate.h:48
Pass parameters to a Background object.
Definition Background.h:57
std::shared_ptr< StatisticsControl > getStatisticsControl()
Definition Background.h:212
Property getStatisticsProperty() const
Definition Background.h:215
std::vector< double > _ycen
y center ...
Definition Background.h:370
std::vector< double > _xcen
x center pix coords of sub images
Definition Background.h:369
Background(ImageT const &img, BackgroundControl const &bgCtrl)
Constructor for Background.
Definition Background.cc:44
std::vector< int > _xsize
x size of sub images
Definition Background.h:373
lsst::geom::Box2I _imgBBox
size and origin of input image
Definition Background.h:364
std::vector< int > _xorig
x origin pix coords of sub images
Definition Background.h:371
UndersampleStyle _asUsedUndersampleStyle
the undersampleStyle we actually used
Definition Background.h:367
std::vector< int > _ysize
y size ...
Definition Background.h:374
float InternalPixelT
type used for any internal images, and returned by getApproximate
Definition Background.h:263
std::vector< int > _yorig
y origin ...
Definition Background.h:372
Interpolate::Style _asUsedInterpStyle
the style we actually used
Definition Background.h:366
std::shared_ptr< BackgroundControl > _bctrl
control info set by user.
Definition Background.h:365
ndarray::Array< double, 1, 1 > getBinCentersY() const
Return the y-coordinate centers of the bins.
BackgroundMI & operator-=(float const delta) override
Subtract a scalar from the Background (equivalent to subtracting a constant from the original image)
ndarray::Array< double, 1, 1 > getBinCentersX() const
Return the x-coordinate centers of the bins.
BackgroundMI & operator+=(float const delta) override
Add a scalar to the Background (equivalent to adding a constant to the original image)
BackgroundMI(ImageT const &img, BackgroundControl const &bgCtrl)
Constructor for BackgroundMI.
Value getResult(Property const prop=NOTHING) const
Return the value and error in the specified statistic (e.g.
int getMinY() const noexcept
int getHeight() const noexcept
Point2I const getMin() const noexcept
int getMinX() const noexcept
int getWidth() const noexcept
int getMaxX() const noexcept
bool contains(Point2I const &point) const noexcept
int getMaxY() const noexcept
Extent2I const getDimensions() const noexcept
T clear(T... args)
T copy(T... args)
T empty(T... args)
T end(T... args)
T isnan(T... args)
Interpolate::Style lookupMaxInterpStyle(int const n)
Get the highest order Interpolation::Style available for 'n' points.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition Statistics.h:361
@ ERRORS
Include errors of requested quantities.
Definition Statistics.h:55
int lookupMinInterpPoints(Interpolate::Style const style)
Get the minimum number of points needed to use the requested interpolation style.
std::shared_ptr< Interpolate > makeInterpolate(std::vector< double > const &x, std::vector< double > const &y, Interpolate::Style const style=Interpolate::AKIMA_SPLINE)
A factory function to make Interpolate objects.
std::shared_ptr< Approximate< PixelT > > makeApproximate(std::vector< double > const &x, std::vector< double > const &y, image::MaskedImage< PixelT > const &im, lsst::geom::Box2I const &bbox, ApproximateControl const &ctrl)
Construct a new Approximate object, inferring the type from the type of the given MaskedImage.
BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_COLUMNVIEW_SCALAR, _, BOOST_PP_TUPLE_TO_SEQ(AFW_TABLE_SCALAR_FIELD_TYPE_N, AFW_TABLE_SCALAR_FIELD_TYPE_TUPLE)) BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_COLUMNVIEW_ARRAY
Extent< int, 2 > Extent2I
Point< int, 2 > Point2I
T push_back(T... args)
T quiet_NaN(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)