23__all__ = (
"SubtractBackgroundConfig",
"SubtractBackgroundTask")
29from lsstDebug
import getDebugFrame
40 """Config for SubtractBackgroundTask
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,
120 """Subtract the background from an exposure
122 ConfigClass = SubtractBackgroundConfig
123 _DefaultName = "subtractBackground"
125 def run(self, exposure, background=None, stats=True, statsKeys=None):
126 """Fit and subtract the background of an exposure.
131 Exposure whose background is to be subtracted.
133 Initial background model already subtracted. May be
None if no background
136 If
True then measure the mean
and variance of the full background model
and
137 record the results
in the exposure
's metadata.
139 Key names used to store the mean and variance of the background
in the
140 exposure
's metadata (another tuple); if None then use ("BGMEAN", "BGVAR");
141 ignored if stats
is false.
145 background : `lsst.afw.math.BackgroundLst`
146 Full background model (initial model
with changes), contained
in an
147 `lsst.pipe.base.Struct`.
149 if background
is None:
152 maskedImage = exposure.getMaskedImage()
154 maskedImage -= fitBg.getImageF(self.config.algorithm, self.config.undersampleStyle)
156 actrl = fitBg.getBackgroundControl().getApproximateControl()
158 fitBg.getAsUsedUndersampleStyle(), actrl.getStyle(),
159 actrl.getOrderX(), actrl.getOrderY(), actrl.getWeighting()))
162 self.
_addStats(exposure, background, statsKeys=statsKeys)
164 subFrame = getDebugFrame(self._display,
"subtracted")
166 subDisp = afwDisplay.getDisplay(frame=subFrame)
167 subDisp.mtv(exposure, title=
"subtracted")
169 bgFrame = getDebugFrame(self._display,
"background")
171 bgDisp = afwDisplay.getDisplay(frame=bgFrame)
172 bgImage = background.getImage()
173 bgDisp.mtv(bgImage, title=
"background")
175 return pipeBase.Struct(
176 background=background,
179 def _addStats(self, exposure, background, statsKeys=None):
180 """Add statistics about the background to the exposure's metadata
185 Exposure whose background was subtracted.
189 Key names used to store the mean and variance of the background
in
190 the exposure
's metadata (a tuple); if None then use
191 ("BGMEAN",
"BGVAR"); ignored
if stats
is false.
193 netBgImg = background.getImage()
194 if statsKeys
is None:
195 statsKeys = (
"BGMEAN",
"BGVAR")
196 mnkey, varkey = statsKeys
197 meta = exposure.getMetadata()
199 bgmean = s.getValue(afwMath.MEAN)
200 bgvar = s.getValue(afwMath.VARIANCE)
201 meta.addDouble(mnkey, bgmean)
202 meta.addDouble(varkey, bgvar)
205 """Estimate the background of a masked image
209 maskedImage : `lsst.afw.image.maskedImage`
210 Masked image whose background is to be computed
212 Number of x bands; if 0 compute
from width
and `self.config.binSizeX`
214 Number of y bands;
if 0 compute
from height
and `self.config.binSizeY`
216 Name of interpolation algorithm;
if None use `self.config.algorithm`
226 Raised
if lsst.afw.math.makeBackground returns
None, an indicator
230 binSizeX = self.config.binSize if self.config.binSizeX == 0
else self.config.binSizeX
231 binSizeY = self.config.binSize
if self.config.binSizeY == 0
else self.config.binSizeY
234 nx = maskedImage.getWidth()//binSizeX + 1
236 ny = maskedImage.getHeight()//binSizeY + 1
238 unsubFrame = getDebugFrame(self._display,
"unsubtracted")
240 unsubDisp = afwDisplay.getDisplay(frame=unsubFrame)
241 unsubDisp.mtv(maskedImage, title=
"unsubtracted")
242 xPosts = numpy.rint(numpy.linspace(0, maskedImage.getWidth() + 1, num=nx, endpoint=
True))
243 yPosts = numpy.rint(numpy.linspace(0, maskedImage.getHeight() + 1, num=ny, endpoint=
True))
244 with unsubDisp.Buffering():
245 for (xMin, xMax), (yMin, yMax)
in itertools.product(zip(xPosts[:-1], xPosts[1:]),
246 zip(yPosts[:-1], yPosts[1:])):
247 unsubDisp.line([(xMin, yMin), (xMin, yMax), (xMax, yMax), (xMax, yMin), (xMin, yMin)])
250 badMask = maskedImage.mask.getPlaneBitMask(self.config.ignoredPixelMask)
252 sctrl.setAndMask(badMask)
253 sctrl.setNanSafe(self.config.isNanSafe)
255 self.log.debug(
"Ignoring mask planes: %s",
", ".join(self.config.ignoredPixelMask))
256 if (maskedImage.mask.getArray() & badMask).all():
257 raise pipeBase.TaskError(
"All pixels masked. Cannot estimate background")
259 if algorithm
is None:
260 algorithm = self.config.algorithm
268 with suppress_deprecations():
270 self.config.undersampleStyle, sctrl,
271 self.config.statisticsProperty)
285 if self.config.useApprox:
286 if self.config.approxOrderY
not in (self.config.approxOrderX, -1):
287 raise ValueError(
"Error: approxOrderY not in (approxOrderX, -1)")
288 order = self.config.approxOrderX
289 minNumberGridPoints = order + 1
290 if min(nx, ny) <= order:
291 self.log.warning(
"Too few points in grid to constrain fit: min(nx, ny) < approxOrder) "
292 "[min(%d, %d) < %d]", nx, ny, order)
293 if self.config.undersampleStyle ==
"THROW_EXCEPTION":
294 raise ValueError(
"Too few points in grid (%d, %d) for order (%d) and binSize (%d, %d)" %
295 (nx, ny, order, binSizeX, binSizeY))
296 elif self.config.undersampleStyle ==
"REDUCE_INTERP_ORDER":
298 raise ValueError(
"Cannot reduce approxOrder below 0. "
299 "Try using undersampleStyle = \"INCREASE_NXNYSAMPLE\" instead?")
300 order =
min(nx, ny) - 1
301 self.log.warning(
"Reducing approxOrder to %d", order)
302 elif self.config.undersampleStyle ==
"INCREASE_NXNYSAMPLE":
304 newBinSize =
min(maskedImage.getWidth(), maskedImage.getHeight())//(minNumberGridPoints-1)
306 raise ValueError(
"Binsize must be greater than 0")
307 newNx = maskedImage.getWidth()//newBinSize + 1
308 newNy = maskedImage.getHeight()//newBinSize + 1
309 bctrl.setNxSample(newNx)
310 bctrl.setNySample(newNy)
311 self.log.warning(
"Decreasing binSize from (%d, %d) to %d for a grid of (%d, %d)",
312 binSizeX, binSizeY, newBinSize, newNx, newNy)
315 self.config.weighting)
316 bctrl.setApproximateControl(actrl)
320 raise RuntimeError(
"lsst.afw.math.makeBackground failed to fit a background model")
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Represent a 2-dimensional array of bitmask pixels.
Control how to make an approximation.
Pass parameters to a Background object.
A virtual base class to evaluate image background levels.
Pass parameters to a Statistics object.
_addStats(self, exposure, background, statsKeys=None)
fitBackground(self, maskedImage, nx=0, ny=0, algorithm=None)
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.
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)