24 __all__ = (
"SourceDetectionConfig", 
"SourceDetectionTask", 
"addExposures")
 
   26 from contextlib 
import contextmanager
 
   27 from deprecated.sphinx 
import deprecated
 
   38 import lsst.pex.config 
as pexConfig
 
   40 from .subtractBackground 
import SubtractBackgroundTask
 
   44     """!Configuration parameters for the SourceDetectionTask 
   46     minPixels = pexConfig.RangeField(
 
   47         doc=
"detected sources with fewer than the specified number of pixels will be ignored",
 
   48         dtype=int, optional=
False, default=1, min=0,
 
   50     isotropicGrow = pexConfig.Field(
 
   51         doc=
"Pixels should be grown as isotropically as possible (slower)",
 
   52         dtype=bool, optional=
False, default=
False,
 
   54     combinedGrow = pexConfig.Field(
 
   55         doc=
"Grow all footprints at the same time? This allows disconnected footprints to merge.",
 
   56         dtype=bool, default=
True,
 
   58     nSigmaToGrow = pexConfig.Field(
 
   59         doc=
"Grow detections by nSigmaToGrow * [PSF RMS width]; if 0 then do not grow",
 
   60         dtype=float, default=2.4,  
 
   62     returnOriginalFootprints = pexConfig.Field(
 
   63         doc=
"Grow detections to set the image mask bits, but return the original (not-grown) footprints",
 
   64         dtype=bool, optional=
False, default=
False,
 
   66     thresholdValue = pexConfig.RangeField(
 
   67         doc=
"Threshold for footprints; exact meaning and units depend on thresholdType.",
 
   68         dtype=float, optional=
False, default=5.0, min=0.0,
 
   70     includeThresholdMultiplier = pexConfig.RangeField(
 
   71         doc=
"Include threshold relative to thresholdValue",
 
   72         dtype=float, default=1.0, min=0.0,
 
   74     thresholdType = pexConfig.ChoiceField(
 
   75         doc=
"specifies the desired flavor of Threshold",
 
   76         dtype=str, optional=
False, default=
"stdev",
 
   78             "variance": 
"threshold applied to image variance",
 
   79             "stdev": 
"threshold applied to image std deviation",
 
   80             "value": 
"threshold applied to image value",
 
   81             "pixel_stdev": 
"threshold applied to per-pixel std deviation",
 
   84     thresholdPolarity = pexConfig.ChoiceField(
 
   85         doc=
"specifies whether to detect positive, or negative sources, or both",
 
   86         dtype=str, optional=
False, default=
"positive",
 
   88             "positive": 
"detect only positive sources",
 
   89             "negative": 
"detect only negative sources",
 
   90             "both": 
"detect both positive and negative sources",
 
   93     adjustBackground = pexConfig.Field(
 
   95         doc=
"Fiddle factor to add to the background; debugging only",
 
   98     reEstimateBackground = pexConfig.Field(
 
  100         doc=
"Estimate the background again after final source detection?",
 
  101         default=
True, optional=
False,
 
  103     background = pexConfig.ConfigurableField(
 
  104         doc=
"Background re-estimation; ignored if reEstimateBackground false",
 
  105         target=SubtractBackgroundTask,
 
  107     tempLocalBackground = pexConfig.ConfigurableField(
 
  108         doc=(
"A local (small-scale), temporary background estimation step run between " 
  109              "detecting above-threshold regions and detecting the peaks within " 
  110              "them; used to avoid detecting spuerious peaks in the wings."),
 
  111         target=SubtractBackgroundTask,
 
  113     doTempLocalBackground = pexConfig.Field(
 
  115         doc=
"Enable temporary local background subtraction? (see tempLocalBackground)",
 
  118     tempWideBackground = pexConfig.ConfigurableField(
 
  119         doc=(
"A wide (large-scale) background estimation and removal before footprint and peak detection. " 
  120              "It is added back into the image after detection. The purpose is to suppress very large " 
  121              "footprints (e.g., from large artifacts) that the deblender may choke on."),
 
  122         target=SubtractBackgroundTask,
 
  124     doTempWideBackground = pexConfig.Field(
 
  126         doc=
"Do temporary wide (large-scale) background subtraction before footprint detection?",
 
  129     nPeaksMaxSimple = pexConfig.Field(
 
  131         doc=(
"The maximum number of peaks in a Footprint before trying to " 
  132              "replace its peaks using the temporary local background"),
 
  135     nSigmaForKernel = pexConfig.Field(
 
  137         doc=(
"Multiple of PSF RMS size to use for convolution kernel bounding box size; " 
  138              "note that this is not a half-size. The size will be rounded up to the nearest odd integer"),
 
  141     statsMask = pexConfig.ListField(
 
  143         doc=
"Mask planes to ignore when calculating statistics of image (for thresholdType=stdev)",
 
  144         default=[
'BAD', 
'SAT', 
'EDGE', 
'NO_DATA'],
 
  157         for maskPlane 
in (
"DETECTED", 
"DETECTED_NEGATIVE"):
 
  171 @anchor SourceDetectionTask_ 
  173 @brief Detect positive and negative sources on an exposure and return a new @link table.SourceCatalog@endlink. 
  175 @section meas_algorithms_detection_Contents Contents 
  177  - @ref meas_algorithms_detection_Purpose 
  178  - @ref meas_algorithms_detection_Initialize 
  179  - @ref meas_algorithms_detection_Invoke 
  180  - @ref meas_algorithms_detection_Config 
  181  - @ref meas_algorithms_detection_Debug 
  182  - @ref meas_algorithms_detection_Example 
  184 @section meas_algorithms_detection_Purpose      Description 
  186 @copybrief SourceDetectionTask 
  188 @section meas_algorithms_detection_Initialize   Task initialisation 
  190 @copydoc \_\_init\_\_ 
  192 @section meas_algorithms_detection_Invoke       Invoking the Task 
  196 @section meas_algorithms_detection_Config       Configuration parameters 
  198 See @ref SourceDetectionConfig 
  200 @section meas_algorithms_detection_Debug                Debug variables 
  202 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 
  203 flag @c -d to import @b debug.py from your @c PYTHONPATH; see @ref baseDebug for more about @b debug.py files. 
  205 The available variables in SourceDetectionTask are: 
  209   - If True, display the exposure on afwDisplay.Display's frame 0. 
  210     +ve detections in blue, -ve detections in cyan 
  211   - If display > 1, display the convolved exposure on frame 1 
  214 @section meas_algorithms_detection_Example      A complete example of using SourceDetectionTask 
  216 This code is in @link measAlgTasks.py@endlink in the examples directory, and can be run as @em e.g. 
  218 examples/measAlgTasks.py --doDisplay 
  220 @dontinclude measAlgTasks.py 
  221 The example also runs the SingleFrameMeasurementTask; see @ref meas_algorithms_measurement_Example for more 
  224 Import the task (there are some other standard imports; read the file if you're confused) 
  225 @skipline SourceDetectionTask 
  227 We need to create our task before processing any data as the task constructor 
  228 can add an extra column to the schema, but first we need an almost-empty Schema 
  229 @skipline makeMinimalSchema 
  230 after which we can call the constructor: 
  231 @skip SourceDetectionTask.ConfigClass 
  234 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 
  235 task objects).  First create the output table: 
  238 And process the image 
  240 (You may not be happy that the threshold was set in the config before creating the Task rather than being set 
  241 separately for each exposure.  You @em can reset it just before calling the run method if you must, but we 
  242 should really implement a better solution). 
  244 We can then unpack and use the results: 
  249 To investigate the @ref meas_algorithms_detection_Debug, put something like 
  253         di = lsstDebug.getInfo(name)        # N.b. lsstDebug.Info(name) would call us recursively 
  254         if name == "lsst.meas.algorithms.detection": 
  259     lsstDebug.Info = DebugInfo 
  261 into your debug.py file and run measAlgTasks.py with the @c --debug flag. 
  263     ConfigClass = SourceDetectionConfig
 
  264     _DefaultName = 
"sourceDetection" 
  267         """!Create the detection task.  Most arguments are simply passed onto pipe.base.Task. 
  269         @param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 
  270         @param **kwds Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 
  272         If schema is not None and configured for 'both' detections, 
  273         a 'flags.negative' field will be added to label detections made with a 
  276         @note This task can add fields to the schema, so any code calling this task must ensure that 
  277         these columns are indeed present in the input match list; see @ref Example 
  279         pipeBase.Task.__init__(self, **kwds)
 
  280         if schema 
