Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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/>. 

21 

22""" 

23Tests for Statistics 

24 

25Run with: 

26 python test_statistics.py 

27or 

28 pytest test_statistics.py 

29""" 

30 

31import math 

32import os 

33import unittest 

34 

35import numpy as np 

36 

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 

43import lsst.pex.exceptions as pexExcept 

44 

45afwDisplay.setDefaultMaskTransparency(75) 

46 

47try: 

48 afwdataDir = lsst.utils.getPackageDir("afwdata") 

49except pexExcept.NotFoundError: 

50 afwdataDir = None 

51 

52try: 

53 type(display) 

54except NameError: 

55 display = False 

56 

57 

58class StatisticsTestCase(lsst.utils.tests.TestCase): 

59 """A test case for Statistics""" 

60 

61 clippedVariance3 = 0.9733369 # variance of an N(0, 1) Gaussian clipped at 3 sigma 

62 

63 def setUp(self): 

64 w, h = 900, 1500 

65 

66 mean = 10.5 # requested mean 

67 std = 1.0 # and standard deviation 

68 

69 self.images = [] 

70 # ImageI 

71 np.random.seed(666) 

72 isInt = True 

73 image = afwImage.ImageI(lsst.geom.ExtentI(w, h)) 

74 image.array[:] = np.floor(np.random.normal(mean, std, (h, w)) + 0.5).astype(int) 

75 

76 # Note that the mean/median/std may not be quite equal to the requested values 

77 self.images.append((image, isInt, np.mean(image.array), np.mean(image.array), np.std(image.array))) 

78 

79 # ImageF 

80 np.random.seed(666) 

81 isInt = False 

82 image = afwImage.ImageF(lsst.geom.ExtentI(w, h)) 

83 image.array[:] = np.random.normal(mean, std, (h, w)) 

84 

85 # Note that the mean/median/std may not be quite equal to the requested values 

86 self.images.append((image, isInt, np.mean(image.array), np.median(image.array), np.std(image.array))) 

87 

88 @staticmethod 

89 def delta(what, isInt): 

90 # Return a tolerance for a test 

91 if what == "mean": 

92 return 4e-6 

93 elif what == "meanclip": 

94 return 4e-5 

95 elif what == "median": 

96 return 0.00022 if isInt else 0.00000075 

97 

98 def tearDown(self): 

99 del self.images 

100 

101 def testDefaultGet(self): 

102 """Test that we can get a single statistic without specifying it""" 

103 for image, isInt, mean, median, std in self.images: 

104 stats = afwMath.makeStatistics(image, afwMath.MEDIAN) 

105 

106 self.assertEqual(stats.getValue(), stats.getValue(afwMath.MEDIAN)) 

107 self.assertEqual(stats.getResult()[0], stats.getResult(afwMath.MEDIAN)[0]) 

108 # 

109 stats = afwMath.makeStatistics(image, afwMath.MEDIAN | afwMath.ERRORS) 

110 

111 self.assertEqual(stats.getValue(), stats.getValue(afwMath.MEDIAN)) 

112 self.assertEqual(stats.getResult(), stats.getResult(afwMath.MEDIAN)) 

113 self.assertEqual(stats.getError(), stats.getError(afwMath.MEDIAN)) 

114 

115 def tst(): 

116 stats.getValue() 

117 stats = afwMath.makeStatistics(image, afwMath.MEDIAN | afwMath.MEAN) 

118 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, tst) 

119 

120 def testStats1(self): 

121 for image, isInt, mean, median, std in self.images: 

122 stats = afwMath.makeStatistics(image, afwMath.NPOINT | afwMath.STDEV | afwMath.MEAN | afwMath.SUM) 

123 

124 self.assertEqual(stats.getValue(afwMath.NPOINT), image.getWidth()*image.getHeight()) 

125 self.assertEqual(stats.getValue(afwMath.NPOINT)*stats.getValue(afwMath.MEAN), 

126 stats.getValue(afwMath.SUM)) 

