Coverage for python/lsst/afw/math/_warper.py: 41%

53 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-12-08 14:31 -0800

1# This file is part of afw. 

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__ = ["Warper", "WarperConfig"] 

23 

24import lsst.pex.config as pexConfig 

25import lsst.geom 

26import lsst.afw.image as afwImage 

27from ._math import warpImage 

28from ._math import WarpingControl 

29from ._math import warpExposure 

30 

31 

32def computeWarpedBBox(destWcs, srcBBox, srcWcs): 

33 """Compute the bounding box of a warped image. 

34 

35 The bounding box includes all warped pixels and it may be a bit oversize. 

36 

37 Parameters 

38 ---------- 

39 destWcs : `lsst.afw.geom.SkyWcs` 

40 WCS of warped exposure 

41 srcBBox : `lsst.geom.Box2I` 

42 parent bounding box of unwarped image 

43 srcWcs : `lsst.afw.geom.SkyWcs` 

44 WCS of unwarped image 

45 

46 Returns 

47 ------- 

48 destBBox: `lsst.geom.Box2I` 

49 bounding box of warped exposure 

50 """ 

51 srcPosBox = lsst.geom.Box2D(srcBBox) 

52 destPosBox = lsst.geom.Box2D() 

53 for inX in (srcPosBox.getMinX(), srcPosBox.getMaxX()): 

54 for inY in (srcPosBox.getMinY(), srcPosBox.getMaxY()): 

55 destPos = destWcs.skyToPixel(srcWcs.pixelToSky(inX, inY)) 

56 destPosBox.include(destPos) 

57 destBBox = lsst.geom.Box2I(destPosBox, lsst.geom.Box2I.EXPAND) 

58 return destBBox 

59 

60 

61_DefaultInterpLength = 10 

62_DefaultCacheSize = 1000000 

63 

64 

65class WarperConfig(pexConfig.Config): 

66 warpingKernelName = pexConfig.ChoiceField( 

67 dtype=str, 

68 doc="Warping kernel", 

69 default="lanczos3", 

70 allowed={ 

71 "bilinear": "bilinear interpolation", 

72 "lanczos3": "Lanczos kernel of order 3", 

73 "lanczos4": "Lanczos kernel of order 4", 

74 "lanczos5": "Lanczos kernel of order 5", 

75 } 

76 ) 

77 maskWarpingKernelName = pexConfig.ChoiceField( 

78 dtype=str, 

79 doc="Warping kernel for mask (use ``warpingKernelName`` if '')", 

80 default="bilinear", 

81 allowed={ 

82 "": "use the regular warping kernel for the mask plane, as well as the image and variance planes", 

83 "bilinear": "bilinear interpolation", 

84 "lanczos3": "Lanczos kernel of order 3", 

85 "lanczos4": "Lanczos kernel of order 4", 

86 "lanczos5": "Lanczos kernel of order 5", 

87 } 

88 ) 

89 interpLength = pexConfig.Field( 

90 dtype=int, 

91 doc="``interpLength`` argument to `lsst.afw.math.warpExposure`", 

92 default=_DefaultInterpLength, 

93 ) 

94 cacheSize = pexConfig.Field( 

95 dtype=int, 

96 doc="``cacheSize`` argument to `lsst.afw.math.SeparableKernel.computeCache`", 

97 default=_DefaultCacheSize, 

98 ) 

99 growFullMask = pexConfig.Field( 

100 dtype=int, 

101 doc="mask bits to grow to full width of image/variance kernel,", 

102 default=afwImage.Mask.getPlaneBitMask("EDGE"), 

103 ) 

104 

105 

106class Warper: 

107 """Warp images. 

108 

109 Parameters 

110 ---------- 

111 warpingKernelName : `str` 

112 see `WarperConfig.warpingKernelName` 

113 interpLength : `int`, optional 

114 ``interpLength`` argument to `lsst.afw.math.warpExposure` 

115 cacheSize : `int`, optional 

116 size of computeCache 

117 maskWarpingKernelName : `str`, optional 

118 name of mask warping kernel (if ``""`` then use ``warpingKernelName``); 

119 see `WarperConfig.maskWarpingKernelName` 

120 growFullMask : `int`, optional 

121 mask bits to grow to full width of image/variance kernel 

122 """ 

123 ConfigClass = WarperConfig 

124 

125 def __init__(self, 

126 warpingKernelName, 

127 interpLength=_DefaultInterpLength, 

128 cacheSize=_DefaultCacheSize, 

129 maskWarpingKernelName="", 

130 growFullMask=afwImage.Mask.getPlaneBitMask("EDGE"),): 

131 self._warpingControl = WarpingControl( 

132 warpingKernelName, maskWarpingKernelName, cacheSize, interpLength, growFullMask) 

133 

134 @classmethod 

135 def fromConfig(cls, config): 

136 """Create a Warper from a config. 

137 

138 Parameters 

139 ---------- 

140 config : `WarperConfig` 

141 The config to initialize the Warper with. 

142 """ 

143 return cls( 

144 warpingKernelName=config.warpingKernelName, 

145 maskWarpingKernelName=config.maskWarpingKernelName, 

146 interpLength=config.interpLength, 

147 cacheSize=config.cacheSize, 

148 growFullMask=config.growFullMask, 

149 ) 

150 

151 def getWarpingKernel(self): 

152 """Get the warping kernel. 

153 """ 

154 return self._warpingControl.getWarpingKernel() 

155 

156 def getMaskWarpingKernel(self): 

157 """Get the mask warping kernel. 

158 """ 

159 return self._warpingControl.getMaskWarpingKernel() 

