LSST Applications g04e9c324dd+8c5ae1fdc5,g134cb467dc+b203dec576,g18429d2f64+358861cd2c,g199a45376c+0ba108daf9,g1fd858c14a+dd066899e3,g262e1987ae+ebfced1d55,g29ae962dfc+72fd90588e,g2cef7863aa+aef1011c0b,g35bb328faa+8c5ae1fdc5,g3fd5ace14f+b668f15bc5,g4595892280+3897dae354,g47891489e3+abcf9c3559,g4d44eb3520+fb4ddce128,g53246c7159+8c5ae1fdc5,g67b6fd64d1+abcf9c3559,g67fd3c3899+1f72b5a9f7,g74acd417e5+cb6b47f07b,g786e29fd12+668abc6043,g87389fa792+8856018cbb,g89139ef638+abcf9c3559,g8d7436a09f+bcf525d20c,g8ea07a8fe4+9f5ccc88ac,g90f42f885a+6054cc57f1,g97be763408+06f794da49,g9dd6db0277+1f72b5a9f7,ga681d05dcb+7e36ad54cd,gabf8522325+735880ea63,gac2eed3f23+abcf9c3559,gb89ab40317+abcf9c3559,gbf99507273+8c5ae1fdc5,gd8ff7fe66e+1f72b5a9f7,gdab6d2f7ff+cb6b47f07b,gdc713202bf+1f72b5a9f7,gdfd2d52018+8225f2b331,ge365c994fd+375fc21c71,ge410e46f29+abcf9c3559,geaed405ab2+562b3308c0,gf9a733ac38+8c5ae1fdc5,w.2025.35
LSST Data Management Base Package
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(
201 ex::LengthError,
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(
230 ex::InvalidParameterError,
231 "nxSample and nySample have too few points for requested interpolation style.");
232 } else if (isXundersampled) {
233 throw LSST_EXCEPT(ex::InvalidParameterError,
234 "nxSample has too few points for requested interpolation style.");
235 } else if (isYundersampled) {
236 throw LSST_EXCEPT(ex::InvalidParameterError,
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)
Add the current location and a message to an existing exception before rethrowing it.
Definition Exception.h:54
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
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.
An integer coordinate rectangle.
Definition Box.h:55
int getMinY() const noexcept
Definition Box.h:158
int getHeight() const noexcept
Definition Box.h:188
int getMinX() const noexcept
Definition Box.h:157
int getMaxX() const noexcept
Definition Box.h:161
bool contains(Point2I const &point) const noexcept
Return true if the box contains the point.
Definition Box.cc:114
int getMaxY() const noexcept
Definition Box.h:162
Reports attempts to access elements outside a valid range of indices.
Definition Runtime.h:89
T clear(T... args)
T copy(T... args)
T empty(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
Definition Extent.h:397
Point< int, 2 > Point2I
Definition Point.h:321
T push_back(T... args)
T quiet_NaN(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)