LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
warper.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 import lsst.pex.config as pexConfig
23 import lsst.afw.geom as afwGeom
24 import lsst.afw.image as afwImage
25 import lsst.afw.gpu as afwGpu
26 import mathLib
27 
28 __all__ = ["Warper", "WarperConfig"]
29 
30 def 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  @param destWcs: WCS of warped exposure
36  @param srcBBox: parent bounding box of unwarped image
37  @param srcWcs: WCS of unwarped image
38 
39  @return destBBox: bounding box of warped exposure
40  """
41  srcPosBox = afwGeom.Box2D(srcBBox)
42  destPosBox = afwGeom.Box2D()
43  for inX in (srcPosBox.getMinX(), srcPosBox.getMaxX()):
44  for inY in (srcPosBox.getMinY(), srcPosBox.getMaxY()):
45  destPos = destWcs.skyToPixel(srcWcs.pixelToSky(afwGeom.Point2D(inX, inY)))
46  destPosBox.include(destPos)
47  destBBox = afwGeom.Box2I(destPosBox, afwGeom.Box2I.EXPAND)
48  return destBBox
49 
50 _DefaultInterpLength = 10
51 _DefaultCacheSize = 0
52 
53 class WarperConfig(pexConfig.Config):
54  warpingKernelName = pexConfig.ChoiceField(
55  dtype = str,
56  doc = "Warping kernel",
57  default = "lanczos4",
58  allowed = {
59  "bilinear": "bilinear interpolation",
60  "lanczos3": "Lanczos kernel of order 3",
61  "lanczos4": "Lanczos kernel of order 4",
62  "lanczos5": "Lanczos kernel of order 5",
63  }
64  )
65  maskWarpingKernelName = pexConfig.ChoiceField(
66  dtype = str,
67  doc = "Warping kernel for mask (use warpingKernelName if '')",
68  default = "bilinear",
69  allowed = {
70  "": "use the regular warping kernel for the mask plane, as well as the image and variance planes",
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  interpLength = pexConfig.Field(
78  dtype = int,
79  doc = "interpLength argument to lsst.afw.math.warpExposure",
80  default = _DefaultInterpLength,
81  )
82  cacheSize = pexConfig.Field(
83  dtype = int,
84  doc = "cacheSize argument to lsst.afw.math.SeparableKernel.computeCache",
85  default = _DefaultCacheSize,
86  )
87  devicePreference = pexConfig.Field(
88  dtype = int,
89  doc = "use GPU acceleration?",
90  default = afwGpu.DEFAULT_DEVICE_PREFERENCE,
91  )
92  growFullMask = pexConfig.Field(
93  dtype = int,
94  doc = "mask bits to grow to full width of image/variance kernel,",
95  default = afwImage.MaskU.getPlaneBitMask("EDGE"),
96  )
97 
98 class Warper(object):
99  """Warp images
100  """
101  ConfigClass = WarperConfig
102  def __init__(self,
103  warpingKernelName,
104  interpLength = _DefaultInterpLength,
105  cacheSize = _DefaultCacheSize,
106  maskWarpingKernelName = "",
107  devicePreference = afwGpu.DEFAULT_DEVICE_PREFERENCE,
108  growFullMask = afwImage.MaskU.getPlaneBitMask("EDGE"),
109  ):
110  """Create a Warper
111 
112  Inputs:
113  - warpingKernelName: argument to lsst.afw.math.makeWarpingKernel
114  - interpLength: interpLength argument to lsst.afw.warpExposure
115  - cacheSize: size of computeCache
116  - maskWarpingKernelName: name of mask warping kernel (if "" then use warpingKernelName);
117  an argument to lsst.afw.math.makeWarpingKernel
118  """
119  self._warpingControl = mathLib.WarpingControl(
120  warpingKernelName, maskWarpingKernelName, cacheSize, interpLength, devicePreference, growFullMask)
121 
122  @classmethod
123  def fromConfig(cls, config):
124  """Create a Warper from a config
125 
126  @param config: an instance of Warper.ConfigClass
127  """
128  return cls(
129  warpingKernelName = config.warpingKernelName,
130  maskWarpingKernelName = config.maskWarpingKernelName,
131  interpLength = config.interpLength,
132  cacheSize = config.cacheSize,
133  devicePreference = config.devicePreference,
134  growFullMask = config.growFullMask,
135  )
136 
137  def getWarpingKernel(self):
138  """Get the warping kernel"""
139  return self._warpingControl.getWarpingKernel()
140 
142  """Get the mask warping kernel"""
143  return self._warpingControl.getMaskWarpingKernel()
144 
145  def warpExposure(self, destWcs, srcExposure, border=0, maxBBox=None, destBBox=None):
146  """Warp an exposure
147 
148  @param destWcs: WCS of warped exposure
149  @param srcExposure: exposure to warp
150  @param border: grow bbox of warped exposure by this amount in all directions (int pixels);
151  if negative then the bbox is shrunk;
152  border is applied before maxBBox;
153  ignored if destBBox is not None
154  @param maxBBox: maximum allowed parent bbox of warped exposure (an afwGeom.Box2I or None);
155  if None then the warped exposure will be just big enough to contain all warped pixels;
156  if provided then the warped exposure may be smaller, and so missing some warped pixels;
157  ignored if destBBox is not None
158  @param destBBox: exact parent bbox of warped exposure (an afwGeom.Box2I or None);
159  if None then border and maxBBox are used to determine the bbox,
160  otherwise border and maxBBox are ignored
161 
162  @return destExposure: warped exposure (of same type as srcExposure)
163 
164  @note: calls mathLib.warpExposure insted of self.warpImage because the former
165  copies attributes such as Calib, and that should be done in one place
166  """
167  destBBox = self._computeDestBBox(
168  destWcs = destWcs,
169  srcImage = srcExposure.getMaskedImage(),
170  srcWcs = srcExposure.getWcs(),
171  border = border,
172  maxBBox = maxBBox,
173  destBBox = destBBox,
174  )
175  destExposure = srcExposure.Factory(destBBox, destWcs)
176  mathLib.warpExposure(destExposure, srcExposure, self._warpingControl)
177  return destExposure
178 
179  def warpImage(self, destWcs, srcImage, srcWcs, border=0, maxBBox=None, destBBox=None):
180  """Warp an image or masked image
181 
182  @param destWcs: WCS of warped image
183  @param srcImage: image or masked image to warp
184  @param srcWcs: WCS of image
185  @param border: grow bbox of warped image by this amount in all directions (int pixels);
186  if negative then the bbox is shrunk;
187  border is applied before maxBBox;
188  ignored if destBBox is not None
189  @param maxBBox: maximum allowed parent bbox of warped image (an afwGeom.Box2I or None);
190  if None then the warped image will be just big enough to contain all warped pixels;
191  if provided then the warped image may be smaller, and so missing some warped pixels;
192  ignored if destBBox is not None
193  @param destBBox: exact parent bbox of warped image (an afwGeom.Box2I or None);
194  if None then border and maxBBox are used to determine the bbox,
195  otherwise border and maxBBox are ignored
196 
197  @return destImage: warped image or masked image (of same type as srcImage)
198  """
199  destBBox = self._computeDestBBox(
200  destWcs = destWcs,
201  srcImage = srcImage,
202  srcWcs = srcWcs,
203  border = border,
204  maxBBox = maxBBox,
205  destBBox = destBBox,
206  )
207  destImage = srcImage.Factory(destBBox)
208  mathLib.warpImage(destImage, destWcs, srcImage, srcWcs, self._warpingControl)
209  return destImage
210 
211  def _computeDestBBox(self, destWcs, srcImage, srcWcs, border, maxBBox, destBBox):
212  """Process destBBox argument for warpImage and warpExposure
213 
214  @param destWcs: WCS of warped image
215  @param srcImage: image or masked image to warp
216  @param srcWcs: WCS of image
217  @param border: grow bbox of warped image by this amount in all directions (int pixels);
218  if negative then the bbox is shrunk;
219  border is applied before maxBBox;
220  ignored if destBBox is not None
221  @param maxBBox: maximum allowed parent bbox of warped image (an afwGeom.Box2I or None);
222  if None then the warped image will be just big enough to contain all warped pixels;
223  if provided then the warped image may be smaller, and so missing some warped pixels;
224  ignored if destBBox is not None
225  @param destBBox: exact parent bbox of warped image (an afwGeom.Box2I or None);
226  if None then border and maxBBox are used to determine the bbox,
227  otherwise border and maxBBox are ignored
228  """
229  if destBBox is None: # warning: == None fails due to Box2I.__eq__
230  destBBox = computeWarpedBBox(destWcs, srcImage.getBBox(afwImage.PARENT), srcWcs)
231  if border:
232  destBBox.grow(border)
233  if maxBBox is not None:
234  destBBox.clip(maxBBox)
235  return destBBox
An integer coordinate rectangle.
Definition: Box.h:53
A floating-point coordinate rectangle geometry.
Definition: Box.h:271