211 def run(self, exposure, background=None, stats=True, statsKeys=None, backgroundToPhotometricRatio=None):
212 """Fit and subtract the background of an exposure.
216 exposure : `lsst.afw.image.Exposure`
217 Exposure whose background is to be subtracted.
218 background : `lsst.afw.math.BackgroundList`
219 Initial background model already subtracted. May be None if no background
222 If True then measure the mean and variance of the full background model and
223 record the results in the exposure's metadata.
225 Key names used to store the mean and variance of the background in the
226 exposure's metadata (another tuple); if None then use ("BGMEAN", "BGVAR");
227 ignored if stats is false.
228 backgroundToPhotometricRatio : `lsst.afw.image.Image`, optional
229 Image to multiply a photometrically-flattened image by to obtain a
230 background-flattened image.
231 Only used if config.doApplyFlatBackgroundRatio = True.
235 background : `lsst.afw.math.BackgroundList`
236 Full background model (initial model with changes), contained in an
237 `lsst.pipe.base.Struct`.
239 if background
is None:
240 background = afwMath.BackgroundList()
242 maskedImage = exposure.maskedImage
246 self.config.doApplyFlatBackgroundRatio,
247 backgroundToPhotometricRatio=backgroundToPhotometricRatio,
250 if self.config.doFilterSuperPixels:
252 superPixelFilterSize=self.config.superPixelFilterSize)
254 maskedImage -= fitBg.getImageF(self.config.algorithm, self.config.undersampleStyle)
256 actrl = fitBg.getBackgroundControl().getApproximateControl()
257 background.append((fitBg, getattr(afwMath.Interpolate, self.config.algorithm),
258 fitBg.getAsUsedUndersampleStyle(), actrl.getStyle(),
259 actrl.getOrderX(), actrl.getOrderY(), actrl.getWeighting()))
262 self.
_addStats(exposure, background, statsKeys=statsKeys)
264 subFrame = getDebugFrame(self._display,
"subtracted")
266 subDisp = afwDisplay.getDisplay(frame=subFrame)
267 subDisp.mtv(exposure, title=
"subtracted")
269 bgFrame = getDebugFrame(self._display,
"background")
271 bgDisp = afwDisplay.getDisplay(frame=bgFrame)
272 bgImage = background.getImage()
273 bgDisp.mtv(bgImage, title=
"background")
275 return pipeBase.Struct(
276 background=background,
279 def _addStats(self, exposure, background, statsKeys=None):
280 """Add statistics about the background to the exposure's metadata
284 exposure : `lsst.afw.image.Exposure`
285 Exposure whose background was subtracted.
286 background : `lsst.afw.math.BackgroundList`
289 Key names used to store the mean and variance of the background in
290 the exposure's metadata (a tuple); if None then use
291 ("BGMEAN", "BGVAR"); ignored if stats is false.
293 netBgImg = background.getImage()
294 if statsKeys
is None:
295 statsKeys = (
"BGMEAN",
"BGVAR")
296 mnkey, varkey = statsKeys
297 meta = exposure.getMetadata()
298 s = afwMath.makeStatistics(netBgImg, afwMath.MEAN | afwMath.VARIANCE)
299 bgmean = s.getValue(afwMath.MEAN)
300 bgvar = s.getValue(afwMath.VARIANCE)
301 meta.addDouble(mnkey, bgmean)
302 meta.addDouble(varkey, bgvar)
305 """Estimate the background of a masked image
309 maskedImage : `lsst.afw.image.maskedImage`
310 Masked image whose background is to be computed
312 Number of x bands; if 0 compute from width and `self.config.binSizeX`
314 Number of y bands; if 0 compute from height and `self.config.binSizeY`
316 Name of interpolation algorithm; if None use `self.config.algorithm`
320 bg : `lsst.afw.math.Background`
326 Raised if lsst.afw.math.makeBackground returns None, an indicator
331 nx = math.ceil(maskedImage.getWidth() / self.
binSizeX)
333 ny = math.ceil(maskedImage.getHeight() / self.
binSizeY)
335 unsubFrame = getDebugFrame(self._display,
"unsubtracted")
337 unsubDisp = afwDisplay.getDisplay(frame=unsubFrame)
338 unsubDisp.mtv(maskedImage, title=
"unsubtracted")
339 xPosts = numpy.rint(numpy.linspace(0, maskedImage.getWidth() + 1, num=nx, endpoint=
True))
340 yPosts = numpy.rint(numpy.linspace(0, maskedImage.getHeight() + 1, num=ny, endpoint=
True))
341 with unsubDisp.Buffering():
342 for (xMin, xMax), (yMin, yMax)
in itertools.product(zip(xPosts[:-1], xPosts[1:]),
343 zip(yPosts[:-1], yPosts[1:])):
344 unsubDisp.line([(xMin, yMin), (xMin, yMax), (xMax, yMax), (xMax, yMin), (xMin, yMin)])
346 sctrl = afwMath.StatisticsControl()
347 badMask = maskedImage.mask.getPlaneBitMask(self.config.ignoredPixelMask)
349 sctrl.setAndMask(badMask)
350 sctrl.setNanSafe(self.config.isNanSafe)
352 self.log.debug(
"Ignoring mask planes: %s",
", ".join(self.config.ignoredPixelMask))
353 if (maskedImage.mask.getArray() & badMask).all():
356 if algorithm
is None:
357 algorithm = self.config.algorithm
365 with suppress_deprecations():
366 bctrl = afwMath.BackgroundControl(algorithm, nx, ny,
367 self.config.undersampleStyle, sctrl,
368 self.config.statisticsProperty)
382 if self.config.useApprox:
383 if self.config.approxOrderY
not in (self.config.approxOrderX, -1):
384 raise ValueError(
"Error: approxOrderY not in (approxOrderX, -1)")
385 order = self.config.approxOrderX
386 minNumberGridPoints = order + 1
387 if min(nx, ny) <= order:
388 self.log.warning(
"Too few points in grid to constrain fit: min(nx, ny) < approxOrder) "
389 "[min(%d, %d) < %d]", nx, ny, order)
390 if self.config.undersampleStyle ==
"THROW_EXCEPTION":
391 raise ValueError(
"Too few points in grid (%d, %d) for order (%d) and binSize (%d, %d)" %
393 elif self.config.undersampleStyle ==
"REDUCE_INTERP_ORDER":
395 raise ValueError(
"Cannot reduce approxOrder below 0. "
396 "Try using undersampleStyle = \"INCREASE_NXNYSAMPLE\" instead?")
397 order = min(nx, ny) - 1
398 self.log.warning(
"Reducing approxOrder to %d", order)
399 elif self.config.undersampleStyle ==
"INCREASE_NXNYSAMPLE":
401 newBinSize = min(maskedImage.getWidth(), maskedImage.getHeight())//(minNumberGridPoints-1)
403 raise ValueError(
"Binsize must be greater than 0")
404 newNx = maskedImage.getWidth()//newBinSize + 1
405 newNy = maskedImage.getHeight()//newBinSize + 1
406 bctrl.setNxSample(newNx)
407 bctrl.setNySample(newNy)
408 self.log.warning(
"Decreasing binSize from (%d, %d) to %d for a grid of (%d, %d)",
411 actrl = afwMath.ApproximateControl(afwMath.ApproximateControl.CHEBYSHEV, order, order,
412 self.config.weighting)
413 bctrl.setApproximateControl(actrl)
415 bg = afwMath.makeBackground(maskedImage, bctrl)
417 raise RuntimeError(
"lsst.afw.math.makeBackground failed to fit a background model")