Coverage for tests/test_kernelPca.py: 12%

170 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-10 04:26 -0700

1import unittest 

2 

3import numpy as np 

4 

5import lsst.utils.tests 

6import lsst.afw.image as afwImage 

7import lsst.afw.math as afwMath 

8import lsst.geom as geom 

9import lsst.ip.diffim as ipDiffim 

10import lsst.utils.logging as logUtils 

11import lsst.pex.config as pexConfig 

12 

13logUtils.trace_set_at("lsst.ip.diffim", 4) 

14 

15 

16def makePoissonNoiseImage(image): 

17 """Return a Poisson noise image based on image 

18 

19 Parameters 

20 ---------- 

21 image : `lsst.afw.image.Image` 

22 image; the output image has the same dtype, dimensions, and shape 

23 and its expectation value is the value of ``image`` at each pixel 

24 

25 Returns 

26 ------- 

27 noiseIm : `lsst.afw.image.Image` 

28 Newly constructed image instance, same type as ``image``. 

29 

30 Notes 

31 ----- 

32 - Warning: This uses an undocumented numpy API (the documented API 

33 uses a single float expectation value instead of an array). 

34 

35 - Uses numpy.random; you may wish to call numpy.random.seed first. 

36 """ 

37 import numpy.random as rand 

38 imArr = image.array 

39 noiseIm = image.Factory(image.getBBox()) 

40 noiseArr = noiseIm.array 

41 

42 intNoiseArr = rand.poisson(np.where(np.isfinite(imArr), imArr, 0.0)) 

43 

44 noiseArr[:, :] = intNoiseArr.astype(noiseArr.dtype) 

45 return noiseIm 

46 

47 

48class DiffimTestCases(lsst.utils.tests.TestCase): 

49 

50 def setUp(self): 

51 self.config = ipDiffim.PsfMatchConfigDF() 

52 

53 self.kList = ipDiffim.makeKernelBasisList(self.config) 

54 self.ps = pexConfig.makePropertySet(self.config) 

55 self.ps["useRegularization"] = False 

56 

57 def makeCandidate(self, kSum, x, y, size=51): 

58 mi1 = afwImage.MaskedImageF(geom.Extent2I(size, size)) 

59 mi1.getVariance().set(1.0) # avoid NaNs 

