Coverage for tests / test_copyGoodPixels.py: 17%

131 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-23 08:26 +0000

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23"""Test lsst.coadd.utils.copyGoodPixels 

24""" 

25import unittest 

26 

27import numpy as np 

28 

29import lsst.utils.tests 

30import lsst.geom as geom 

31import lsst.afw.image as afwImage 

32import lsst.coadd.utils as coaddUtils 

33 

34try: 

35 display 

36except NameError: 

37 display = False 

38 

39 

40def referenceCopyGoodPixelsImage(destImage, srcImage): 

41 """Reference implementation of lsst.coadd.utils.copyGoodPixels for Images 

42 

43 Unlike lsst.coadd.utils.copyGoodPixels this one does not update the input destImage, 

44 but instead returns the new version 

45 

46 Inputs: 

47 - destImage: source image before adding srcImage (a MaskedImage) 

48 - srcImage: masked image to add to destImage (a MaskedImage) 

49 - badPixelMask: mask of bad pixels to ignore (an int) 

50 

51 Returns: 

52 - destImage: new destImage 

53 - numGoodPix: number of good pixels 

54 """ 

55 destImage = destImage.Factory(destImage, True) # make deep copy 

56 

57 overlapBBox = destImage.getBBox() 

58 overlapBBox.clip(srcImage.getBBox()) 

59 

60 if overlapBBox.isEmpty(): 

61 return (destImage, 0) 

62 

63 destImageView = destImage.Factory(destImage, overlapBBox, afwImage.PARENT, False) 

64 destImageArray = destImageView.array 

65 

66 srcImageView = srcImage.Factory(srcImage, overlapBBox, afwImage.PARENT, False) 

67 srcImageArray = srcImageView.array 

68 

69 isBadArray = np.isnan(srcImageArray) 

70 

71 destImageArray[:] = np.where(isBadArray, destImageArray, srcImageArray) 

72 numGoodPix = np.sum(np.logical_not(isBadArray)) 

73 return destImage, numGoodPix 

74 

75 

76def referenceCopyGoodPixelsMaskedImage(destImage, srcImage, badPixelMask): 

77 """Reference implementation of lsst.coadd.utils.copyGoodPixels for MaskedImages 

78 

79 Unlike lsst.coadd.utils.copyGoodPixels this one does not update the input destImage, 

80 but instead returns an updated copy 

81 

82 @param[in] destImage: source image before adding srcImage (a MaskedImage) 

83 @param[in] srcImage: masked image to add to destImage (a MaskedImage) 

84 @param[in] badPixelMask: mask of bad pixels to ignore (an int) 

85 

86 Returns: 

87 - destImage: new destImage 

88 - numGoodPix: number of good pixels 

89 """ 

90 destImage = destImage.Factory(destImage, True) # make deep copy 

91 

92 overlapBBox = destImage.getBBox() 

93 overlapBBox.clip(srcImage.getBBox()) 

94 

95 if overlapBBox.isEmpty(): 

96 return (destImage, 0) 

97 

98 destImageView = destImage.Factory(destImage, overlapBBox, afwImage.PARENT, False) 

99 srcImageView = srcImage.Factory(srcImage, overlapBBox, afwImage.PARENT, False) 

100 

101 isBadArray = (srcImageView.mask.array & badPixelMask) != 0 

102 

103 destImageView.image.array = np.where(isBadArray, destImageView.image.array, srcImageView.image.array) 

104 destImageView.mask.array = np.where(isBadArray, destImageView.mask.array, srcImageView.mask.array) 

105 destImageView.variance.array = np.where(isBadArray, 

106 destImageView.variance.array, 

107 srcImageView.variance.array) 

108 

109 numGoodPix = np.sum(np.logical_not(isBadArray)) 

110 return destImage, numGoodPix 

111 

112 

113MaxMask = 0xFFFF 

114 

115 

116class CopyGoodPixelsTestCase(lsst.utils.tests.TestCase): 

117 """A test case for copyGoodPixels 

118 """ 

119 

120 def getSolidMaskedImage(self, bbox, val, badMask=0): 

121 afwDim = bbox.getDimensions() 

122 npShape = (afwDim[1], afwDim[0]) 

123 

124 np.random.seed(0) 

125 maskedImage = afwImage.MaskedImageF(bbox) 

126 maskedImage.image.array[:] = val 

127 maskedImage.variance.array[:] = val * 0.5 

128 maskedImage.mask.array[:, 0:npShape[1]/2] = 0 

129 maskedImage.mask.array[:, npShape[1]/2:] = badMask 

130 return maskedImage 

131 

132 def getRandomMaskedImage(self, bbox, excludeMask=0): 

133 """Get a randomly generated masked image 

134 """ 

135 if excludeMask > MaxMask: 

136 raise RuntimeError("excludeMask = %s > %s = MaxMask" % (excludeMask, MaxMask)) 

137 

138 afwDim = bbox.getDimensions() 

139 npShape = (afwDim[1], afwDim[0]) 

140 

141 np.random.seed(0) 