127 

128 self.assertAlmostEqual(stats.getValue(afwMath.MEAN), mean, delta=self.delta("mean", isInt)) 

129 # didn't ask for error, so it's a NaN 

130 self.assertTrue(np.isnan(stats.getError(afwMath.MEAN))) 

131 self.assertAlmostEqual(stats.getValue(afwMath.STDEV), std, delta=0.000008) 

132 

133 def testStats2(self): 

134 for image, isInt, mean, median, std in self.images: 

135 stats = afwMath.makeStatistics(image, afwMath.STDEV | afwMath.MEAN | afwMath.ERRORS) 

136 meanRes = stats.getResult(afwMath.MEAN) 

137 sd = stats.getValue(afwMath.STDEV) 

138 

139 self.assertAlmostEqual(meanRes[0], mean, delta=self.delta("mean", isInt)) 

140 self.assertAlmostEqual(meanRes[1], sd/math.sqrt(image.getWidth()*image.getHeight())) 

141 

142 def testStats3(self): 

143 for image, isInt, mean, median, std in self.images: 

144 stats = afwMath.makeStatistics(image, afwMath.NPOINT) 

145 

146 def getMean(): 

147 stats.getValue(afwMath.MEAN) 

148 

149 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, getMean) 

150 

151 def testStatsZebra(self): 

152 """Add 1 to every other row""" 

153 for image, isInt, mean, median, std in self.images: 

154 image2 = image.clone() 

155 # 

156 # Add 1 to every other row, so the variance is increased by 1/4 

157 # 

158 self.assertEqual(image2.getHeight() % 2, 0) 

159 width = image2.getWidth() 

160 for y in range(1, image2.getHeight(), 2): 

161 sim = image2[lsst.geom.Box2I(lsst.geom.Point2I(0, y), lsst.geom.Extent2I(width, 1))] 

162 sim += 1 

163 

164 if display: 

165 afwDisplay.Display(frame=0).mtv(image, "Image 1") 

166 afwDisplay.Display(frame=1).mtv(image2, "Image 2 (var inc by 1/4)") 

167 

168 stats = afwMath.makeStatistics(image2, 

169 afwMath.NPOINT | afwMath.STDEV | afwMath.MEAN | afwMath.ERRORS) 

170 meanRes = stats.getResult(afwMath.MEAN) 

171 n = stats.getValue(afwMath.NPOINT) 

172 sd = stats.getValue(afwMath.STDEV) 

173 

174 self.assertAlmostEqual(meanRes[0], mean + 0.5, delta=self.delta("mean", isInt)) 

175 self.assertAlmostEqual(sd, np.hypot(std, 1/math.sqrt(4.0)*math.sqrt(n/(n - 1))), 

176 delta=0.00011) 

177 self.assertAlmostEqual(meanRes[1], sd/math.sqrt(image2.getWidth()*image2.getHeight()), 10) 

178 

179 meanSquare = afwMath.makeStatistics(image2, afwMath.MEANSQUARE).getValue() 

180 self.assertAlmostEqual(meanSquare, 0.5*(mean**2 + (mean + 1)**2) + std**2, 

181 delta=0.00025 if isInt else 0.00006) 

182 

183 def testStatsStdevclip(self): 

184 """Test STDEVCLIP; cf. #611""" 

185 for image, isInt, mean, median, std in self.images: 

186 image2 = image.clone() 

187 

188 stats = afwMath.makeStatistics(image2, afwMath.STDEVCLIP | afwMath.NPOINT | afwMath.SUM) 

189 self.assertAlmostEqual(stats.getValue(afwMath.STDEVCLIP), math.sqrt(self.clippedVariance3)*std, 

190 delta=0.0015) 

191 # 

192 # Check we get the correct sum even when clipping 

193 # 

194 self.assertEqual( 

195 stats.getValue(afwMath.NPOINT)*afwMath.makeStatistics(image2, afwMath.MEAN).getValue(), 

196 stats.getValue(afwMath.SUM)) 

