24 __all__ = (
"SourceDetectionConfig",
"SourceDetectionTask",
"addExposures")
26 from contextlib
import contextmanager
38 from .subtractBackground
import SubtractBackgroundTask
42 """!Configuration parameters for the SourceDetectionTask 44 minPixels = pexConfig.RangeField(
45 doc=
"detected sources with fewer than the specified number of pixels will be ignored",
46 dtype=int, optional=
False, default=1, min=0,
48 isotropicGrow = pexConfig.Field(
49 doc=
"Pixels should be grown as isotropically as possible (slower)",
50 dtype=bool, optional=
False, default=
False,
52 combinedGrow = pexConfig.Field(
53 doc=
"Grow all footprints at the same time? This allows disconnected footprints to merge.",
54 dtype=bool, default=
True,
56 nSigmaToGrow = pexConfig.Field(
57 doc=
"Grow detections by nSigmaToGrow * [PSF RMS width]; if 0 then do not grow",
58 dtype=float, default=2.4,
60 returnOriginalFootprints = pexConfig.Field(
61 doc=
"Grow detections to set the image mask bits, but return the original (not-grown) footprints",
62 dtype=bool, optional=
False, default=
False,
64 thresholdValue = pexConfig.RangeField(
65 doc=
"Threshold for footprints",
66 dtype=float, optional=
False, default=5.0, min=0.0,
68 includeThresholdMultiplier = pexConfig.RangeField(
69 doc=
"Include threshold relative to thresholdValue",
70 dtype=float, default=1.0, min=0.0,
72 thresholdType = pexConfig.ChoiceField(
73 doc=
"specifies the desired flavor of Threshold",
74 dtype=str, optional=
False, default=
"stdev",
76 "variance":
"threshold applied to image variance",
77 "stdev":
"threshold applied to image std deviation",
78 "value":
"threshold applied to image value",
79 "pixel_stdev":
"threshold applied to per-pixel std deviation",
82 thresholdPolarity = pexConfig.ChoiceField(
83 doc=
"specifies whether to detect positive, or negative sources, or both",
84 dtype=str, optional=
False, default=
"positive",
86 "positive":
"detect only positive sources",
87 "negative":
"detect only negative sources",
88 "both":
"detect both positive and negative sources",
91 adjustBackground = pexConfig.Field(
93 doc=
"Fiddle factor to add to the background; debugging only",
96 reEstimateBackground = pexConfig.Field(
98 doc=
"Estimate the background again after final source detection?",
99 default=
True, optional=
False,
101 background = pexConfig.ConfigurableField(
102 doc=
"Background re-estimation; ignored if reEstimateBackground false",
103 target=SubtractBackgroundTask,
105 tempLocalBackground = pexConfig.ConfigurableField(
106 doc=(
"A local (small-scale), temporary background estimation step run between " 107 "detecting above-threshold regions and detecting the peaks within " 108 "them; used to avoid detecting spuerious peaks in the wings."),
109 target=SubtractBackgroundTask,
111 doTempLocalBackground = pexConfig.Field(
113 doc=
"Enable temporary local background subtraction? (see tempLocalBackground)",
116 tempWideBackground = pexConfig.ConfigurableField(
117 doc=(
"A wide (large-scale) background estimation and removal before footprint and peak detection. " 118 "It is added back into the image after detection. The purpose is to suppress very large " 119 "footprints (e.g., from large artifacts) that the deblender may choke on."),
120 target=SubtractBackgroundTask,
122 doTempWideBackground = pexConfig.Field(
124 doc=
"Do temporary wide (large-scale) background subtraction before footprint detection?",
127 nPeaksMaxSimple = pexConfig.Field(
129 doc=(
"The maximum number of peaks in a Footprint before trying to " 130 "replace its peaks using the temporary local background"),
133 nSigmaForKernel = pexConfig.Field(
135 doc=(
"Multiple of PSF RMS size to use for convolution kernel bounding box size; " 136 "note that this is not a half-size. The size will be rounded up to the nearest odd integer"),
139 statsMask = pexConfig.ListField(
141 doc=
"Mask planes to ignore when calculating statistics of image (for thresholdType=stdev)",
142 default=[
'BAD',
'SAT',
'EDGE',
'NO_DATA'],
155 for maskPlane
in (
"DETECTED",
"DETECTED_NEGATIVE"):
169 @anchor SourceDetectionTask_ 171 @brief Detect positive and negative sources on an exposure and return a new @link table.SourceCatalog@endlink. 173 @section meas_algorithms_detection_Contents Contents 175 - @ref meas_algorithms_detection_Purpose 176 - @ref meas_algorithms_detection_Initialize 177 - @ref meas_algorithms_detection_Invoke 178 - @ref meas_algorithms_detection_Config 179 - @ref meas_algorithms_detection_Debug 180 - @ref meas_algorithms_detection_Example 182 @section meas_algorithms_detection_Purpose Description 184 @copybrief SourceDetectionTask 186 @section meas_algorithms_detection_Initialize Task initialisation 188 @copydoc \_\_init\_\_ 190 @section meas_algorithms_detection_Invoke Invoking the Task 194 @section meas_algorithms_detection_Config Configuration parameters 196 See @ref SourceDetectionConfig 198 @section meas_algorithms_detection_Debug Debug variables 200 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 201 flag @c -d to import @b debug.py from your @c PYTHONPATH; see @ref baseDebug for more about @b debug.py files. 203 The available variables in SourceDetectionTask are: 207 - If True, display the exposure on ds9's frame 0. +ve detections in blue, -ve detections in cyan 208 - If display > 1, display the convolved exposure on frame 1 211 @section meas_algorithms_detection_Example A complete example of using SourceDetectionTask 213 This code is in @link measAlgTasks.py@endlink in the examples directory, and can be run as @em e.g. 215 examples/measAlgTasks.py --ds9 217 @dontinclude measAlgTasks.py 218 The example also runs the SourceMeasurementTask; see @ref meas_algorithms_measurement_Example for more 221 Import the task (there are some other standard imports; read the file if you're confused) 222 @skipline SourceDetectionTask 224 We need to create our task before processing any data as the task constructor 225 can add an extra column to the schema, but first we need an almost-empty Schema 226 @skipline makeMinimalSchema 227 after which we can call the constructor: 228 @skip SourceDetectionTask.ConfigClass 231 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 232 task objects). First create the output table: 235 And process the image 237 (You may not be happy that the threshold was set in the config before creating the Task rather than being set 238 separately for each exposure. You @em can reset it just before calling the run method if you must, but we 239 should really implement a better solution). 241 We can then unpack and use the results: 246 To investigate the @ref meas_algorithms_detection_Debug, put something like 250 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 251 if name == "lsst.meas.algorithms.detection": 256 lsstDebug.Info = DebugInfo 258 into your debug.py file and run measAlgTasks.py with the @c --debug flag. 260 ConfigClass = SourceDetectionConfig
261 _DefaultName =
"sourceDetection" 264 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task. 266 @param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 267 @param **kwds Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 269 If schema is not None and configured for 'both' detections, 270 a 'flags.negative' field will be added to label detections made with a 273 @note This task can add fields to the schema, so any code calling this task must ensure that 274 these columns are indeed present in the input match list; see @ref Example 276 pipeBase.Task.__init__(self, **kwds)
277 if schema
is not None and self.config.thresholdPolarity ==
"both":
279 "flags_negative", type=
"Flag",
280 doc=
"set if source was detected as significantly negative" 283 if self.config.thresholdPolarity ==
"both":
284 self.log.warn(
"Detection polarity set to 'both', but no flag will be " 285 "set to distinguish between positive and negative detections")
287 if self.config.reEstimateBackground:
288 self.makeSubtask(
"background")
289 if self.config.doTempLocalBackground:
290 self.makeSubtask(
"tempLocalBackground")
291 if self.config.doTempWideBackground:
292 self.makeSubtask(
"tempWideBackground")
295 def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
296 """!Run source detection and create a SourceCatalog. 298 @param table lsst.afw.table.SourceTable object that will be used to create the SourceCatalog. 299 @param exposure Exposure to process; DETECTED mask plane will be set in-place. 300 @param doSmooth if True, smooth the image before detection using a Gaussian of width sigma 302 @param sigma sigma of PSF (pixels); used for smoothing and to grow detections; 303 if None then measure the sigma of the PSF of the exposure (default: None) 304 @param clearMask Clear DETECTED{,_NEGATIVE} planes before running detection (default: True) 305 @param expId Exposure identifier (integer); unused by this implementation, but used for 306 RNG seed by subclasses. 308 @return a lsst.pipe.base.Struct with: 309 - sources -- an lsst.afw.table.SourceCatalog object 310 - fpSets --- lsst.pipe.base.Struct returned by @link detectFootprints @endlink 312 @throws ValueError if flags.negative is needed, but isn't in table's schema 313 @throws lsst.pipe.base.TaskError if sigma=None, doSmooth=True and the exposure has no PSF 316 If you want to avoid dealing with Sources and Tables, you can use detectFootprints() 317 to just get the afw::detection::FootprintSet%s. 320 raise ValueError(
"Table has incorrect Schema")
321 results = self.
detectFootprints(exposure=exposure, doSmooth=doSmooth, sigma=sigma,
322 clearMask=clearMask, expId=expId)
323 sources = afwTable.SourceCatalog(table)
324 table.preallocate(results.numPos + results.numNeg)
326 results.negative.makeSources(sources)
328 for record
in sources:
331 results.positive.makeSources(sources)
332 results.fpSets = results.copy()
333 results.sources = sources
337 makeSourceCatalog = run
339 def display(self, exposure, results, convolvedImage=None):
340 """Display detections if so configured 342 Displays the ``exposure`` in frame 0, overlays the detection peaks. 344 Requires that ``lsstDebug`` has been set up correctly, so that 345 ``lsstDebug.Info("lsst.meas.algorithms.detection")`` evaluates `True`. 347 If the ``convolvedImage`` is non-`None` and 348 ``lsstDebug.Info("lsst.meas.algorithms.detection") > 1``, the 349 ``convolvedImage`` will be displayed in frame 1. 353 exposure : `lsst.afw.image.Exposure` 354 Exposure to display, on which will be plotted the detections. 355 results : `lsst.pipe.base.Struct` 356 Results of the 'detectFootprints' method, containing positive and 357 negative footprints (which contain the peak positions that we will 358 plot). This is a `Struct` with ``positive`` and ``negative`` 359 elements that are of type `lsst.afw.detection.FootprintSet`. 360 convolvedImage : `lsst.afw.image.Image`, optional 361 Convolved image used for thresholding. 374 disp0 = lsst.afw.display.Display(frame=0)
375 disp0.mtv(exposure, title=
"detection")
377 def plotPeaks(fps, ctype):
380 with disp0.Buffering():
381 for fp
in fps.getFootprints():
382 for pp
in fp.getPeaks():
383 disp0.dot(
"+", pp.getFx(), pp.getFy(), ctype=ctype)
384 plotPeaks(results.positive,
"yellow")
385 plotPeaks(results.negative,
"red")
387 if convolvedImage
and display > 1:
388 disp1 = lsst.afw.display.Display(frame=1)
389 disp1.mtv(convolvedImage, title=
"PSF smoothed")
392 """Apply a temporary local background subtraction 394 This temporary local background serves to suppress noise fluctuations 395 in the wings of bright objects. 397 Peaks in the footprints will be updated. 401 exposure : `lsst.afw.image.Exposure` 402 Exposure for which to fit local background. 403 middle : `lsst.afw.image.MaskedImage` 404 Convolved image on which detection will be performed 405 (typically smaller than ``exposure`` because the 406 half-kernel has been removed around the edges). 407 results : `lsst.pipe.base.Struct` 408 Results of the 'detectFootprints' method, containing positive and 409 negative footprints (which contain the peak positions that we will 410 plot). This is a `Struct` with ``positive`` and ``negative`` 411 elements that are of type `lsst.afw.detection.FootprintSet`. 416 bg = self.tempLocalBackground.fitBackground(exposure.getMaskedImage())
417 bgImage = bg.getImageF()
418 middle -= bgImage.Factory(bgImage, middle.getBBox())
421 if self.config.thresholdPolarity !=
"negative":
422 self.
updatePeaks(results.positive, middle, thresholdPos)
423 if self.config.thresholdPolarity !=
"positive":
424 self.
updatePeaks(results.negative, middle, thresholdNeg)
427 """Clear the DETECTED and DETECTED_NEGATIVE mask planes 429 Removes any previous detection mask in preparation for a new 434 mask : `lsst.afw.image.Mask` 437 mask &= ~(mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
440 """Calculate size of smoothing kernel 442 Uses the ``nSigmaForKernel`` configuration parameter. Note 443 that that is the full width of the kernel bounding box 444 (so a value of 7 means 3.5 sigma on either side of center). 445 The value will be rounded up to the nearest odd integer. 450 Gaussian sigma of smoothing kernel. 455 Size of the smoothing kernel. 457 return (int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1
460 """Retrieve the PSF for an exposure 462 If ``sigma`` is provided, we make a ``GaussianPsf`` with that, 463 otherwise use the one from the ``exposure``. 467 exposure : `lsst.afw.image.Exposure` 468 Exposure from which to retrieve the PSF. 469 sigma : `float`, optional 470 Gaussian sigma to use if provided. 474 psf : `lsst.afw.detection.Psf` 475 PSF to use for detection. 478 psf = exposure.getPsf()
480 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
481 sigma = psf.computeShape().getDeterminantRadius()
483 psf = afwDet.GaussianPsf(size, size, sigma)
487 """Convolve the image with the PSF 489 We convolve the image with a Gaussian approximation to the PSF, 490 because this is separable and therefore fast. It's technically a 491 correlation rather than a convolution, but since we use a symmetric 492 Gaussian there's no difference. 494 The convolution can be disabled with ``doSmooth=False``. If we do 495 convolve, we mask the edges as ``EDGE`` and return the convolved image 496 with the edges removed. This is because we can't convolve the edges 497 because the kernel would extend off the image. 501 maskedImage : `lsst.afw.image.MaskedImage` 503 psf : `lsst.afw.detection.Psf` 504 PSF to convolve with (actually with a Gaussian approximation 507 Actually do the convolution? 509 Return Struct contents 510 ---------------------- 511 middle : `lsst.afw.image.MaskedImage` 512 Convolved image, without the edges. 514 Gaussian sigma used for the convolution. 516 self.metadata.set(
"doSmooth", doSmooth)
517 sigma = psf.computeShape().getDeterminantRadius()
518 self.metadata.set(
"sigma", sigma)
521 middle = maskedImage.Factory(maskedImage)
522 return pipeBase.Struct(middle=middle, sigma=sigma)
527 self.metadata.set(
"smoothingKernelWidth", kWidth)
528 gaussFunc = afwMath.GaussianFunction1D(sigma)
531 convolvedImage = maskedImage.Factory(maskedImage.getBBox())
537 goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
538 middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT,
False)
542 self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
544 return pipeBase.Struct(middle=middle, sigma=sigma)
547 """Apply thresholds to the convolved image 549 Identifies ``Footprint``s, both positive and negative. 551 The threshold can be modified by the provided multiplication 556 middle : `lsst.afw.image.MaskedImage` 557 Convolved image to threshold. 558 bbox : `lsst.afw.geom.Box2I` 559 Bounding box of unconvolved image. 561 Multiplier for the configured threshold. 563 Return Struct contents 564 ---------------------- 565 positive : `lsst.afw.detection.FootprintSet` or `None` 566 Positive detection footprints, if configured. 567 negative : `lsst.afw.detection.FootprintSet` or `None` 568 Negative detection footprints, if configured. 570 Multiplier for the configured threshold. 572 results = pipeBase.Struct(positive=
None, negative=
None, factor=factor)
574 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"negative":
575 threshold = self.
makeThreshold(middle,
"positive", factor=factor)
576 results.positive = afwDet.FootprintSet(
580 self.config.minPixels
582 results.positive.setRegion(bbox)
583 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"positive":
584 threshold = self.
makeThreshold(middle,
"negative", factor=factor)
585 results.negative = afwDet.FootprintSet(
589 self.config.minPixels
591 results.negative.setRegion(bbox)
596 """Finalize the detected footprints 598 Grows the footprints, sets the ``DETECTED`` and ``DETECTED_NEGATIVE`` 599 mask planes, and logs the results. 601 ``numPos`` (number of positive footprints), ``numPosPeaks`` (number 602 of positive peaks), ``numNeg`` (number of negative footprints), 603 ``numNegPeaks`` (number of negative peaks) entries are added to the 608 mask : `lsst.afw.image.Mask` 609 Mask image on which to flag detected pixels. 610 results : `lsst.pipe.base.Struct` 611 Struct of detection results, including ``positive`` and 612 ``negative`` entries; modified. 614 Gaussian sigma of PSF. 616 Multiplier for the configured threshold. 618 for polarity, maskName
in ((
"positive",
"DETECTED"), (
"negative",
"DETECTED_NEGATIVE")):
619 fpSet = getattr(results, polarity)
622 if self.config.nSigmaToGrow > 0:
623 nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
624 self.metadata.set(
"nGrow", nGrow)
625 if self.config.combinedGrow:
626 fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow)
628 stencil = (afwGeom.Stencil.CIRCLE
if self.config.isotropicGrow
else 629 afwGeom.Stencil.MANHATTAN)
631 fp.dilate(nGrow, stencil)
632 fpSet.setMask(mask, maskName)
633 if not self.config.returnOriginalFootprints:
634 setattr(results, polarity, fpSet)
637 results.numPosPeaks = 0
639 results.numNegPeaks = 0
643 if results.positive
is not None:
644 results.numPos = len(results.positive.getFootprints())
645 results.numPosPeaks = sum(len(fp.getPeaks())
for fp
in results.positive.getFootprints())
646 positive =
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
647 if results.negative
is not None:
648 results.numNeg = len(results.negative.getFootprints())
649 results.numNegPeaks = sum(len(fp.getPeaks())
for fp
in results.negative.getFootprints())
650 negative =
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
652 self.log.info(
"Detected%s%s%s to %g %s" %
653 (positive,
" and" if positive
and negative
else "", negative,
654 self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
655 "DN" if self.config.thresholdType ==
"value" else "sigma"))
658 """Estimate the background after detection 662 maskedImage : `lsst.afw.image.MaskedImage` 663 Image on which to estimate the background. 664 backgrounds : `lsst.afw.math.BackgroundList` 665 List of backgrounds; modified. 669 bg : `lsst.afw.math.backgroundMI` 670 Empirical background model. 672 bg = self.background.fitBackground(maskedImage)
673 if self.config.adjustBackground:
674 self.log.warn(
"Fiddling the background by %g", self.config.adjustBackground)
675 bg += self.config.adjustBackground
676 self.log.info(
"Resubtracting the background after object detection")
677 maskedImage -= bg.getImageF()
678 backgrounds.append(bg)
682 """Clear unwanted results from the Struct of results 684 If we specifically want only positive or only negative detections, 685 drop the ones we don't want, and its associated mask plane. 689 mask : `lsst.afw.image.Mask` 691 results : `lsst.pipe.base.Struct` 692 Detection results, with ``positive`` and ``negative`` elements; 695 if self.config.thresholdPolarity ==
"positive":
696 if self.config.reEstimateBackground:
697 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
698 results.negative =
None 699 elif self.config.thresholdPolarity ==
"negative":
700 if self.config.reEstimateBackground:
701 mask &= ~mask.getPlaneBitMask(
"DETECTED")
702 results.positive =
None 705 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
706 """Detect footprints. 710 exposure : `lsst.afw.image.Exposure` 711 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be 713 doSmooth : `bool`, optional 714 If True, smooth the image before detection using a Gaussian 716 sigma : `float`, optional 717 Gaussian Sigma of PSF (pixels); used for smoothing and to grow 718 detections; if `None` then measure the sigma of the PSF of the 720 clearMask : `bool`, optional 721 Clear both DETECTED and DETECTED_NEGATIVE planes before running 723 expId : `dict`, optional 724 Exposure identifier; unused by this implementation, but used for 725 RNG seed by subclasses. 727 Return Struct contents 728 ---------------------- 729 positive : `lsst.afw.detection.FootprintSet` 730 Positive polarity footprints (may be `None`) 731 negative : `lsst.afw.detection.FootprintSet` 732 Negative polarity footprints (may be `None`) 734 Number of footprints in positive or 0 if detection polarity was 737 Number of footprints in negative or 0 if detection polarity was 739 background : `lsst.afw.math.BackgroundList` 740 Re-estimated background. `None` if 741 ``reEstimateBackground==False``. 743 Multiplication factor applied to the configured detection 746 maskedImage = exposure.maskedImage
751 psf = self.
getPsf(exposure, sigma=sigma)
753 convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
754 middle = convolveResults.middle
755 sigma = convolveResults.sigma
758 results.background = afwMath.BackgroundList()
759 if self.config.doTempLocalBackground:
763 if self.config.reEstimateBackground:
767 self.
display(exposure, results, middle)
772 """Make an afw.detection.Threshold object corresponding to the task's 773 configuration and the statistics of the given image. 777 image : `afw.image.MaskedImage` 778 Image to measure noise statistics from if needed. 779 thresholdParity: `str` 780 One of "positive" or "negative", to set the kind of fluctuations 781 the Threshold will detect. 783 Factor by which to multiply the configured detection threshold. 784 This is useful for tweaking the detection threshold slightly. 788 threshold : `lsst.afw.detection.Threshold` 791 parity =
False if thresholdParity ==
"negative" else True 792 thresholdValue = self.config.thresholdValue
793 thresholdType = self.config.thresholdType
794 if self.config.thresholdType ==
'stdev':
795 bad = image.getMask().getPlaneBitMask(self.config.statsMask)
797 sctrl.setAndMask(bad)
799 thresholdValue *= stats.getValue(afwMath.STDEVCLIP)
800 thresholdType =
'value' 802 threshold = afwDet.createThreshold(thresholdValue*factor, thresholdType, parity)
803 threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
807 """Update the Peaks in a FootprintSet by detecting new Footprints and 808 Peaks in an image and using the new Peaks instead of the old ones. 812 fpSet : `afw.detection.FootprintSet` 813 Set of Footprints whose Peaks should be updated. 814 image : `afw.image.MaskedImage` 815 Image to detect new Footprints and Peak in. 816 threshold : `afw.detection.Threshold` 817 Threshold object for detection. 819 Input Footprints with fewer Peaks than self.config.nPeaksMaxSimple 820 are not modified, and if no new Peaks are detected in an input 821 Footprint, the brightest original Peak in that Footprint is kept. 823 for footprint
in fpSet.getFootprints():
824 oldPeaks = footprint.getPeaks()
825 if len(oldPeaks) <= self.config.nPeaksMaxSimple:
830 sub = image.Factory(image, footprint.getBBox())
831 fpSetForPeaks = afwDet.FootprintSet(
835 self.config.minPixels
837 newPeaks = afwDet.PeakCatalog(oldPeaks.getTable())
838 for fpForPeaks
in fpSetForPeaks.getFootprints():
839 for peak
in fpForPeaks.getPeaks():
840 if footprint.contains(peak.getI()):
841 newPeaks.append(peak)
842 if len(newPeaks) > 0:
844 oldPeaks.extend(newPeaks)
850 """Set the edgeBitmask bits for all of maskedImage outside goodBBox 854 maskedImage : `lsst.afw.image.MaskedImage` 855 Image on which to set edge bits in the mask. 856 goodBBox : `lsst.afw.geom.Box2I` 857 Bounding box of good pixels, in ``LOCAL`` coordinates. 858 edgeBitmask : `lsst.afw.image.MaskPixel` 859 Bit mask to OR with the existing mask bits in the region 860 outside ``goodBBox``. 862 msk = maskedImage.getMask()
864 mx0, my0 = maskedImage.getXY0()
865 for x0, y0, w, h
in ([0, 0,
866 msk.getWidth(), goodBBox.getBeginY() - my0],
867 [0, goodBBox.getEndY() - my0, msk.getWidth(),
868 maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
870 goodBBox.getBeginX() - mx0, msk.getHeight()],
871 [goodBBox.getEndX() - mx0, 0,
872 maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
876 edgeMask |= edgeBitmask
880 """Context manager for removing wide (large-scale) background 882 Removing a wide (large-scale) background helps to suppress the 883 detection of large footprints that may overwhelm the deblender. 884 It does, however, set a limit on the maximum scale of objects. 886 The background that we remove will be restored upon exit from 891 exposure : `lsst.afw.image.Exposure` 892 Exposure on which to remove large-scale background. 896 context : context manager 897 Context manager that will ensure the temporary wide background 900 doTempWideBackground = self.config.doTempWideBackground
901 if doTempWideBackground:
902 self.log.info(
"Applying temporary wide background subtraction")
903 original = exposure.maskedImage.image.array[:].copy()
904 self.tempWideBackground.
run(exposure).background
907 image = exposure.maskedImage.image
908 mask = exposure.maskedImage.mask
909 noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
910 isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
911 image.array[noData] = np.median(image.array[~noData & isGood])
915 if doTempWideBackground:
916 exposure.maskedImage.image.array[:] = original
920 """Add a set of exposures together. 924 exposureList : `list` of `lsst.afw.image.Exposure` 925 Sequence of exposures to add. 929 addedExposure : `lsst.afw.image.Exposure` 930 An exposure of the same size as each exposure in ``exposureList``, 931 with the metadata from ``exposureList[0]`` and a masked image equal 932 to the sum of all the exposure's masked images. 934 exposure0 = exposureList[0]
935 image0 = exposure0.getMaskedImage()
937 addedImage = image0.Factory(image0,
True)
938 addedImage.setXY0(image0.getXY0())
940 for exposure
in exposureList[1:]:
941 image = exposure.getMaskedImage()
944 addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())
def updatePeaks(self, fpSet, image, threshold)
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.
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
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)
Run source detection and create a SourceCatalog.
def clearMask(self, mask)
def setEdgeBits(maskedImage, goodBBox, edgeBitmask)
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)