142 maskedImage = afwImage.MaskedImageF(bbox) 

143 maskedImage.image.array[:] = np.random.normal(5000, 5000, npShape) 

144 maskedImage.variance.array[:] = np.random.normal(3000, 3000, npShape) 

145 maskedImage.mask.array[:] = np.logical_and(np.random.randint(0, 8, npShape), ~excludeMask) 

146 return maskedImage 

147 

148 def getRandomImage(self, bbox, nanSigma=0): 

149 """Get a randomly generated image 

150 """ 

151 afwDim = bbox.getDimensions() 

152 npShape = (afwDim[1], afwDim[0]) 

153 

154 np.random.seed(0) 

155 image = afwImage.ImageF(bbox) 

156 imageArray = image.array 

157 imageArray[:] = np.random.normal(5000, 5000, npShape) 

158 if nanSigma > 0: 

159 # add NaNs at nanSigma above mean of a test array 

160 nanTest = np.random.normal(0, 1, npShape) 

161 imageArray[:] = np.where(nanTest > nanSigma, np.nan, imageArray) 

162 return image 

163 

164 def basicMaskedImageTest(self, srcImage, destImage, badMask): 

165 refDestImage, refNumGoodPix = referenceCopyGoodPixelsMaskedImage(destImage, srcImage, badMask) 

166 numGoodPix = coaddUtils.copyGoodPixels(destImage, srcImage, badMask) 

167 

168 self.assertEqual(numGoodPix, refNumGoodPix) 

169 

170 msg = "masked image != reference masked image" 

171 try: 

172 self.assertMaskedImagesAlmostEqual(destImage, refDestImage, msg=msg) 

173 except Exception: 

174 destImage.writeFits("destMaskedImage.fits") 

175 refDestImage.writeFits("refDestMaskedImage.fits") 

176 raise 

177 

178 def basicImageTest(self, srcImage, destImage): 

179 refDestImage, refNumGoodPix = referenceCopyGoodPixelsImage(destImage, srcImage) 

180 numGoodPix = coaddUtils.copyGoodPixels(destImage, srcImage) 

181 

182 msg = "image != reference image" 

183 try: 

184 self.assertImagesAlmostEqual(destImage, refDestImage, msg=msg) 

185 except Exception: 

186 destImage.writeFits("destImage.fits") 

187 refDestImage.writeFits("refDestImage.fits") 

188 raise 

189 

190 self.assertEqual(numGoodPix, refNumGoodPix) 

191 

192 def testMaskedImage(self): 

193 """Test image version of copyGoodPixels""" 

194 srcBBox = geom.Box2I(geom.Point2I(2, 17), geom.Point2I(100, 101)) 

195 destBBox = geom.Box2I(geom.Point2I(13, 4), geom.Point2I(95, 130)) 

196 destXY0 = destBBox.getMin() 

197 

198 srcImage = self.getRandomMaskedImage(srcBBox) 

199 for badMask in (0, 3, MaxMask): 

200 destImage = self.getRandomMaskedImage(destBBox, excludeMask=badMask) 

201 destBBox = destImage.getBBox() 

202 self.basicMaskedImageTest(srcImage, destImage, badMask) 

203 

204 for bboxStart in (destXY0, (50, 51)): 

205 for bboxDim in ((25, 36), (200, 200)): 

206 destViewBox = geom.Box2I(geom.Point2I(*bboxStart), geom.Extent2I(*bboxDim)) 

207 destViewBox.clip(destBBox) 

208 destView = destImage.Factory(destImage, destViewBox, afwImage.PARENT, False) 

209 self.basicMaskedImageTest(srcImage, destView, badMask) 

210 

211 def testImage(self): 

212 """Test image version of copyGoodPixels""" 

213 srcBBox = geom.Box2I(geom.Point2I(2, 17), geom.Point2I(100, 101)) 

214 destBBox = geom.Box2I(geom.Point2I(13, 4), geom.Point2I(95, 130)) 

215 destXY0 = destBBox.getMin() 

216 

217 srcImage = self.getRandomImage(srcBBox) 

218 for nanSigma in (0, 0.7, 2.0): 

219 destImage = self.getRandomImage(destBBox, nanSigma=nanSigma) 

220 destBBox = destImage.getBBox() 

221 self.basicImageTest(srcImage, destImage) 

222 

223 for bboxStart in (destXY0, (50, 51)): 

224 for bboxDim in ((25, 36), (200, 200)): 

225 destViewBox = geom.Box2I(geom.Point2I(*bboxStart), geom.Extent2I(*bboxDim)) 

226 destViewBox.clip(destBBox) 

227 destView = destImage.Factory(destImage, destViewBox, afwImage.PARENT, False) 

228 self.basicImageTest(srcImage, destView) 

229 

230 

231class MemoryTester(lsst.utils.tests.MemoryTestCase): 

232 pass 

233 

234 

235def setup_module(module): 

236 lsst.utils.tests.init() 

237 

238 

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

240 lsst.utils.tests.init() 

241 unittest.main()