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)
345 sources = afwTable.SourceCatalog(table)
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()
444 middle -= bgImage.Factory(bgImage, middle.getBBox())
447 if self.config.thresholdPolarity !=
"negative":
448 self.
updatePeaks(results.positive, middle, thresholdPos)
449 if self.config.thresholdPolarity !=
"positive":
450 self.
updatePeaks(results.negative, middle, thresholdNeg)
453 """Clear the DETECTED and DETECTED_NEGATIVE mask planes 455 Removes any previous detection mask in preparation for a new 460 mask : `lsst.afw.image.Mask` 463 mask &= ~(mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
466 """Calculate size of smoothing kernel 468 Uses the ``nSigmaForKernel`` configuration parameter. Note 469 that that is the full width of the kernel bounding box 470 (so a value of 7 means 3.5 sigma on either side of center). 471 The value will be rounded up to the nearest odd integer. 476 Gaussian sigma of smoothing kernel. 481 Size of the smoothing kernel. 483 return (int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1
486 """Retrieve the PSF for an exposure 488 If ``sigma`` is provided, we make a ``GaussianPsf`` with that, 489 otherwise use the one from the ``exposure``. 493 exposure : `lsst.afw.image.Exposure` 494 Exposure from which to retrieve the PSF. 495 sigma : `float`, optional 496 Gaussian sigma to use if provided. 500 psf : `lsst.afw.detection.Psf` 501 PSF to use for detection. 504 psf = exposure.getPsf()
506 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
507 sigma = psf.computeShape().getDeterminantRadius()
509 psf = afwDet.GaussianPsf(size, size, sigma)
513 """Convolve the image with the PSF 515 We convolve the image with a Gaussian approximation to the PSF, 516 because this is separable and therefore fast. It's technically a 517 correlation rather than a convolution, but since we use a symmetric 518 Gaussian there's no difference. 520 The convolution can be disabled with ``doSmooth=False``. If we do 521 convolve, we mask the edges as ``EDGE`` and return the convolved image 522 with the edges removed. This is because we can't convolve the edges 523 because the kernel would extend off the image. 527 maskedImage : `lsst.afw.image.MaskedImage` 529 psf : `lsst.afw.detection.Psf` 530 PSF to convolve with (actually with a Gaussian approximation 533 Actually do the convolution? Set to False when running on 534 e.g. a pre-convolved image, or a mask plane. 536 Return Struct contents 537 ---------------------- 538 middle : `lsst.afw.image.MaskedImage` 539 Convolved image, without the edges. 541 Gaussian sigma used for the convolution. 543 self.metadata.set(
"doSmooth", doSmooth)
544 sigma = psf.computeShape().getDeterminantRadius()
545 self.metadata.set(
"sigma", sigma)
548 middle = maskedImage.Factory(maskedImage)
549 return pipeBase.Struct(middle=middle, sigma=sigma)
554 self.metadata.set(
"smoothingKernelWidth", kWidth)
555 gaussFunc = afwMath.GaussianFunction1D(sigma)
556 gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc)
558 convolvedImage = maskedImage.Factory(maskedImage.getBBox())
560 afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl())
564 goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
565 middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT,
False)
569 self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
571 return pipeBase.Struct(middle=middle, sigma=sigma)
574 """Apply thresholds to the convolved image 576 Identifies ``Footprint``s, both positive and negative. 578 The threshold can be modified by the provided multiplication 583 middle : `lsst.afw.image.MaskedImage` 584 Convolved image to threshold. 585 bbox : `lsst.geom.Box2I` 586 Bounding box of unconvolved image. 588 Multiplier for the configured threshold. 590 Return Struct contents 591 ---------------------- 592 positive : `lsst.afw.detection.FootprintSet` or `None` 593 Positive detection footprints, if configured. 594 negative : `lsst.afw.detection.FootprintSet` or `None` 595 Negative detection footprints, if configured. 597 Multiplier for the configured threshold. 599 results = pipeBase.Struct(positive=
None, negative=
None, factor=factor)
601 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"negative":
602 threshold = self.
makeThreshold(middle,
"positive", factor=factor)
603 results.positive = afwDet.FootprintSet(
607 self.config.minPixels
609 results.positive.setRegion(bbox)
610 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"positive":
611 threshold = self.
makeThreshold(middle,
"negative", factor=factor)
612 results.negative = afwDet.FootprintSet(
616 self.config.minPixels
618 results.negative.setRegion(bbox)
623 """Finalize the detected footprints 625 Grows the footprints, sets the ``DETECTED`` and ``DETECTED_NEGATIVE`` 626 mask planes, and logs the results. 628 ``numPos`` (number of positive footprints), ``numPosPeaks`` (number 629 of positive peaks), ``numNeg`` (number of negative footprints), 630 ``numNegPeaks`` (number of negative peaks) entries are added to the 635 mask : `lsst.afw.image.Mask` 636 Mask image on which to flag detected pixels. 637 results : `lsst.pipe.base.Struct` 638 Struct of detection results, including ``positive`` and 639 ``negative`` entries; modified. 641 Gaussian sigma of PSF. 643 Multiplier for the configured threshold. 645 for polarity, maskName
in ((
"positive",
"DETECTED"), (
"negative",
"DETECTED_NEGATIVE")):
646 fpSet = getattr(results, polarity)
649 if self.config.nSigmaToGrow > 0:
650 nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
651 self.metadata.set(
"nGrow", nGrow)
652 if self.config.combinedGrow:
653 fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow)
655 stencil = (afwGeom.Stencil.CIRCLE
if self.config.isotropicGrow
else 656 afwGeom.Stencil.MANHATTAN)
658 fp.dilate(nGrow, stencil)
659 fpSet.setMask(mask, maskName)
660 if not self.config.returnOriginalFootprints:
661 setattr(results, polarity, fpSet)
664 results.numPosPeaks = 0
666 results.numNegPeaks = 0
670 if results.positive
is not None:
671 results.numPos = len(results.positive.getFootprints())
672 results.numPosPeaks = sum(len(fp.getPeaks())
for fp
in results.positive.getFootprints())
673 positive =
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
674 if results.negative
is not None:
675 results.numNeg = len(results.negative.getFootprints())
676 results.numNegPeaks = sum(len(fp.getPeaks())
for fp
in results.negative.getFootprints())
677 negative =
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
679 self.log.info(
"Detected%s%s%s to %g %s" %
680 (positive,
" and" if positive
and negative
else "", negative,
681 self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
682 "DN" if self.config.thresholdType ==
"value" else "sigma"))
685 """Estimate the background after detection 689 maskedImage : `lsst.afw.image.MaskedImage` 690 Image on which to estimate the background. 691 backgrounds : `lsst.afw.math.BackgroundList` 692 List of backgrounds; modified. 696 bg : `lsst.afw.math.backgroundMI` 697 Empirical background model. 699 bg = self.background.fitBackground(maskedImage)
700 if self.config.adjustBackground:
701 self.log.warn(
"Fiddling the background by %g", self.config.adjustBackground)
702 bg += self.config.adjustBackground
703 self.log.info(
"Resubtracting the background after object detection")
704 maskedImage -= bg.getImageF()
705 backgrounds.append(bg)
709 """Clear unwanted results from the Struct of results 711 If we specifically want only positive or only negative detections, 712 drop the ones we don't want, and its associated mask plane. 716 mask : `lsst.afw.image.Mask` 718 results : `lsst.pipe.base.Struct` 719 Detection results, with ``positive`` and ``negative`` elements; 722 if self.config.thresholdPolarity ==
"positive":
723 if self.config.reEstimateBackground:
724 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
725 results.negative =
None 726 elif self.config.thresholdPolarity ==
"negative":
727 if self.config.reEstimateBackground:
728 mask &= ~mask.getPlaneBitMask(
"DETECTED")
729 results.positive =
None 732 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
733 """Detect footprints on an exposure. 737 exposure : `lsst.afw.image.Exposure` 738 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be 740 doSmooth : `bool`, optional 741 If True, smooth the image before detection using a Gaussian 742 of width ``sigma``, or the measured PSF width of ``exposure``. 743 Set to False when running on e.g. a pre-convolved image, or a mask 745 sigma : `float`, optional 746 Gaussian Sigma of PSF (pixels); used for smoothing and to grow 747 detections; if `None` then measure the sigma of the PSF of the 749 clearMask : `bool`, optional 750 Clear both DETECTED and DETECTED_NEGATIVE planes before running 752 expId : `dict`, optional 753 Exposure identifier; unused by this implementation, but used for 754 RNG seed by subclasses. 756 Return Struct contents 757 ---------------------- 758 positive : `lsst.afw.detection.FootprintSet` 759 Positive polarity footprints (may be `None`) 760 negative : `lsst.afw.detection.FootprintSet` 761 Negative polarity footprints (may be `None`) 763 Number of footprints in positive or 0 if detection polarity was 766 Number of footprints in negative or 0 if detection polarity was 768 background : `lsst.afw.math.BackgroundList` 769 Re-estimated background. `None` if 770 ``reEstimateBackground==False``. 772 Multiplication factor applied to the configured detection 775 maskedImage = exposure.maskedImage
780 psf = self.
getPsf(exposure, sigma=sigma)
782 convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
783 middle = convolveResults.middle
784 sigma = convolveResults.sigma
787 results.background = afwMath.BackgroundList()
788 if self.config.doTempLocalBackground:
792 if self.config.reEstimateBackground:
796 self.
display(exposure, results, middle)
801 """Make an afw.detection.Threshold object corresponding to the task's 802 configuration and the statistics of the given image. 806 image : `afw.image.MaskedImage` 807 Image to measure noise statistics from if needed. 808 thresholdParity: `str` 809 One of "positive" or "negative", to set the kind of fluctuations 810 the Threshold will detect. 812 Factor by which to multiply the configured detection threshold. 813 This is useful for tweaking the detection threshold slightly. 817 threshold : `lsst.afw.detection.Threshold` 820 parity =
False if thresholdParity ==
"negative" else True 821 thresholdValue = self.config.thresholdValue
822 thresholdType = self.config.thresholdType
823 if self.config.thresholdType ==
'stdev':
824 bad = image.getMask().getPlaneBitMask(self.config.statsMask)
825 sctrl = afwMath.StatisticsControl()
826 sctrl.setAndMask(bad)
827 stats = afwMath.makeStatistics(image, afwMath.STDEVCLIP, sctrl)
828 thresholdValue *= stats.getValue(afwMath.STDEVCLIP)
829 thresholdType =
'value' 831 threshold = afwDet.createThreshold(thresholdValue*factor, thresholdType, parity)
832 threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
836 """Update the Peaks in a FootprintSet by detecting new Footprints and 837 Peaks in an image and using the new Peaks instead of the old ones. 841 fpSet : `afw.detection.FootprintSet` 842 Set of Footprints whose Peaks should be updated. 843 image : `afw.image.MaskedImage` 844 Image to detect new Footprints and Peak in. 845 threshold : `afw.detection.Threshold` 846 Threshold object for detection. 848 Input Footprints with fewer Peaks than self.config.nPeaksMaxSimple 849 are not modified, and if no new Peaks are detected in an input 850 Footprint, the brightest original Peak in that Footprint is kept. 852 for footprint
in fpSet.getFootprints():
853 oldPeaks = footprint.getPeaks()
854 if len(oldPeaks) <= self.config.nPeaksMaxSimple:
859 sub = image.Factory(image, footprint.getBBox())
860 fpSetForPeaks = afwDet.FootprintSet(
864 self.config.minPixels
866 newPeaks = afwDet.PeakCatalog(oldPeaks.getTable())
867 for fpForPeaks
in fpSetForPeaks.getFootprints():
868 for peak
in fpForPeaks.getPeaks():
869 if footprint.contains(peak.getI()):
870 newPeaks.append(peak)
871 if len(newPeaks) > 0:
873 oldPeaks.extend(newPeaks)
879 """Set the edgeBitmask bits for all of maskedImage outside goodBBox 883 maskedImage : `lsst.afw.image.MaskedImage` 884 Image on which to set edge bits in the mask. 885 goodBBox : `lsst.geom.Box2I` 886 Bounding box of good pixels, in ``LOCAL`` coordinates. 887 edgeBitmask : `lsst.afw.image.MaskPixel` 888 Bit mask to OR with the existing mask bits in the region 889 outside ``goodBBox``. 891 msk = maskedImage.getMask()
893 mx0, my0 = maskedImage.getXY0()
894 for x0, y0, w, h
in ([0, 0,
895 msk.getWidth(), goodBBox.getBeginY() - my0],
896 [0, goodBBox.getEndY() - my0, msk.getWidth(),
897 maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
899 goodBBox.getBeginX() - mx0, msk.getHeight()],
900 [goodBBox.getEndX() - mx0, 0,
901 maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
905 edgeMask |= edgeBitmask
909 """Context manager for removing wide (large-scale) background 911 Removing a wide (large-scale) background helps to suppress the 912 detection of large footprints that may overwhelm the deblender. 913 It does, however, set a limit on the maximum scale of objects. 915 The background that we remove will be restored upon exit from 920 exposure : `lsst.afw.image.Exposure` 921 Exposure on which to remove large-scale background. 925 context : context manager 926 Context manager that will ensure the temporary wide background 929 doTempWideBackground = self.config.doTempWideBackground
930 if doTempWideBackground:
931 self.log.info(
"Applying temporary wide background subtraction")
932 original = exposure.maskedImage.image.array[:].copy()
933 self.tempWideBackground.
run(exposure).background
936 image = exposure.maskedImage.image
937 mask = exposure.maskedImage.mask
938 noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
939 isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
940 image.array[noData] = np.median(image.array[~noData & isGood])
944 if doTempWideBackground:
945 exposure.maskedImage.image.array[:] = original
949 """Add a set of exposures together. 953 exposureList : `list` of `lsst.afw.image.Exposure` 954 Sequence of exposures to add. 958 addedExposure : `lsst.afw.image.Exposure` 959 An exposure of the same size as each exposure in ``exposureList``, 960 with the metadata from ``exposureList[0]`` and a masked image equal 961 to the sum of all the exposure's masked images. 963 exposure0 = exposureList[0]
964 image0 = exposure0.getMaskedImage()
966 addedImage = image0.Factory(image0,
True)
967 addedImage.setXY0(image0.getXY0())
969 for exposure
in exposureList[1:]:
970 image = exposure.getMaskedImage()
973 addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())
def updatePeaks(self, fpSet, image, threshold)
def makeSourceCatalog(self, args, kwargs)
def applyThreshold(self, middle, bbox, factor=1.0)
def addExposures(exposureList)
def tempWideBackgroundContext(self, exposure)
def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
def convolveImage(self, maskedImage, psf, doSmooth=True)
def applyTempLocalBackground(self, exposure, middle, results)
def calculateKernelSize(self, sigma)
def display(self, exposure, results, convolvedImage=None)
Detect positive and negative sources on an exposure and return a new table.SourceCatalog.
def makeThreshold(self, image, thresholdParity, factor=1.0)
Configuration parameters for the SourceDetectionTask.
def clearUnwantedResults(self, mask, results)
def getPsf(self, exposure, sigma=None)
def finalizeFootprints(self, mask, results, sigma, factor=1.0)
def __init__(self, schema=None, kwds)
Create the detection task.
def reEstimateBackground(self, maskedImage, backgrounds)
def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
def clearMask(self, mask)
def setEdgeBits(maskedImage, goodBBox, edgeBitmask)