160 

161 def warpExposure(self, destWcs, srcExposure, border=0, maxBBox=None, destBBox=None): 

162 """Warp an exposure. 

163 

164 Parameters 

165 ----------- 

166 destWcs : `lsst.afw.geom.SkyWcs` 

167 WCS of warped exposure 

168 srcExposure 

169 exposure to warp 

170 border : `int`, optional 

171 grow bbox of warped exposure by this amount in all directions 

172 (in pixels); if negative then the bbox is shrunk; border is applied 

173 before ``maxBBox``; ignored if ``destBBox`` is not `None` 

174 maxBBox : `lsst.geom.Box2I`, optional 

175 maximum allowed parent bbox of warped exposure; if `None` then the 

176 warped exposure will be just big enough to contain all warped pixels; 

177 if provided then the warped exposure may be smaller, and so 

178 missing some warped pixels; ignored if ``destBBox`` is not `None` 

179 destBBox : `lsst.geom.Box2I`, optional 

180 exact parent bbox of warped exposure; if `None` then ``border`` and 

181 ``maxBBox`` are used to determine the bbox, otherwise ``border`` 

182 and ``maxBBox`` are ignored 

183 

184 Returns 

185 ------- 

186 destExposure : same type as ``srcExposure`` 

187 warped exposure 

188 

189 Notes 

190 ----- 

191 calls `lsst.afw.math.warpExposure` insted of `~Warper.warpImage` because the former 

192 copies attributes such as ``Calib``, and that should be done in one place 

193 

194 The PSF is not warped. To warp the PSF, use `lsst.meas.algorithms.WarpedPsf` 

195 """ 

196 destBBox = self._computeDestBBox( 

197 destWcs=destWcs, 

198 srcImage=srcExposure.getMaskedImage(), 

199 srcWcs=srcExposure.getWcs(), 

200 border=border, 

201 maxBBox=maxBBox, 

202 destBBox=destBBox, 

203 ) 

204 destExposure = srcExposure.Factory(destBBox, destWcs) 

205 warpExposure(destExposure, srcExposure, self._warpingControl) 

206 return destExposure 

207 

208 def warpImage(self, destWcs, srcImage, srcWcs, border=0, maxBBox=None, destBBox=None): 

209 """Warp an image or masked image. 

210 

211 Parameters 

212 ---------- 

213 destWcs : `lsst.afw.geom.SkyWcs` 

214 WCS of warped image 

215 srcImage 

216 image or masked image to warp 

217 srcWcs : `lsst.afw.geom.SkyWcs` 

218 WCS of image 

219 border : `int`, optional 

220 grow bbox of warped image by this amount in all directions 

221 (in pixels); if negative then the bbox is shrunk; border is applied 

222 before ``maxBBox``; ignored if ``destBBox`` is not `None` 

223 maxBBox : `lsst.geom.Box2I`, optional 

224 maximum allowed parent bbox of warped image; if `None` then the 

225 warped image will be just big enough to contain all warped pixels; 

226 if provided then the warped image may be smaller, and so 

227 missing some warped pixels; ignored if ``destBBox`` is not `None` 

228 destBBox : `lsst.geom.Box2I`, optional 

229 exact parent bbox of warped image; if `None` then ``border`` and 

230 ``maxBBox`` are used to determine the bbox, otherwise ``border`` 

231 and ``maxBBox`` are ignored 

232 

233 Returns 

234 ------- 

235 destImage : same type as ``srcExposure`` 

236 warped image or masked image 

237 """ 

238 destBBox = self._computeDestBBox( 

239 destWcs=destWcs, 

240 srcImage=srcImage, 

241 srcWcs=srcWcs, 

242 border=border, 

243 maxBBox=maxBBox, 

244 destBBox=destBBox, 

245 ) 

246 destImage = srcImage.Factory(destBBox) 

247 warpImage(destImage, destWcs, srcImage, 

248 srcWcs, self._warpingControl) 

249 return destImage 

250 

251 def _computeDestBBox(self, destWcs, srcImage, srcWcs, border, maxBBox, destBBox): 

252 """Process destBBox argument for warpImage and warpExposure. 

253 

254 Parameters 

255 ---------- 

256 destWcs : `lsst.afw.geom.SkyWcs` 

257 WCS of warped image 

258 srcImage 

259 image or masked image to warp 

260 srcWcs : `lsst.afw.geom.SkyWcs` 

261 WCS of image 

262 border : `int`, optional 

263 grow bbox of warped image by this amount in all directions 

264 (in pixels); if negative then the bbox is shrunk; border is applied 

265 before ``maxBBox``; ignored if ``destBBox`` is not `None` 

266 maxBBox : `lsst.geom.Box2I`, optional 

267 maximum allowed parent bbox of warped image; if `None` then the 

268 warped image will be just big enough to contain all warped pixels; 

269 if provided then the warped image may be smaller, and so 

270 missing some warped pixels; ignored if ``destBBox`` is not `None` 

271 destBBox : `lsst.geom.Box2I`, optional 

272 exact parent bbox of warped image; if `None` then ``border`` and 

273 ``maxBBox`` are used to determine the bbox, otherwise ``border`` 

274 and ``maxBBox`` are ignored 

275 """ 

276 if destBBox is None: # warning: == None fails due to Box2I.__eq__ 

277 destBBox = computeWarpedBBox( 

278 destWcs, srcImage.getBBox(afwImage.PARENT), srcWcs) 

279 if border: 

280 destBBox.grow(border) 

281 if maxBBox is not None: 

282 destBBox.clip(maxBBox) 

283 return destBBox