197 

198 def testMedian(self): 

199 """Test the median code""" 

200 for image, isInt, mean, median, std in self.images: 

201 med = afwMath.makeStatistics(image, afwMath.MEDIAN).getValue() 

202 self.assertAlmostEqual(med, median, delta=self.delta("median", isInt)) 

203 

204 values = [1.0, 2.0, 3.0, 2.0] 

205 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 2.0) 

206 

207 def testIqrange(self): 

208 """Test the inter-quartile range""" 

209 for image, isInt, mean, median, std in self.images: 

210 iqr = afwMath.makeStatistics(image, afwMath.IQRANGE).getValue() 

211 # pretty loose constraint for isInt; probably because the distribution 

212 # isn't very Gaussian with the added rounding to integer values 

213 self.assertAlmostEqual(iqr, std/0.741301109252802, delta=0.063 if isInt else 0.00011) 

214 

215 def testMeanClip(self): 

216 """Test the clipped mean""" 

217 

218 sctrl = afwMath.StatisticsControl() 

219 sctrl.setNumSigmaClip(6) 

220 

221 for image, isInt, mean, median, std in self.images: 

222 stats = afwMath.makeStatistics(image, afwMath.MEANCLIP | afwMath.NCLIPPED, sctrl) 

223 self.assertAlmostEqual(stats.getValue(afwMath.MEANCLIP), mean, delta=self.delta("mean", isInt)) 

224 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 0) 

225 

226 def testVarianceClip(self): 

227 """Test the 3-sigma clipped standard deviation and variance""" 

228 for image, isInt, mean, median, std in self.images: 

229 delta = 0.0006 if isInt else 0.0014 

230 stdevClip = afwMath.makeStatistics(image, afwMath.STDEVCLIP).getValue() 

231 self.assertAlmostEqual(stdevClip, math.sqrt(self.clippedVariance3)*std, delta=delta) 

232 

233 varianceClip = afwMath.makeStatistics(image, afwMath.VARIANCECLIP).getValue() 

234 self.assertAlmostEqual(varianceClip, self.clippedVariance3*std**2, delta=2*delta) 

235 

236 def _testBadValue(self, badVal): 

237 """Test that we can handle an instance of `badVal` in the data correctly 

238 

239 Note that we only test ImageF here (as ImageI can't contain a NaN) 

240 """ 

241 mean = self.images[0][1] 

242 x, y = 10, 10 

243 for useImage in [True, False]: 

244 if useImage: 

245 image = afwImage.ImageF(100, 100) 

246 image.set(mean) 

247 image[x, y] = badVal 

248 else: 

249 image = afwImage.MaskedImageF(100, 100) 

250 image.set(mean, 0x0, 1.0) 

251 image[x, y] = (badVal, 0x0, 1.0) 

252 

253 self.assertEqual(afwMath.makeStatistics(image, afwMath.MAX).getValue(), mean) 

254 self.assertEqual(afwMath.makeStatistics(image, afwMath.MEAN).getValue(), mean) 

255 

256 sctrl = afwMath.StatisticsControl() 

257 

258 sctrl.setNanSafe(False) 

259 self.assertFalse(np.isfinite(afwMath.makeStatistics(image, afwMath.MAX, sctrl).getValue())) 

260 self.assertFalse(np.isfinite(afwMath.makeStatistics(image, afwMath.MEAN, sctrl).getValue())) 

261 

262 def testMaxWithNan(self): 

263 """Test that we can handle NaNs correctly""" 

264 self._testBadValue(np.nan) 

265 

266 def testMaxWithInf(self): 

267 """Test that we can handle infinities correctly""" 

268 self._testBadValue(np.inf) 

269 

270 @unittest.skipIf(afwdataDir is None, "afwdata not setup") 

271 def testSampleImageStats(self): 

272 """ Compare our results to known values in test data """ 

273 

274 imgfiles = [] 

275 imgfiles.append("v1_i1_g_m400_s20_f.fits") 

