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

91 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-18 02:44 -0800

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__all__ = ["RepairConfig", "RepairTask"] 

23 

24import lsst.pex.config as pexConfig 

25import lsst.afw.math as afwMath 

26import lsst.afw.detection as afwDet 

27import lsst.meas.algorithms as measAlg 

28import lsst.pipe.base as pipeBase 

29from lsstDebug import getDebugFrame 

30import lsst.afw.display as afwDisplay 

31from lsst.pipe.tasks.interpImage import InterpImageTask 

32from lsst.utils.timer import timeMethod 

33 

34 

35class RepairConfig(pexConfig.Config): 

36 doInterpolate = pexConfig.Field( 

37 dtype=bool, 

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

39 default=True, 

40 ) 

41 doCosmicRay = pexConfig.Field( 

42 dtype=bool, 

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

44 default=True, 

45 ) 

46 cosmicray = pexConfig.ConfigField( 

47 dtype=measAlg.FindCosmicRaysConfig, 

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

49 ) 

50 interp = pexConfig.ConfigurableField( 

51 target=InterpImageTask, 

52 doc="Interpolate over bad image pixels", 

53 ) 

54 

55 def setDefaults(self): 

56 self.interp.useFallbackValueAtEdge = True 

57 self.interp.fallbackValueType = "MEANCLIP" 

58 self.interp.negativeFallbackAllowed = True 

59 

60 

61class RepairTask(pipeBase.Task): 

62 """Repair an exposures defects and cosmic rays via interpolation. 

63 

64 This task operates on an lsst.afw.image.Exposure in place to 

65 interpolate over a set of `~lsst.meas.algorithms.Defect` objects. 

66 

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

68 

69 Notes 

70 ----- 

71 Debugging: 

72 The available debug variables in RepairTask are: 

73 

74 display : 

75 A dictionary containing debug point names as keys with frame number as value. Valid keys are: 

76 repair.before : 

77 display image before any repair is done 

78 repair.after : 

79 display image after cosmic ray and defect correction 

80 displayCR : 

81 If True, display the exposure on ds9's frame 1 and overlay bounding boxes around detects CRs. 

82 

83 To investigate the pipe_tasks_repair_Debug, put something like 

84 

85 .. code-block :: none 

86 

87 import lsstDebug 

88 def DebugInfo(name): 

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

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

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

92 di.displayCR = True 

93 return di 

94 

95 lsstDebug.Info = DebugInfo 

96 into your debug.py file and run runRepair.py with the --debug flag. 

97 

98 Conversion notes: 

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

100 """ 

101 

102 ConfigClass = RepairConfig 

103 _DefaultName = "repair" 

104 

105 def __init__(self, **kwargs): 

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

107 if self.config.doInterpolate: 

108 self.makeSubtask("interp") 

109 

110 @timeMethod 

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

112 """Repair an Exposure's defects and cosmic rays. 

113 

114 Parameters 

115 ---------- 

116 exposure : `lsst.afw.image.Exposure` 

117 Exposure must have a valid Psf. 

118 Modified in place. 

119 defects : `lsst.meas.algorithms.DefectListT` or `None`, optional 

120 If `None`, do no defect correction. 

121 keepCRs : `Unknown` or `None`, optional 

122 Don't interpolate over the CR pixels (defer to ``RepairConfig`` if `None`). 

123 

124 Raises 

125 ------ 

126 AssertionError 

127 Raised if any of the following occur: 

128 - No exposure provided. 

129 - The object provided as exposure evaluates to False. 

130 - No PSF provided. 

131 - The Exposure has no associated Psf. 

132 """ 

133 assert exposure, "No exposure provided" 

134 psf = exposure.getPsf() 

135 assert psf, "No PSF provided" 

136 

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

138 if frame: 

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

140 

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

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

143 

144 if self.config.doCosmicRay: 

145 self.cosmicRay(exposure, keepCRs=keepCRs) 

146 

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

148 if frame: 

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

150 

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

152 """Mask cosmic rays. 

153 

154 Parameters 

155 ---------- 

156 exposure : `lsst.afw.image.Exposure` 

157 Exposure to process. 

158 keepCRs : `Unknown` or `None`, optional 

159 Don't interpolate over the CR pixels (defer to ``pex_config`` if `None`). 

160 """ 

161 import lsstDebug 

162 display = lsstDebug.Info(__name__).display 

163 displayCR = lsstDebug.Info(__name__).displayCR 

164 

165 assert exposure, "No exposure provided" 

166 psf = exposure.getPsf() 

167 assert psf, "No psf provided" 

168 

169 # Blow away old mask 

170 try: 

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

172 crBit = mask.getMaskPlane("CR") 

173 mask.clearMaskPlane(crBit) 

174 except Exception: 

175 pass 

176 

177 exposure0 = exposure # initial value of exposure 

178 binSize = self.config.cosmicray.background.binSize 

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

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

181 # measAlg.SubtractBackgroundTask(). 

182 if nx*ny <= 1: 

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

184 modelBg = None 

185 else: 

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

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

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

189 exposure = exposure.Factory(exposure, True) 

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

191 modelBg = subtractBackgroundTask.run(exposure).background 

192 medianBg = 0.0 

193 

194 if keepCRs is None: 

195 keepCRs = self.config.cosmicray.keepCRs 

196 try: 

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

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

199 if modelBg: 

200 # Add back background image 

201 img = exposure.getMaskedImage() 

202 img += modelBg.getImageF() 

203 del img 

204 # Replace original image with CR subtracted image 

205 exposure0.setMaskedImage(exposure.getMaskedImage()) 

206 

207 except Exception: 

208 if display: 

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

210 raise 

211 

212 num = 0 

213 if crs is not None: 

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

215 crBit = mask.getPlaneBitMask("CR") 

216 afwDet.setMaskFromFootprintList(mask, crs, crBit) 

217 num = len(crs) 

218 

219 if display and displayCR: 

220 disp = afwDisplay.Display() 

221 disp.incrDefaultFrame() 

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

223 

224 with disp.Buffering(): 

225 for cr in crs: 

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

227 

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