Coverage for tests/test_interpImageTask.py: 19%

96 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-17 10:06 +0000

1# This file is part of pipe_tasks. 

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 bad pixel interpolation task 

24 

25Run with: 

26 python test_interpImageTask.py 

27or 

28 pytest test_interpImageTask.py 

29""" 

30import unittest 

31 

32import numpy as np 

33 

34import lsst.utils.tests 

35import lsst.geom 

36import lsst.afw.image as afwImage 

37import lsst.pex.config as pexConfig 

38import lsst.ip.isr as ipIsr 

39from lsst.pipe.tasks.interpImage import InterpImageTask 

40 

41try: 

42 display 

43except NameError: 

44 display = False 

45else: 

46 import lsst.afw.display as afwDisplay 

47 afwDisplay.setDefaultMaskTransparency(75) 

48 

49 

50class InterpolationTestCase(lsst.utils.tests.TestCase): 

51 """A test case for interpolation. 

52 """ 

53 

54 def setUp(self): 

55 self.FWHM = 5 

56 

57 def testEdge(self): 

58 """Test that we can interpolate to the edge. 

59 """ 

60 mi = afwImage.MaskedImageF(80, 30) 

61 ima = mi.getImage().getArray() 

62 # 

63 # We'll set the BAD bit in pixels we wish to interpolate over 

64 # 

65 pixelPlane = "BAD" 

66 badBit = afwImage.Mask.getPlaneBitMask(pixelPlane) 

67 # 

68 # Set bad columns near left and right sides of image 

69 # 

70 nBadCol = 10 

71 mi.set((0, 0x0, 0)) 

72 

73 np.random.seed(666) 

74 ima[:] = np.random.uniform(-1, 1, ima.shape) 

75 

76 mi[0:nBadCol, :, afwImage.LOCAL] = (10, badBit, 0) # Bad left edge 

77 # With another bad set of columns next to bad left edge 

78 mi[-nBadCol:, :, afwImage.LOCAL] = (10, badBit, 0) 

79 mi[nBadCol + 1:nBadCol + 4, 0:10, afwImage.LOCAL] = (100, badBit, 0) # Bad right edge 

80 # more bad of columns next to bad right edge 

81 mi[-nBadCol - 4:-nBadCol - 1, 0:10, afwImage.LOCAL] = (100, badBit, 0) 

82 

83 defectList = ipIsr.Defects.fromMask(mi, pixelPlane) 

84 

85 if display: 

86 afwDisplay.Display(frame=0).mtv(mi, title=self._testMethodName + ": image") 

87 

88 def validateInterp(miInterp, useFallbackValueAtEdge, fallbackValue): 

89 imaInterp = miInterp.getImage().getArray() 

90 if display: 

91 afwDisplay.Display(frame=1).mtv(miInterp, title=self._testMethodName + ": interp image") 

92 self.assertGreater(np.min(imaInterp), min(-2, 2*fallbackValue)) 

93 self.assertGreater(max(2, 2*fallbackValue), np.max(imaInterp)) 

94 val0 = np.mean(miInterp.image[1:2, :, afwImage.LOCAL].array, dtype=float) 

95 if useFallbackValueAtEdge: 

96 self.assertAlmostEqual(val0, fallbackValue, 6) 

97 else: 

98 self.assertNotEqual(val0, 0) 

99 

100 for useFallbackValueAtEdge in (False, True): 

101 miInterp = mi.clone() 

102 config = InterpImageTask.ConfigClass() 

103 config.useFallbackValueAtEdge = useFallbackValueAtEdge 

104 interpTask = InterpImageTask(config) 

105 

106 if useFallbackValueAtEdge: 

107 config.fallbackUserValue = -1.0 

108 # choiceField fallbackValueType cannot be None if useFallbackValueAtEdge is True 

109 config.fallbackValueType = None 

110 self.assertRaises(NotImplementedError, interpTask._setFallbackValue, miInterp) 

111 # make sure an invalid fallbackValueType raises a pexConfig.FieldValidationError 

112 with self.assertRaises(pexConfig.FieldValidationError): 

113 config.fallbackValueType = "NOTUSED" 

114 # make sure ValueError is raised if both a planeName and defects list are provided 

115 self.assertRaises(ValueError, interpTask.run, miInterp, defects=defectList, 

116 planeName=pixelPlane, fwhmPixels=self.FWHM) 

117 

118 for fallbackValueType in ("USER", "MEAN", "MEDIAN", "MEANCLIP"): 

119 for negativeFallbackAllowed in (True, False): 

120 config.negativeFallbackAllowed = negativeFallbackAllowed 

121 config.fallbackValueType = fallbackValueType 

122 # Should raise if negative not allowed, but USER supplied negative value 

123 if not negativeFallbackAllowed and fallbackValueType == "USER": 

124 self.assertRaises(ValueError, config.validate) 

125 continue 

126 

127 interpTask = InterpImageTask(config) 

128 fallbackValue = interpTask._setFallbackValue(mi) 

129 # 

130 # Time to interpolate 

131 # 

132 miInterp = mi.clone() 

133 interpTask.run(miInterp, planeName=pixelPlane, fwhmPixels=self.FWHM) 

134 validateInterp(miInterp, useFallbackValueAtEdge, fallbackValue) 

135 miInterp = mi.clone() 

136 interpTask.run(miInterp, defects=defectList) 

137 validateInterp(miInterp, useFallbackValueAtEdge, fallbackValue) 

138 else: 

139 # 

140 # Time to interpolate 

141 # 

142 miInterp = mi.clone() 

143 interpTask.run(miInterp, planeName=pixelPlane, fwhmPixels=self.FWHM) 

144 validateInterp(miInterp, useFallbackValueAtEdge, 0) 

145 

146 def testTranspose(self): 

147 """Test transposition before interpolation 

148 

149 Interpolate over a bad row (not a bad column). 

150 """ 

151 box = lsst.geom.Box2I(lsst.geom.Point2I(12345, 6789), lsst.geom.Extent2I(123, 45)) 

152 value = 123.45 

153 bad = "BAD" 

154 image = afwImage.MaskedImageF(box) 

155 image.image.set(value) 

156 image.mask.set(0) 

157 

158 badRow = box.getHeight()//2 

159 image.image.array[badRow] = 10*value 

160 image.mask.array[badRow] = image.mask.getPlaneBitMask(bad) 

161 

162 config = InterpImageTask.ConfigClass() 

163 config.transpose = True 

164 task = InterpImageTask(config) 

165 task.run(image, planeName=bad, fwhmPixels=self.FWHM) 

166 self.assertFloatsEqual(image.image.array, value) 

167 

168 

169def setup_module(module): 

170 lsst.utils.tests.init() 

171 

172 

173class MatchMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

174 pass 

175 

176 

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

178 lsst.utils.tests.init() 

179 unittest.main()