276 imgfiles.append("v1_i1_g_m400_s20_u16.fits") 

277 imgfiles.append("v1_i2_g_m400_s20_f.fits") 

278 imgfiles.append("v1_i2_g_m400_s20_u16.fits") 

279 imgfiles.append("v2_i1_p_m9_f.fits") 

280 imgfiles.append("v2_i1_p_m9_u16.fits") 

281 imgfiles.append("v2_i2_p_m9_f.fits") 

282 imgfiles.append("v2_i2_p_m9_u16.fits") 

283 

284 afwdataDir = os.getenv("AFWDATA_DIR") 

285 

286 for imgfile in imgfiles: 

287 

288 imgPath = os.path.join(afwdataDir, "Statistics", imgfile) 

289 

290 # get the image and header 

291 dimg = afwImage.DecoratedImageF(imgPath) 

292 fitsHdr = dimg.getMetadata() 

293 

294 # get the true values of the mean and stdev 

295 trueMean = fitsHdr.getAsDouble("MEANCOMP") 

296 trueStdev = fitsHdr.getAsDouble("SIGCOMP") 

297 

298 # measure the mean and stdev with the Statistics class 

299 img = dimg.getImage() 

300 statobj = afwMath.makeStatistics(img, afwMath.MEAN | afwMath.STDEV) 

301 mean = statobj.getValue(afwMath.MEAN) 

302 stdev = statobj.getValue(afwMath.STDEV) 

303 

304 # print trueMean, mean, trueStdev, stdev 

305 self.assertAlmostEqual(mean, trueMean, 8) 

306 self.assertAlmostEqual(stdev, trueStdev, 8) 

307 

308 def testStatisticsRamp(self): 

309 """ Tests Statistics on a 'ramp' (image with constant gradient) """ 

310 

311 nx = 101 

312 ny = 64 

313 img = afwImage.ImageF(lsst.geom.Extent2I(nx, ny)) 

314 

315 z0 = 10.0 

316 dzdx = 1.0 

317 mean = z0 + (nx//2)*dzdx 

318 stdev = 0.0 

319 for y in range(ny): 

320 for x in range(nx): 

321 z = z0 + dzdx*x 

322 img[x, y] = z 

323 stdev += (z - mean)*(z - mean) 

324 

325 stdev = math.sqrt(stdev/(nx*ny - 1)) 

326 

327 stats = afwMath.makeStatistics( 

328 img, afwMath.NPOINT | afwMath.STDEV | afwMath.MEAN) 

329 testmean = stats.getValue(afwMath.MEAN) 

330 teststdev = stats.getValue(afwMath.STDEV) 

331 

332 self.assertEqual(stats.getValue(afwMath.NPOINT), nx*ny) 

333 self.assertEqual(testmean, mean) 

334 self.assertAlmostEqual(teststdev, stdev) 

335 

336 stats = afwMath.makeStatistics( 

337 img, afwMath.STDEV | afwMath.MEAN | afwMath.ERRORS) 

338 mean, meanErr = stats.getResult(afwMath.MEAN) 

339 sd = stats.getValue(afwMath.STDEV) 

340 

341 self.assertEqual(mean, img[nx//2, ny//2]) 

342 self.assertEqual(meanErr, sd/math.sqrt(img.getWidth()*img.getHeight())) 

343 

344 # =============================================================================== 

345 # sjb code for percentiles and clipped stats 

346 

347 stats = afwMath.makeStatistics(img, afwMath.MEDIAN) 

348 self.assertEqual(z0 + dzdx*(nx - 1)/2.0, stats.getValue(afwMath.MEDIAN)) 

349 

350 stats = afwMath.makeStatistics(img, afwMath.IQRANGE) 

351 self.assertEqual(dzdx*(nx - 1)/2.0, stats.getValue(afwMath.IQRANGE)) 

352 

353 stats = afwMath.makeStatistics(img, afwMath.MEANCLIP) 

354 self.assertEqual(z0 + dzdx*(nx - 1)/2.0, stats.getValue(afwMath.MEANCLIP)) 

355 

356 def testMask(self): 

357 mask = afwImage.Mask(lsst.geom.Extent2I(10, 10)) 

358 mask.set(0x0) 

359 

360 mask[1, 1] = 0x10 

361 mask[3, 1] = 0x08 

362 mask[5, 4] = 0x08 

363 mask[4, 5] = 0x02 

364 

365 stats = afwMath.makeStatistics(mask, afwMath.SUM | afwMath.NPOINT) 

366 self.assertEqual(mask.getWidth()*mask.getHeight(), 

367 stats.getValue(afwMath.NPOINT)) 

368 self.assertEqual(0x1a, stats.getValue(afwMath.SUM)) 

369 

370 def tst(): 

371 afwMath.makeStatistics(mask, afwMath.MEAN) 

372 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, tst) 

373 

374 def testTicket1025(self): 

375 """ 

376 Ticket #1025 reported that the Statistics median was getting '3' as the median of [1,2,3,2] 

377 it was caused by an off-by-one error in the implementation 

378 """ 

379 

380 # check the exact example in the ticket 

381 values = [1.0, 2.0, 3.0, 2.0] 

382 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 2) 

383 self.assertEqual(afwMath.makeStatistics(sorted(values), afwMath.MEDIAN).getValue(), 2) 

384 

385 # check some other possible ways it could show up 

386 values = list(range(10)) 

387 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 4.5) 

