23 __all__ = (
"SubtractBackgroundConfig",
"SubtractBackgroundTask")
29 from lsstDebug
import getDebugFrame
33 import lsst.pex.config
as pexConfig
35 from functools
import reduce
39 """!Config for SubtractBackgroundTask 41 @note Many of these fields match fields in lsst.afw.math.BackgroundControl, 42 the control class for lsst.afw.math.makeBackground 44 statisticsProperty = pexConfig.ChoiceField(
45 doc=
"type of statistic to use for grid points",
46 dtype=str, default=
"MEANCLIP",
48 "MEANCLIP":
"clipped mean",
49 "MEAN":
"unclipped mean",
53 undersampleStyle = pexConfig.ChoiceField(
54 doc=
"behaviour if there are too few points in grid for requested interpolation style",
55 dtype=str, default=
"REDUCE_INTERP_ORDER",
57 "THROW_EXCEPTION":
"throw an exception if there are too few points",
58 "REDUCE_INTERP_ORDER":
"use an interpolation style with a lower order.",
59 "INCREASE_NXNYSAMPLE":
"Increase the number of samples used to make the interpolation grid.",
62 binSize = pexConfig.RangeField(
63 doc=
"how large a region of the sky should be used for each background point",
64 dtype=int, default=128, min=1,
66 binSizeX = pexConfig.RangeField(
67 doc=(
"Sky region size to be used for each background point in X direction. " 68 "If 0, the binSize config is used."),
69 dtype=int, default=0, min=0,
71 binSizeY = pexConfig.RangeField(
72 doc=(
"Sky region size to be used for each background point in Y direction. " 73 "If 0, the binSize config is used."),
74 dtype=int, default=0, min=0,
76 algorithm = pexConfig.ChoiceField(
77 doc=
"how to interpolate the background values. This maps to an enum; see afw::math::Background",
78 dtype=str, default=
"AKIMA_SPLINE", optional=
True,
80 "CONSTANT":
"Use a single constant value",
81 "LINEAR":
"Use linear interpolation",
82 "NATURAL_SPLINE":
"cubic spline with zero second derivative at endpoints",
83 "AKIMA_SPLINE":
"higher-level nonlinear spline that is more robust to outliers",
84 "NONE":
"No background estimation is to be attempted",
87 ignoredPixelMask = pexConfig.ListField(
88 doc=
"Names of mask planes to ignore while estimating the background",
89 dtype=str, default=[
"BAD",
"EDGE",
"DETECTED",
"DETECTED_NEGATIVE",
"NO_DATA", ],
92 isNanSafe = pexConfig.Field(
93 doc=
"Ignore NaNs when estimating the background",
94 dtype=bool, default=
False,
97 useApprox = pexConfig.Field(
98 doc=
"Use Approximate (Chebyshev) to model background.",
99 dtype=bool, default=
True,
101 approxOrderX = pexConfig.Field(
102 doc=
"Approximation order in X for background Chebyshev (valid only with useApprox=True)",
103 dtype=int, default=6,
108 approxOrderY = pexConfig.Field(
109 doc=
"Approximation order in Y for background Chebyshev (valid only with useApprox=True)",
110 dtype=int, default=-1,
112 weighting = pexConfig.Field(
113 doc=
"Use inverse variance weighting in calculation (valid only with useApprox=True)",
114 dtype=bool, default=
True,
126 r"""!Subtract the background from an exposure 128 @anchor SubtractBackgroundTask_ 130 @section meas_algorithms_subtractBackground_Contents Contents 132 - @ref meas_algorithms_subtractBackground_Purpose 133 - @ref meas_algorithms_subtractBackground_Initialize 134 - @ref meas_algorithms_subtractBackground_IO 135 - @ref meas_algorithms_subtractBackground_Config 136 - @ref meas_algorithms_subtractBackground_Metadata 137 - @ref meas_algorithms_subtractBackground_Debug 138 - @ref meas_algorithms_subtractBackground_Example 140 @section meas_algorithms_subtractBackground_Purpose Description 142 Fit a model of the background of an exposure and subtract it. 144 @section meas_algorithms_subtractBackground_Initialize Task initialisation 146 @copydoc \_\_init\_\_ 148 @section meas_algorithms_subtractBackground_IO Invoking the Task 150 Call `run` to fit the background and subtract it. 152 Call `fitBackground` to fit the background without subtracting it. 154 @section meas_algorithms_subtractBackground_Config Configuration parameters 156 See @ref SubtractBackgroundConfig 158 @section meas_algorithms_subtractBackground_Metadata Quantities set in exposure Metadata 160 The `run` method will optionally set the following items of exposure metadata; 161 the names may be overridden; the defaults are shown: 163 <dt>BGMEAN <dd>mean value of background 164 <dt>BGVAR <dd>standard deviation of background 167 @section meas_algorithms_subtractBackground_Debug Debug variables 169 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a flag 170 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug for more about `debug.py`. 172 SubtractBackgroundTask has a debug dictionary containing three integer keys: 175 <dd>If >0: `fitBackground` displays the unsubtracted masked image overlaid with the grid of cells 176 used to fit the background in the specified frame 178 <dd>If >0: `run` displays the background-subtracted exposure is the specified frame 180 <dd>If >0: `run` displays the background image in the specified frame 183 For example, put something like: 187 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 188 if name == "lsst.meas.algorithms.subtractBackground": 197 lsstDebug.Info = DebugInfo 199 into your `debug.py` file and run your task with the `--debug` flag. 201 @section meas_algorithms_subtractBackground_Example A complete example of using SubtractBackgroundTask 203 This code is in @link subtractBackgroundExample.py@endlink in the examples directory, and can be run as: 205 python examples/subtractBackgroundExample.py 207 @dontinclude subtractBackgroundExample.py 209 Import the task (there are some other standard imports; read the file if you're curious) 210 @skipline import SubtractBackgroundTask 212 Create the task, run it, and report mean and variance of background. 213 @skip create the task 216 ConfigClass = SubtractBackgroundConfig
217 _DefaultName =
"subtractBackground" 219 def run(self, exposure, background=None, stats=True, statsKeys=None):
220 """!Fit and subtract the background of an exposure 222 @param[in,out] exposure exposure whose background is to be subtracted 223 @param[in,out] background initial background model already subtracted from exposure 224 (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted. 225 @param[in] stats if True then measure the mean and variance of the full background model 226 and record the results in the exposure's metadata 227 @param[in] statsKeys key names used to store the mean and variance of the background 228 in the exposure's metadata (a pair of strings); if None then use ("BGMEAN", "BGVAR"); 229 ignored if stats is false 231 @return an lsst.pipe.base.Struct containing: 232 - background full background model (initial model with changes), an lsst.afw.math.BackgroundList 234 if background
is None:
237 maskedImage = exposure.getMaskedImage()
239 maskedImage -= fitBg.getImageF()
240 background.append(fitBg)
243 self.
_addStats(exposure, background, statsKeys=statsKeys)
247 subDisp = afwDisplay.getDisplay(frame=subFrame)
248 subDisp.mtv(exposure, title=
"subtracted")
252 bgDisp = afwDisplay.getDisplay(frame=bgFrame)
253 bgImage = background.getImage()
254 bgDisp.mtv(bgImage, title=
"background")
256 return pipeBase.Struct(
257 background=background,
260 def _addStats(self, exposure, background, statsKeys=None):
261 """Add statistics about the background to the exposure's metadata 263 @param[in,out] exposure exposure whose background was subtracted 264 @param[in,out] background background model (an lsst.afw.math.BackgroundList) 265 @param[in] statsKeys key names used to store the mean and variance of the background 266 in the exposure's metadata (a pair of strings); if None then use ("BGMEAN", "BGVAR"); 267 ignored if stats is false 269 netBgImg = background.getImage()
270 if statsKeys
is None:
271 statsKeys = (
"BGMEAN",
"BGVAR")
272 mnkey, varkey = statsKeys
273 meta = exposure.getMetadata()
275 bgmean = s.getValue(afwMath.MEAN)
276 bgvar = s.getValue(afwMath.VARIANCE)
277 meta.addDouble(mnkey, bgmean)
278 meta.addDouble(varkey, bgvar)
281 """!Estimate the background of a masked image 283 @param[in] maskedImage masked image whose background is to be computed 284 @param[in] nx number of x bands; if 0 compute from width and config.binSizeX 285 @param[in] ny number of y bands; if 0 compute from height and config.binSizeY 286 @param[in] algorithm name of interpolation algorithm; if None use self.config.algorithm 288 @return fit background as an lsst.afw.math.Background 290 @throw RuntimeError if lsst.afw.math.makeBackground returns None, 291 which is apparently one way it indicates failure 294 binSizeX = self.config.binSize
if self.config.binSizeX == 0
else self.config.binSizeX
295 binSizeY = self.config.binSize
if self.config.binSizeY == 0
else self.config.binSizeY
298 nx = maskedImage.getWidth()//binSizeX + 1
300 ny = maskedImage.getHeight()//binSizeY + 1
304 unsubDisp = afwDisplay.getDisplay(frame=unsubFrame)
305 unsubDisp.mtv(maskedImage, title=
"unsubtracted")
306 xPosts = numpy.rint(numpy.linspace(0, maskedImage.getWidth() + 1, num=nx, endpoint=
True))
307 yPosts = numpy.rint(numpy.linspace(0, maskedImage.getHeight() + 1, num=ny, endpoint=
True))
308 with unsubDisp.Buffering():
309 for (xMin, xMax), (yMin, yMax)
in itertools.product(zip(xPosts[:-1], xPosts[1:]),
310 zip(yPosts[:-1], yPosts[1:])):
311 unsubDisp.line([(xMin, yMin), (xMin, yMax), (xMax, yMax), (xMax, yMin), (xMin, yMin)])
314 sctrl.setAndMask(reduce(
lambda x, y: x | maskedImage.getMask().getPlaneBitMask(y),
315 self.config.ignoredPixelMask, 0x0))
316 sctrl.setNanSafe(self.config.isNanSafe)
318 self.log.
debug(
"Ignoring mask planes: %s" %
", ".join(self.config.ignoredPixelMask))
320 if algorithm
is None:
321 algorithm = self.config.algorithm
324 self.config.undersampleStyle, sctrl,
325 self.config.statisticsProperty)
339 if self.config.useApprox:
340 if self.config.approxOrderY
not in (self.config.approxOrderX, -1):
341 raise ValueError(
"Error: approxOrderY not in (approxOrderX, -1)")
342 order = self.config.approxOrderX
343 minNumberGridPoints = order + 1
344 if min(nx, ny) <= order:
345 self.log.
warn(
"Too few points in grid to constrain fit: min(nx, ny) < approxOrder) " 346 "[min(%d, %d) < %d]" % (nx, ny, order))
347 if self.config.undersampleStyle ==
"THROW_EXCEPTION":
348 raise ValueError(
"Too few points in grid (%d, %d) for order (%d) and binSize (%d, %d)" %
349 (nx, ny, order, binSizeX, binSizeY))
350 elif self.config.undersampleStyle ==
"REDUCE_INTERP_ORDER":
352 raise ValueError(
"Cannot reduce approxOrder below 0. " 353 "Try using undersampleStyle = \"INCREASE_NXNYSAMPLE\" instead?")
354 order =
min(nx, ny) - 1
355 self.log.
warn(
"Reducing approxOrder to %d" % order)
356 elif self.config.undersampleStyle ==
"INCREASE_NXNYSAMPLE":
358 newBinSize =
min(maskedImage.getWidth(), maskedImage.getHeight())//(minNumberGridPoints-1)
360 raise ValueError(
"Binsize must be greater than 0")
361 newNx = maskedImage.getWidth()//newBinSize + 1
362 newNy = maskedImage.getHeight()//newBinSize + 1
363 bctrl.setNxSample(newNx)
364 bctrl.setNySample(newNy)
365 self.log.
warn(
"Decreasing binSize from (%d, %d) to %d for a grid of (%d, %d)" %
366 (binSizeX, binSizeY, newBinSize, newNx, newNy))
369 self.config.weighting)
370 bctrl.setApproximateControl(actrl)
374 raise RuntimeError(
"lsst.afw.math.makeBackground failed to fit a background model")
def _addStats(self, exposure, background, statsKeys=None)
def fitBackground(self, maskedImage, nx=0, ny=0, algorithm=None)
Estimate the background of a masked image.
std::shared_ptr< Background > makeBackground(ImageT const &img, BackgroundControl const &bgCtrl)
A convenience function that uses function overloading to make the correct type of Background...
Pass parameters to a Background object.
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
Control how to make an approximation.
Pass parameters to a Statistics object.
Represent a 2-dimensional array of bitmask pixels.
Config for SubtractBackgroundTask.
Subtract the background from an exposure.
def getDebugFrame(debugDisplay, name)
def run(self, exposure, background=None, stats=True, statsKeys=None)
Fit and subtract the background of an exposure.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...