is not None and self.config.thresholdPolarity == 
"both":
 
  282                 "flags_negative", type=
"Flag",
 
  283                 doc=
"set if source was detected as significantly negative" 
  286             if self.config.thresholdPolarity == 
"both":
 
  287                 self.log.
warn(
"Detection polarity set to 'both', but no flag will be " 
  288                               "set to distinguish between positive and negative detections")
 
  290         if self.config.reEstimateBackground:
 
  291             self.makeSubtask(
"background")
 
  292         if self.config.doTempLocalBackground:
 
  293             self.makeSubtask(
"tempLocalBackground")
 
  294         if self.config.doTempWideBackground:
 
  295             self.makeSubtask(
"tempWideBackground")
 
  298     def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
 
  299         """Run source detection and create a SourceCatalog of detections. 
  303         table : `lsst.afw.table.SourceTable` 
  304             Table object that will be used to create the SourceCatalog. 
  305         exposure : `lsst.afw.image.Exposure` 
  306             Exposure to process; DETECTED mask plane will be set in-place. 
  308             If True, smooth the image before detection using a Gaussian of width 
  309             ``sigma``, or the measured PSF width. Set to False when running on 
  310             e.g. a pre-convolved image, or a mask plane. 
  312             Sigma of PSF (pixels); used for smoothing and to grow detections; 
  313             if None then measure the sigma of the PSF of the exposure 
  315             Clear DETECTED{,_NEGATIVE} planes before running detection. 
  317             Exposure identifier; unused by this implementation, but used for 
  318             RNG seed by subclasses. 
  322         result : `lsst.pipe.base.Struct` 
  324               The detected sources (`lsst.afw.table.SourceCatalog`) 
  326               The result resturned by `detectFootprints` 
  327               (`lsst.pipe.base.Struct`). 
  332             If flags.negative is needed, but isn't in table's schema. 
  333         lsst.pipe.base.TaskError 
  334             If sigma=None, doSmooth=True and the exposure has no PSF. 
  338         If you want to avoid dealing with Sources and Tables, you can use 
  339         detectFootprints() to just get the `lsst.afw.detection.FootprintSet`s. 
  342             raise ValueError(
"Table has incorrect Schema")
 
  343         results = self.
detectFootprints(exposure=exposure, doSmooth=doSmooth, sigma=sigma,
 
  344                                         clearMask=clearMask, expId=expId)
 
  346         sources.reserve(results.numPos + results.numNeg)
 
  348             results.negative.makeSources(sources)
 
  350                 for record 
in sources:
 
  353             results.positive.makeSources(sources)
 
  354         results.fpSets = results.copy()  
 
  355         results.sources = sources
 
  358     @deprecated(reason=
"Replaced by SourceDetectionTask.run(). Will be removed after v20.",
 
  359                 category=FutureWarning)
 
  361         return self.
run(*args, **kwargs)
 
  363     def display(self, exposure, results, convolvedImage=None):
 
  364         """Display detections if so configured 
  366         Displays the ``exposure`` in frame 0, overlays the detection peaks. 
  368         Requires that ``lsstDebug`` has been set up correctly, so that 
  369         ``lsstDebug.Info("lsst.meas.algorithms.detection")`` evaluates `True`. 
  371         If the ``convolvedImage`` is non-`None` and 
  372         ``lsstDebug.Info("lsst.meas.algorithms.detection") > 1``, the 
  373         ``convolvedImage`` will be displayed in frame 1. 
  377         exposure : `lsst.afw.image.Exposure` 
  378             Exposure to display, on which will be plotted the detections. 
  379         results : `lsst.pipe.base.Struct` 
  380             Results of the 'detectFootprints' method, containing positive and 
  381             negative footprints (which contain the peak positions that we will 
  382             plot). This is a `Struct` with ``positive`` and ``negative`` 
  383             elements that are of type `lsst.afw.detection.FootprintSet`. 
  384         convolvedImage : `lsst.afw.image.Image`, optional 
  385             Convolved image used for thresholding. 
  398         afwDisplay.setDefaultMaskTransparency(75)
 
  400         disp0 = afwDisplay.Display(frame=0)
 
  401         disp0.mtv(exposure, title=
"detection")
 
  403         def plotPeaks(fps, ctype):
 
  406             with disp0.Buffering():
 
  407                 for fp 
