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