23 from __future__
import absolute_import, division, print_function
25 __all__ = (
"SourceDetectionConfig",
"SourceDetectionTask",
"addExposures")
27 from contextlib
import contextmanager
39 from .subtractBackground
import SubtractBackgroundTask
43 """!Configuration parameters for the SourceDetectionTask 45 minPixels = pexConfig.RangeField(
46 doc=
"detected sources with fewer than the specified number of pixels will be ignored",
47 dtype=int, optional=
False, default=1, min=0,
49 isotropicGrow = pexConfig.Field(
50 doc=
"Pixels should be grown as isotropically as possible (slower)",
51 dtype=bool, optional=
False, default=
False,
53 combinedGrow = pexConfig.Field(
54 doc=
"Grow all footprints at the same time? This allows disconnected footprints to merge.",
55 dtype=bool, default=
True,
57 nSigmaToGrow = pexConfig.Field(
58 doc=
"Grow detections by nSigmaToGrow * [PSF RMS width]; if 0 then do not grow",
59 dtype=float, default=2.4,
61 returnOriginalFootprints = pexConfig.Field(
62 doc=
"Grow detections to set the image mask bits, but return the original (not-grown) footprints",
63 dtype=bool, optional=
False, default=
False,
65 thresholdValue = pexConfig.RangeField(
66 doc=
"Threshold for footprints",
67 dtype=float, optional=
False, default=5.0, min=0.0,
69 includeThresholdMultiplier = pexConfig.RangeField(
70 doc=
"Include threshold relative to thresholdValue",
71 dtype=float, default=1.0, min=0.0,
73 thresholdType = pexConfig.ChoiceField(
74 doc=
"specifies the desired flavor of Threshold",
75 dtype=str, optional=
False, default=
"stdev",
77 "variance":
"threshold applied to image variance",
78 "stdev":
"threshold applied to image std deviation",
79 "value":
"threshold applied to image value",
80 "pixel_stdev":
"threshold applied to per-pixel std deviation",
83 thresholdPolarity = pexConfig.ChoiceField(
84 doc=
"specifies whether to detect positive, or negative sources, or both",
85 dtype=str, optional=
False, default=
"positive",
87 "positive":
"detect only positive sources",
88 "negative":
"detect only negative sources",
89 "both":
"detect both positive and negative sources",
92 adjustBackground = pexConfig.Field(
94 doc=
"Fiddle factor to add to the background; debugging only",
97 reEstimateBackground = pexConfig.Field(
99 doc=
"Estimate the background again after final source detection?",
100 default=
True, optional=
False,
102 background = pexConfig.ConfigurableField(
103 doc=
"Background re-estimation; ignored if reEstimateBackground false",
104 target=SubtractBackgroundTask,
106 tempLocalBackground = pexConfig.ConfigurableField(
107 doc=(
"A local (small-scale), temporary background estimation step run between " 108 "detecting above-threshold regions and detecting the peaks within " 109 "them; used to avoid detecting spuerious peaks in the wings."),
110 target=SubtractBackgroundTask,
112 doTempLocalBackground = pexConfig.Field(
114 doc=
"Enable temporary local background subtraction? (see tempLocalBackground)",
117 tempWideBackground = pexConfig.ConfigurableField(
118 doc=(
"A wide (large-scale) background estimation and removal before footprint and peak detection. " 119 "It is added back into the image after detection. The purpose is to suppress very large " 120 "footprints (e.g., from large artifacts) that the deblender may choke on."),
121 target=SubtractBackgroundTask,
123 doTempWideBackground = pexConfig.Field(
125 doc=
"Do temporary wide (large-scale) background subtraction before footprint detection?",
128 nPeaksMaxSimple = pexConfig.Field(
130 doc=(
"The maximum number of peaks in a Footprint before trying to " 131 "replace its peaks using the temporary local background"),
134 nSigmaForKernel = pexConfig.Field(
136 doc=(
"Multiple of PSF RMS size to use for convolution kernel bounding box size; " 137 "note that this is not a half-size. The size will be rounded up to the nearest odd integer"),
140 statsMask = pexConfig.ListField(
142 doc=
"Mask planes to ignore when calculating statistics of image (for thresholdType=stdev)",
143 default=[
'BAD',
'SAT',
'EDGE',
'NO_DATA'],
156 for maskPlane
in (
"DETECTED",
"DETECTED_NEGATIVE"):
170 \anchor SourceDetectionTask_ 172 \brief Detect positive and negative sources on an exposure and return a new \link table.SourceCatalog\endlink. 174 \section meas_algorithms_detection_Contents Contents 176 - \ref meas_algorithms_detection_Purpose 177 - \ref meas_algorithms_detection_Initialize 178 - \ref meas_algorithms_detection_Invoke 179 - \ref meas_algorithms_detection_Config 180 - \ref meas_algorithms_detection_Debug 181 - \ref meas_algorithms_detection_Example 183 \section meas_algorithms_detection_Purpose Description 185 \copybrief SourceDetectionTask 187 \section meas_algorithms_detection_Initialize Task initialisation 189 \copydoc \_\_init\_\_ 191 \section meas_algorithms_detection_Invoke Invoking the Task 195 \section meas_algorithms_detection_Config Configuration parameters 197 See \ref SourceDetectionConfig 199 \section meas_algorithms_detection_Debug Debug variables 201 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 202 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files. 204 The available variables in SourceDetectionTask are: 208 - If True, display the exposure on ds9's frame 0. +ve detections in blue, -ve detections in cyan 209 - If display > 1, display the convolved exposure on frame 1 212 \section meas_algorithms_detection_Example A complete example of using SourceDetectionTask 214 This code is in \link measAlgTasks.py\endlink in the examples directory, and can be run as \em e.g. 216 examples/measAlgTasks.py --ds9 218 \dontinclude measAlgTasks.py 219 The example also runs the SourceMeasurementTask; see \ref meas_algorithms_measurement_Example for more 222 Import the task (there are some other standard imports; read the file if you're confused) 223 \skipline SourceDetectionTask 225 We need to create our task before processing any data as the task constructor 226 can add an extra column to the schema, but first we need an almost-empty Schema 227 \skipline makeMinimalSchema 228 after which we can call the constructor: 229 \skip SourceDetectionTask.ConfigClass 232 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 233 task objects). First create the output table: 236 And process the image 238 (You may not be happy that the threshold was set in the config before creating the Task rather than being set 239 separately for each exposure. You \em can reset it just before calling the run method if you must, but we 240 should really implement a better solution). 242 We can then unpack and use the results: 247 To investigate the \ref meas_algorithms_detection_Debug, put something like 251 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 252 if name == "lsst.meas.algorithms.detection": 257 lsstDebug.Info = DebugInfo 259 into your debug.py file and run measAlgTasks.py with the \c --debug flag. 261 ConfigClass = SourceDetectionConfig
262 _DefaultName =
"sourceDetection" 265 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task. 267 \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 268 \param **kwds Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 270 If schema is not None and configured for 'both' detections, 271 a 'flags.negative' field will be added to label detections made with a 274 \note This task can add fields to the schema, so any code calling this task must ensure that 275 these columns are indeed present in the input match list; see \ref Example 277 pipeBase.Task.__init__(self, **kwds)
278 if schema
is not None and self.config.thresholdPolarity ==
"both":
280 "flags_negative", type=
"Flag",
281 doc=
"set if source was detected as significantly negative" 284 if self.config.thresholdPolarity ==
"both":
285 self.log.warn(
"Detection polarity set to 'both', but no flag will be " 286 "set to distinguish between positive and negative detections")
288 if self.config.reEstimateBackground:
289 self.makeSubtask(
"background")
290 if self.config.doTempLocalBackground:
291 self.makeSubtask(
"tempLocalBackground")
292 if self.config.doTempWideBackground:
293 self.makeSubtask(
"tempWideBackground")
296 def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
297 """!Run source detection and create a SourceCatalog. 299 \param table lsst.afw.table.SourceTable object that will be used to create the SourceCatalog. 300 \param exposure Exposure to process; DETECTED mask plane will be set in-place. 301 \param doSmooth if True, smooth the image before detection using a Gaussian of width sigma 303 \param sigma sigma of PSF (pixels); used for smoothing and to grow detections; 304 if None then measure the sigma of the PSF of the exposure (default: None) 305 \param clearMask Clear DETECTED{,_NEGATIVE} planes before running detection (default: True) 306 \param expId Exposure identifier (integer); unused by this implementation, but used for 307 RNG seed by subclasses. 309 \return a lsst.pipe.base.Struct with: 310 - sources -- an lsst.afw.table.SourceCatalog object 311 - fpSets --- lsst.pipe.base.Struct returned by \link detectFootprints \endlink 313 \throws ValueError if flags.negative is needed, but isn't in table's schema 314 \throws lsst.pipe.base.TaskError if sigma=None, doSmooth=True and the exposure has no PSF 317 If you want to avoid dealing with Sources and Tables, you can use detectFootprints() 318 to just get the afw::detection::FootprintSet%s. 321 raise ValueError(
"Table has incorrect Schema")
322 results = self.
detectFootprints(exposure=exposure, doSmooth=doSmooth, sigma=sigma,
323 clearMask=clearMask, expId=expId)
324 sources = afwTable.SourceCatalog(table)
325 table.preallocate(results.numPos + results.numNeg)
327 results.negative.makeSources(sources)
329 for record
in sources:
332 results.positive.makeSources(sources)
333 results.fpSets = results.copy()
334 results.sources = sources
338 makeSourceCatalog = run
340 def display(self, exposure, results, convolvedImage=None):
341 """Display detections if so configured 343 Displays the ``exposure`` in frame 0, overlays the detection peaks. 345 Requires that ``lsstDebug`` has been set up correctly, so that 346 ``lsstDebug.Info("lsst.meas.algorithms.detection")`` evaluates `True`. 348 If the ``convolvedImage`` is non-`None` and 349 ``lsstDebug.Info("lsst.meas.algorithms.detection") > 1``, the 350 ``convolvedImage`` will be displayed in frame 1. 354 exposure : `lsst.afw.image.Exposure` 355 Exposure to display, on which will be plotted the detections. 356 results : `lsst.pipe.base.Struct` 357 Results of the 'detectFootprints' method, containing positive and 358 negative footprints (which contain the peak positions that we will 359 plot). This is a `Struct` with ``positive`` and ``negative`` 360 elements that are of type `lsst.afw.detection.FootprintSet`. 361 convolvedImage : `lsst.afw.image.Image`, optional 362 Convolved image used for thresholding. 375 disp0 = lsst.afw.display.Display(frame=0)
376 disp0.mtv(exposure, title=
"detection")
378 def plotPeaks(fps, ctype):
381 with disp0.Buffering():
382 for fp
in fps.getFootprints():
383 for pp
in fp.getPeaks():
384 disp0.dot(
"+", pp.getFx(), pp.getFy(), ctype=ctype)
385 plotPeaks(results.positive,
"yellow")
386 plotPeaks(results.negative,
"red")
388 if convolvedImage
and display > 1:
389 disp1 = Display(frame=1)
390 disp1.mtv(convolvedImage, title=
"PSF smoothed")
393 """Apply a temporary local background subtraction 395 This temporary local background serves to suppress noise fluctuations 396 in the wings of bright objects. 398 Peaks in the footprints will be updated. 402 exposure : `lsst.afw.image.Exposure` 403 Exposure for which to fit local background. 404 middle : `lsst.afw.image.MaskedImage` 405 Convolved image on which detection will be performed 406 (typically smaller than ``exposure`` because the 407 half-kernel has been removed around the edges). 408 results : `lsst.pipe.base.Struct` 409 Results of the 'detectFootprints' method, containing positive and 410 negative footprints (which contain the peak positions that we will 411 plot). This is a `Struct` with ``positive`` and ``negative`` 412 elements that are of type `lsst.afw.detection.FootprintSet`. 417 bg = self.tempLocalBackground.fitBackground(exposure.getMaskedImage())
418 bgImage = bg.getImageF()
419 middle -= bgImage.Factory(bgImage, middle.getBBox())
422 if self.config.thresholdPolarity !=
"negative":
423 self.
updatePeaks(results.positive, middle, thresholdPos)
424 if self.config.thresholdPolarity !=
"positive":
425 self.
updatePeaks(results.negative, middle, thresholdNeg)
428 """Clear the DETECTED and DETECTED_NEGATIVE mask planes 430 Removes any previous detection mask in preparation for a new 435 mask : `lsst.afw.image.Mask` 438 mask &= ~(mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
441 """Calculate size of smoothing kernel 443 Uses the ``nSigmaForKernel`` configuration parameter. Note 444 that that is the full width of the kernel bounding box 445 (so a value of 7 means 3.5 sigma on either side of center). 446 The value will be rounded up to the nearest odd integer. 451 Gaussian sigma of smoothing kernel. 456 Size of the smoothing kernel. 458 return (int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1
461 """Retrieve the PSF for an exposure 463 If ``sigma`` is provided, we make a ``GaussianPsf`` with that, 464 otherwise use the one from the ``exposure``. 468 exposure : `lsst.afw.image.Exposure` 469 Exposure from which to retrieve the PSF. 470 sigma : `float`, optional 471 Gaussian sigma to use if provided. 475 psf : `lsst.afw.detection.Psf` 476 PSF to use for detection. 479 psf = exposure.getPsf()
481 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
482 sigma = psf.computeShape().getDeterminantRadius()
484 psf = afwDet.GaussianPsf(size, size, sigma)
488 """Convolve the image with the PSF 490 We convolve the image with a Gaussian approximation to the PSF, 491 because this is separable and therefore fast. It's technically a 492 correlation rather than a convolution, but since we use a symmetric 493 Gaussian there's no difference. 495 The convolution can be disabled with ``doSmooth=False``. If we do 496 convolve, we mask the edges as ``EDGE`` and return the convolved image 497 with the edges removed. This is because we can't convolve the edges 498 because the kernel would extend off the image. 502 maskedImage : `lsst.afw.image.MaskedImage` 504 psf : `lsst.afw.detection.Psf` 505 PSF to convolve with (actually with a Gaussian approximation 508 Actually do the convolution? 510 Return Struct contents 511 ---------------------- 512 middle : `lsst.afw.image.MaskedImage` 513 Convolved image, without the edges. 515 Gaussian sigma used for the convolution. 517 self.metadata.set(
"doSmooth", doSmooth)
518 sigma = psf.computeShape().getDeterminantRadius()
519 self.metadata.set(
"sigma", sigma)
522 middle = maskedImage.Factory(maskedImage)
523 return pipeBase.Struct(middle=middle, sigma=sigma)
528 self.metadata.set(
"smoothingKernelWidth", kWidth)
529 gaussFunc = afwMath.GaussianFunction1D(sigma)
532 convolvedImage = maskedImage.Factory(maskedImage.getBBox())
538 goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
539 middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT,
False)
543 self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
545 return pipeBase.Struct(middle=middle, sigma=sigma)
548 """Apply thresholds to the convolved image 550 Identifies ``Footprint``s, both positive and negative. 552 The threshold can be modified by the provided multiplication 557 middle : `lsst.afw.image.MaskedImage` 558 Convolved image to threshold. 559 bbox : `lsst.afw.geom.Box2I` 560 Bounding box of unconvolved image. 562 Multiplier for the configured threshold. 564 Return Struct contents 565 ---------------------- 566 positive : `lsst.afw.detection.FootprintSet` or `None` 567 Positive detection footprints, if configured. 568 negative : `lsst.afw.detection.FootprintSet` or `None` 569 Negative detection footprints, if configured. 571 Multiplier for the configured threshold. 573 results = pipeBase.Struct(positive=
None, negative=
None, factor=factor)
575 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"negative":
576 threshold = self.
makeThreshold(middle,
"positive", factor=factor)
577 results.positive = afwDet.FootprintSet(
581 self.config.minPixels
583 results.positive.setRegion(bbox)
584 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"positive":
585 threshold = self.
makeThreshold(middle,
"negative", factor=factor)
586 results.negative = afwDet.FootprintSet(
590 self.config.minPixels
592 results.negative.setRegion(bbox)
597 """Finalize the detected footprints 599 Grows the footprints, sets the ``DETECTED`` and ``DETECTED_NEGATIVE`` 600 mask planes, and logs the results. 602 ``numPos`` (number of positive footprints), ``numPosPeaks`` (number 603 of positive peaks), ``numNeg`` (number of negative footprints), 604 ``numNegPeaks`` (number of negative peaks) entries are added to the 609 mask : `lsst.afw.image.Mask` 610 Mask image on which to flag detected pixels. 611 results : `lsst.pipe.base.Struct` 612 Struct of detection results, including ``positive`` and 613 ``negative`` entries; modified. 615 Gaussian sigma of PSF. 617 Multiplier for the configured threshold. 619 for polarity, maskName
in ((
"positive",
"DETECTED"), (
"negative",
"DETECTED_NEGATIVE")):
620 fpSet = getattr(results, polarity)
623 if self.config.nSigmaToGrow > 0:
624 nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
625 self.metadata.set(
"nGrow", nGrow)
626 if self.config.combinedGrow:
627 fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow)
629 stencil = (afwGeom.Stencil.CIRCLE
if self.config.isotropicGrow
else 630 afwGeom.Stencil.MANHATTAN)
632 fp.dilate(nGrow, stencil)
633 fpSet.setMask(mask, maskName)
634 if not self.config.returnOriginalFootprints:
635 setattr(results, polarity, fpSet)
638 results.numPosPeaks = 0
640 results.numNegPeaks = 0
644 if results.positive
is not None:
645 results.numPos = len(results.positive.getFootprints())
646 results.numPosPeaks = sum(len(fp.getPeaks())
for fp
in results.positive.getFootprints())
647 positive =
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
648 if results.negative
is not None:
649 results.numNeg = len(results.negative.getFootprints())
650 results.numNegPeaks = sum(len(fp.getPeaks())
for fp
in results.negative.getFootprints())
651 negative =
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
653 self.log.info(
"Detected%s%s%s to %g %s" %
654 (positive,
" and" if positive
and negative
else "", negative,
655 self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
656 "DN" if self.config.thresholdType ==
"value" else "sigma"))
659 """Estimate the background after detection 663 maskedImage : `lsst.afw.image.MaskedImage` 664 Image on which to estimate the background. 665 results : `lsst.pipe.base.Struct` 666 Detection results; modified. 670 bg : `lsst.afw.math.backgroundMI` 671 Empirical background model. 673 bg = self.background.fitBackground(maskedImage)
674 if self.config.adjustBackground:
675 self.log.warn(
"Fiddling the background by %g", self.config.adjustBackground)
676 bg += self.config.adjustBackground
677 self.log.info(
"Resubtracting the background after object detection")
678 maskedImage -= bg.getImageF()
679 results.background = bg
683 """Clear unwanted results from the Struct of results 685 If we specifically want only positive or only negative detections, 686 drop the ones we don't want, and its associated mask plane. 690 mask : `lsst.afw.image.Mask` 692 results : `lsst.pipe.base.Struct` 693 Detection results, with ``positive`` and ``negative`` elements; 696 if self.config.thresholdPolarity ==
"positive":
697 if self.config.reEstimateBackground:
698 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
699 results.negative =
None 700 elif self.config.thresholdPolarity ==
"negative":
701 if self.config.reEstimateBackground:
702 mask &= ~mask.getPlaneBitMask(
"DETECTED")
703 results.positive =
None 706 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
707 """Detect footprints. 711 exposure : `lsst.afw.image.Exposure` 712 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be 714 doSmooth : `bool`, optional 715 If True, smooth the image before detection using a Gaussian 717 sigma : `float`, optional 718 Gaussian Sigma of PSF (pixels); used for smoothing and to grow 719 detections; if `None` then measure the sigma of the PSF of the 721 clearMask : `bool`, optional 722 Clear both DETECTED and DETECTED_NEGATIVE planes before running 724 expId : `dict`, optional 725 Exposure identifier; unused by this implementation, but used for 726 RNG seed by subclasses. 728 Return Struct contents 729 ---------------------- 730 positive : `lsst.afw.detection.FootprintSet` 731 Positive polarity footprints (may be `None`) 732 negative : `lsst.afw.detection.FootprintSet` 733 Negative polarity footprints (may be `None`) 735 Number of footprints in positive or 0 if detection polarity was 738 Number of footprints in negative or 0 if detection polarity was 740 background : `lsst.afw.math.BackgroundMI` 741 Re-estimated background. `None` if 742 ``reEstimateBackground==False``. 744 Multiplication factor applied to the configured detection 747 maskedImage = exposure.maskedImage
752 psf = self.
getPsf(exposure, sigma=sigma)
754 convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
755 middle = convolveResults.middle
756 sigma = convolveResults.sigma
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 background is restored. 899 doTempWideBackground = self.config.doTempWideBackground
900 if doTempWideBackground:
901 self.log.info(
"Applying temporary wide background subtraction")
902 original = exposure.maskedImage.image.array[:]
903 self.tempWideBackground.
run(exposure).background
906 image = exposure.maskedImage.image
907 mask = exposure.maskedImage.mask
908 noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
909 isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
910 image.array[noData] = np.median(image.array[~noData & isGood])
914 if doTempWideBackground:
915 exposure.maskedImage.image.array[:] = original
919 """Add a set of exposures together. 923 exposureList : `list` of `lsst.afw.image.Exposure` 924 Sequence of exposures to add. 928 addedExposure : `lsst.afw.image.Exposure` 929 An exposure of the same size as each exposure in ``exposureList``, 930 with the metadata from ``exposureList[0]`` and a masked image equal 931 to the sum of all the exposure's masked images. 933 exposure0 = exposureList[0]
934 image0 = exposure0.getMaskedImage()
936 addedImage = image0.Factory(image0,
True)
937 addedImage.setXY0(image0.getXY0())
939 for exposure
in exposureList[1:]:
940 image = exposure.getMaskedImage()
943 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 reEstimateBackground(self, maskedImage, 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 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)