in fps.getFootprints():
 
  408                     for pp 
in fp.getPeaks():
 
  409                         disp0.dot(
"+", pp.getFx(), pp.getFy(), ctype=ctype)
 
  410         plotPeaks(results.positive, 
"yellow")
 
  411         plotPeaks(results.negative, 
"red")
 
  413         if convolvedImage 
and display > 1:
 
  414             disp1 = afwDisplay.Display(frame=1)
 
  415             disp1.mtv(convolvedImage, title=
"PSF smoothed")
 
  418         """Apply a temporary local background subtraction 
  420         This temporary local background serves to suppress noise fluctuations 
  421         in the wings of bright objects. 
  423         Peaks in the footprints will be updated. 
  427         exposure : `lsst.afw.image.Exposure` 
  428             Exposure for which to fit local background. 
  429         middle : `lsst.afw.image.MaskedImage` 
  430             Convolved image on which detection will be performed 
  431             (typically smaller than ``exposure`` because the 
  432             half-kernel has been removed around the edges). 
  433         results : `lsst.pipe.base.Struct` 
  434             Results of the 'detectFootprints' method, containing positive and 
  435             negative footprints (which contain the peak positions that we will 
  436             plot). This is a `Struct` with ``positive`` and ``negative`` 
  437             elements that are of type `lsst.afw.detection.FootprintSet`. 
  442         bg = self.tempLocalBackground.fitBackground(exposure.getMaskedImage())
 
  443         bgImage = bg.getImageF(self.tempLocalBackground.config.algorithm,
 
  444                                self.tempLocalBackground.config.undersampleStyle)
 
  445         middle -= bgImage.Factory(bgImage, middle.getBBox())
 
  448         if self.config.thresholdPolarity != 
"negative":
 
  449             self.
updatePeaks(results.positive, middle, thresholdPos)
 
  450         if self.config.thresholdPolarity != 
"positive":
 
  451             self.
updatePeaks(results.negative, middle, thresholdNeg)
 
  454         """Clear the DETECTED and DETECTED_NEGATIVE mask planes 
  456         Removes any previous detection mask in preparation for a new 
  461         mask : `lsst.afw.image.Mask` 
  464         mask &= ~(mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
 
  467         """Calculate size of smoothing kernel 
  469         Uses the ``nSigmaForKernel`` configuration parameter. Note 
  470         that that is the full width of the kernel bounding box 
  471         (so a value of 7 means 3.5 sigma on either side of center). 
  472         The value will be rounded up to the nearest odd integer. 
  477             Gaussian sigma of smoothing kernel. 
  482             Size of the smoothing kernel. 
  484         return (int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1  
 
  487         """Retrieve the PSF for an exposure 
  489         If ``sigma`` is provided, we make a ``GaussianPsf`` with that, 
  490         otherwise use the one from the ``exposure``. 
  494         exposure : `lsst.afw.image.Exposure` 
  495             Exposure from which to retrieve the PSF. 
  496         sigma : `float`, optional 
  497             Gaussian sigma to use if provided. 
  501         psf : `lsst.afw.detection.Psf` 
  502             PSF to use for detection. 
  505             psf = exposure.getPsf()
 
  507                 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
 
  508             sigma = psf.computeShape().getDeterminantRadius()
 
  514         """Convolve the image with the PSF 
  516         We convolve the image with a Gaussian approximation to the PSF, 
  517         because this is separable and therefore fast. It's technically a 
  518         correlation rather than a convolution, but since we use a symmetric 
  519         Gaussian there's no difference. 
  521         The convolution can be disabled with ``doSmooth=False``. If we do 
  522         convolve, we mask the edges as ``EDGE`` and return the convolved image 
  523         with the edges removed. This is because we can't convolve the edges 
  524         because the kernel would extend off the image. 
  528         maskedImage : `lsst.afw.image.MaskedImage` 
  530         psf : `lsst.afw.detection.Psf` 
  531             PSF to convolve with (actually with a Gaussian approximation 
  534             Actually do the convolution? Set to False when running on 
  535             e.g. a pre-convolved image, or a mask plane. 
  537         Return Struct contents 
  538         ---------------------- 
  539         middle : `lsst.afw.image.MaskedImage` 
  540             Convolved image, without the edges. 
  542             Gaussian sigma used for the convolution. 
  544         self.metadata.
set(
"doSmooth", doSmooth)
 
  545         sigma = psf.computeShape().getDeterminantRadius()
 
  546         self.metadata.
set(
"sigma", sigma)
 
  549             middle = maskedImage.Factory(maskedImage)
 
  550             return pipeBase.Struct(middle=middle, sigma=sigma)
 
  555         self.metadata.
set(
"smoothingKernelWidth", kWidth)
 
  556         gaussFunc = afwMath.GaussianFunction1D(sigma)
 
  559         convolvedImage = maskedImage.Factory(maskedImage.getBBox())
 
  565         goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
 
  566         middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT, 
False)
 
  570         self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
 
  572         return pipeBase.Struct(middle=middle, sigma=sigma)
 
  575         """Apply thresholds to the convolved image 
  577         Identifies ``Footprint``s, both positive and negative. 
  579         The threshold can be modified by the provided multiplication 
  584         middle : `lsst.afw.image.MaskedImage` 
  585             Convolved image to threshold. 
  586         bbox : `lsst.geom.Box2I` 
  587             Bounding box of unconvolved image. 
  589             Multiplier for the configured threshold. 
  591         Return Struct contents 
  592         ---------------------- 
  593         positive : `lsst.afw.detection.FootprintSet` or `None` 
  594             Positive detection footprints, if configured. 
  595         negative : `lsst.afw.detection.FootprintSet` or `None` 
  596             Negative detection footprints, if configured. 
  598             Multiplier for the configured threshold. 
  600         results = pipeBase.Struct(positive=
None, negative=
None, factor=factor)
 
  602         if self.config.reEstimateBackground 
or self.config.thresholdPolarity != 
"negative":
 
  603             threshold = self.
makeThreshold(middle, 
"positive", factor=factor)
 
  608                 self.config.minPixels
 
  610             results.positive.setRegion(bbox)
 
  611         if self.config.reEstimateBackground 
or self.config.thresholdPolarity != 
"positive":
 
  612             threshold = self.
makeThreshold(middle, 
"negative", factor=factor)
 
  617                 self.config.minPixels
 
  619             results.negative.setRegion(bbox)
 
  624         """Finalize the detected footprints 
  626         Grows the footprints, sets the ``DETECTED`` and ``DETECTED_NEGATIVE`` 
  627         mask planes, and logs the results. 
  629         ``numPos`` (number of positive footprints), ``numPosPeaks`` (number 
  630         of positive peaks), ``numNeg`` (number of negative footprints), 
  631         ``numNegPeaks`` (number of negative peaks) entries are added to the 
  636         mask : `lsst.afw.image.Mask` 
  637             Mask image on which to flag detected pixels. 
  638         results : `lsst.pipe.base.Struct` 
  639             Struct of detection results, including ``positive`` and 
  640             ``negative`` entries; modified. 
  642             Gaussian sigma of PSF. 
  644             Multiplier for the configured threshold. 
  646         for polarity, maskName 
in ((
"positive", 
"DETECTED"), (
"negative", 
"DETECTED_NEGATIVE")):
 
  647             fpSet = getattr(results, polarity)
 
  650             if self.config.nSigmaToGrow > 0:
 
  651                 nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
 
  652                 self.metadata.
set(
"nGrow", nGrow)
 
  653                 if self.config.combinedGrow:
 
  656                     stencil = (afwGeom.Stencil.CIRCLE 
if self.config.isotropicGrow 
else 
  657                                afwGeom.Stencil.MANHATTAN)
 
  659                         fp.dilate(nGrow, stencil)
 
  660             fpSet.setMask(mask, maskName)
 
  661             if not self.config.returnOriginalFootprints:
 
  662                 setattr(results, polarity, fpSet)
 
  665         results.numPosPeaks = 0
 
  667         results.numNegPeaks = 0
 
  671         if results.positive 
is not None:
 
  672             results.numPos = len(results.positive.getFootprints())
 
  673             results.numPosPeaks = sum(len(fp.getPeaks()) 
for fp 
in results.positive.getFootprints())
 
  674             positive = 
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
 
  675         if results.negative 
is not None:
 
  676             results.numNeg = len(results.negative.getFootprints())
 
  677             results.numNegPeaks = sum(len(fp.getPeaks()) 
for fp 
in results.negative.getFootprints())
 
  678             negative = 
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
 
  680         self.log.
info(
"Detected%s%s%s to %g %s" %
 
  681                       (positive, 
" and" if positive 
and negative 
else "", negative,
 
  682                        self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
 
  683                        "DN" if self.config.thresholdType == 
"value" else "sigma"))
 
  686         """Estimate the background after detection 
  690         maskedImage : `lsst.afw.image.MaskedImage` 
  691             Image on which to estimate the background. 
  692         backgrounds : `lsst.afw.math.BackgroundList` 
  693             List of backgrounds; modified. 
  697         bg : `lsst.afw.math.backgroundMI` 
  698             Empirical background model. 
  700         bg = self.background.fitBackground(maskedImage)
 
  701         if self.config.adjustBackground:
 
  702             self.log.
warn(
"Fiddling the background by %g", self.config.adjustBackground)
 
  703             bg += self.config.adjustBackground
 
  704         self.log.
info(
"Resubtracting the background after object detection")
 
  705         maskedImage -= bg.getImageF(self.background.config.algorithm,
 
  706                                     self.background.config.undersampleStyle)
 
  708         actrl = bg.getBackgroundControl().getApproximateControl()
 
  710                             bg.getAsUsedUndersampleStyle(), actrl.getStyle(), actrl.getOrderX(),
 
  711                             actrl.getOrderY(), actrl.getWeighting()))
 
  715         """Clear unwanted results from the Struct of results 
  717         If we specifically want only positive or only negative detections, 
  718         drop the ones we don't want, and its associated mask plane. 
  722         mask : `lsst.afw.image.Mask` 
  724         results : `lsst.pipe.base.Struct` 
  725             Detection results, with ``positive`` and ``negative`` elements; 
  728         if self.config.thresholdPolarity == 
"positive":
 
  729             if self.config.reEstimateBackground:
 
  730                 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
 
  731             results.negative = 
None 
  732         elif self.config.thresholdPolarity == 
"negative":
 
  733             if self.config.reEstimateBackground:
 
  734                 mask &= ~mask.getPlaneBitMask(
"DETECTED")
 
  735             results.positive = 
None 
  738     def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
 
  739         """Detect footprints on an exposure. 
  743         exposure : `lsst.afw.image.Exposure` 
  744             Exposure to process; DETECTED{,_NEGATIVE} mask plane will be 
  746         doSmooth : `bool`, optional 
  747             If True, smooth the image before detection using a Gaussian 
  748             of width ``sigma``, or the measured PSF width of ``exposure``. 
  749             Set to False when running on e.g. a pre-convolved image, or a mask 
  751         sigma : `float`, optional 
  752             Gaussian Sigma of PSF (pixels); used for smoothing and to grow 
  753             detections; if `None` then measure the sigma of the PSF of the 
  755         clearMask : `bool`, optional 
  756             Clear both DETECTED and DETECTED_NEGATIVE planes before running 
  758         expId : `dict`, optional 
  759             Exposure identifier; unused by this implementation, but used for 
  760             RNG seed by subclasses. 
  762         Return Struct contents 
  763         ---------------------- 
  764         positive : `lsst.afw.detection.FootprintSet` 
  765             Positive polarity footprints (may be `None`) 
  766         negative : `lsst.afw.detection.FootprintSet` 
  767             Negative polarity footprints (may be `None`) 
  769             Number of footprints in positive or 0 if detection polarity was 
  772             Number of footprints in negative or 0 if detection polarity was 
  774         background : `lsst.afw.math.BackgroundList` 
  775             Re-estimated background.  `None` if 
  776             ``reEstimateBackground==False``. 
  778             Multiplication factor applied to the configured detection 
  781         maskedImage = exposure.maskedImage
 
  786         psf = self.
