23 __all__ = (
"SubtractBackgroundConfig",
"SubtractBackgroundTask")
25 from builtins
import zip
30 from lsstDebug
import getDebugFrame
31 import lsst.afw.display
as afwDisplay
33 import lsst.afw.math
as afwMath
34 import lsst.pex.config
as pexConfig
35 import lsst.pipe.base
as pipeBase
36 from functools
import reduce
40 """!Config for SubtractBackgroundTask
42 @note Many of these fields match fields in lsst.afw.math.BackgroundControl,
43 the control class for lsst.afw.math.makeBackground
45 statisticsProperty = pexConfig.ChoiceField(
46 doc=
"type of statistic to use for grid points",
47 dtype=str, default=
"MEANCLIP",
49 "MEANCLIP":
"clipped mean",
50 "MEAN":
"unclipped mean",
54 undersampleStyle = pexConfig.ChoiceField(
55 doc=
"behaviour if there are too few points in grid for requested interpolation style",
56 dtype=str, default=
"REDUCE_INTERP_ORDER",
58 "THROW_EXCEPTION":
"throw an exception if there are too few points",
59 "REDUCE_INTERP_ORDER":
"use an interpolation style with a lower order.",
60 "INCREASE_NXNYSAMPLE":
"Increase the number of samples used to make the interpolation grid.",
63 binSize = pexConfig.RangeField(
64 doc=
"how large a region of the sky should be used for each background point",
65 dtype=int, default=128, min=1,
67 binSizeX = pexConfig.RangeField(
68 doc=(
"Sky region size to be used for each background point in X direction. "
69 "If 0, the binSize config is used."),
70 dtype=int, default=0, min=0,
72 binSizeY = pexConfig.RangeField(
73 doc=(
"Sky region size to be used for each background point in Y direction. "
74 "If 0, the binSize config is used."),
75 dtype=int, default=0, min=0,
77 algorithm = pexConfig.ChoiceField(
78 doc=
"how to interpolate the background values. This maps to an enum; see afw::math::Background",
79 dtype=str, default=
"AKIMA_SPLINE", optional=
True,
81 "CONSTANT":
"Use a single constant value",
82 "LINEAR":
"Use linear interpolation",
83 "NATURAL_SPLINE":
"cubic spline with zero second derivative at endpoints",
84 "AKIMA_SPLINE":
"higher-level nonlinear spline that is more robust to outliers",
85 "NONE":
"No background estimation is to be attempted",
88 ignoredPixelMask = pexConfig.ListField(
89 doc=
"Names of mask planes to ignore while estimating the background",
90 dtype=str, default=[
"BAD",
"EDGE",
"DETECTED",
"DETECTED_NEGATIVE",
"NO_DATA", ],
91 itemCheck=
lambda x: x
in afwImage.Mask().getMaskPlaneDict().keys(),
93 isNanSafe = pexConfig.Field(
94 doc=
"Ignore NaNs when estimating the background",
95 dtype=bool, default=
False,
98 useApprox = pexConfig.Field(
99 doc=
"Use Approximate (Chebyshev) to model background.",
100 dtype=bool, default=
True,
102 approxOrderX = pexConfig.Field(
103 doc=
"Approximation order in X for background Chebyshev (valid only with useApprox=True)",
104 dtype=int, default=6,
109 approxOrderY = pexConfig.Field(
110 doc=
"Approximation order in Y for background Chebyshev (valid only with useApprox=True)",
111 dtype=int, default=-1,
113 weighting = pexConfig.Field(
114 doc=
"Use inverse variance weighting in calculation (valid only with useApprox=True)",
115 dtype=bool, default=
True,
127 """!Subtract the background from an exposure
129 @anchor SubtractBackgroundTask_
131 @section meas_algorithms_subtractBackground_Contents Contents
133 - @ref meas_algorithms_subtractBackground_Purpose
134 - @ref meas_algorithms_subtractBackground_Initialize
135 - @ref meas_algorithms_subtractBackground_IO
136 - @ref meas_algorithms_subtractBackground_Config
137 - @ref meas_algorithms_subtractBackground_Metadata
138 - @ref meas_algorithms_subtractBackground_Debug
139 - @ref meas_algorithms_subtractBackground_Example
141 @section meas_algorithms_subtractBackground_Purpose Description
143 Fit a model of the background of an exposure and subtract it.
145 @section meas_algorithms_subtractBackground_Initialize Task initialisation
147 @copydoc \_\_init\_\_
149 @section meas_algorithms_subtractBackground_IO Invoking the Task
151 Call `run` to fit the background and subtract it.
153 Call `fitBackground` to fit the background without subtracting it.
155 @section meas_algorithms_subtractBackground_Config Configuration parameters
157 See @ref SubtractBackgroundConfig
159 @section meas_algorithms_subtractBackground_Metadata Quantities set in exposure Metadata
161 The `run` method will optionally set the following items of exposure metadata;
162 the names may be overridden; the defaults are shown:
164 <dt>BGMEAN <dd>mean value of background
165 <dt>BGVAR <dd>standard deviation of background
168 @section meas_algorithms_subtractBackground_Debug Debug variables
170 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a flag
171 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug for more about `debug.py`.
173 SubtractBackgroundTask has a debug dictionary containing three integer keys:
176 <dd>If >0: `fitBackground` displays the unsubtracted masked image overlaid with the grid of cells
177 used to fit the background in the specified frame
179 <dd>If >0: `run` displays the background-subtracted exposure is the specified frame
181 <dd>If >0: `run` displays the background image in the specified frame
184 For example, put something like:
188 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
189 if name == "lsst.meas.algorithms.subtractBackground":
198 lsstDebug.Info = DebugInfo
200 into your `debug.py` file and run your task with the `--debug` flag.
202 @section meas_algorithms_subtractBackground_Example A complete example of using SubtractBackgroundTask
204 This code is in @link subtractBackgroundExample.py@endlink in the examples directory, and can be run as:
206 python examples/subtractBackgroundExample.py
208 @dontinclude subtractBackgroundExample.py
210 Import the task (there are some other standard imports; read the file if you're curious)
211 @skipline import SubtractBackgroundTask
213 Create the task, run it, and report mean and variance of background.
214 @skip create the task
217 ConfigClass = SubtractBackgroundConfig
218 _DefaultName =
"subtractBackground"
220 def run(self, exposure, background=None, stats=True, statsKeys=None):
221 """!Fit and subtract the background of an exposure
223 @param[in,out] exposure exposure whose background is to be subtracted
224 @param[in,out] background initial background model already subtracted from exposure
225 (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted.
226 @param[in] stats if True then measure the mean and variance of the full background model
227 and record the results in the exposure's metadata
228 @param[in] statsKeys key names used to store the mean and variance of the background
229 in the exposure's metadata (a pair of strings); if None then use ("BGMEAN", "BGVAR");
230 ignored if stats is false
232 @return an lsst.pipe.base.Struct containing:
233 - background full background model (initial model with changes), an lsst.afw.math.BackgroundList
235 if background
is None:
236 background = afwMath.BackgroundList()
238 maskedImage = exposure.getMaskedImage()
240 maskedImage -= fitBg.getImageF()
241 background.append(fitBg)
244 self.
_addStats(exposure, background, statsKeys=statsKeys)
246 subFrame = getDebugFrame(self._display,
"subtracted")
248 subDisp = afwDisplay.getDisplay(frame=subFrame)
249 subDisp.mtv(exposure, title=
"subtracted")
251 bgFrame = getDebugFrame(self._display,
"background")
253 bgDisp = afwDisplay.getDisplay(frame=bgFrame)
254 bgImage = background.getImage()
255 bgDisp.mtv(bgImage, title=
"background")
257 return pipeBase.Struct(
258 background=background,
261 def _addStats(self, exposure, background, statsKeys=None):
262 """Add statistics about the background to the exposure's metadata
264 @param[in,out] exposure exposure whose background was subtracted
265 @param[in,out] background background model (an lsst.afw.math.BackgroundList)
266 @param[in] statsKeys key names used to store the mean and variance of the background
267 in the exposure's metadata (a pair of strings); if None then use ("BGMEAN", "BGVAR");
268 ignored if stats is false
270 netBgImg = background.getImage()
271 if statsKeys
is None:
272 statsKeys = (
"BGMEAN",
"BGVAR")
273 mnkey, varkey = statsKeys
274 meta = exposure.getMetadata()
275 s = afwMath.makeStatistics(netBgImg, afwMath.MEAN | afwMath.VARIANCE)
276 bgmean = s.getValue(afwMath.MEAN)
277 bgvar = s.getValue(afwMath.VARIANCE)
278 meta.addDouble(mnkey, bgmean)
279 meta.addDouble(varkey, bgvar)
282 """!Estimate the background of a masked image
284 @param[in] maskedImage masked image whose background is to be computed
285 @param[in] nx number of x bands; if 0 compute from width and config.binSizeX
286 @param[in] ny number of y bands; if 0 compute from height and config.binSizeY
287 @param[in] algorithm name of interpolation algorithm; if None use self.config.algorithm
289 @return fit background as an lsst.afw.math.Background
291 @throw RuntimeError if lsst.afw.math.makeBackground returns None,
292 which is apparently one way it indicates failure
295 binSizeX = self.config.binSize
if self.config.binSizeX == 0
else self.config.binSizeX
296 binSizeY = self.config.binSize
if self.config.binSizeY == 0
else self.config.binSizeY
299 nx = maskedImage.getWidth()//binSizeX + 1
301 ny = maskedImage.getHeight()//binSizeY + 1
303 unsubFrame = getDebugFrame(self._display,
"unsubtracted")
305 unsubDisp = afwDisplay.getDisplay(frame=unsubFrame)
306 unsubDisp.mtv(maskedImage, title=
"unsubtracted")
307 xPosts = numpy.rint(numpy.linspace(0, maskedImage.getWidth() + 1, num=nx, endpoint=
True))
308 yPosts = numpy.rint(numpy.linspace(0, maskedImage.getHeight() + 1, num=ny, endpoint=
True))
309 with unsubDisp.Buffering():
310 for (xMin, xMax), (yMin, yMax)
in itertools.product(zip(xPosts[:-1], xPosts[1:]),
311 zip(yPosts[:-1], yPosts[1:])):
312 unsubDisp.line([(xMin, yMin), (xMin, yMax), (xMax, yMax), (xMax, yMin), (xMin, yMin)])
314 sctrl = afwMath.StatisticsControl()
315 sctrl.setAndMask(reduce(
lambda x, y: x | maskedImage.getMask().getPlaneBitMask(y),
316 self.config.ignoredPixelMask, 0x0))
317 sctrl.setNanSafe(self.config.isNanSafe)
319 self.log.debug(
"Ignoring mask planes: %s" %
", ".join(self.config.ignoredPixelMask))
321 if algorithm
is None:
322 algorithm = self.config.algorithm
324 bctrl = afwMath.BackgroundControl(algorithm, nx, ny,
325 self.config.undersampleStyle, sctrl,
326 self.config.statisticsProperty)
340 if self.config.useApprox:
341 if self.config.approxOrderY
not in (self.config.approxOrderX, -1):
342 raise ValueError(
"Error: approxOrderY not in (approxOrderX, -1)")
343 order = self.config.approxOrderX
344 minNumberGridPoints = order + 1
345 if min(nx, ny) <= order:
346 self.log.warn(
"Too few points in grid to constrain fit: min(nx, ny) < approxOrder) "
347 "[min(%d, %d) < %d]" % (nx, ny, order))
348 if self.config.undersampleStyle ==
"THROW_EXCEPTION":
349 raise ValueError(
"Too few points in grid (%d, %d) for order (%d) and binSize (%d, %d)" %
350 (nx, ny, order, binSizeX, binSizeY))
351 elif self.config.undersampleStyle ==
"REDUCE_INTERP_ORDER":
353 raise ValueError(
"Cannot reduce approxOrder below 0. " +
354 "Try using undersampleStyle = \"INCREASE_NXNYSAMPLE\" instead?")
355 order = min(nx, ny) - 1
356 self.log.warn(
"Reducing approxOrder to %d" % order)
357 elif self.config.undersampleStyle ==
"INCREASE_NXNYSAMPLE":
359 newBinSize = min(maskedImage.getWidth(), maskedImage.getHeight())//(minNumberGridPoints-1)
361 raise ValueError(
"Binsize must be greater than 0")
362 newNx = maskedImage.getWidth()//newBinSize + 1
363 newNy = maskedImage.getHeight()//newBinSize + 1
364 bctrl.setNxSample(newNx)
365 bctrl.setNySample(newNy)
366 self.log.warn(
"Decreasing binSize from (%d, %d) to %d for a grid of (%d, %d)" %
367 (binSizeX, binSizeY, newBinSize, newNx, newNy))
369 actrl = afwMath.ApproximateControl(afwMath.ApproximateControl.CHEBYSHEV, order, order,
370 self.config.weighting)
371 bctrl.setApproximateControl(actrl)
373 bg = afwMath.makeBackground(maskedImage, bctrl)
375 raise RuntimeError(
"lsst.afw.math.makeBackground failed to fit a background model")
Config for SubtractBackgroundTask.
def run
Fit and subtract the background of an exposure.
Subtract the background from an exposure.
def fitBackground
Estimate the background of a masked image.