388 values = list(range(11)) 

389 self.assertEqual(afwMath.makeStatistics(values, afwMath.MEDIAN).getValue(), 5.0) 

390 

391 def testTicket1123(self): 

392 """ 

393 Ticket #1123 reported that the Statistics stack routine throws an exception 

394 when all pixels in a stack are masked. Returning a NaN pixel in the stack is preferred 

395 """ 

396 

397 mean = self.images[0][1] 

398 

399 ctrl = afwMath.StatisticsControl() 

400 ctrl.setAndMask(~0x0) 

401 

402 mimg = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10)) 

403 mimg.set([mean, 0x1, mean]) 

404 

405 # test the case with no valid pixels ... both mean and stdev should be 

406 # nan 

407 stat = afwMath.makeStatistics(mimg, afwMath.MEAN | afwMath.STDEV, ctrl) 

408 mean = stat.getValue(afwMath.MEAN) 

409 stdev = stat.getValue(afwMath.STDEV) 

410 self.assertNotEqual(mean, mean) # NaN does not equal itself 

411 self.assertNotEqual(stdev, stdev) # NaN does not equal itself 

412 

413 # test the case with one valid pixel ... mean is ok, but stdev should 

414 # still be nan 

415 mimg.getMask()[1, 1] = 0x0 

416 stat = afwMath.makeStatistics(mimg, afwMath.MEAN | afwMath.STDEV, ctrl) 

417 mean = stat.getValue(afwMath.MEAN) 

418 stdev = stat.getValue(afwMath.STDEV) 

419 self.assertEqual(mean, mean) 

420 self.assertNotEqual(stdev, stdev) # NaN does not equal itself 

421 

422 # test the case with two valid pixels ... both mean and stdev are ok 

423 mimg.getMask()[1, 2] = 0x0 

424 stat = afwMath.makeStatistics(mimg, afwMath.MEAN | afwMath.STDEV, ctrl) 

425 mean = stat.getValue(afwMath.MEAN) 

426 stdev = stat.getValue(afwMath.STDEV) 

427 self.assertEqual(mean, mean) 

428 self.assertEqual(stdev, 0.0) 

429 

430 def testTicket1125(self): 

431 """Ticket 1125 reported that the clipped routines were aborting when called with no valid pixels. """ 

432 

433 mean = self.images[0][1] 

434 

435 mimg = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10)) 

436 mimg.set([mean, 0x1, mean]) 

437 

438 ctrl = afwMath.StatisticsControl() 

