Coverage for tests/test_Variance.py: 22%
128 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-23 02:44 -0700
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-23 02:44 -0700
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
34try:
35 display
36except NameError:
37 display = False
40class VarianceTest(lsst.utils.tests.TestCase):
42 def setUp(self):
43 size = 128 # size of image (pixels)
44 center = lsst.geom.Point2D(size//2, size//2) # object center
45 width = 2 # PSF width
46 flux = 10.0 # Flux of object
47 variance = 1.0 # Mean variance value
48 varianceStd = 0.1 # Standard deviation of the variance value
50 # Set a seed for predictable randomness
51 np.random.seed(300)
53 # Create a random image to be used as variance plane
54 variancePlane = np.random.normal(variance, varianceStd, size*size).reshape(size, size)
56 # Initial setup of an image
57 exp = afwImage.ExposureF(size, size)
58 image = exp.getMaskedImage().getImage()
59 mask = exp.getMaskedImage().getMask()
60 var = exp.getMaskedImage().getVariance()
61 image.set(0.0)
62 mask.set(0)
63 var.getArray()[:, :] = variancePlane
65 # Put down a PSF
66 psfSize = int(6*width + 1) # Size of PSF image; must be odd
67 psf = afwDetection.GaussianPsf(psfSize, psfSize, width)
68 exp.setPsf(psf)
69 psfImage = psf.computeImage(center).convertF()
70 psfImage *= flux
71 image.Factory(image, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage)
72 var.Factory(var, psfImage.getBBox(afwImage.PARENT)).__iadd__(psfImage)
74 # Put in some bad pixels to ensure they're ignored
75 for i in range(-5, 6):
76 bad = size//2 + i*width
77 var.getArray()[bad, :] = float("nan")
78 mask.getArray()[bad, :] = mask.getPlaneBitMask("BAD")
79 var.getArray()[:, bad] = float("nan")
80 mask.getArray()[:, bad] = mask.getPlaneBitMask("BAD")
82 # Put in some unmasked bad pixels outside the expected aperture, to
83 # ensure the aperture is working
84 var.getArray()[0, 0] = float("nan")
85 var.getArray()[0, -1] = float("nan")
86 var.getArray()[-1, 0] = float("nan")
87 var.getArray()[-1, -1] = float("nan")
89 if display:
90 import lsst.afw.display as afwDisplay
91 afwDisplay.getDisplay(1).mtv(image)
92 afwDisplay.getDisplay(2).mtv(mask)
93 afwDisplay.getDisplay(3).mtv(var)
95 config = measBase.SingleFrameMeasurementConfig()
96 config.plugins.names = ["base_NaiveCentroid", "base_SdssShape", "base_Variance"]
97 config.slots.centroid = "base_NaiveCentroid"
98 config.slots.psfFlux = None
99 config.slots.apFlux = None
100 config.slots.modelFlux = None
101 config.slots.gaussianFlux = None
102 config.slots.calibFlux = None
103 config.slots.shape = "base_SdssShape"
104 config.slots.psfShape = None
105 config.plugins["base_Variance"].mask = ["BAD", "SAT"]
107 config.validate()
108 schema = afwTable.SourceTable.makeMinimalSchema()
110 task = measBase.SingleFrameMeasurementTask(schema, config=config)
111 catalog = afwTable.SourceCatalog(schema)
113 spans = afwGeom.SpanSet.fromShape(int(width))
114 spans = spans.shiftedBy(int(center.getX()), int(center.getY()))
115 foot = afwDetection.Footprint(spans)
116 peak = foot.getPeaks().addNew()
117 peak.setIx(int(center.getX()))
118 peak.setIy(int(center.getY()))
119 peak.setFx(center.getX())
120 peak.setFy(center.getY())
121 peak.setPeakValue(flux)
123 source = catalog.addNew()
124 source.setFootprint(foot)
126 self.variance = variance
127 self.varianceStd = varianceStd
128 self.mask = mask
129 self.catalog = catalog
130 self.exp = exp
131 self.task = task
132 self.source = source
134 def tearDown(self):
135 del self.mask
136 del self.catalog
137 del self.exp
138 del self.task
139 del self.source
141 def testVariance(self):
142 self.task.run(self.catalog, self.exp)
144 self.assertLess(np.abs(self.source.get("base_Variance_value") - self.variance), self.varianceStd)
146 # flag_emptyFootprint should not have been set since the footprint has
147 # non-masked pixels at this point.
148 self.assertFalse(self.source.get("base_Variance_flag_emptyFootprint"))
150 def testEmptyFootprint(self):
151 # Set the pixel mask for all pixels to ``BAD`` and remeasure.
152 self.mask.getArray()[:, :] = self.mask.getPlaneBitMask("BAD")
153 self.task.run(self.catalog, self.exp)
155 # The computed variance should be NaN and flag_emptyFootprint should
156 # have been set since the footprint has all masked pixels at this
157 # point.
158 self.assertTrue(np.isnan(self.source.get("base_Variance_value")))
159 self.assertTrue(self.source.get("base_Variance_flag_emptyFootprint"))
162class BadCentroidTest(lsst.utils.tests.TestCase):
164 def testBadCentroid(self):
165 """Test propagation of flags to ``badCentroid``.
167 If the centroid is flagged as bad, the ``badCentroid`` flag should be
168 set on the variance measurement.
169 """
170 schema = afwTable.SourceTable.makeMinimalSchema()
171 measBase.SingleFramePeakCentroidPlugin(measBase.SingleFramePeakCentroidConfig(),
172 "centroid", schema, None)
173 schema.getAliasMap().set("slot_Centroid", "centroid")
174 variance = measBase.SingleFrameVariancePlugin(measBase.VarianceConfig(),
175 "variance", schema, None)
176 catalog = afwTable.SourceCatalog(schema)
178 # The centroid is not flagged as bad, but there's no way the algorithm
179 # can run without valid data in the SourceRecord and Exposure: this
180 # should throw a logic error.
181 record = catalog.addNew()
182 record.set("centroid_flag", False)
183 with self.assertRaises(measBase.MeasurementError) as measErr:
184 variance.measure(record, None)
185 variance.fail(record, measErr.exception)
186 self.assertTrue(record.get("variance_flag"))
187 self.assertFalse(record.get("variance_flag_badCentroid"))
189 # The centroid is flagged as bad, so we should get a MeasurementError
190 # indicating an expected failure.
191 record = catalog.addNew()
192 record.set("centroid_flag", True)
193 with self.assertRaises(measBase.MeasurementError) as measErr:
194 variance.measure(record, None)
195 variance.fail(record, measErr.exception)
196 self.assertTrue(record.get("variance_flag"))
197 self.assertTrue(record.get("variance_flag_badCentroid"))
200class TestMemory(lsst.utils.tests.MemoryTestCase):
201 pass
204def setup_module(module):
205 lsst.utils.tests.init()
208if __name__ == "__main__": 208 ↛ 209line 208 didn't jump to line 209, because the condition on line 208 was never true
209 lsst.utils.tests.init()
210 unittest.main()