125 def run(self, exposure, background=None, stats=True, statsKeys=None):
126 """Fit and subtract the background of an exposure.
130 exposure : `lsst.afw.image.Exposure`
131 Exposure whose background is to be subtracted.
132 background : `lsst.afw.math.BackgroundList`
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
184 exposure : `lsst.afw.image.Exposure`
185 Exposure whose background was subtracted.
186 background : `lsst.afw.math.BackgroundList`
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`
220 bg : `lsst.afw.math.Background`
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")