getPsf(exposure, sigma=sigma)
 
  788             convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
 
  789             middle = convolveResults.middle
 
  790             sigma = convolveResults.sigma
 
  794             if self.config.doTempLocalBackground:
 
  798             if self.config.reEstimateBackground:
 
  802             self.
display(exposure, results, middle)
 
  807         """Make an afw.detection.Threshold object corresponding to the task's 
  808         configuration and the statistics of the given image. 
  812         image : `afw.image.MaskedImage` 
  813             Image to measure noise statistics from if needed. 
  814         thresholdParity: `str` 
  815             One of "positive" or "negative", to set the kind of fluctuations 
  816             the Threshold will detect. 
  818             Factor by which to multiply the configured detection threshold. 
  819             This is useful for tweaking the detection threshold slightly. 
  823         threshold : `lsst.afw.detection.Threshold` 
  826         parity = 
False if thresholdParity == 
"negative" else True 
  827         thresholdValue = self.config.thresholdValue
 
  828         thresholdType = self.config.thresholdType
 
  829         if self.config.thresholdType == 
'stdev':
 
  830             bad = image.getMask().getPlaneBitMask(self.config.statsMask)
 
  832             sctrl.setAndMask(bad)
 
  834             thresholdValue *= stats.getValue(afwMath.STDEVCLIP)
 
  835             thresholdType = 
'value' 
  838         threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
 
  842         """Update the Peaks in a FootprintSet by detecting new Footprints and 
  843         Peaks in an image and using the new Peaks instead of the old ones. 
  847         fpSet : `afw.detection.FootprintSet` 
  848             Set of Footprints whose Peaks should be updated. 
  849         image : `afw.image.MaskedImage` 
  850             Image to detect new Footprints and Peak in. 
  851         threshold : `afw.detection.Threshold` 
  852             Threshold object for detection. 
  854         Input Footprints with fewer Peaks than self.config.nPeaksMaxSimple 
  855         are not modified, and if no new Peaks are detected in an input 
  856         Footprint, the brightest original Peak in that Footprint is kept. 
  858         for footprint 