60 mi1[size//2, size//2, afwImage.LOCAL] = (1, 0x0, 1) 

61 mi2 = afwImage.MaskedImageF(geom.Extent2I(size, size)) 

62 mi2.getVariance().set(1.0) # avoid NaNs 

63 mi2[size//2, size//2, afwImage.LOCAL] = (kSum, 0x0, kSum) 

64 kc = ipDiffim.makeKernelCandidate(x, y, mi1, mi2, self.ps) 

65 return kc 

66 

67 def testGaussian(self, size=51): 

68 gaussFunction = afwMath.GaussianFunction2D(2, 3) 

69 gaussKernel = afwMath.AnalyticKernel(size, size, gaussFunction) 

70 

71 imagePca1 = ipDiffim.KernelPcaD() # mean subtract 

72 imagePca2 = ipDiffim.KernelPcaD() # don't mean subtract 

73 kpv1 = ipDiffim.KernelPcaVisitorF(imagePca1) 

74 kpv2 = ipDiffim.KernelPcaVisitorF(imagePca2) 

75 

76 kRefIm = None 

77 

78 for i in range(100): 

79 kImage1 = afwImage.ImageD(gaussKernel.getDimensions()) 

80 gaussKernel.computeImage(kImage1, False) 

81 kImage1 *= 10000 # to get some decent peak source counts 

82 kImage1 += 10 # to get some sky background noise 

83 

84 if kRefIm is None: 

85 kRefIm = kImage1 

86 

87 kImage1 = makePoissonNoiseImage(kImage1) 

88 kImage2 = afwImage.ImageD(kImage1, True) 

89 

90 imagePca1.addImage(kImage1, 1.0) 

91 imagePca2.addImage(kImage2, 1.0) 

92 

93 kpv1.subtractMean() 

94 

95 imagePca1.analyze() 

96 imagePca2.analyze() 

97 

98 pcaBasisList1 = kpv1.getEigenKernels() 

99 pcaBasisList2 = kpv2.getEigenKernels() 

100 

101 eVal1 = imagePca1.getEigenValues() 

102 eVal2 = imagePca2.getEigenValues() 

103 

104 # First term is far more signficant without mean subtraction 

105 self.assertGreater(eVal2[0], eVal1[0]) 

106 

107 # Last term basically zero with mean subtraction 

108 self.assertAlmostEqual(eVal1[-1], 0.0) 

109 

110 # Extra image with mean subtraction 

111 self.assertEqual(len(pcaBasisList1), (len(eVal1) + 1)) 

112 

113 # Same shape 

114 self.assertEqual(len(pcaBasisList2), len(eVal2)) 

115 

116 # Mean kernel close to kRefIm 

117 kImageM = afwImage.ImageD(gaussKernel.getDimensions()) 

118 pcaBasisList1[0].computeImage(kImageM, False) 

119 for y in range(kRefIm.getHeight()): 

120 for x in range(kRefIm.getWidth()): 

121 self.assertLess(abs(kRefIm[x, y, afwImage.LOCAL] - kImageM[x, y, afwImage.LOCAL]) 

122 / kRefIm[x, y, afwImage.LOCAL], 0.2) 

123 

124 # First mean-unsubtracted Pca kernel close to kRefIm (normalized to peak of 1.0) 

125 kImage0 = afwImage.ImageD(gaussKernel.getDimensions()) 

126 pcaBasisList2[0].computeImage(kImage0, False) 

127 maxVal = afwMath.makeStatistics(kRefIm, afwMath.MAX).getValue(afwMath.MAX) 

128 kRefIm /= maxVal 

129 for y in range(kRefIm.getHeight()): 

130 for x in range(kRefIm.getWidth()): 

131 self.assertLess(abs(kRefIm[x, y, afwImage.LOCAL] - kImage0[x, y, afwImage.LOCAL]) 

132 / kRefIm[x, y, afwImage.LOCAL], 0.2) 

133 

134 def testImagePca(self): 

135 # Test out the ImagePca behavior 

136 kc1 = self.makeCandidate(1, 0.0, 0.0) 

137 kc1.build(self.kList) 

138 kc2 = self.makeCandidate(2, 0.0, 0.0) 

139 kc2.build(self.kList) 

140 kc3 = self.makeCandidate(3, 0.0, 0.0) 

141 kc3.build(self.kList) 

142 

143 imagePca = ipDiffim.KernelPcaD() 

144 kpv = ipDiffim.KernelPcaVisitorF(imagePca) 

145 kpv.processCandidate(kc1) 

146 kpv.processCandidate(kc2) 

147 kpv.processCandidate(kc3) 

148 

149 imagePca.analyze() 

150 eigenImages = imagePca.getEigenImages() 

151 # NOTE : this needs to be changed once ticket #1649 is resolved 

152 for i in range(len(eigenImages)): 

153 for j in range(i, len(eigenImages)): 

154 print(i, j, afwImage.innerProduct(eigenImages[i], eigenImages[j])) 

155 

156 def testEigenValues(self): 

157 kc1 = self.makeCandidate(1, 0.0, 0.0) 

158 kc1.build(self.kList) 

159 

160 kc2 = self.makeCandidate(2, 0.0, 0.0) 

161 kc2.build(self.kList) 

162 

163 kc3 = self.makeCandidate(3, 0.0, 0.0) 

164 kc3.build(self.kList) 

165 

166 imagePca = ipDiffim.KernelPcaD() 

167 kpv = ipDiffim.KernelPcaVisitorF(imagePca) 

168 kpv.processCandidate(kc1) 

169 kpv.processCandidate(kc2) 

170 kpv.processCandidate(kc3) 

171 

172 imagePca.analyze() 

173 eigenImages = imagePca.getEigenImages() 

174 eigenValues = imagePca.getEigenValues() 

175 

176 # took in 3 images 

177 self.assertEqual(len(eigenImages), 3) 

178 self.assertEqual(len(eigenValues), 3) 

179 

180 # all the same shape, only 1 eigenvalue 

181 self.assertAlmostEqual(eigenValues[0], 1.0) 

182 self.assertAlmostEqual(eigenValues[1], 0.0) 

183 self.assertAlmostEqual(eigenValues[2], 0.0) 

184 

185 def testMeanSubtraction(self): 

186 kc1 = self.makeCandidate(1, 0.0, 0.0) 

187 kc1.build(self.kList) 

188 

189 kc2 = self.makeCandidate(2, 0.0, 0.0) 

190 kc2.build(self.kList) 

191 

192 kc3 = self.makeCandidate(3, 0.0, 0.0) 

193 kc3.build(self.kList) 

194 

195 imagePca = ipDiffim.KernelPcaD() 

196 kpv = ipDiffim.KernelPcaVisitorF(imagePca) 

197 kpv.processCandidate(kc1) 

198 kpv.processCandidate(kc2) 

199 kpv.processCandidate(kc3) 

200 kpv.subtractMean() # subtract it *from* imagePca 

201 

202 imagePca.analyze() 

203 eigenImages = imagePca.getEigenImages() 

204 eigenValues = imagePca.getEigenValues() 

205 

206 # took in 3 images 

207 self.assertEqual(len(eigenImages), 3) 

208 self.assertEqual(len(eigenValues), 3) 

209 

210 # all the same shape, mean subtracted, so *no* eigenvalues 

211 self.assertAlmostEqual(eigenValues[0], 0.0) 

212 self.assertAlmostEqual(eigenValues[1], 0.0) 

213 self.assertAlmostEqual(eigenValues[2], 0.0) 

214 

215 # finally, since imagePca normalizes by the sum, this should 

216 # have central pixel value 1.0 and the rest 0.0 

217 imageMean = kpv.returnMean() 

218 rows = imageMean.getHeight() 

219 cols = imageMean.getWidth() 

220 for y in range(rows): 

221 for x in range(cols): 

222 if x == cols // 2 and y == rows // 2: 

223 self.assertAlmostEqual(imageMean[x, y, afwImage.LOCAL], 1.0) 

224 else: 

225 self.assertAlmostEqual(imageMean[x, y, afwImage.LOCAL], 0.0) 

226 

227 def testVisit(self, nCell=3): 

228 imagePca = ipDiffim.KernelPcaD() 

229 kpv = ipDiffim.makeKernelPcaVisitor(imagePca) 

230 

231 sizeCellX = self.ps["sizeCellX"] 

232 sizeCellY = self.ps["sizeCellY"] 

233 

234 kernelCellSet = afwMath.SpatialCellSet(geom.Box2I(geom.Point2I(0, 0), 

235 geom.Extent2I(sizeCellX * nCell, 

236 sizeCellY * nCell)), 

237 sizeCellX, 

238 sizeCellY) 

239 

240 for candX in range(nCell): 

241 for candY in range(nCell): 

242 if candX == nCell // 2 and candY == nCell // 2: 

243 kc = self.makeCandidate(100.0, 

244 candX * sizeCellX + sizeCellX // 2, 

245 candY * sizeCellY + sizeCellY // 2) 

246 else: 

247 kc = self.makeCandidate(1.0, 

248 candX * sizeCellX + sizeCellX // 2, 

249 candY * sizeCellY + sizeCellY // 2) 

250 kc.build(self.kList) 

251 kernelCellSet.insertCandidate(kc) 

252 

253 kernelCellSet.visitCandidates(kpv, 1) 

254 imagePca.analyze() 

255 eigenImages = imagePca.getEigenImages() 

256 eigenValues = imagePca.getEigenValues() 

257 

258 # took in 3 images 

259 self.assertEqual(len(eigenImages), nCell * nCell) 

260 self.assertEqual(len(eigenValues), nCell * nCell) 

261 

262 # all the same shape, only 1 eigenvalue 

263 self.assertAlmostEqual(eigenValues[0], 1.0) 

264 self.assertAlmostEqual(eigenValues[1], 0.0) 

265 self.assertAlmostEqual(eigenValues[2], 0.0) 

266 

267##### 

268 

269 

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

271 pass 

272 

273 

274def setup_module(module): 

275 lsst.utils.tests.init() 

276 

277 

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

279 lsst.utils.tests.init() 

280 unittest.main()