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

89 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-12-01 21:12 +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 

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 

30 

31 

32class RepairConfig(pexConfig.Config): 

33 doInterpolate = pexConfig.Field( 

34 dtype=bool, 

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

36 default=True, 

37 ) 

38 doCosmicRay = pexConfig.Field( 

39 dtype=bool, 

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

41 default=True, 

42 ) 

43 cosmicray = pexConfig.ConfigField( 

44 dtype=measAlg.FindCosmicRaysConfig, 

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

46 ) 

47 interp = pexConfig.ConfigurableField( 

48 target=InterpImageTask, 

49 doc="Interpolate over bad image pixels", 

50 ) 

51 

52 def setDefaults(self): 

53 self.interp.useFallbackValueAtEdge = True 

54 self.interp.fallbackValueType = "MEANCLIP" 

55 self.interp.negativeFallbackAllowed = True 

56 

57## @addtogroup LSST_task_documentation 

58## @{ 

59## @page RepairTask 

60## @ref RepairTask_ "RepairTask" 

61## @copybrief RepairTask 

62## @} 

63 

64 

65class RepairTask(pipeBase.Task): 

66 r"""! 

67 @anchor RepairTask_ 

68 

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

70 

71 @section pipe_tasks_repair_Contents Contents 

72 

73 - @ref pipe_tasks_repair_Purpose 

74 - @ref pipe_tasks_repair_Initialize 

75 - @ref pipe_tasks_repair_IO 

76 - @ref pipe_tasks_repair_Config 

77 - @ref pipe_tasks_repair_Debug 

78 - @ref pipe_tasks_repair_Example 

79 

80 @section pipe_tasks_repair_Purpose Description 

81 

82 @copybrief RepairTask 

83 

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

85 lsst.meas.algorithms.Defect objects. 

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

87 

88 @section pipe_tasks_repair_Initialize Task initialization 

89 

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

91 

92 @section pipe_tasks_repair_IO Inputs/Outputs to the run method 

93 

94 @copydoc run 

95 

96 @section pipe_tasks_repair_Config Configuration parameters 

97 

98 See @ref RepairConfig 

99 

100 @section pipe_tasks_repair_Debug Debug variables 

101 

102 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 

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

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

105 about @b debug.py files. 

106 

107 The available variables in RepairTask are: 

108 <DL> 

109 <DT> @c display 

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

111 <DL> 

112 <DT> repair.before 

113 <DD> display image before any repair is done 

114 <DT> repair.after 

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

116 </DL> 

117 <DT> @c displayCR 

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

119 </DL> 

120 @section pipe_tasks_repair_Example A complete example of using RepairTask 

121 

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

123 @code 

124 examples/runRepair.py 

125 @endcode 

126 @dontinclude runRepair.py 

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

128 @skipline RepairTask 

129 

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

131 

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

133 and variance can be constructed at the same time. 

134 @skip poisson 

135 @until mask 

136 

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

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

139 @skip some CRs 

140 @until setPsf 

141 

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

143 on to the RepairTask. 

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

145 

146 @skip addDefects 

147 @until push_back 

148 

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

150 is modified in place. 

151 @skip RepairTask 

152 @until repair.run 

153 

154 <HR> 

155 To investigate the @ref pipe_tasks_repair_Debug, put something like 

156 @code{.py} 

157 import lsstDebug 

158 def DebugInfo(name): 

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

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

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

162 di.displayCR = True 

163 return di 

164 

165 lsstDebug.Info = DebugInfo 

166 @endcode 

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

168 

169 

170 Conversion notes: 

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

172 """ 

173 ConfigClass = RepairConfig 

174 _DefaultName = "repair" 

175 

176 def __init__(self, **kwargs): 

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

178 if self.config.doInterpolate: 

179 self.makeSubtask("interp") 

180 

181 @pipeBase.timeMethod 

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

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

184 

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

186 Modified in place. 

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

188 defect correction. 

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

190 

191 @throws AssertionError with the following strings: 

192 

193 <DL> 

194 <DT> No exposure provided 

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

196 <DT> No PSF provided 

197 <DD> The Exposure has no associated Psf 

198 </DL> 

199 """ 

200 assert exposure, "No exposure provided" 

201 psf = exposure.getPsf() 

202 assert psf, "No PSF provided" 

203 

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

205 if frame: 

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

207 

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

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

210 

211 if self.config.doCosmicRay: 

212 self.cosmicRay(exposure, keepCRs=keepCRs) 

213 

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

215 if frame: 

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

217 

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

219 """Mask cosmic rays 

220 

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

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

223 """ 

224 import lsstDebug 

225 display = lsstDebug.Info(__name__).display 

226 displayCR = lsstDebug.Info(__name__).displayCR 

227 

228 assert exposure, "No exposure provided" 

229 psf = exposure.getPsf() 

230 assert psf, "No psf provided" 

231 

232 # Blow away old mask 

233 try: 

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

235 crBit = mask.getMaskPlane("CR") 

236 mask.clearMaskPlane(crBit) 

237 except Exception: 

238 pass 

239 

240 exposure0 = exposure # initial value of exposure 

241 binSize = self.config.cosmicray.background.binSize 

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

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

244 # measAlg.SubtractBackgroundTask(). 

245 if nx*ny <= 1: 

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

247 modelBg = None 

248 else: 

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

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

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

252 exposure = exposure.Factory(exposure, True) 

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

254 modelBg = subtractBackgroundTask.run(exposure).background 

255 medianBg = 0.0 

256 

257 if keepCRs is None: 

258 keepCRs = self.config.cosmicray.keepCRs 

259 try: 

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

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

262 if modelBg: 

263 # Add back background image 

264 img = exposure.getMaskedImage() 

265 img += modelBg.getImageF() 

266 del img 

267 # Replace original image with CR subtracted image 

268 exposure0.setMaskedImage(exposure.getMaskedImage()) 

269 

270 except Exception: 

271 if display: 

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

273 raise 

274 

275 num = 0 

276 if crs is not None: 

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

278 crBit = mask.getPlaneBitMask("CR") 

279 afwDet.setMaskFromFootprintList(mask, crs, crBit) 

280 num = len(crs) 

281 

282 if display and displayCR: 

283 disp = afwDisplay.Display() 

284 disp.incrDefaultFrame() 

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

286 

287 with disp.Buffering(): 

288 for cr in crs: 

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

290 

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