Hide keyboard shortcuts

Hot-keys 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

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 . import mathLib 

28 

29 

30def computeWarpedBBox(destWcs, srcBBox, srcWcs): 

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

32 

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

34 

35 Parameters 

36 ---------- 

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

38 WCS of warped exposure 

39 srcBBox : `lsst.geom.Box2I` 

40 parent bounding box of unwarped image 

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

42 WCS of unwarped image 

43 

44 Returns 

45 ------- 

46 destBBox: `lsst.geom.Box2I` 

47 bounding box of warped exposure 

48 """ 

49 srcPosBox = lsst.geom.Box2D(srcBBox) 

50 destPosBox = lsst.geom.Box2D() 

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

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

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

54 destPosBox.include(destPos) 

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

56 return destBBox 

57 

58 

59_DefaultInterpLength = 10 

60_DefaultCacheSize = 1000000 

61 

62 

63class WarperConfig(pexConfig.Config): 

64 warpingKernelName = pexConfig.ChoiceField( 

65 dtype=str, 

66 doc="Warping kernel", 

67 default="lanczos3", 

68 allowed={ 

69 "bilinear": "bilinear interpolation", 

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

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

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

73 } 

74 ) 

75 maskWarpingKernelName = pexConfig.ChoiceField( 

76 dtype=str, 

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

78 default="bilinear", 

79 allowed={ 

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

81 "bilinear": "bilinear interpolation", 

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

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

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

85 } 

86 ) 

87 interpLength = pexConfig.Field( 

88 dtype=int, 

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

90 default=_DefaultInterpLength, 

91 ) 

92 cacheSize = pexConfig.Field( 

93 dtype=int, 

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

95 default=_DefaultCacheSize, 

96 ) 

97 growFullMask = pexConfig.Field( 

98 dtype=int, 

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

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

101 ) 

102 

103 

104class Warper: 

105 """Warp images. 

106 

107 Parameters 

108 ---------- 

109 warpingKernelName : `str` 

110 see `WarperConfig.warpingKernelName` 

111 interpLength : `int`, optional 

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

113 cacheSize : `int`, optional 

114 size of computeCache 

115 maskWarpingKernelName : `str`, optional 

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

117 see `WarperConfig.maskWarpingKernelName` 

118 growFullMask : `int`, optional 

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

120 """ 

121 ConfigClass = WarperConfig 

122 

123 def __init__(self, 

124 warpingKernelName, 

125 interpLength=_DefaultInterpLength, 

126 cacheSize=_DefaultCacheSize, 

127 maskWarpingKernelName="", 

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

129 self._warpingControl = mathLib.WarpingControl( 

130 warpingKernelName, maskWarpingKernelName, cacheSize, interpLength, growFullMask) 

131 

132 @classmethod 

133 def fromConfig(cls, config): 

134 """Create a Warper from a config. 

135 

136 Parameters 

137 ---------- 

138 config : `WarperConfig` 

139 The config to initialize the Warper with. 

140 """ 

141 return cls( 

142 warpingKernelName=config.warpingKernelName, 

143 maskWarpingKernelName=config.maskWarpingKernelName, 

144 interpLength=config.interpLength, 

145 cacheSize=config.cacheSize, 

146 growFullMask=config.growFullMask, 

147 ) 

148 

149 def getWarpingKernel(self): 

150 """Get the warping kernel. 

151 """ 

152 return self._warpingControl.getWarpingKernel() 

153 

154 def getMaskWarpingKernel(self): 

155 """Get the mask warping kernel. 

156 """ 

157 return self._warpingControl.getMaskWarpingKernel() 

158 

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

160 """Warp an exposure. 

161 

162 Parameters 

163 ----------- 

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

165 WCS of warped exposure 

166 srcExposure 

167 exposure to warp 

168 border : `int`, optional 

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

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

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

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

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

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

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

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

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

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

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

180 and ``maxBBox`` are ignored 

181 

182 Returns 

183 ------- 

184 destExposure : same type as ``srcExposure`` 

185 warped exposure 

186 

187 Notes 

188 ----- 

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

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

191 

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

193 """ 

194 destBBox = self._computeDestBBox( 

195 destWcs=destWcs, 

196 srcImage=srcExposure.getMaskedImage(), 

197 srcWcs=srcExposure.getWcs(), 

198 border=border, 

199 maxBBox=maxBBox, 

200 destBBox=destBBox, 

201 ) 

202 destExposure = srcExposure.Factory(destBBox, destWcs) 

203 mathLib.warpExposure(destExposure, srcExposure, self._warpingControl) 

204 return destExposure 

205 

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

207 """Warp an image or masked image. 

208 

209 Parameters 

210 ---------- 

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

212 WCS of warped image 

213 srcImage 

214 image or masked image to warp 

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

216 WCS of image 

217 border : `int`, optional 

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

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

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

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

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

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

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

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

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

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

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

229 and ``maxBBox`` are ignored 

230 

231 Returns 

232 ------- 

233 destImage : same type as ``srcExposure`` 

234 warped image or masked image 

235 """ 

236 destBBox = self._computeDestBBox( 

237 destWcs=destWcs, 

238 srcImage=srcImage, 

239 srcWcs=srcWcs, 

240 border=border, 

241 maxBBox=maxBBox, 

242 destBBox=destBBox, 

243 ) 

244 destImage = srcImage.Factory(destBBox) 

245 mathLib.warpImage(destImage, destWcs, srcImage, 

246 srcWcs, self._warpingControl) 

247 return destImage 

248 

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

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

251 

252 Parameters 

253 ---------- 

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

255 WCS of warped image 

256 srcImage 

257 image or masked image to warp 

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

259 WCS of image 

260 border : `int`, optional 

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

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

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

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

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

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

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

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

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

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

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

272 and ``maxBBox`` are ignored 

273 """ 

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

275 destBBox = computeWarpedBBox( 

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

277 if border: 

278 destBBox.grow(border) 

279 if maxBBox is not None: 

280 destBBox.clip(maxBBox) 

281 return destBBox