in fpSet.getFootprints():
 
  859             oldPeaks = footprint.getPeaks()
 
  860             if len(oldPeaks) <= self.config.nPeaksMaxSimple:
 
  865             sub = image.Factory(image, footprint.getBBox())
 
  870                 self.config.minPixels
 
  873             for fpForPeaks 
in fpSetForPeaks.getFootprints():
 
  874                 for peak 
in fpForPeaks.getPeaks():
 
  875                     if footprint.contains(peak.getI()):
 
  876                         newPeaks.append(peak)
 
  877             if len(newPeaks) > 0:
 
  879                 oldPeaks.extend(newPeaks)
 
  885         """Set the edgeBitmask bits for all of maskedImage outside goodBBox 
  889         maskedImage : `lsst.afw.image.MaskedImage` 
  890             Image on which to set edge bits in the mask. 
  891         goodBBox : `lsst.geom.Box2I` 
  892             Bounding box of good pixels, in ``LOCAL`` coordinates. 
  893         edgeBitmask : `lsst.afw.image.MaskPixel` 
  894             Bit mask to OR with the existing mask bits in the region 
  895             outside ``goodBBox``. 
  897         msk = maskedImage.getMask()
 
  899         mx0, my0 = maskedImage.getXY0()
 
  900         for x0, y0, w, h 
in ([0, 0,
 
  901                               msk.getWidth(), goodBBox.getBeginY() - my0],
 
  902                              [0, goodBBox.getEndY() - my0, msk.getWidth(),
 
  903                               maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
 
  905                               goodBBox.getBeginX() - mx0, msk.getHeight()],
 
  906                              [goodBBox.getEndX() - mx0, 0,
 
  907                               maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
 
  911             edgeMask |= edgeBitmask
 
  915         """Context manager for removing wide (large-scale) background 
  917         Removing a wide (large-scale) background helps to suppress the 
  918         detection of large footprints that may overwhelm the deblender. 
  919         It does, however, set a limit on the maximum scale of objects. 
  921         The background that we remove will be restored upon exit from 
  926         exposure : `lsst.afw.image.Exposure` 
  927             Exposure on which to remove large-scale background. 
  931         context : context manager 
  932             Context manager that will ensure the temporary wide background 
  935         doTempWideBackground = self.config.doTempWideBackground
 
  936         if doTempWideBackground:
 
  937             self.log.
info(
"Applying temporary wide background subtraction")
 
  938             original = exposure.maskedImage.image.array[:].copy()
 
  939             self.tempWideBackground.
run(exposure).background
 
  942             image = exposure.maskedImage.image
 
  943             mask = exposure.maskedImage.mask
 
  944             noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
 
  945             isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
 
  946             image.array[noData] = np.median(image.array[~noData & isGood])
 
  950             if doTempWideBackground:
 
  951                 exposure.maskedImage.image.array[:] = original
 
  955     """Add a set of exposures together. 
  959     exposureList : `list` of `lsst.afw.image.Exposure` 
  960         Sequence of exposures to add. 
  964     addedExposure : `lsst.afw.image.Exposure` 
  965         An exposure of the same size as each exposure in ``exposureList``, 
  966         with the metadata from ``exposureList[0]`` and a masked image equal 
  967         to the sum of all the exposure's masked images. 
  969     exposure0 = exposureList[0]
 
  970     image0 = exposure0.getMaskedImage()
 
  972     addedImage = image0.Factory(image0, 
True)
 
  973     addedImage.setXY0(image0.getXY0())
 
  975     for exposure 
in exposureList[1:]:
 
  976         image = exposure.getMaskedImage()
 
  979     addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())