LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
detection.py
Go to the documentation of this file.
1 
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2011 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 import lsstDebug
23 import lsst.pex.logging as pexLogging
24 
25 import lsst.pex.config as pexConfig
26 import lsst.afw.math as afwMath
27 import lsst.afw.table as afwTable
28 import lsst.afw.image as afwImage
29 import lsst.afw.geom as afwGeom
30 import lsst.afw.detection as afwDet
31 import lsst.pipe.base as pipeBase
32 
33 __all__ = ("SourceDetectionConfig", "SourceDetectionTask", "getBackground",
34  "estimateBackground", "BackgroundConfig", "addExposures")
35 
36 import lsst.afw.display.ds9 as ds9
37 
38 class BackgroundConfig(pexConfig.Config):
39  """!Config for background estimation
40  """
41  statisticsProperty = pexConfig.ChoiceField(
42  doc="type of statistic to use for grid points",
43  dtype=str, default="MEANCLIP",
44  allowed={
45  "MEANCLIP": "clipped mean",
46  "MEAN": "unclipped mean",
47  "MEDIAN": "median",
48  }
49  )
50  undersampleStyle = pexConfig.ChoiceField(
51  doc="behaviour if there are too few points in grid for requested interpolation style",
52  dtype=str, default="REDUCE_INTERP_ORDER",
53  allowed={
54  "THROW_EXCEPTION": "throw an exception if there are too few points",
55  "REDUCE_INTERP_ORDER": "use an interpolation style with a lower order.",
56  "INCREASE_NXNYSAMPLE": "Increase the number of samples used to make the interpolation grid.",
57  }
58  )
59  binSize = pexConfig.RangeField(
60  doc="how large a region of the sky should be used for each background point",
61  dtype=int, default=256, min=10
62  )
63  algorithm = pexConfig.ChoiceField(
64  doc="how to interpolate the background values. This maps to an enum; see afw::math::Background",
65  dtype=str, default="NATURAL_SPLINE", optional=True,
66  allowed={
67  "CONSTANT" : "Use a single constant value",
68  "LINEAR" : "Use linear interpolation",
69  "NATURAL_SPLINE" : "cubic spline with zero second derivative at endpoints",
70  "AKIMA_SPLINE": "higher-level nonlinear spline that is more robust to outliers",
71  "NONE": "No background estimation is to be attempted",
72  }
73  )
74  ignoredPixelMask = pexConfig.ListField(
75  doc="Names of mask planes to ignore while estimating the background",
76  dtype=str, default = ["EDGE", "DETECTED", "DETECTED_NEGATIVE"],
77  itemCheck = lambda x: x in afwImage.MaskU().getMaskPlaneDict().keys(),
78  )
79  isNanSafe = pexConfig.Field(
80  doc="Ignore NaNs when estimating the background",
81  dtype=bool, default=False,
82  )
83 
84  def validate(self):
85  pexConfig.Config.validate(self)
86  # Allow None to be used as an equivalent for "NONE", even though C++ expects the latter.
87  if self.algorithm is None:
88  self.algorithm = "NONE"
89 
90 class SourceDetectionConfig(pexConfig.Config):
91  """!Configuration parameters for the SourceDetectionTask
92  """
93  minPixels = pexConfig.RangeField(
94  doc="detected sources with fewer than the specified number of pixels will be ignored",
95  dtype=int, optional=False, default=1, min=0,
96  )
97  isotropicGrow = pexConfig.Field(
98  doc="Pixels should be grown as isotropically as possible (slower)",
99  dtype=bool, optional=False, default=False,
100  )
101  nSigmaToGrow = pexConfig.Field(
102  doc="Grow detections by nSigmaToGrow * sigma; if 0 then do not grow",
103  dtype=float, default=2.4, # 2.4 pixels/sigma is roughly one pixel/FWHM
104  )
105  returnOriginalFootprints = pexConfig.Field(
106  doc="Grow detections to set the image mask bits, but return the original (not-grown) footprints",
107  dtype=bool, optional=False, default=True # TODO: set default to False once we have a deblender; ticket #2138
108  )
109  thresholdValue = pexConfig.RangeField(
110  doc="Threshold for footprints",
111  dtype=float, optional=False, default=5.0, min=0.0,
112  )
113  includeThresholdMultiplier = pexConfig.RangeField(
114  doc="Include threshold relative to thresholdValue",
115  dtype=float, default=1.0, min=0.0,
116  )
117  thresholdType = pexConfig.ChoiceField(
118  doc="specifies the desired flavor of Threshold",
119  dtype=str, optional=False, default="stdev",
120  allowed={
121  "variance": "threshold applied to image variance",
122  "stdev": "threshold applied to image std deviation",
123  "value": "threshold applied to image value",
124  "pixel_stdev": "threshold applied to per-pixel std deviation",
125  }
126  )
127  thresholdPolarity = pexConfig.ChoiceField(
128  doc="specifies whether to detect positive, or negative sources, or both",
129  dtype=str, optional=False, default="positive",
130  allowed={
131  "positive": "detect only positive sources",
132  "negative": "detect only negative sources",
133  "both": "detect both positive and negative sources",
134  }
135  )
136  adjustBackground = pexConfig.Field(
137  dtype = float,
138  doc = "Fiddle factor to add to the background; debugging only",
139  default = 0.0,
140  )
141  reEstimateBackground = pexConfig.Field(
142  dtype = bool,
143  doc = "Estimate the background again after final source detection?",
144  default = True, optional=False,
145  )
146  background = pexConfig.ConfigField(
147  dtype=BackgroundConfig,
148  doc="Background re-estimation configuration"
149  )
150 
151 ## \addtogroup LSST_task_documentation
152 ## \{
153 ## \page sourceDetectionTask
154 ## \ref SourceDetectionTask_ "SourceDetectionTask"
155 ## \copybrief SourceDetectionTask
156 ## \}
157 
158 class SourceDetectionTask(pipeBase.Task):
159  """!
160 \anchor SourceDetectionTask_
161 
162 \brief Detect positive and negative sources on an exposure and return a new \link table.SourceCatalog\endlink.
163 
164 \section meas_algorithms_detection_Contents Contents
165 
166  - \ref meas_algorithms_detection_Purpose
167  - \ref meas_algorithms_detection_Initialize
168  - \ref meas_algorithms_detection_Invoke
169  - \ref meas_algorithms_detection_Config
170  - \ref meas_algorithms_detection_Debug
171  - \ref meas_algorithms_detection_Example
172 
173 \section meas_algorithms_detection_Purpose Description
174 
175 \copybrief SourceDetectionTask
176 
177 \section meas_algorithms_detection_Initialize Task initialisation
178 
179 \copydoc init
180 
181 \section meas_algorithms_detection_Invoke Invoking the Task
182 
183 \copydoc run
184 
185 \section meas_algorithms_detection_Config Configuration parameters
186 
187 See \ref SourceDetectionConfig
188 
189 \section meas_algorithms_detection_Debug Debug variables
190 
191 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
192 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
193 
194 The available variables in SourceDetectionTask are:
195 <DL>
196  <DT> \c display
197  <DD>
198  - If True, display the exposure on ds9's frame 0. +ve detections in blue, -ve detections in cyan
199  - If display > 1, display the convolved exposure on frame 1
200 </DL>
201 
202 \section meas_algorithms_detection_Example A complete example of using SourceDetectionTask
203 
204 This code is in \link measAlgTasks.py\endlink in the examples directory, and can be run as \em e.g.
205 \code
206 examples/measAlgTasks.py --ds9
207 \endcode
208 \dontinclude measAlgTasks.py
209 The example also runs the SourceMeasurementTask; see \ref meas_algorithms_measurement_Example for more explanation.
210 
211 Import the task (there are some other standard imports; read the file if you're confused)
212 \skipline SourceDetectionTask
213 
214 We need to create our task before processing any data as the task constructor
215 can add an extra column to the schema, but first we need an almost-empty Schema
216 \skipline makeMinimalSchema
217 after which we can call the constructor:
218 \skip SourceDetectionTask.ConfigClass
219 \until detectionTask
220 
221 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same
222 task objects). First create the output table:
223 \skipline afwTable
224 
225 And process the image
226 \skipline result
227 (You may not be happy that the threshold was set in the config before creating the Task rather than being set
228 separately for each exposure. You \em can reset it just before calling the run method if you must, but we
229 should really implement a better solution).
230 
231 We can then unpack and use the results:
232 \skip sources
233 \until print
234 
235 <HR>
236 To investigate the \ref meas_algorithms_detection_Debug, put something like
237 \code{.py}
238  import lsstDebug
239  def DebugInfo(name):
240  di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
241  if name == "lsst.meas.algorithms.detection":
242  di.display = 1
243 
244  return di
245 
246  lsstDebug.Info = DebugInfo
247 \endcode
248 into your debug.py file and run measAlgTasks.py with the \c --debug flag.
249  """
250  ConfigClass = SourceDetectionConfig
251  _DefaultName = "sourceDetection"
252 
253  # Need init as well as __init__ because "\copydoc __init__" fails (doxygen bug 732264)
254  def init(self, schema=None, **kwds):
255  """!Create the detection task. Most arguments are simply passed onto pipe.base.Task.
256 
257  \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog
258  \param **kwds Keyword arguments passed to lsst.pipe.base.task.Task.__init__.
259 
260  If schema is not None, a 'flags.negative' field will be added to label detections
261  made with a negative threshold.
262 
263  \note This task can add fields to the schema, so any code calling this task must ensure that
264  these columns are indeed present in the input match list; see \ref Example
265  """
266  self.__init__(schema, **kwds)
267 
268  def __init__(self, schema=None, **kwds):
269  """!Create the detection task. See SourceDetectionTask.init for documentation
270  """
271  pipeBase.Task.__init__(self, **kwds)
272  if schema is not None:
273  self.negativeFlagKey = schema.addField(
274  "flags_negative", type="Flag",
275  doc="set if source was detected as significantly negative"
276  )
277  else:
278  if self.config.thresholdPolarity == "both":
279  self.log.log(self.log.WARN, "Detection polarity set to 'both', but no flag will be "\
280  "set to distinguish between positive and negative detections")
281  self.negativeFlagKey = None
282 
283  @pipeBase.timeMethod
284  def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True):
285  """!Run source detection and create a SourceCatalog.
286 
287  \param table lsst.afw.table.SourceTable object that will be used to create the SourceCatalog.
288  \param exposure Exposure to process; DETECTED mask plane will be set in-place.
289  \param doSmooth if True, smooth the image before detection using a Gaussian of width sigma (default: True)
290  \param sigma sigma of PSF (pixels); used for smoothing and to grow detections;
291  if None then measure the sigma of the PSF of the exposure (default: None)
292  \param clearMask Clear DETECTED{,_NEGATIVE} planes before running detection (default: True)
293 
294  \return a lsst.pipe.base.Struct with:
295  - sources -- an lsst.afw.table.SourceCatalog object
296  - fpSets --- lsst.pipe.base.Struct returned by \link detectFootprints \endlink
297 
298  \throws ValueError if flags.negative is needed, but isn't in table's schema
299  \throws lsst.pipe.base.TaskError if sigma=None, doSmooth=True and the exposure has no PSF
300 
301  \note
302  If you want to avoid dealing with Sources and Tables, you can use detectFootprints()
303  to just get the afw::detection::FootprintSet%s.
304 
305  """
306  if self.negativeFlagKey is not None and self.negativeFlagKey not in table.getSchema():
307  raise ValueError("Table has incorrect Schema")
308  fpSets = self.detectFootprints(exposure=exposure, doSmooth=doSmooth, sigma=sigma,
309  clearMask=clearMask)
310  sources = afwTable.SourceCatalog(table)
311  table.preallocate(fpSets.numPos + fpSets.numNeg) # not required, but nice
312  if fpSets.negative:
313  fpSets.negative.makeSources(sources)
314  if self.negativeFlagKey:
315  for record in sources:
316  record.set(self.negativeFlagKey, True)
317  if fpSets.positive:
318  fpSets.positive.makeSources(sources)
319  return pipeBase.Struct(
320  sources = sources,
321  fpSets = fpSets
322  )
323 
324  ## An alias for run \deprecated Remove this alias after checking for where it's used
325  makeSourceCatalog = run
326 
327  @pipeBase.timeMethod
328  def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True):
329  """!Detect footprints.
330 
331  \param exposure Exposure to process; DETECTED{,_NEGATIVE} mask plane will be set in-place.
332  \param doSmooth if True, smooth the image before detection using a Gaussian of width sigma
333  \param sigma sigma of PSF (pixels); used for smoothing and to grow detections;
334  if None then measure the sigma of the PSF of the exposure
335  \param clearMask Clear both DETECTED and DETECTED_NEGATIVE planes before running detection
336 
337  \return a lsst.pipe.base.Struct with fields:
338  - positive: lsst.afw.detection.FootprintSet with positive polarity footprints (may be None)
339  - negative: lsst.afw.detection.FootprintSet with negative polarity footprints (may be None)
340  - numPos: number of footprints in positive or 0 if detection polarity was negative
341  - numNeg: number of footprints in negative or 0 if detection polarity was positive
342  - background: re-estimated background. None if reEstimateBackground==False
343 
344  \throws lsst.pipe.base.TaskError if sigma=None and the exposure has no PSF
345  """
346  try:
347  import lsstDebug
348  display = lsstDebug.Info(__name__).display
349  except ImportError:
350  try:
351  display
352  except NameError:
353  display = False
354 
355  if exposure is None:
356  raise RuntimeError("No exposure for detection")
357 
358  maskedImage = exposure.getMaskedImage()
359  region = maskedImage.getBBox()
360 
361  if clearMask:
362  mask = maskedImage.getMask()
363  mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask("DETECTED_NEGATIVE"))
364  del mask
365 
366  if sigma is None:
367  psf = exposure.getPsf()
368  if psf is None:
369  raise pipeBase.TaskError("exposure has no PSF; must specify sigma")
370  shape = psf.computeShape()
371  sigma = shape.getDeterminantRadius()
372 
373  self.metadata.set("sigma", sigma)
374  self.metadata.set("doSmooth", doSmooth)
375 
376  if not doSmooth:
377  convolvedImage = maskedImage.Factory(maskedImage)
378  middle = convolvedImage
379  else:
380  # smooth using a Gaussian (which is separate, hence fast) of width sigma
381  # make a SingleGaussian (separable) kernel with the 'sigma'
382  psf = exposure.getPsf()
383  kWidth = (int(sigma * 7 + 0.5) // 2) * 2 + 1 # make sure it is odd
384  self.metadata.set("smoothingKernelWidth", kWidth)
385  gaussFunc = afwMath.GaussianFunction1D(sigma)
386  gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc)
387 
388  convolvedImage = maskedImage.Factory(maskedImage.getBBox())
389 
390  afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl())
391  #
392  # Only search psf-smooth part of frame
393  #
394  goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
395  middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT, False)
396  #
397  # Mark the parts of the image outside goodBBox as EDGE
398  #
399  self.setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask("EDGE"))
400 
401  fpSets = pipeBase.Struct(positive=None, negative=None)
402 
403  if self.config.thresholdPolarity != "negative":
404  fpSets.positive = self.thresholdImage(middle, "positive")
405  if self.config.reEstimateBackground or self.config.thresholdPolarity != "positive":
406  fpSets.negative = self.thresholdImage(middle, "negative")
407 
408  for polarity, maskName in (("positive", "DETECTED"), ("negative", "DETECTED_NEGATIVE")):
409  fpSet = getattr(fpSets, polarity)
410  if fpSet is None:
411  continue
412  fpSet.setRegion(region)
413  if self.config.nSigmaToGrow > 0:
414  nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
415  self.metadata.set("nGrow", nGrow)
416  fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow)
417  fpSet.setMask(maskedImage.getMask(), maskName)
418  if not self.config.returnOriginalFootprints:
419  setattr(fpSets, polarity, fpSet)
420 
421  fpSets.numPos = len(fpSets.positive.getFootprints()) if fpSets.positive is not None else 0
422  fpSets.numNeg = len(fpSets.negative.getFootprints()) if fpSets.negative is not None else 0
423 
424  if self.config.thresholdPolarity != "negative":
425  self.log.log(self.log.INFO, "Detected %d positive sources to %g sigma." %
426  (fpSets.numPos, self.config.thresholdValue))
427 
428  fpSets.background = None
429  if self.config.reEstimateBackground:
430  mi = exposure.getMaskedImage()
431  bkgd = getBackground(mi, self.config.background)
432 
433  if self.config.adjustBackground:
434  self.log.log(self.log.WARN, "Fiddling the background by %g" % self.config.adjustBackground)
435 
436  bkgd += self.config.adjustBackground
437  fpSets.background = bkgd
438  self.log.log(self.log.INFO, "Resubtracting the background after object detection")
439  mi -= bkgd.getImageF()
440  del mi
441 
442  if self.config.thresholdPolarity == "positive":
443  if self.config.reEstimateBackground:
444  mask = maskedImage.getMask()
445  mask &= ~mask.getPlaneBitMask("DETECTED_NEGATIVE")
446  del mask
447  fpSets.negative = None
448  else:
449  self.log.log(self.log.INFO, "Detected %d negative sources to %g %s" %
450  (fpSets.numNeg, self.config.thresholdValue,
451  ("DN" if self.config.thresholdType == "value" else "sigma")))
452 
453  if display:
454  ds9.mtv(exposure, frame=0, title="detection")
455 
456  if convolvedImage and display and display > 1:
457  ds9.mtv(convolvedImage, frame=1, title="PSF smoothed")
458 
459  return fpSets
460 
461  def thresholdImage(self, image, thresholdParity, maskName="DETECTED"):
462  """!Threshold the convolved image, returning a FootprintSet.
463  Helper function for detect().
464 
465  \param image The (optionally convolved) MaskedImage to threshold
466  \param thresholdParity Parity of threshold
467  \param maskName Name of mask to set
468 
469  \return FootprintSet
470  """
471  parity = False if thresholdParity == "negative" else True
472  threshold = afwDet.createThreshold(self.config.thresholdValue, self.config.thresholdType, parity)
473  threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
474  fpSet = afwDet.FootprintSet(image, threshold, maskName, self.config.minPixels)
475  return fpSet
476 
477  @staticmethod
478  def setEdgeBits(maskedImage, goodBBox, edgeBitmask):
479  """!Set the edgeBitmask bits for all of maskedImage outside goodBBox
480 
481  \param[in,out] maskedImage image on which to set edge bits in the mask
482  \param[in] goodBBox bounding box of good pixels, in LOCAL coordinates
483  \param[in] edgeBitmask bit mask to OR with the existing mask bits in the region outside goodBBox
484  """
485  msk = maskedImage.getMask()
486 
487  mx0, my0 = maskedImage.getXY0()
488  for x0, y0, w, h in ([0, 0,
489  msk.getWidth(), goodBBox.getBeginY() - my0],
490  [0, goodBBox.getEndY() - my0, msk.getWidth(),
491  maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
492  [0, 0,
493  goodBBox.getBeginX() - mx0, msk.getHeight()],
494  [goodBBox.getEndX() - mx0, 0,
495  maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
496  ):
497  edgeMask = msk.Factory(msk, afwGeom.BoxI(afwGeom.PointI(x0, y0),
498  afwGeom.ExtentI(w, h)), afwImage.LOCAL)
499  edgeMask |= edgeBitmask
500 
501 def addExposures(exposureList):
502  """!Add a set of exposures together.
503 
504  \param[in] exposureList sequence of exposures to add
505 
506  \return an exposure of the same size as each exposure in exposureList,
507  with the metadata from exposureList[0] and a masked image equal to the
508  sum of all the exposure's masked images.
509 
510  \throw LsstException if the exposures do not all have the same dimensions (but does not check xy0)
511  """
512  exposure0 = exposureList[0]
513  image0 = exposure0.getMaskedImage()
514 
515  addedImage = image0.Factory(image0, True)
516  addedImage.setXY0(image0.getXY0())
517 
518  for exposure in exposureList[1:]:
519  image = exposure.getMaskedImage()
520  addedImage += image
521 
522  addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())
523  return addedExposure
524 
525 def getBackground(image, backgroundConfig, nx=0, ny=0, algorithm=None):
526  """!Estimate the background of an image (a thin layer on lsst.afw.math.makeBackground)
527 
528  \param[in] image image whose background is to be computed
529  \param[in] backgroundConfig configuration (a BackgroundConfig)
530  \param[in] nx number of x bands; 0 for default
531  \param[in] ny number of y bands; 0 for default
532  \param[in] algorithm name of interpolation algorithm; see lsst.afw.math.BackgroundControl for details
533  """
534  backgroundConfig.validate();
535 
536  if not nx:
537  nx = image.getWidth()//backgroundConfig.binSize + 1
538  if not ny:
539  ny = image.getHeight()//backgroundConfig.binSize + 1
540 
541  sctrl = afwMath.StatisticsControl()
542  sctrl.setAndMask(reduce(lambda x, y: x | image.getMask().getPlaneBitMask(y),
543  backgroundConfig.ignoredPixelMask, 0x0))
544  sctrl.setNanSafe(backgroundConfig.isNanSafe)
545 
546  pl = pexLogging.Debug("meas.utils.sourceDetection.getBackground")
547  pl.debug(3, "Ignoring mask planes: %s" % ", ".join(backgroundConfig.ignoredPixelMask))
548 
549  if not algorithm:
550  algorithm = backgroundConfig.algorithm
551 
552  bctrl = afwMath.BackgroundControl(algorithm, nx, ny,
553  backgroundConfig.undersampleStyle, sctrl,
554  backgroundConfig.statisticsProperty)
555 
556  return afwMath.makeBackground(image, bctrl)
557 
558 getBackground.ConfigClass = BackgroundConfig
559 
560 def estimateBackground(exposure, backgroundConfig, subtract=True, stats=True,
561  statsKeys=None):
562  """!Estimate exposure's background using parameters in backgroundConfig.
563 
564  If subtract is true, make a copy of the exposure and subtract the background.
565  If `stats` is True, measure the mean and variance of the background and
566  add them to the background-subtracted exposure's metadata with keys
567  "BGMEAN" and "BGVAR", or the keys given in `statsKeys` (2-tuple of strings).
568 
569  Return background, backgroundSubtractedExposure
570  """
571  displayBackground = lsstDebug.Info(__name__).displayBackground
572 
573  maskedImage = exposure.getMaskedImage()
574 
575  background = getBackground(maskedImage, backgroundConfig)
576 
577  if not background:
578  raise RuntimeError, "Unable to estimate background for exposure"
579 
580  bgimg = None
581 
582  if displayBackground > 1:
583  bgimg = background.getImageF()
584  ds9.mtv(bgimg, title="background", frame=1)
585 
586  if not subtract:
587  return background, None
588 
589  bbox = maskedImage.getBBox()
590  backgroundSubtractedExposure = exposure.Factory(exposure, bbox, afwImage.PARENT, True)
591  copyImage = backgroundSubtractedExposure.getMaskedImage().getImage()
592  if bgimg is None:
593  bgimg = background.getImageF()
594  copyImage -= bgimg
595 
596  # Record statistics of the background in the bgsub exposure metadata.
597  # (lsst.daf.base.PropertySet)
598  if stats:
599  if statsKeys is None:
600  mnkey = 'BGMEAN'
601  varkey = 'BGVAR'
602  else:
603  mnkey,varkey = statsKeys
604  meta = backgroundSubtractedExposure.getMetadata()
605  s = afwMath.makeStatistics(bgimg, afwMath.MEAN | afwMath.VARIANCE)
606  bgmean = s.getValue(afwMath.MEAN)
607  bgvar = s.getValue(afwMath.VARIANCE)
608  meta.addDouble(mnkey, bgmean)
609  meta.addDouble(varkey, bgvar)
610 
611  if displayBackground:
612  ds9.mtv(backgroundSubtractedExposure, title="subtracted")
613 
614  return background, backgroundSubtractedExposure
615 estimateBackground.ConfigClass = BackgroundConfig
boost::shared_ptr< Background > makeBackground(ImageT const &img, BackgroundControl const &bgCtrl)
A convenience function that uses function overloading to make the correct type of Background...
Definition: Background.h:446
def addExposures
Add a set of exposures together.
Definition: detection.py:501
Parameters to control convolution.
Definition: ConvolveImage.h:58
Config for background estimation.
Definition: detection.py:38
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
Definition: Kernel.h:986
def setEdgeBits
Set the edgeBitmask bits for all of maskedImage outside goodBBox.
Definition: detection.py:478
Detect positive and negative sources on an exposure and return a new table.SourceCatalog.
Definition: detection.py:158
Pass parameters to a Background object.
Definition: Background.h:62
An integer coordinate rectangle.
Definition: Box.h:53
Pass parameters to a Statistics objectA class to pass parameters which control how the stats are calc...
Definition: Statistics.h:92
def run
Run source detection and create a SourceCatalog.
Definition: detection.py:284
Configuration parameters for the SourceDetectionTask.
Definition: detection.py:90
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Definition: fwd.h:55
def estimateBackground
Estimate exposure&#39;s background using parameters in backgroundConfig.
Definition: detection.py:561
Threshold createThreshold(const double value, const std::string type="value", const bool polarity=true)
Factory method for creating Threshold objects.
Definition: Threshold.cc:138
def thresholdImage
Threshold the convolved image, returning a FootprintSet.
Definition: detection.py:461
A set of Footprints, associated with a MaskedImage.
Definition: FootprintSet.h:53
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Definition: Statistics.cc:1023
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
def getBackground
Estimate the background of an image (a thin layer on lsst.afw.math.makeBackground) ...
Definition: detection.py:525