Coverage for tests/test_fakeProcessing.py: 33%

Shortcuts 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

96 statements  

1# 

2# LSST Data Management System 

3# Copyright 2008-2017 AURA/LSST. 

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 <https://www.lsstcorp.org/LegalNotices/>. 

21 

22import numpy as np 

23import os 

24import shutil 

25import tempfile 

26import unittest 

27 

28from collections import namedtuple 

29 

30import lsst.utils.tests 

31import lsst.geom as geom 

32 

33from lsst.pipe.tasks.processCcd import ProcessCcdTask 

34from lsst.pipe.tasks.fakes import BaseFakeSourcesConfig, BaseFakeSourcesTask 

35 

36obsTestDir = lsst.utils.getPackageDir('obs_test') 

37InputDir = os.path.join(obsTestDir, "data", "input") 

38 

39OutputName = None # specify a name (as a string) to save the output repository 

40 

41positionTuple = namedtuple("positionTuple", "y x") 

42 

43 

44class FakeSourcesTestConfig(BaseFakeSourcesConfig): 

45 pass 

46 

47 

48class FakeSourcesTestTask(BaseFakeSourcesTask): 

49 ''' 

50 A task to insert fake objects into test data to verify the hooks for the 

51 fake object pipeline work. 

52 ''' 

53 

54 ConfigClass = FakeSourcesTestConfig 

55 _DefaultName = "fakeSourcesTest" 

56 

57 # Ground truth position and intensities for the fake sources 

58 fakeSources = [(positionTuple(800, 435), 11342), 

59 (positionTuple(400, 350), 18235), 

60 (positionTuple(1834, 379), 13574), 

61 (positionTuple(1234, 642), 12456)] 

62 

63 def __init__(self, **kwargs): 

64 BaseFakeSourcesTask.__init__(self, **kwargs) 

65 

66 def run(self, exposure, background): 

67 if not exposure.hasPsf(): 

68 raise RuntimeError("Exposure object must have a PSF") 

69 # Fetch objects from the exposure 

70 psf = exposure.getPsf() 

71 image = exposure.getMaskedImage().getImage() 

72 mask = exposure.getMaskedImage().getMask() 

73 variance = exposure.getMaskedImage().getVariance() 

74 

75 y0 = image.getY0() 

76 x0 = image.getX0() 

77 

78 # Bitplane to set corresponding to the FAKE bit 

79 fakeMaskValue = 2**mask.getMaskPlaneDict()['FAKE'] 

80 

81 # At each position create a star with the given intensity and add it 

82 # to the image. 

83 for pos, intensity in self.fakeSources: 

84 objArray, noiseArray = self.makeFakeStar(pos, intensity, psf) 

85 psfRad = int((objArray.shape[0]-1)/2.) 

86 yslice, xslice = slice(pos.y-psfRad-y0, pos.y+psfRad+y0+1),\ 

87 slice(pos.x-psfRad-x0, pos.x+psfRad+x0+1) 

88 

89 image.getArray()[yslice, xslice] += objArray 

90 mask.getArray()[yslice, xslice] += fakeMaskValue 

91 variance.getArray()[yslice, xslice] += noiseArray**2 

92 

93 # make stars at a given position with a given intensity 

94 @staticmethod 

95 def makeFakeStar(position, intensity, psf): 

96 psfImage = psf.computeImage(geom.Point2D(position.x, position.y)).getArray() 

97 psfImage *= intensity 

98 noise = np.random.normal(0, np.sqrt(abs(psfImage))) 

99 return psfImage + noise, noise 

100 

101 

102def getObsTestConfig(TaskClass): 

103 """Helper function to get a command-line task config customized by obs_test. 

104 

105 This duplicates the config override code in pipe_base's ArgumentParser, but 

106 essentially in order to test it. 

107 """ 

108 config = TaskClass.ConfigClass() 

109 filename = os.path.join(obsTestDir, "config", 

110 TaskClass._DefaultName + ".py") 

111 if os.path.exists(filename): 

112 config.load(filename) 

113 return config 

114 

115 

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

117 def testFakeProcessing(self): 

118 # Set the random seed for predictability 

119 np.random.seed(500) 

120 

121 # Set ouput path and create a dataId 

122 outPath = tempfile.mkdtemp() if OutputName is None \ 

123 else "{}-ProcessCcd".format(OutputName) 

124 dataId = dict(visit=1) 

125 dataIdStrList = ["{}={}".format(*item) for item in dataId.items()] 

126 mask = None 

127 maskPlaneName = "FAKE" 

128 

129 try: 

130 # Set the configurations for running the fake object piepline 

131 processCcdConfig = getObsTestConfig(ProcessCcdTask) 

132 processCcdConfig.calibrate.doInsertFakes = True 

133 processCcdConfig.calibrate.insertFakes.retarget(FakeSourcesTestTask) 

134 

135 # Run ProcessCcd 

136 pCcdResult = ProcessCcdTask.parseAndRun( 

137 args=[InputDir, "--output", outPath, 

138 "--clobber-config", "--doraise", "--id"] 

139 + dataIdStrList, 

140 doReturnResults=True, config=processCcdConfig) 

141 

142 # Check the Catalog contains properly measured fake sources 

143 sourceCat = pCcdResult.resultList[0].result.calibRes.sourceCat 

144 self.checkSourceCatalog(sourceCat) 

145 

146 exposure = pCcdResult.resultList[0].result.calibRes.exposure 

147 mask = exposure.getMaskedImage().getMask() 

148 maskBit = 2**mask.getMaskPlaneDict()[maskPlaneName] 

149 fakeMask = np.bitwise_and(mask.getArray(), maskBit) 

150 self.assertGreater(fakeMask.sum(), 0) 

151 

152 # Clean up temporary directories if needed 

153 finally: 

154 if mask: 

155 # Remove FAKE so as not to contaminate later tests 

156 lsst.afw.image.Mask[lsst.afw.image.MaskPixel].removeMaskPlane(maskPlaneName) 

157 if OutputName is None: 

158 shutil.rmtree(outPath) 

159 else: 

160 message = "testFakeProcessing.py's output data saved to {}" 

161 print(message.format(OutputName)) 

162 

163 def checkSourceCatalog(self, srcCat, thresh=5): 

164 # Find the fake objects in the output source catalog, verify the 

165 # measured flux is plausable given ground truth 

166 fakeSourceCounter = 0 

167 for source in srcCat: 

168 srcX = source.getX() 

169 srcY = source.getY() 

170 for fakePos, fakeIntensity in FakeSourcesTestTask.fakeSources: 

171 distSq = (srcX - fakePos.x)**2 + (srcY - fakePos.y)**2 

172 if distSq <= thresh**2: 

173 fluxDiff = abs(source.getPsfInstFlux() - fakeIntensity) 

174 self.assertLessEqual(fluxDiff, 5*np.sqrt(fakeIntensity)) 

175 fakeSourceCounter += 1 

176 # Verify the correct number of fake sources were found 

177 self.assertEqual(fakeSourceCounter, 

178 len(FakeSourcesTestTask.fakeSources)) 

179 

180 

181def setup_module(module): 

182 lsst.utils.tests.init() 

183 

184 

185class MemoryTestCase(lsst.utils.tests.MemoryTestCase): 

186 pass 

187 

188 

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

190 lsst.utils.tests.init() 

191 unittest.main()