Coverage for python/lsst/pipe/tasks/repair.py: 22%

90 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-19 13:00 -0700

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 

22import lsst.pex.config as pexConfig 

23import lsst.afw.math as afwMath 

24import lsst.afw.detection as afwDet 

25import lsst.meas.algorithms as measAlg 

26import lsst.pipe.base as pipeBase 

27from lsstDebug import getDebugFrame 

28import lsst.afw.display as afwDisplay 

29from lsst.pipe.tasks.interpImage import InterpImageTask 

30from lsst.utils.timer import timeMethod 

31 

32 

33class RepairConfig(pexConfig.Config): 

34 doInterpolate = pexConfig.Field( 

35 dtype=bool, 

36 doc="Interpolate over defects? (ignored unless you provide a list of defects)", 

37 default=True, 

38 ) 

39 doCosmicRay = pexConfig.Field( 

40 dtype=bool, 

41 doc="Find and mask out cosmic rays?", 

42 default=True, 

43 ) 

44 cosmicray = pexConfig.ConfigField( 

45 dtype=measAlg.FindCosmicRaysConfig, 

46 doc="Options for finding and masking cosmic rays", 

47 ) 

48 interp = pexConfig.ConfigurableField( 

49 target=InterpImageTask, 

50 doc="Interpolate over bad image pixels", 

51 ) 

52 

53 def setDefaults(self): 

54 self.interp.useFallbackValueAtEdge = True 

55 self.interp.fallbackValueType = "MEANCLIP" 

56 self.interp.negativeFallbackAllowed = True 

57 

58## @addtogroup LSST_task_documentation 

59## @{ 

60## @page page_RepairTask RepairTask 

61## @ref RepairTask_ "RepairTask" 

62## @copybrief RepairTask 

63## @} 

64 

65 

66class RepairTask(pipeBase.Task): 

67 r"""! 

68 @anchor RepairTask_ 

69 

70 @brief Interpolate over defects in an exposure and handle cosmic rays 

71 

72 @section pipe_tasks_repair_Contents Contents 

73 

74 - @ref pipe_tasks_repair_Purpose 

75 - @ref pipe_tasks_repair_Initialize 

76 - @ref pipe_tasks_repair_IO 

77 - @ref pipe_tasks_repair_Config 

78 - @ref pipe_tasks_repair_Debug 

79 - @ref pipe_tasks_repair_Example 

80 

81 @section pipe_tasks_repair_Purpose Description 

82 

83 @copybrief RepairTask 

84 

85 This task operates on an lsst.afw.image.Exposure in place to interpolate over a set of 

86 lsst.meas.algorithms.Defect objects. 

87 It will also, optionally, find and interpolate any cosmic rays in the lsst.afw.image.Exposure. 

88 

89 @section pipe_tasks_repair_Initialize Task initialization 

90 

91 See: lsst.pipe.base.task.Task.__init__ 

92 

93 @section pipe_tasks_repair_IO Inputs/Outputs to the run method 

94 

95 @copydoc run 

96 

97 @section pipe_tasks_repair_Config Configuration parameters 

98 

99 See @ref RepairConfig 

100 

101 @section pipe_tasks_repair_Debug Debug variables 

102 

103 The command line task interface supports a 

104 flag @c -d to import @b debug.py from your @c PYTHONPATH; see <a 

105 href="https://developer.lsst.io/stack/debug.html">Debugging Tasks with lsstDebug</a> for more 

106 about @b debug.py files. 

107 

108 The available variables in RepairTask are: 

109 <DL> 

110 <DT> @c display 

111 <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are: 

112 <DL> 

113 <DT> repair.before 

114 <DD> display image before any repair is done 

115 <DT> repair.after 

116 <DD> display image after cosmic ray and defect correction 

117 </DL> 

118 <DT> @c displayCR 

119 <DD> If True, display the exposure on display's frame 1 and overlay bounding boxes around detects CRs. 

120 </DL> 

121 @section pipe_tasks_repair_Example A complete example of using RepairTask 

122 

123 This code is in runRepair.py in the examples directory, and can be run as @em e.g. 

124 @code 

125 examples/runRepair.py 

126 @endcode 

127 @dontinclude runRepair.py 

128 Import the task. There are other imports. Read the source file for more info. 

129 @skipline RepairTask 

130 

131 For this example, we manufacture a test image to run on. 

132 

133 First, create a pure Poisson noise image and a Psf to go with it. The mask plane 

134 and variance can be constructed at the same time. 

135 @skip poisson 

136 @until mask 

137 

138 Inject some cosmic rays and generate the Exposure. Exposures are MaskedImages (image + mask + variance) 

139 with other metadata (e.g. Psf and Wcs objects). 

140 @skip some CRs 

141 @until setPsf 

142 

143 Defects are represented as bad columns of random lengths. A defect list must be constructed to pass 

144 on to the RepairTask. 

145 @bug This is addressed in <a href="https://jira.lsstcorp.org/browse/DM-963"> DM-963</a> 

146 

147 @skip addDefects 

148 @until push_back 

149 

150 Finally, the exposure can be repaired. Create an instance of the task and run it. The exposure 

151 is modified in place. 

152 @skip RepairTask 

153 @until repair.run 

154 

155 <HR> 

156 To investigate the @ref pipe_tasks_repair_Debug, put something like 

157 @code{.py} 

158 import lsstDebug 

159 def DebugInfo(name): 

160 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 

161 if name == "lsst.pipe.tasks.repair": 

162 di.display = {'repair.before':2, 'repair.after':3} 

163 di.displayCR = True 

164 return di 

165 

166 lsstDebug.Info = DebugInfo 

167 @endcode 

168 into your debug.py file and run runRepair.py with the @c --debug flag. 

169 

170 

171 Conversion notes: 

172 Display code should be updated once we settle on a standard way of controlling what is displayed. 

173 """ 

