Coverage for tests/test_statistics.py: 14%
362 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-15 02:47 -0700
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-15 02:47 -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 Statistics
25Run with:
26 python test_statistics.py
27or
28 pytest test_statistics.py
29"""
31import math
32import os
33import unittest
35import numpy as np
37import lsst.utils.tests
38import lsst.pex.exceptions
39import lsst.geom
40import lsst.afw.image as afwImage
41import lsst.afw.math as afwMath
42import lsst.afw.display as afwDisplay
44afwDisplay.setDefaultMaskTransparency(75)
46try:
47 afwdataDir = lsst.utils.getPackageDir("afwdata")
48except LookupError:
49 afwdataDir = None
51try:
52 type(display)
53except NameError:
54 display = False
57class StatisticsTestCase(lsst.utils.tests.TestCase):
58 """A test case for Statistics"""
60 clippedVariance3 = 0.9733369 # variance of an N(0, 1) Gaussian clipped at 3 sigma
62 def setUp(self):
63 w, h = 900, 1500
65 mean = 10.5 # requested mean
66 std = 1.0 # and standard deviation
68 self.images = []
69 # ImageI
70 np.random.seed(666)
71 isInt = True
72 image = afwImage.ImageI(lsst.geom.ExtentI(w, h))
73 image.array[:] = np.floor(np.random.normal(mean, std, (h, w)) + 0.5).astype(int)
75 # Note that the mean/median/std may not be quite equal to the requested values
76 self.images.append((image, isInt, np.mean(image.array), np.mean(image.array), np.std(image.array)))
78 # ImageF
79 np.random.seed(666)
80 isInt = False
81 image = afwImage.ImageF(lsst.geom.ExtentI(w, h))
82 image.array[:] = np.random.normal(mean, std, (h, w))
84 # Note that the mean/median/std may not be quite equal to the requested values
85 self.images.append((image, isInt, np.mean(image.array), np.median(image.array), np.std(image.array)))
87 @staticmethod
88 def delta(what, isInt):
89 # Return a tolerance for a test
90 if what == "mean":
91 return 4e-6
92 elif what == "meanclip":
93 return 4e-5
94 elif what == "median":
95 return 0.00022 if isInt else 0.00000075
97 def tearDown(self):
98 del self.images
100 def testDefaultGet(self):
101 """Test that we can get a single statistic without specifying it"""
102 for image, isInt, mean, median, std in self.images:
103 stats = afwMath.makeStatistics(image, afwMath.MEDIAN)
105 self.assertEqual(stats.getValue(), stats.getValue(afwMath.MEDIAN))
106 self.assertEqual(stats.getResult()[0], stats.getResult(afwMath.MEDIAN)[0])
107 #
108 stats = afwMath.makeStatistics(image, afwMath.MEDIAN | afwMath.ERRORS)
110 self.assertEqual(stats.getValue(), stats.getValue(afwMath.MEDIAN))
111 self.assertEqual(stats.getResult(), stats.getResult(afwMath.MEDIAN))
112 self.assertEqual(stats.getError(), stats.getError(afwMath.MEDIAN))
114 def tst():
115 stats.getValue()
116 stats = afwMath.makeStatistics(image, afwMath.MEDIAN | afwMath.MEAN)
117 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, tst)
119 def testStats1(self):
120 for image, isInt, mean, median, std in self.images:
121 stats = afwMath.makeStatistics(image, afwMath.NPOINT | afwMath.STDEV | afwMath.MEAN | afwMath.SUM)
123 self.assertEqual(stats.getValue(afwMath.NPOINT), image.getWidth()*image.getHeight())
124 self.assertEqual(stats.getValue(afwMath.NPOINT)*stats.getValue(afwMath.MEAN),
125 stats.getValue(afwMath.SUM))
127 self.assertAlmostEqual(stats.getValue(afwMath.MEAN), mean, delta=self.delta("mean", isInt))
128 # didn't ask for error, so it's a NaN
129 self.assertTrue(np.isnan(stats.getError(afwMath.MEAN)))
130 self.assertAlmostEqual(stats.getValue(afwMath.STDEV), std, delta=0.000008)
132 def testStats2(self):
133 for image, isInt, mean, median, std in self.images:
134 stats = afwMath.makeStatistics(image, afwMath.STDEV | afwMath.MEAN | afwMath.ERRORS)
135 meanRes = stats.getResult(afwMath.MEAN)
136 sd = stats.getValue(afwMath.STDEV)
138 self.assertAlmostEqual(meanRes[0], mean, delta=self.delta("mean", isInt))
139 self.assertAlmostEqual(meanRes[1], sd/math.sqrt(image.getWidth()*image.getHeight()))
141 def testStats3(self):
142 for image, isInt, mean, median, std in self.images:
143 stats = afwMath.makeStatistics(image, afwMath.NPOINT)
145 def getMean():
146 stats.getValue(afwMath.MEAN)
148 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, getMean)
150 def testStatsZebra(self):
151 """Add 1 to every other row"""
152 for image, isInt, mean, median, std in self.images:
153 image2 = image.clone()
154 #
155 # Add 1 to every other row, so the variance is increased by 1/4
156 #
157 self.assertEqual(image2.getHeight() % 2, 0)
158 width = image2.getWidth()
159 for y in range(1, image2.getHeight(), 2):
160 sim = image2[lsst.geom.Box2I(lsst.geom.Point2I(0, y), lsst.geom.Extent2I(width, 1))]
161 sim += 1
163 if display:
164 afwDisplay.Display(frame=0).mtv(image, "Image 1")
165 afwDisplay.Display(frame=1).mtv(image2, "Image 2 (var inc by 1/4)")
167 stats = afwMath.makeStatistics(image2,
168 afwMath.NPOINT | afwMath.STDEV | afwMath.MEAN | afwMath.ERRORS)
169 meanRes = stats.getResult(afwMath.MEAN)
170 n = stats.getValue(afwMath.NPOINT)
171 sd = stats.getValue(afwMath.STDEV)
173 self.assertAlmostEqual(meanRes[0], mean + 0.5, delta=self.delta("mean", isInt))
174 self.assertAlmostEqual(sd, np.hypot(std, 1/math.sqrt(4.0)*math.sqrt(n/(n - 1))),
175 delta=0.00011)
176 self.assertAlmostEqual(meanRes[1], sd/math.sqrt(image2.getWidth()*image2.getHeight()), 10)
178 meanSquare = afwMath.makeStatistics(image2, afwMath.MEANSQUARE).getValue()
179 self.assertAlmostEqual(meanSquare, 0.5*(mean**2 + (mean + 1)**2) + std**2,
180 delta=0.00025 if isInt else 0.00006)
182 def testStatsStdevclip(self):
183 """Test STDEVCLIP; cf. #611"""
184 for image, isInt, mean, median, std in self.images:
185 image2 = image.clone()
187 stats = afwMath.makeStatistics(image2, afwMath.STDEVCLIP | afwMath.NPOINT | afwMath.SUM)
188 self.assertAlmostEqual(stats.getValue(afwMath.STDEVCLIP), math.sqrt(self.clippedVariance3)*std,
189 delta=0.0015)
190 #
191 # Check we get the correct sum even when clipping
192 #
193 self.assertEqual(
194 stats.getValue(afwMath.NPOINT)*afwMath.makeStatistics(image2, afwMath.MEAN).getValue(),
195 stats.getValue(afwMath.SUM))
197 def testMedian(self):
198 """Test the median code"""
199 for image, isInt, mean, median, std in self.images:
200 med = afwMath.makeStatistics(image, afwMath.MEDIAN).getValue()
201 self.assertAlmostEqual(med, median, delta=self.delta("median", isInt))
203 values = [1.0, 2.0, 3.0, 2.0]
204 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 2.0)
206 def testIqrange(self):
207 """Test the inter-quartile range"""
208 for image, isInt, mean, median, std in self.images:
209 iqr = afwMath.makeStatistics(image, afwMath.IQRANGE).getValue()
210 # pretty loose constraint for isInt; probably because the distribution
211 # isn't very Gaussian with the added rounding to integer values
212 self.assertAlmostEqual(iqr, std/0.741301109252802, delta=0.063 if isInt else 0.00011)
214 def testMeanClip(self):
215 """Test the clipped mean"""
217 sctrl = afwMath.StatisticsControl()
218 sctrl.setNumSigmaClip(6)
220 for image, isInt, mean, median, std in self.images:
221 stats = afwMath.makeStatistics(image, afwMath.MEANCLIP | afwMath.NCLIPPED, sctrl)
222 self.assertAlmostEqual(stats.getValue(afwMath.MEANCLIP), mean, delta=self.delta("mean", isInt))
223 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 0)
225 def testVarianceClip(self):
226 """Test the 3-sigma clipped standard deviation and variance"""
227 for image, isInt, mean, median, std in self.images:
228 delta = 0.0006 if isInt else 0.0014
229 stdevClip = afwMath.makeStatistics(image, afwMath.STDEVCLIP).getValue()
230 self.assertAlmostEqual(stdevClip, math.sqrt(self.clippedVariance3)*std, delta=delta)
232 varianceClip = afwMath.makeStatistics(image, afwMath.VARIANCECLIP).getValue()
233 self.assertAlmostEqual(varianceClip, self.clippedVariance3*std**2, delta=2*delta)
235 def _testBadValue(self, badVal):
236 """Test that we can handle an instance of `badVal` in the data correctly
238 Note that we only test ImageF here (as ImageI can't contain a NaN)
239 """
240 mean = self.images[0][1]
241 x, y = 10, 10
242 for useImage in [True, False]:
243 if useImage:
244 image = afwImage.ImageF(100, 100)
245 image.set(mean)
246 image[x, y] = badVal
247 else:
248 image = afwImage.MaskedImageF(100, 100)
249 image.set(mean, 0x0, 1.0)
250 image[x, y] = (badVal, 0x0, 1.0)
252 self.assertEqual(afwMath.makeStatistics(image, afwMath.MAX).getValue(), mean)
253 self.assertEqual(afwMath.makeStatistics(image, afwMath.MEAN).getValue(), mean)
255 sctrl = afwMath.StatisticsControl()
257 sctrl.setNanSafe(False)
258 self.assertFalse(np.isfinite(afwMath.makeStatistics(image, afwMath.MAX, sctrl).getValue()))
259 self.assertFalse(np.isfinite(afwMath.makeStatistics(image, afwMath.MEAN, sctrl).getValue()))
261 def testMaxWithNan(self):
262 """Test that we can handle NaNs correctly"""
263 self._testBadValue(np.nan)
265 def testMaxWithInf(self):
266 """Test that we can handle infinities correctly"""
267 self._testBadValue(np.inf)
269 @unittest.skipIf(afwdataDir is None, "afwdata not setup")
270 def testSampleImageStats(self):
271 """ Compare our results to known values in test data """
273 imgfiles = []
274 imgfiles.append("v1_i1_g_m400_s20_f.fits")
275 imgfiles.append("v1_i1_g_m400_s20_u16.fits")
276 imgfiles.append("v1_i2_g_m400_s20_f.fits")
277 imgfiles.append("v1_i2_g_m400_s20_u16.fits")
278 imgfiles.append("v2_i1_p_m9_f.fits")
279 imgfiles.append("v2_i1_p_m9_u16.fits")
280 imgfiles.append("v2_i2_p_m9_f.fits")
281 imgfiles.append("v2_i2_p_m9_u16.fits")
283 afwdataDir = os.getenv("AFWDATA_DIR")
285 for imgfile in imgfiles:
287 imgPath = os.path.join(afwdataDir, "Statistics", imgfile)
289 # get the image and header
290 dimg = afwImage.DecoratedImageF(imgPath)
291 fitsHdr = dimg.getMetadata()
293 # get the true values of the mean and stdev
294 trueMean = fitsHdr.getAsDouble("MEANCOMP")
295 trueStdev = fitsHdr.getAsDouble("SIGCOMP")
297 # measure the mean and stdev with the Statistics class
298 img = dimg.getImage()
299 statobj = afwMath.makeStatistics(img, afwMath.MEAN | afwMath.STDEV)
300 mean = statobj.getValue(afwMath.MEAN)
301 stdev = statobj.getValue(afwMath.STDEV)
303 # print trueMean, mean, trueStdev, stdev
304 self.assertAlmostEqual(mean, trueMean, 8)
305 self.assertAlmostEqual(stdev, trueStdev, 8)
307 def testStatisticsRamp(self):
308 """ Tests Statistics on a 'ramp' (image with constant gradient) """
310 nx = 101
311 ny = 64
312 img = afwImage.ImageF(lsst.geom.Extent2I(nx, ny))
314 z0 = 10.0
315 dzdx = 1.0
316 mean = z0 + (nx//2)*dzdx
317 stdev = 0.0
318 for y in range(ny):
319 for x in range(nx):
320 z = z0 + dzdx*x
321 img[x, y] = z
322 stdev += (z - mean)*(z - mean)
324 stdev = math.sqrt(stdev/(nx*ny - 1))
326 stats = afwMath.makeStatistics(
327 img, afwMath.NPOINT | afwMath.STDEV | afwMath.MEAN)
328 testmean = stats.getValue(afwMath.MEAN)
329 teststdev = stats.getValue(afwMath.STDEV)
331 self.assertEqual(stats.getValue(afwMath.NPOINT), nx*ny)
332 self.assertEqual(testmean, mean)
333 self.assertAlmostEqual(teststdev, stdev)
335 stats = afwMath.makeStatistics(
336 img, afwMath.STDEV | afwMath.MEAN | afwMath.ERRORS)
337 mean, meanErr = stats.getResult(afwMath.MEAN)
338 sd = stats.getValue(afwMath.STDEV)
340 self.assertEqual(mean, img[nx//2, ny//2])
341 self.assertEqual(meanErr, sd/math.sqrt(img.getWidth()*img.getHeight()))
343 # ===============================================================================
344 # sjb code for percentiles and clipped stats
346 stats = afwMath.makeStatistics(img, afwMath.MEDIAN)
347 self.assertEqual(z0 + dzdx*(nx - 1)/2.0, stats.getValue(afwMath.MEDIAN))
349 stats = afwMath.makeStatistics(img, afwMath.IQRANGE)
350 self.assertEqual(dzdx*(nx - 1)/2.0, stats.getValue(afwMath.IQRANGE))
352 stats = afwMath.makeStatistics(img, afwMath.MEANCLIP)
353 self.assertEqual(z0 + dzdx*(nx - 1)/2.0, stats.getValue(afwMath.MEANCLIP))
355 def testMask(self):
356 mask = afwImage.Mask(lsst.geom.Extent2I(10, 10))
357 mask.set(0x0)
359 mask[1, 1] = 0x10
360 mask[3, 1] = 0x08
361 mask[5, 4] = 0x08
362 mask[4, 5] = 0x02
364 stats = afwMath.makeStatistics(mask, afwMath.SUM | afwMath.NPOINT)
365 self.assertEqual(mask.getWidth()*mask.getHeight(),
366 stats.getValue(afwMath.NPOINT))
367 self.assertEqual(0x1a, stats.getValue(afwMath.SUM))
369 def tst():
370 afwMath.makeStatistics(mask, afwMath.MEAN)
371 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, tst)
373 def testTicket1025(self):
374 """
375 Ticket #1025 reported that the Statistics median was getting '3' as the median of [1,2,3,2]
376 it was caused by an off-by-one error in the implementation
377 """
379 # check the exact example in the ticket
380 values = [1.0, 2.0, 3.0, 2.0]
381 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 2)
382 self.assertEqual(afwMath.makeStatistics(sorted(values), afwMath.MEDIAN).getValue(), 2)
384 # check some other possible ways it could show up
385 values = list(range(10))
386 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 4.5)
387 values = list(range(11))
388 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 5.0)
390 def testTicket1123(self):
391 """
392 Ticket #1123 reported that the Statistics stack routine throws an exception
393 when all pixels in a stack are masked. Returning a NaN pixel in the stack is preferred
394 """
396 mean = self.images[0][1]
398 ctrl = afwMath.StatisticsControl()
399 ctrl.setAndMask(~0x0)
401 mimg = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10))
402 mimg.set([mean, 0x1, mean])
404 # test the case with no valid pixels ... both mean and stdev should be
405 # nan
406 stat = afwMath.makeStatistics(mimg, afwMath.MEAN | afwMath.STDEV, ctrl)
407 mean = stat.getValue(afwMath.MEAN)
408 stdev = stat.getValue(afwMath.STDEV)
409 self.assertNotEqual(mean, mean) # NaN does not equal itself
410 self.assertNotEqual(stdev, stdev) # NaN does not equal itself
412 # test the case with one valid pixel ... mean is ok, but stdev should
413 # still be nan
414 mimg.getMask()[1, 1] = 0x0
415 stat = afwMath.makeStatistics(mimg, afwMath.MEAN | afwMath.STDEV, ctrl)
416 mean = stat.getValue(afwMath.MEAN)
417 stdev = stat.getValue(afwMath.STDEV)
418 self.assertEqual(mean, mean)
419 self.assertNotEqual(stdev, stdev) # NaN does not equal itself
421 # test the case with two valid pixels ... both mean and stdev are ok
422 mimg.getMask()[1, 2] = 0x0
423 stat = afwMath.makeStatistics(mimg, afwMath.MEAN | afwMath.STDEV, ctrl)
424 mean = stat.getValue(afwMath.MEAN)
425 stdev = stat.getValue(afwMath.STDEV)
426 self.assertEqual(mean, mean)
427 self.assertEqual(stdev, 0.0)
429 def testTicket1125(self):
430 """Ticket 1125 reported that the clipped routines were aborting when called with no valid pixels. """
432 mean = self.images[0][1]
434 mimg = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10))
435 mimg.set([mean, 0x1, mean])
437 ctrl = afwMath.StatisticsControl()
438 ctrl.setAndMask(~0x0)
440 # test the case with no valid pixels ... try MEANCLIP and STDEVCLIP
441 stat = afwMath.makeStatistics(
442 mimg, afwMath.MEANCLIP | afwMath.STDEVCLIP, ctrl)
443 mean = stat.getValue(afwMath.MEANCLIP)
444 stdev = stat.getValue(afwMath.STDEVCLIP)
445 self.assertNotEqual(mean, mean) # NaN does not equal itself
446 self.assertNotEqual(stdev, stdev) # NaN does not equal itself
448 def testWeightedSum(self):
449 ctrl = afwMath.StatisticsControl()
450 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10))
451 mi.getImage().set(1.0)
452 mi.getVariance().set(0.1)
454 stats = afwMath.makeStatistics(mi, afwMath.SUM, ctrl)
455 self.assertEqual(stats.getValue(afwMath.SUM), 100.0)
457 ctrl.setWeighted(True)
458 weighted = afwMath.makeStatistics(mi, afwMath.SUM, ctrl)
459 # precision at "4 places" as images are floats
460 # ... variance = 0.1 is stored as 0.100000001
461 self.assertAlmostEqual(weighted.getValue(afwMath.SUM), 1000.0, 4)
463 def testWeightedSum2(self):
464 """Test using a weight image separate from the variance plane"""
465 weight, mean = 0.1, 1.0
467 ctrl = afwMath.StatisticsControl()
468 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10))
469 npix = 10*10
470 mi.getImage().set(mean)
471 mi.getVariance().set(np.nan)
473 weights = afwImage.ImageF(mi.getDimensions())
474 weights.set(weight)
476 stats = afwMath.makeStatistics(mi, afwMath.SUM, ctrl)
477 self.assertEqual(stats.getValue(afwMath.SUM), mean*npix)
479 weighted = afwMath.makeStatistics(mi, weights, afwMath.SUM, ctrl)
480 # precision at "4 places" as images are floats
481 # ... variance = 0.1 is stored as 0.100000001
482 self.assertAlmostEqual(weighted.getValue(afwMath.SUM), mean*npix*weight, 4)
484 def testErrorsFromVariance(self):
485 """Test that we can estimate the errors from the incoming variances"""
486 weight, mean, variance = 0.1, 1.0, 10.0
488 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10))
489 npix = 10*10
490 mi.getImage().set(mean)
491 mi.getVariance().set(variance)
493 weights = afwImage.ImageF(mi.getDimensions())
494 weights.set(weight)
496 ctrl = afwMath.StatisticsControl()
497 ctrl.setCalcErrorFromInputVariance(True)
498 weighted = afwMath.makeStatistics(mi, weights,
499 afwMath.MEAN | afwMath.MEANCLIP | afwMath.SUM | afwMath.ERRORS,
500 ctrl)
502 self.assertAlmostEqual(weighted.getValue(afwMath.SUM)/(npix*mean*weight), 1)
503 self.assertAlmostEqual(weighted.getValue(afwMath.MEAN), mean)
504 self.assertAlmostEqual(weighted.getError(afwMath.MEAN)**2, variance/npix)
505 self.assertAlmostEqual(weighted.getError(afwMath.MEANCLIP)**2, variance/npix)
507 # calcErrorMosaicMode and calcErrorFromInputVariance cannot both be selected
508 ctrl.setCalcErrorMosaicMode(True)
509 with self.assertRaises(lsst.pex.exceptions.InvalidParameterError):
510 afwMath.makeStatistics(mi, weights, afwMath.MEAN, ctrl)
512 # Test only mosaic mode wherein output variance is the mean of the input variance
513 ctrl.setCalcErrorFromInputVariance(False)
514 statsMosaic = afwMath.makeStatistics(mi, weights,
515 afwMath.MEAN | afwMath.MEANCLIP | afwMath.SUM | afwMath.ERRORS,
516 ctrl)
518 self.assertAlmostEqual(statsMosaic.getValue(afwMath.SUM)/(npix*mean*weight), 1)
519 self.assertAlmostEqual(statsMosaic.getValue(afwMath.MEAN), mean)
520 self.assertAlmostEqual(statsMosaic.getError(afwMath.MEAN)**2, variance)
521 self.assertAlmostEqual(statsMosaic.getError(afwMath.MEANCLIP)**2, variance)
523 def testMeanClipSingleValue(self):
524 """Verify that the clipped mean doesn't not return NaN for a single value."""
525 sctrl = afwMath.StatisticsControl()
526 sctrl.setNumSigmaClip(6)
528 for image, isInt, mean, median, std in self.images:
529 stats = afwMath.makeStatistics(image, afwMath.MEANCLIP | afwMath.NCLIPPED, sctrl)
530 self.assertAlmostEqual(stats.getValue(afwMath.MEANCLIP), mean,
531 delta=self.delta("meanclip", isInt))
532 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 0)
534 # this bug was caused by the iterative nature of the MEANCLIP.
535 # With only one point, the sample variance returns NaN to avoid a divide by zero error
536 # Thus, on the second iteration, the clip width (based on _variance) is NaN and corrupts
537 # all further calculations.
538 img = afwImage.ImageF(lsst.geom.Extent2I(1, 1))
539 img.set(0)
540 stats = afwMath.makeStatistics(img, afwMath.MEANCLIP | afwMath.NCLIPPED)
541 self.assertEqual(stats.getValue(afwMath.MEANCLIP), 0)
542 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 0)
544 def testMismatch(self):
545 """Test that we get an exception when there's a size mismatch"""
546 scale = 5
547 for image, isInt, mean, median, std in self.images:
548 dims = image.getDimensions()
549 mask = afwImage.Mask(dims*scale)
550 mask.set(0xFF)
551 ctrl = afwMath.StatisticsControl()
552 ctrl.setAndMask(0xFF)
553 # If it didn't raise, this would result in a NaN (the image data is
554 # completely masked).
555 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, afwMath.makeStatistics,
556 image, mask, afwMath.MEDIAN, ctrl)
557 subMask = afwImage.Mask(mask, lsst.geom.Box2I(lsst.geom.Point2I(dims*(scale - 1)), dims))
558 subMask.set(0)
559 # Using subMask is successful.
560 self.assertAlmostEqual(afwMath.makeStatistics(image, subMask, afwMath.MEDIAN, ctrl).getValue(),
561 median, delta=self.delta("median", isInt))
563 def testClipping(self):
564 """Test that clipping statistics work
566 Insert a single bad pixel; it should be clipped.
567 """
568 sctrl = afwMath.StatisticsControl()
569 sctrl.setNumSigmaClip(10)
571 for image, isInt, mean, median, std in self.images:
572 nval = 1000*mean
573 if isInt:
574 nval = int(nval)
575 image[0, 0] = nval
577 stats = afwMath.makeStatistics(image, afwMath.MEANCLIP | afwMath.NCLIPPED | afwMath.NPOINT, sctrl)
578 self.assertAlmostEqual(stats.getValue(afwMath.MEANCLIP), mean,
579 delta=self.delta("meanclip", isInt))
580 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 1)
581 self.assertEqual(stats.getValue(afwMath.NPOINT), image.getBBox().getArea())
583 def testNMasked(self):
584 """Test that NMASKED works"""
585 maskVal = 0xBE
586 ctrl = afwMath.StatisticsControl()
587 ctrl.setAndMask(maskVal)
588 for image, isInt, mean, median, std in self.images:
589 mask = afwImage.Mask(image.getBBox())
590 mask.set(0)
591 self.assertEqual(afwMath.makeStatistics(image, mask, afwMath.NMASKED, ctrl).getValue(), 0)
592 mask[1, 1] = maskVal
593 self.assertEqual(afwMath.makeStatistics(image, mask, afwMath.NMASKED, ctrl).getValue(), 1)
596class TestMemory(lsst.utils.tests.MemoryTestCase):
597 pass
600def setup_module(module):
601 lsst.utils.tests.init()
604if __name__ == "__main__": 604 ↛ 605line 604 didn't jump to line 605, because the condition on line 604 was never true
605 lsst.utils.tests.init()
606 unittest.main()