439 ctrl.setAndMask(~0x0) 

440 

441 # test the case with no valid pixels ... try MEANCLIP and STDEVCLIP 

442 stat = afwMath.makeStatistics( 

443 mimg, afwMath.MEANCLIP | afwMath.STDEVCLIP, ctrl) 

444 mean = stat.getValue(afwMath.MEANCLIP) 

445 stdev = stat.getValue(afwMath.STDEVCLIP) 

446 self.assertNotEqual(mean, mean) # NaN does not equal itself 

447 self.assertNotEqual(stdev, stdev) # NaN does not equal itself 

448 

449 def testWeightedSum(self): 

450 ctrl = afwMath.StatisticsControl() 

451 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10)) 

452 mi.getImage().set(1.0) 

453 mi.getVariance().set(0.1) 

454 

455 stats = afwMath.makeStatistics(mi, afwMath.SUM, ctrl) 

456 self.assertEqual(stats.getValue(afwMath.SUM), 100.0) 

457 

458 ctrl.setWeighted(True) 

459 weighted = afwMath.makeStatistics(mi, afwMath.SUM, ctrl) 

460 # precision at "4 places" as images are floats 

461 # ... variance = 0.1 is stored as 0.100000001 

462 self.assertAlmostEqual(weighted.getValue(afwMath.SUM), 1000.0, 4) 

463 

464 def testWeightedSum2(self): 

465 """Test using a weight image separate from the variance plane""" 

466 weight, mean = 0.1, 1.0 

467 

468 ctrl = afwMath.StatisticsControl() 

469 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10)) 

470 npix = 10*10 

471 mi.getImage().set(mean) 

472 mi.getVariance().set(np.nan) 

473 

474 weights = afwImage.ImageF(mi.getDimensions()) 

475 weights.set(weight) 

476 

477 stats = afwMath.makeStatistics(mi, afwMath.SUM, ctrl) 

478 self.assertEqual(stats.getValue(afwMath.SUM), mean*npix) 

479 

480 weighted = afwMath.makeStatistics(mi, weights, afwMath.SUM, ctrl) 

481 # precision at "4 places" as images are floats 

482 # ... variance = 0.1 is stored as 0.100000001 

483 self.assertAlmostEqual(weighted.getValue(afwMath.SUM), mean*npix*weight, 4) 

484 

485 def testErrorsFromVariance(self): 

486 """Test that we can estimate the errors from the incoming variances""" 

487 weight, mean, variance = 0.1, 1.0, 10.0 

488 

489 ctrl = afwMath.StatisticsControl() 

490 mi = afwImage.MaskedImageF(lsst.geom.Extent2I(10, 10)) 

491 npix = 10*10 

492 mi.getImage().set(mean) 

493 mi.getVariance().set(variance) 

494 

495 weights = afwImage.ImageF(mi.getDimensions()) 

496 weights.set(weight) 

497 

498 ctrl.setCalcErrorFromInputVariance(True) 

499 weighted = afwMath.makeStatistics(mi, weights, 

500 afwMath.MEAN | afwMath.MEANCLIP | afwMath.SUM | afwMath.ERRORS, 

501 ctrl) 

502 

503 self.assertAlmostEqual(weighted.getValue(afwMath.SUM)/(npix*mean*weight), 1) 

504 self.assertAlmostEqual(weighted.getValue(afwMath.MEAN), mean) 

505 self.assertAlmostEqual(weighted.getError(afwMath.MEAN)**2, variance/npix) 

506 self.assertAlmostEqual(weighted.getError(afwMath.MEANCLIP)**2, variance/npix) 

507 

508 def testMeanClipSingleValue(self): 

509 """Verify that the clipped mean doesn't not return NaN for a single value.""" 

510 sctrl = afwMath.StatisticsControl() 

511 sctrl.setNumSigmaClip(6) 

512 

513 for image, isInt, mean, median, std in self.images: 

514 stats = afwMath.makeStatistics(image, afwMath.MEANCLIP | afwMath.NCLIPPED, sctrl) 

