Coverage for tests/test_dynamicDetection.py: 31%
66 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-14 02:29 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-14 02:29 -0700
1import unittest
2import numpy as np
4import lsst.utils.tests
6from lsst.geom import Box2I, Point2I, Point2D, Extent2I, SpherePoint, degrees
7from lsst.afw.geom import makeCdMatrix, makeSkyWcs
8from lsst.afw.image import PARENT
9from lsst.afw.table import SourceTable
10from lsst.meas.algorithms import DynamicDetectionTask
11from lsst.meas.algorithms.testUtils import plantSources
14class DynamicDetectionTest(lsst.utils.tests.TestCase):
15 def setUp(self):
16 xy0 = Point2I(12345, 67890) # xy0 for image
17 dims = Extent2I(2345, 2345) # Dimensions of image
18 box = Box2I(xy0, dims) # Bounding box of image
19 sigma = 3.21 # PSF sigma
20 buffer = 4.0 # Buffer for star centers around edge
21 nSigmaForKernel = 5.0 # Number of PSF sigmas for kernel
22 sky = 12345.6 # Sky level
23 numStars = 100 # Number of stars
24 noise = np.sqrt(sky)*np.pi*sigma**2 # Poisson noise per PSF
25 faint = 1.0*noise # Faintest level for star fluxes
26 bright = 100.0*noise # Brightest level for star fluxes
27 starBox = Box2I(box) # Area on image in which we can put star centers
28 starBox.grow(-int(buffer*sigma))
29 scale = 1.0e-5*degrees # Pixel scale
31 np.random.seed(12345)
32 stars = [(xx, yy, ff, sigma) for xx, yy, ff in
33 zip(np.random.uniform(starBox.getMinX(), starBox.getMaxX(), numStars),
34 np.random.uniform(starBox.getMinY(), starBox.getMaxY(), numStars),
35 np.linspace(faint, bright, numStars))]
36 self.exposure = plantSources(box, 2*int(nSigmaForKernel*sigma) + 1, sky, stars, True)
37 self.exposure.setWcs(makeSkyWcs(crpix=Point2D(0, 0),
38 crval=SpherePoint(0, 0, degrees),
39 cdMatrix=makeCdMatrix(scale=scale)))
41 # Make a large area of extra background; we should be robust against it
42 # Unfortunately, some tuning is required here to get something challenging but not impossible:
43 # * A very large box will cause failures because the "extra" and the "normal" are reversed.
44 # * A small box will not be challenging because it's simple to clip out.
45 # * A large value will cause failures because it produces large edges in background-subtrction that
46 # broaden flux distributions.
47 # * A small value will not be challenging because it has little effect.
48 extraBox = Box2I(xy0 + Extent2I(345, 456), Extent2I(1234, 1234)) # Box for extra background
49 extraValue = 0.5*noise # Extra background value to add in
50 self.exposure.image[extraBox, PARENT] += extraValue
52 self.config = DynamicDetectionTask.ConfigClass()
53 self.config.skyObjects.nSources = 300
54 self.config.reEstimateBackground = False
55 self.config.doTempWideBackground = True
56 self.config.thresholdType = "pixel_stdev"
58 # Relative tolerance for tweak factor
59 # Not sure why this isn't smaller; maybe due to use of Poisson instead of Gaussian noise?
60 # It seems as if some sky objects are being placed in the extra
61 # background region, which is causing the offset between the expected
62 # factor and the measured factor to be larger than otherwise expected.
63 # This relative tolerance was increased from 0.1 to 0.15 on DM-23781 to
64 # account for this.
65 self.rtol = 0.15
67 def tearDown(self):
68 del self.exposure
70 def check(self, expectFactor):
71 schema = SourceTable.makeMinimalSchema()
72 task = DynamicDetectionTask(config=self.config, schema=schema)
73 table = SourceTable.make(schema)
75 results = task.run(table, self.exposure, expId=12345)
76 self.assertFloatsAlmostEqual(results.factor, expectFactor, rtol=self.rtol)
78 def testVanilla(self):
79 """Dynamic detection used as normal detection"""
80 self.check(1.0)
82 def testDynamic(self):
83 """Modify the variance plane, and see if the task is able to determine what we did"""
84 factor = 2.0
85 self.exposure.maskedImage.variance /= factor
86 self.check(1.0/np.sqrt(factor))
88 def testNoWcs(self):
89 """Check that dynamic detection runs when the exposure wcs is None"""
90 self.exposure.setWcs(None)
91 self.check(1.0)
93 def testNoSources(self):
94 self.config.skyObjects.nSources = self.config.minNumSources - 1
95 self.check(1.0)
98class TestMemory(lsst.utils.tests.MemoryTestCase):
99 pass
102def setup_module(module):
103 lsst.utils.tests.init()
106if __name__ == "__main__": 106 ↛ 107line 106 didn't jump to line 107, because the condition on line 106 was never true
107 import sys
108 setup_module(sys.modules['__main__'])
109 unittest.main()