Coverage for tests/test_Variance.py: 19%
129 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-16 10:43 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-16 10:43 +0000
1# This file is part of meas_base.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22import unittest
24import numpy as np
26import lsst.geom
27import lsst.afw.geom as afwGeom
28import lsst.afw.table as afwTable
29import lsst.afw.image as afwImage
30import lsst.afw.detection as afwDetection
31import lsst.meas.base as measBase
32import lsst.utils.tests
33import testLib # noqa: F401 need this for SillyCentroid
35try:
36 display
37except NameError:
38 display = False
41class VarianceTest(lsst.utils.tests.TestCase):
43 def setUp(self):
44 size = 128 # size of image (pixels)
45 center = lsst.geom.Point2D(size//2, size//2) # object center
46 width = 2 # PSF width
47 flux = 10.0 # Flux of object
48 variance = 1.0 # Mean variance value
49 varianceStd = 0.1 # Standard deviation of the variance value
51 # Set a seed for predictable randomness
52 np.random.seed(300)
54 # Create a random image to be used as variance plane
55 variancePlane = np.random.normal(variance, varianceStd, size*size).reshape(size, size)
57 # Initial setup of an image
58 exp = afwImage.ExposureF(size, size)
59 image = exp.image
60 mask = exp.mask
61 var = exp.variance
62 image.set(0.0)
63 mask.set(0)
64 var.array[:, :] = variancePlane
66 # Put down a PSF
67 psfSize = int(6*width + 1) # Size of PSF image; must be odd
68 psf = afwDetection.GaussianPsf(psfSize, psfSize, width)
69 exp.setPsf(psf)
70 psfImage = psf.computeImage(center).convertF()
71 psfImage *= flux
72 image.Factory(image, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage)
73 var.Factory(var, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage)
75 # Put in some bad pixels to ensure they're ignored
76 for i in range(-5, 6):
77 bad = size//2 + i*width
78 var.array[bad, :] = float("nan")
79 mask.array[bad, :] = mask.getPlaneBitMask("BAD")
80 var.array[:, bad] = float("nan")
81 mask.array[:, bad] = mask.getPlaneBitMask("BAD")
83 # Put in some unmasked bad pixels outside the expected aperture, to
84 # ensure the aperture is working
85 var.array[0, 0] = float("nan")
86 var.array[0, -1] = float("nan")
87 var.array[-1, 0] = float("nan")
88 var.array[-1, -1] = float("nan")
90 if display:
91 import lsst.afw.display as afwDisplay
92 afwDisplay.getDisplay(1).mtv(image)
93 afwDisplay.getDisplay(2).mtv(mask)
94 afwDisplay.getDisplay(3).mtv(var)
96 config = measBase.SingleFrameMeasurementConfig()
97 config.plugins.names = ["testLib_SillyCentroid", "base_SdssShape", "base_Variance"]
98 config.slots.centroid = "testLib_SillyCentroid"
99 config.slots.psfFlux = None
100 config.slots.apFlux = None
101 config.slots.modelFlux = None
102 config.slots.gaussianFlux = None
103 config.slots.calibFlux = None
104 config.slots.shape = "base_SdssShape"
105 config.slots.psfShape = None
106 config.plugins["base_Variance"].mask = ["BAD", "SAT"]
108 config.validate()
109 schema = afwTable.SourceTable.makeMinimalSchema()
111 task = measBase.SingleFrameMeasurementTask(schema, config=config)
112 catalog = afwTable.SourceCatalog(schema)
114 spans = afwGeom.SpanSet.fromShape(int(width))
115 spans = spans.shiftedBy(int(center.getX()), int(center.getY()))
116 foot = afwDetection.Footprint(spans)
117 peak = foot.getPeaks().addNew()
118 peak.setIx(int(center.getX()))
119 peak.setIy(int(center.getY()))
120 peak.setFx(center.getX())
121 peak.setFy(center.getY())
122 peak.setPeakValue(flux)
124 source = catalog.addNew()
125 source.setFootprint(foot)
127 self.variance = variance
128 self.varianceStd = varianceStd
129 self.mask = mask
130 self.catalog = catalog
131 self.exp = exp
132 self.task = task
133 self.source = source
135 def tearDown(self):
136 del self.mask
137 del self.catalog
138 del self.exp
139 del self.task
140 del self.source
142 def testVariance(self):
143 self.task.run(self.catalog, self.exp)
145 self.assertLess(np.abs(self.source.get("base_Variance_value") - self.variance), self.varianceStd)
147 # flag_emptyFootprint should not have been set since the footprint has
148 # non-masked pixels at this point.
149 self.assertFalse(self.source.get("base_Variance_flag_emptyFootprint"))
151 def testEmptyFootprint(self):
152 # Set the pixel mask for all pixels to ``BAD`` and remeasure.
153 self.mask.array[:, :] = self.mask.getPlaneBitMask("BAD")
154 self.task.run(self.catalog, self.exp)
156 # The computed variance should be NaN and flag_emptyFootprint should
157 # have been set since the footprint has all masked pixels at this
158 # point.
159 self.assertTrue(np.isnan(self.source.get("base_Variance_value")))
160 self.assertTrue(self.source.get("base_Variance_flag_emptyFootprint"))
163class BadCentroidTest(lsst.utils.tests.TestCase):
165 def testBadCentroid(self):
166 """Test propagation of flags to ``badCentroid``.
168 If the centroid is flagged as bad, the ``badCentroid`` flag should be
169 set on the variance measurement.
170 """
171 schema = afwTable.SourceTable.makeMinimalSchema()
172 measBase.SingleFramePeakCentroidPlugin(measBase.SingleFramePeakCentroidConfig(),
173 "centroid", schema, None)
174 schema.getAliasMap().set("slot_Centroid", "centroid")
175 variance = measBase.SingleFrameVariancePlugin(measBase.VarianceConfig(),
176 "variance", schema, None)
177 catalog = afwTable.SourceCatalog(schema)
179 # The centroid is not flagged as bad, but there's no way the algorithm
180 # can run without valid data in the SourceRecord and Exposure: this
181 # should throw a logic error.
182 record = catalog.addNew()
183 record.set("centroid_flag", False)
184 with self.assertRaises(measBase.MeasurementError) as measErr:
185 variance.measure(record, None)
186 variance.fail(record, measErr.exception)
187 self.assertTrue(record.get("variance_flag"))
188 self.assertFalse(record.get("variance_flag_badCentroid"))
190 # The centroid is flagged as bad, so we should get a MeasurementError
191 # indicating an expected failure.
192 record = catalog.addNew()
193 record.set("centroid_flag", True)
194 with self.assertRaises(measBase.MeasurementError) as measErr:
195 variance.measure(record, None)
196 variance.fail(record, measErr.exception)
197 self.assertTrue(record.get("variance_flag"))
198 self.assertTrue(record.get("variance_flag_badCentroid"))
201class TestMemory(lsst.utils.tests.MemoryTestCase):
202 pass
205def setup_module(module):
206 lsst.utils.tests.init()
209if __name__ == "__main__": 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true
210 lsst.utils.tests.init()
211 unittest.main()