Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions python/lsst/meas/algorithms/dynamicDetection.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class DynamicDetectionConfig(SourceDetectionConfig):
doBrightPrelimDetection = Field(dtype=bool, default=True,
doc="Do initial bright detection pass where footprints are grown "
"by brightGrowFactor?")
brightDetectionIterMax = Field(dtype=int, default=10,
doc="Maximum number of iterations in the initial bright detection "
"pass.")
brightMultiplier = Field(dtype=float, default=2000.0,
doc="Multiplier to apply to the prelimThresholdFactor for the "
"\"bright\" detections stage (want this to be large to only "
Expand Down Expand Up @@ -660,12 +663,12 @@ def _computeBrightDetectionMask(self, maskedImage, convolveResults):

Perform an initial bright object detection pass using a high detection
threshold. The footprints in this pass are grown significantly more
than is typical to account for wings around bright sources. The
than is typical to account for wings around bright sources. The
negative polarity detections in this pass help in masking severely
over-subtracted regions.

A maximum fraction of masked pixel from this pass is ensured via
the config ``brightMaskFractionMax``. If the masked pixel fraction is
the config ``brightMaskFractionMax``. If the masked pixel fraction is
above this value, the detection thresholds here are increased by
``bisectFactor`` in a while loop until the detected masked fraction
falls below this value.
Expand Down Expand Up @@ -703,7 +706,7 @@ def _computeBrightDetectionMask(self, maskedImage, convolveResults):
# brightMaskFractionMax, increasing the thresholds by
# config.bisectFactor on each iteration (rarely necessary
# for current defaults).
while nPixDetNeg/nPix > brightMaskFractionMax or nPixDet/nPix > brightMaskFractionMax:
for nIter in range(self.config.brightDetectionIterMax):
self.clearMask(maskedImage.mask)
brightPosFactor *= self.config.bisectFactor
brightNegFactor *= self.config.bisectFactor
Expand All @@ -722,10 +725,17 @@ def _computeBrightDetectionMask(self, maskedImage, convolveResults):
self.log.info("Number (%) of bright DETECTED_NEGATIVE pix: {} ({:.1f}%)".
format(nPixDetNeg, 100*nPixDetNeg/nPix))
if nPixDetNeg/nPix > brightMaskFractionMax or nPixDet/nPix > brightMaskFractionMax:
self.log.warn("Too high a fraction (%.1f > %.1f) of pixels were masked with current "
"\"bright\" detection round thresholds. Increasing by a factor of %.2f "
"and trying again.", max(nPixDetNeg, nPixDet)/nPix,
brightMaskFractionMax, self.config.bisectFactor)
self.log.warning("Too high a fraction (%.2f > %.2f) of pixels were masked with current "
"\"bright\" detection round thresholds (at nIter = %d). Increasing by "
"a factor of %.2f and trying again.", max(nPixDetNeg, nPixDet)/nPix,
brightMaskFractionMax, nIter, self.config.bisectFactor)
if nIter == self.config.brightDetectionIterMax - 1:
self.log.warning("Reached maximum number of iterations and still have too high "
"detected mask fractions in bright detection pass. Image is "
"likely mostly masked with BAD or NO_DATA or \"bad\" in some "
"other respect (so expected to likely fail further downstream).")
else:
break

# Save the mask planes from the "bright" detection round, then
# clear them before moving on to the "prelim" detection phase.
Expand Down
17 changes: 16 additions & 1 deletion tests/test_dynamicDetection.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from lsst.afw.geom import makeCdMatrix, makeSkyWcs
from lsst.afw.table import SourceTable
from lsst.geom import Box2I, Extent2I, Point2D, Point2I, SpherePoint, degrees
from lsst.meas.algorithms import DynamicDetectionTask
from lsst.meas.algorithms import DynamicDetectionTask, InsufficientSourcesError
from lsst.meas.algorithms.testUtils import plantSources
from lsst.pex.config import FieldValidationError

Expand Down Expand Up @@ -110,6 +110,21 @@ def testThresholdScalingAndNoBackgroundTweak(self):
config.doBackgroundTweak = False
self.check(1.0, config)

def testBrightDetectionPass(self):
"""Check that the maximum number of bright detection iterations is
observed.

The bright detection loop is forced by setting config.brightMultiplier
so low such that the entire image is marked as DETECTED. As such, the
task is doomed to fail where it tries to lay down sky sources."
"""
config = DynamicDetectionTask.ConfigClass()
config.brightMultiplier = 0.05
config.brightDetectionIterMax = 2
with self.assertRaisesRegex(
InsufficientSourcesError, "Insufficient good sky source flux measurements"):
self.check(1.0, config)

def testThresholdsOutsideBounds(self):
"""Check that dynamic detection properly sets threshold limits.
"""
Expand Down