Coverage for python/lsst/afw/math/warper.py : 38%

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/>.
22__all__ = ["Warper", "WarperConfig"]
24import lsst.pex.config as pexConfig
25import lsst.geom
26import lsst.afw.image as afwImage
27from . import mathLib
30def computeWarpedBBox(destWcs, srcBBox, srcWcs):
31 """Compute the bounding box of a warped image.
33 The bounding box includes all warped pixels and it may be a bit oversize.
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
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
59_DefaultInterpLength = 10
60_DefaultCacheSize = 1000000
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 )
104class Warper:
105 """Warp images.
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
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)
132 @classmethod
133 def fromConfig(cls, config):
134 """Create a Warper from a config.
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 )
149 def getWarpingKernel(self):
150 """Get the warping kernel.
151 """
152 return self._warpingControl.getWarpingKernel()
154 def getMaskWarpingKernel(self):
155 """Get the mask warping kernel.
156 """
157 return self._warpingControl.getMaskWarpingKernel()
159 def warpExposure(self, destWcs, srcExposure, border=0, maxBBox=None, destBBox=None):
160 """Warp an exposure.
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
182 Returns
183 -------
184 destExposure : same type as ``srcExposure``
185 warped exposure
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
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
206 def warpImage(self, destWcs, srcImage, srcWcs, border=0, maxBBox=None, destBBox=None):
207 """Warp an image or masked image.
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
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
249 def _computeDestBBox(self, destWcs, srcImage, srcWcs, border, maxBBox, destBBox):
250 """Process destBBox argument for warpImage and warpExposure.
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