Coverage for tests/test_statisticsMasked.py: 20%
93 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 03:31 -0700
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 03:31 -0700
1# This file is part of afw.
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/>.
22"""
23Tests for statisticsMasked
25Run with:
26 python test_statisticsMasked.py
27or
28 pytest test_statisticsMasked.py
29"""
31import unittest
33import numpy as np
35import lsst.utils.tests
36import lsst.geom
37import lsst.afw.image as afwImage
38import lsst.afw.math as afwMath
41class StatisticsTestCase(unittest.TestCase):
43 """A test case to check that special values (NaN and Masks) are begin handled in Statistics"""
45 def setUp(self):
46 self.valL, self.valR = 10, 20
47 self.nRow, self.nCol = 100, 200
48 self.n = self.nRow*self.nCol
50 self.bboxL = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
51 lsst.geom.Point2I(self.nRow//2 - 1, self.nCol - 1))
52 self.bboxR = lsst.geom.Box2I(lsst.geom.Point2I(self.nRow//2, 0),
53 lsst.geom.Point2I(self.nRow - 1, self.nCol - 1))
55 # create masked images and set the left side to valL, and right to valR
56 self.mimg = afwImage.MaskedImageF(
57 lsst.geom.Extent2I(self.nRow, self.nCol))
58 self.mimg.set(0.0, 0x0, 0.0)
59 self.mimgL = afwImage.MaskedImageF(
60 self.mimg, self.bboxL, afwImage.LOCAL)
61 self.mimgL.set(self.valL, 0x0, self.valL)
62 self.mimgR = afwImage.MaskedImageF(
63 self.mimg, self.bboxR, afwImage.LOCAL)
64 self.mimgR.set(self.valR, 0x0, self.valR)
66 def tearDown(self):
67 del self.mimg
68 del self.mimgL
69 del self.mimgR
71 # Verify that NaN values are being ignored
72 # (by default, StatisticsControl.useNanSafe = True)
73 # We'll set the L and R sides of an image to two different values and verify mean and stdev
74 # ... then set R-side to NaN and try again ... we should get mean,stdev for L-side
75 def testNaN(self):
77 # get the stats for the image with two values
78 stats = afwMath.makeStatistics(
79 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV)
80 mean = 0.5*(self.valL + self.valR)
81 nL, nR = (self.mimgL.getWidth()*self.mimgL.getHeight(),
82 self.mimgR.getWidth()*self.mimgR.getHeight())
83 stdev = ((nL*(self.valL - mean)**2 + nR*(self.valR - mean)**2)/(nL + nR - 1))**0.5
85 self.assertEqual(stats.getValue(afwMath.NPOINT), self.n)
86 self.assertEqual(stats.getValue(afwMath.MEAN), mean)
87 self.assertEqual(stats.getValue(afwMath.STDEV), stdev)
89 # set the right side to NaN and stats should be just for the left side
90 self.mimgR.set(np.nan, 0x0, self.valR)
92 statsNaN = afwMath.makeStatistics(
93 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV)
94 mean = self.valL
95 stdev = 0.0
96 self.assertEqual(statsNaN.getValue(afwMath.NPOINT), nL)
97 self.assertEqual(statsNaN.getValue(afwMath.MEAN), mean)
98 self.assertEqual(statsNaN.getValue(afwMath.STDEV), stdev)
100 # Verify that Masked pixels are being ignored according to the andMask
101 # (by default, StatisticsControl.andMask = 0x0)
102 # We'll set the L and R sides of an image to two different values and verify mean and stdev
103 # ... then set R-side Mask and the andMask to 0x1 and try again ... we should get mean,stdev for L-side
104 def testMasked(self):
106 # get the stats for the image with two values
107 self.mimgR.set(self.valR, 0x0, self.valR)
108 stats = afwMath.makeStatistics(
109 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV)
110 mean = 0.5*(self.valL + self.valR)
111 nL, nR = (self.mimgL.getWidth()*self.mimgL.getHeight(),
112 self.mimgR.getWidth()*self.mimgR.getHeight())
113 stdev = ((nL*(self.valL - mean)**2 + nR*(self.valR - mean)**2)/(nL + nR - 1))**0.5
115 self.assertEqual(stats.getValue(afwMath.NPOINT), self.n)
116 self.assertEqual(stats.getValue(afwMath.MEAN), mean)
117 self.assertEqual(stats.getValue(afwMath.STDEV), stdev)
119 # set the right side Mask and the StatisticsControl andMask to 0x1
120 # Stats should be just for the left side!
121 maskBit = 0x1
122 self.mimgR.getMask().set(maskBit)
124 sctrl = afwMath.StatisticsControl()
125 sctrl.setAndMask(maskBit)
126 statsNaN = afwMath.makeStatistics(
127 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV, sctrl)
129 mean = self.valL
130 stdev = 0.0
132 self.assertEqual(statsNaN.getValue(afwMath.NPOINT), nL)
133 self.assertEqual(statsNaN.getValue(afwMath.MEAN), mean)
134 self.assertEqual(statsNaN.getValue(afwMath.STDEV), stdev)
136 # Verify that pixels are being weighted according to the variance plane (1/var)
137 # We'll set the L and R sides of an image to two different values and verify mean
138 # ... then set R-side Variance to equal the Image value, and set 'weighted' and try again ...
139 def testWeighted(self):
141 self.mimgR.set(self.valR, 0x0, self.valR)
142 sctrl = afwMath.StatisticsControl()
143 sctrl.setWeighted(True)
144 stats = afwMath.makeStatistics(
145 self.mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV, sctrl)
146 nL, nR = self.mimgL.getWidth()*self.mimgL.getHeight(), self.mimgR.getWidth() * \
147 self.mimgR.getHeight()
149 mean = 1.0*(nL + nR)/(nL/self.valL + nR/self.valR)
151 # get the stats for the image with two values
152 self.assertEqual(stats.getValue(afwMath.NPOINT), self.n)
153 self.assertAlmostEqual(stats.getValue(afwMath.MEAN), mean, 10)
155 def testWeightedSimple(self):
156 mimg = afwImage.MaskedImageF(lsst.geom.Extent2I(1, 2))
157 mimg[0, 0, afwImage.LOCAL] = (self.valR, 0x0, self.valR)
158 mimg[0, 1, afwImage.LOCAL] = (self.valL, 0x0, self.valL)
160 sctrl = afwMath.StatisticsControl()
161 sctrl.setWeighted(True)
162 stats = afwMath.makeStatistics(
163 mimg, afwMath.NPOINT | afwMath.MEAN | afwMath.STDEV, sctrl)
164 vsum = 2.0
165 vsum2 = self.valR + self.valL
166 wsum = 1.0/self.valR + 1.0/self.valL
167 wwsum = 1.0/self.valR**2 + 1.0/self.valL**2
168 mean = vsum/wsum
169 variance = vsum2/wsum - mean**2 # biased variance
171 n = 2
172 # original estimate; just a rewrite of the usual n/(n - 1) correction
173 stddev = (1.0*(vsum2)/(wsum*(1.0-1.0/n)) - (vsum**2)/(wsum**2*(1.0-1.0/n)))**0.5
174 self.assertAlmostEqual(stddev, np.sqrt(variance*n/(n - 1)))
175 #
176 # The correct formula:
177 stddev = np.sqrt(variance*wsum**2/(wsum**2 - wwsum))
179 # get the stats for the image with two values
180 self.assertAlmostEqual(stats.getValue(afwMath.MEAN), mean, 10)
181 self.assertAlmostEqual(stats.getValue(afwMath.STDEV), stddev, 10)
184class TestMemory(lsst.utils.tests.MemoryTestCase):
185 pass
188def setup_module(module):
189 lsst.utils.tests.init()
192if __name__ == "__main__": 192 ↛ 193line 192 didn't jump to line 193, because the condition on line 192 was never true
193 lsst.utils.tests.init()
194 unittest.main()