Coverage for tests/test_negative.py: 20%
108 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-23 02:49 -0700
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-23 02:49 -0700
1# This file is part of meas_algorithms.
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 lsst.geom
25import lsst.afw.math as afwMath
26import lsst.afw.table as afwTable
27import lsst.daf.base as dafBase
28from lsst.meas.algorithms import SourceDetectionTask
29from lsst.meas.base import SingleFrameMeasurementTask as SourceMeasurementTask
30from lsst.meas.algorithms.testUtils import plantSources
31import lsst.utils.tests
33try:
34 display
35except NameError:
36 display = False
37else:
38 import lsst.afw.display as afwDisplay
39 afwDisplay.setDefaultMaskTransparency(75)
42class NegativeMeasurementTestCase(lsst.utils.tests.TestCase):
43 """Testing detection and measurement on negative objects.
44 """
46 def _create_exposure(self):
47 bbox = lsst.geom.Box2I(lsst.geom.Point2I(256, 100), lsst.geom.Extent2I(128, 127))
48 minCounts = 2000
49 maxCounts = 20000
50 starSigma = 1.5
51 numX = 4
52 numY = 4
53 coordList = self.makeCoordList(
54 bbox=bbox,
55 numX=numX,
56 numY=numY,
57 minCounts=minCounts,
58 maxCounts=maxCounts,
59 sigma=starSigma,
60 )
61 kwid = 11
62 sky = 2000
63 addPoissonNoise = True
64 exposure = plantSources(bbox=bbox, kwid=kwid, sky=sky, coordList=coordList,
65 addPoissonNoise=addPoissonNoise)
66 return exposure, numX, numY
68 def test_detection_stdev(self):
69 """Test detection and measurement on an exposure with negative sources
70 for thresholdType="stdev".
71 """
72 exposure, numX, numY = self._create_exposure()
74 if display:
75 disp = afwDisplay.Display(frame=1)
76 disp.mtv(exposure, title=self._testMethodName + ": image with -ve sources")
78 schema = afwTable.SourceTable.makeMinimalSchema()
79 config = SourceDetectionTask.ConfigClass()
80 config.reEstimateBackground = False
81 config.thresholdPolarity = 'both'
82 detection = SourceDetectionTask(config=config, schema=schema)
83 algMetadata = dafBase.PropertyList()
84 measurement = SourceMeasurementTask(schema=schema, algMetadata=algMetadata)
86 table = afwTable.SourceTable.make(schema)
87 detections = detection.run(table, exposure)
88 sources = detections.sources
89 fpSets = detections.fpSets
91 self.assertEqual(len(sources), numX*numY)
92 self.assertEqual(fpSets.numPos, numX*numY/2)
93 self.assertEqual(fpSets.numNeg, numX*numY/2)
95 measurement.run(sources, exposure)
97 nGoodCent = 0
98 nGoodShape = 0
99 for s in sources:
100 cent = s.getCentroid()
101 shape = s.getShape()
103 if cent[0] == cent[0] and cent[1] == cent[1]:
104 nGoodCent += 1
106 if (shape.getIxx() == shape.getIxx()
107 and shape.getIyy() == shape.getIyy()
108 and shape.getIxy() == shape.getIxy()):
109 nGoodShape += 1
111 if display:
112 xy = cent[0], cent[1]
113 disp.dot('+', *xy)
114 disp.dot(shape, *xy, ctype=afwDisplay.RED)
116 self.assertEqual(nGoodCent, numX*numY)
117 self.assertEqual(nGoodShape, numX*numY)
119 def test_significance(self):
120 """Test that negative peaks have the right significance for
121 thresholdType='stdev' for the non-convolved, non-local-background case.
122 """
123 exposure, numX, numY = self._create_exposure()
125 schema = afwTable.SourceTable.makeMinimalSchema()
126 config = SourceDetectionTask.ConfigClass()
127 config.thresholdPolarity = 'both'
128 # don't modify the image after detection.
129 config.reEstimateBackground = False
130 config.doTempLocalBackground = False
131 detection = SourceDetectionTask(config=config, schema=schema)
132 result = detection.detectFootprints(exposure, doSmooth=False)
134 bad = exposure.mask.getPlaneBitMask(config.statsMask)
135 sctrl = afwMath.StatisticsControl()
136 sctrl.setAndMask(bad)
137 stats = afwMath.makeStatistics(exposure.maskedImage, afwMath.STDEVCLIP, sctrl)
138 stddev = stats.getValue(afwMath.STDEVCLIP)
139 # Don't bother checking positive footprints: those are tested more
140 # thoroughly in test_detection.py.
141 for footprint in result.negative.getFootprints():
142 for peak in footprint.peaks:
143 point = lsst.geom.Point2I(peak.getIx(), peak.getIy())
144 value = exposure.image[point]
145 with self.subTest(str(point)):
146 self.assertFloatsAlmostEqual(peak["significance"],
147 -value/stddev, # S/N for negative peak
148 rtol=1e-7,
149 msg=str(point))
151 def makeCoordList(self, bbox, numX, numY, minCounts, maxCounts, sigma):
152 """Make a coordList for makeExposure.
153 """
154 dX = bbox.getWidth()/float(numX)
155 dY = bbox.getHeight()/float(numY)
156 minX = bbox.getMinX() + (dX/2.0)
157 minY = bbox.getMinY() + (dY/2.0)
158 dCounts = (maxCounts - minCounts)/(numX*numY/2 - 1)
160 coordList = []
161 counts = minCounts
162 for i in range(numX):
163 x = minX + (dX*i)
164 for j in range(numY):
165 y = minY + (dY*j)
166 if j%2 == 0:
167 coordList.append([x, y, counts, sigma])
168 else:
169 coordList.append([x, y, -counts, sigma])
170 counts += dCounts
171 return coordList
174class TestMemory(lsst.utils.tests.MemoryTestCase):
175 pass
178def setup_module(module):
179 lsst.utils.tests.init()
182if __name__ == "__main__": 182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true
183 lsst.utils.tests.init()
184 unittest.main()