174 ConfigClass = RepairConfig 

175 _DefaultName = "repair" 

176 

177 def __init__(self, **kwargs): 

178 pipeBase.Task.__init__(self, **kwargs) 

179 if self.config.doInterpolate: 

180 self.makeSubtask("interp") 

181 

182 @timeMethod 

183 def run(self, exposure, defects=None, keepCRs=None): 

184 """!Repair an Exposure's defects and cosmic rays 

185 

186 @param[in, out] exposure lsst.afw.image.Exposure to process. Exposure must have a valid Psf. 

187 Modified in place. 

188 @param[in] defects an lsst.meas.algorithms.DefectListT object. If None, do no 

189 defect correction. 

190 @param[in] keepCRs don't interpolate over the CR pixels (defer to RepairConfig if None) 

191 

192 @throws AssertionError with the following strings: 

193 

194 <DL> 

195 <DT> No exposure provided 

196 <DD> The object provided as exposure evaluates to False 

197 <DT> No PSF provided 

198 <DD> The Exposure has no associated Psf 

199 </DL> 

200 """ 

201 assert exposure, "No exposure provided" 

202 psf = exposure.getPsf() 

203 assert psf, "No PSF provided" 

204 

205 frame = getDebugFrame(self._display, "repair.before") 

206 if frame: 

207 afwDisplay.Display(frame).mtv(exposure) 

208 

209 if defects is not None and self.config.doInterpolate: 

210 self.interp.run(exposure, defects=defects) 

211 

212 if self.config.doCosmicRay: 

213 self.cosmicRay(exposure, keepCRs=keepCRs) 

214 

215 frame = getDebugFrame(self._display, "repair.after") 

216 if frame: 

217 afwDisplay.Display(frame).mtv(exposure) 

218 

219 def cosmicRay(self, exposure, keepCRs=None): 

220 """Mask cosmic rays 

221 

222 @param[in,out] exposure Exposure to process 

223 @param[in] keepCRs Don't interpolate over the CR pixels (defer to pex_config if None) 

224 """ 

225 import lsstDebug 

226 display = lsstDebug.Info(__name__).display 

227 displayCR = lsstDebug.Info(__name__).displayCR 

228 

229 assert exposure, "No exposure provided" 

230 psf = exposure.getPsf() 

231 assert psf, "No psf provided" 

232 

233 # Blow away old mask 

234 try: 

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

236 crBit = mask.getMaskPlane("CR") 

237 mask.clearMaskPlane(crBit) 

238 except Exception: 

239 pass 

240 

241 exposure0 = exposure # initial value of exposure 

242 binSize = self.config.cosmicray.background.binSize 

243 nx, ny = exposure.getWidth()/binSize, exposure.getHeight()/binSize 

244 # Treat constant background as a special case to avoid the extra complexity in calling 

245 # measAlg.SubtractBackgroundTask(). 

246 if nx*ny <= 1: 

247 medianBg = afwMath.makeStatistics(exposure.getMaskedImage(), afwMath.MEDIAN).getValue() 

248 modelBg = None 

249 else: 

250 # make a deep copy of the exposure before subtracting its background, 

251 # because this routine is only allowed to modify the exposure by setting mask planes 

252 # and interpolating over defects, not changing the background level 

253 exposure = exposure.Factory(exposure, True) 

254 subtractBackgroundTask = measAlg.SubtractBackgroundTask(config=self.config.cosmicray.background) 

255 modelBg = subtractBackgroundTask.run(exposure).background 

256 medianBg = 0.0 

257 

258 if keepCRs is None: 

259 keepCRs = self.config.cosmicray.keepCRs 

260 try: 

261 crs = measAlg.findCosmicRays(exposure.getMaskedImage(), psf, medianBg, 

262 pexConfig.makePropertySet(self.config.cosmicray), keepCRs) 

263 if modelBg: 

264 # Add back background image 

265 img = exposure.getMaskedImage() 

266 img += modelBg.getImageF() 

267 del img 

268 # Replace original image with CR subtracted image 

269 exposure0.setMaskedImage(exposure.getMaskedImage()) 

270 

271 except Exception: 

272 if display: 

273 afwDisplay.Display().mtv(exposure0, title="Failed CR") 

274 raise 

275 

276 num = 0 

277 if crs is not None: 

278 mask = exposure0.getMaskedImage().getMask() 

279 crBit = mask.getPlaneBitMask("CR") 

280 afwDet.setMaskFromFootprintList(mask, crs, crBit) 

281 num = len(crs) 

282 

283 if display and displayCR: 

284 disp = afwDisplay.Display() 

285 disp.incrDefaultFrame() 

286 disp.mtv(exposure0, title="Post-CR") 

287 

288 with disp.Buffering(): 

289 for cr in crs: 

290 afwDisplay.utils.drawBBox(cr.getBBox(), borderWidth=0.55) 

291 

292 self.log.info("Identified %s cosmic rays.", num)