515 self.assertAlmostEqual(stats.getValue(afwMath.MEANCLIP), mean, 

516 delta=self.delta("meanclip", isInt)) 

517 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 0) 

518 

519 # this bug was caused by the iterative nature of the MEANCLIP. 

520 # With only one point, the sample variance returns NaN to avoid a divide by zero error 

521 # Thus, on the second iteration, the clip width (based on _variance) is NaN and corrupts 

522 # all further calculations. 

523 img = afwImage.ImageF(lsst.geom.Extent2I(1, 1)) 

524 img.set(0) 

525 stats = afwMath.makeStatistics(img, afwMath.MEANCLIP | afwMath.NCLIPPED) 

526 self.assertEqual(stats.getValue(afwMath.MEANCLIP), 0) 

527 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 0) 

528 

529 def testMismatch(self): 

530 """Test that we get an exception when there's a size mismatch""" 

531 scale = 5 

532 for image, isInt, mean, median, std in self.images: 

533 dims = image.getDimensions() 

534 mask = afwImage.Mask(dims*scale) 

535 mask.set(0xFF) 

536 ctrl = afwMath.StatisticsControl() 

537 ctrl.setAndMask(0xFF) 

538 # If it didn't raise, this would result in a NaN (the image data is 

539 # completely masked). 

540 self.assertRaises(lsst.pex.exceptions.InvalidParameterError, afwMath.makeStatistics, 

541 image, mask, afwMath.MEDIAN, ctrl) 

542 subMask = afwImage.Mask(mask, lsst.geom.Box2I(lsst.geom.Point2I(dims*(scale - 1)), dims)) 

543 subMask.set(0) 

544 # Using subMask is successful. 

545 self.assertAlmostEqual(afwMath.makeStatistics(image, subMask, afwMath.MEDIAN, ctrl).getValue(), 

546 median, delta=self.delta("median", isInt)) 

547 

548 def testClipping(self): 

549 """Test that clipping statistics work 

550 

551 Insert a single bad pixel; it should be clipped. 

552 """ 

553 sctrl = afwMath.StatisticsControl() 

554 sctrl.setNumSigmaClip(10) 

555 

556 for image, isInt, mean, median, std in self.images: 

557 nval = 1000*mean 

558 if isInt: 

559 nval = int(nval) 

560 image[0, 0] = nval 

561 

562 stats = afwMath.makeStatistics(image, afwMath.MEANCLIP | afwMath.NCLIPPED | afwMath.NPOINT, sctrl) 

563 self.assertAlmostEqual(stats.getValue(afwMath.MEANCLIP), mean, 

564 delta=self.delta("meanclip", isInt)) 

565 self.assertEqual(stats.getValue(afwMath.NCLIPPED), 1) 

566 self.assertEqual(stats.getValue(afwMath.NPOINT), image.getBBox().getArea()) 

567 

568 def testNMasked(self): 

569 """Test that NMASKED works""" 

570 maskVal = 0xBE 

571 ctrl = afwMath.StatisticsControl() 

572 ctrl.setAndMask(maskVal) 

573 for image, isInt, mean, median, std in self.images: 

574 mask = afwImage.Mask(image.getBBox()) 

575 mask.set(0) 

576 self.assertEqual(afwMath.makeStatistics(image, mask, afwMath.NMASKED, ctrl).getValue(), 0) 

577 mask[1, 1] = maskVal 

578 self.assertEqual(afwMath.makeStatistics(image, mask, afwMath.NMASKED, ctrl).getValue(), 1) 

579 

580 

581class TestMemory(lsst.utils.tests.MemoryTestCase): 

582 pass 

583 

584 

585def setup_module(module): 

586 lsst.utils.tests.init() 

587 

588 

589if __name__ == "__main__": 589 ↛ 590line 589 didn't jump to line 590, because the condition on line 589 was never true

590 lsst.utils.tests.init() 

591 unittest.main()