206 def run(self, exposure, background=None, stats=True, statsKeys=None, backgroundToPhotometricRatio=None):
207 """Fit and subtract the background of an exposure.
211 exposure : `lsst.afw.image.Exposure`
212 Exposure whose background is to be subtracted.
213 background : `lsst.afw.math.BackgroundList`
214 Initial background model already subtracted. May be None if no background
217 If True then measure the mean and variance of the full background model and
218 record the results in the exposure's metadata.
220 Key names used to store the mean and variance of the background in the
221 exposure's metadata (another tuple); if None then use ("BGMEAN", "BGVAR");
222 ignored if stats is false.
223 backgroundToPhotometricRatio : `lsst.afw.image.Image`, optional
224 Image to multiply a photometrically-flattened image by to obtain a
225 background-flattened image.
226 Only used if config.doApplyFlatBackgroundRatio = True.
230 background : `lsst.afw.math.BackgroundList`
231 Full background model (initial model with changes), contained in an
232 `lsst.pipe.base.Struct`.
234 if background
is None:
235 background = afwMath.BackgroundList()
237 maskedImage = exposure.maskedImage
241 self.config.doApplyFlatBackgroundRatio,
242 backgroundToPhotometricRatio=backgroundToPhotometricRatio,
245 if self.config.doFilterSuperPixels:
247 superPixelFilterSize=self.config.superPixelFilterSize)
249 maskedImage -= fitBg.getImageF(self.config.algorithm, self.config.undersampleStyle)
251 actrl = fitBg.getBackgroundControl().getApproximateControl()
252 background.append((fitBg, getattr(afwMath.Interpolate, self.config.algorithm),
253 fitBg.getAsUsedUndersampleStyle(), actrl.getStyle(),
254 actrl.getOrderX(), actrl.getOrderY(), actrl.getWeighting()))
257 self.
_addStats(exposure, background, statsKeys=statsKeys)
259 subFrame = getDebugFrame(self._display,
"subtracted")
261 subDisp = afwDisplay.getDisplay(frame=subFrame)
262 subDisp.mtv(exposure, title=
"subtracted")
264 bgFrame = getDebugFrame(self._display,
"background")
266 bgDisp = afwDisplay.getDisplay(frame=bgFrame)
267 bgImage = background.getImage()
268 bgDisp.mtv(bgImage, title=
"background")
270 return pipeBase.Struct(
271 background=background,
274 def _addStats(self, exposure, background, statsKeys=None):
275 """Add statistics about the background to the exposure's metadata
279 exposure : `lsst.afw.image.Exposure`
280 Exposure whose background was subtracted.
281 background : `lsst.afw.math.BackgroundList`
284 Key names used to store the mean and variance of the background in
285 the exposure's metadata (a tuple); if None then use
286 ("BGMEAN", "BGVAR"); ignored if stats is false.
288 netBgImg = background.getImage()
289 if statsKeys
is None:
290 statsKeys = (
"BGMEAN",
"BGVAR")
291 mnkey, varkey = statsKeys
292 meta = exposure.getMetadata()
293 s = afwMath.makeStatistics(netBgImg, afwMath.MEAN | afwMath.VARIANCE)
294 bgmean = s.getValue(afwMath.MEAN)
295 bgvar = s.getValue(afwMath.VARIANCE)
296 meta.addDouble(mnkey, bgmean)
297 meta.addDouble(varkey, bgvar)
300 """Estimate the background of a masked image
304 maskedImage : `lsst.afw.image.maskedImage`
305 Masked image whose background is to be computed
307 Number of x bands; if 0 compute from width and `self.config.binSizeX`
309 Number of y bands; if 0 compute from height and `self.config.binSizeY`
311 Name of interpolation algorithm; if None use `self.config.algorithm`
315 bg : `lsst.afw.math.Background`
321 Raised if lsst.afw.math.makeBackground returns None, an indicator
326 nx = math.ceil(maskedImage.getWidth() / self.
binSizeX)
328 ny = math.ceil(maskedImage.getHeight() / self.
binSizeY)
330 unsubFrame = getDebugFrame(self._display,
"unsubtracted")
332 unsubDisp = afwDisplay.getDisplay(frame=unsubFrame)
333 unsubDisp.mtv(maskedImage, title=
"unsubtracted")
334 xPosts = numpy.rint(numpy.linspace(0, maskedImage.getWidth() + 1, num=nx, endpoint=
True))
335 yPosts = numpy.rint(numpy.linspace(0, maskedImage.getHeight() + 1, num=ny, endpoint=
True))
336 with unsubDisp.Buffering():
337 for (xMin, xMax), (yMin, yMax)
in itertools.product(zip(xPosts[:-1], xPosts[1:]),
338 zip(yPosts[:-1], yPosts[1:])):
339 unsubDisp.line([(xMin, yMin), (xMin, yMax), (xMax, yMax), (xMax, yMin), (xMin, yMin)])
341 sctrl = afwMath.StatisticsControl()
342 badMask = maskedImage.mask.getPlaneBitMask(self.config.ignoredPixelMask)
344 sctrl.setAndMask(badMask)
345 sctrl.setNanSafe(self.config.isNanSafe)
347 self.log.debug(
"Ignoring mask planes: %s",
", ".join(self.config.ignoredPixelMask))
348 if (maskedImage.mask.getArray() & badMask).all():
351 if algorithm
is None:
352 algorithm = self.config.algorithm
360 with suppress_deprecations():
361 bctrl = afwMath.BackgroundControl(algorithm, nx, ny,
362 self.config.undersampleStyle, sctrl,
363 self.config.statisticsProperty)
377 if self.config.useApprox:
378 if self.config.approxOrderY
not in (self.config.approxOrderX, -1):
379 raise ValueError(
"Error: approxOrderY not in (approxOrderX, -1)")
380 order = self.config.approxOrderX
381 minNumberGridPoints = order + 1
382 if min(nx, ny) <= order:
383 self.log.warning(
"Too few points in grid to constrain fit: min(nx, ny) < approxOrder) "
384 "[min(%d, %d) < %d]", nx, ny, order)
385 if self.config.undersampleStyle ==
"THROW_EXCEPTION":
386 raise ValueError(
"Too few points in grid (%d, %d) for order (%d) and binSize (%d, %d)" %
388 elif self.config.undersampleStyle ==
"REDUCE_INTERP_ORDER":
390 raise ValueError(
"Cannot reduce approxOrder below 0. "
391 "Try using undersampleStyle = \"INCREASE_NXNYSAMPLE\" instead?")
392 order = min(nx, ny) - 1
393 self.log.warning(
"Reducing approxOrder to %d", order)
394 elif self.config.undersampleStyle ==
"INCREASE_NXNYSAMPLE":
396 newBinSize = min(maskedImage.getWidth(), maskedImage.getHeight())//(minNumberGridPoints-1)
398 raise ValueError(
"Binsize must be greater than 0")
399 newNx = maskedImage.getWidth()//newBinSize + 1
400 newNy = maskedImage.getHeight()//newBinSize + 1
401 bctrl.setNxSample(newNx)
402 bctrl.setNySample(newNy)
403 self.log.warning(
"Decreasing binSize from (%d, %d) to %d for a grid of (%d, %d)",
406 actrl = afwMath.ApproximateControl(afwMath.ApproximateControl.CHEBYSHEV, order, order,
407 self.config.weighting)
408 bctrl.setApproximateControl(actrl)
410 bg = afwMath.makeBackground(maskedImage, bctrl)
412 raise RuntimeError(
"lsst.afw.math.makeBackground failed to fit a background model")