LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
scaleZeroPoint.py
Go to the documentation of this file.
1 from builtins import object
2 #
3 # LSST Data Management System
4 # Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <http://www.lsstcorp.org/LegalNotices/>.
22 #
23 import numpy
24 import lsst.afw.geom as afwGeom
25 import lsst.afw.image as afwImage
26 import lsst.pex.config as pexConfig
27 import lsst.pipe.base as pipeBase
28 from lsst.pipe.tasks.selectImages import BaseSelectImagesTask
29 
30 __all__ = ["ImageScaler", "SpatialImageScaler", "ScaleZeroPointTask"]
31 
32 
33 class ImageScaler(object):
34  """A class that scales an image
35 
36  This version uses a single scalar. Fancier versions may use a spatially varying scale.
37  """
38 
39  def __init__(self, scale=1.0):
40  """Construct an ImageScaler
41 
42  @param[in] scale: scale correction to apply (see scaleMaskedImage);
43  """
44  self._scale = scale
45 
46  def scaleMaskedImage(self, maskedImage):
47  """Scale the specified image or masked image in place.
48 
49  @param[in,out] maskedImage: masked image to scale
50  """
51  maskedImage *= self._scale
52 
53 
55  """Multiplicative image scaler using interpolation over a grid of points.
56 
57  Contains the x, y positions in tract coordinates and the scale factors.
58  Interpolates only when scaleMaskedImage() or getInterpImage() is called.
59 
60  Currently the only type of 'interpolation' implemented is CONSTANT which calculates the mean.
61  """
62 
63  def __init__(self, interpStyle, xList, yList, scaleList):
64  """Constructor
65 
66  @param[in] interpStyle: interpolation style (CONSTANT is only option)
67  @param[in] xList: list of X pixel positions
68  @param[in] yList: list of Y pixel positions
69  @param[in] scaleList: list of multiplicative scale factors at (x,y)
70 
71  @raise RuntimeError if the lists have different lengths
72  """
73  if len(xList) != len(yList) or len(xList) != len(scaleList):
74  raise RuntimeError(
75  "len(xList)=%s len(yList)=%s, len(scaleList)=%s but all lists must have the same length" %
76  (len(xList), len(yList), len(scaleList)))
77 
78  # Eventually want this do be: self.interpStyle = getattr(afwMath.Interpolate2D, interpStyle)
79  self._xList = xList
80  self._yList = yList
81  self._scaleList = scaleList
82 
83  def scaleMaskedImage(self, maskedImage):
84  """Apply scale correction to the specified masked image
85 
86  @param[in,out] image to scale; scale is applied in place
87  """
88  scale = self.getInterpImage(maskedImage.getBBox())
89  maskedImage *= scale
90 
91  def getInterpImage(self, bbox):
92  """Return an image containing the scale correction with same bounding box as supplied.
93 
94  @param[in] bbox: integer bounding box for image (afwGeom.Box2I)
95  """
96  npoints = len(self._xList)
97 
98  if npoints < 1:
99  raise RuntimeError("Cannot create scaling image. Found no fluxMag0s to interpolate")
100 
101  image = afwImage.ImageF(bbox, numpy.mean(self._scaleList))
102 
103  return image
104 
105 
106 class ScaleZeroPointConfig(pexConfig.Config):
107  """Config for ScaleZeroPointTask
108  """
109  zeroPoint = pexConfig.Field(
110  dtype=float,
111  doc="desired photometric zero point",
112  default=27.0,
113  )
114 
115 
117  selectFluxMag0 = pexConfig.ConfigurableField(
118  doc="Task to select data to compute spatially varying photometric zeropoint",
119  target=BaseSelectImagesTask,
120  )
121 
122  interpStyle = pexConfig.ChoiceField(
123  dtype=str,
124  doc="Algorithm to interpolate the flux scalings;"
125  "Currently only one choice implemented",
126  default="CONSTANT",
127  allowed={
128  "CONSTANT": "Use a single constant value",
129  }
130  )
131 
132 
133 class ScaleZeroPointTask(pipeBase.Task):
134  """Compute scale factor to scale exposures to a desired photometric zero point
135 
136  This simple version assumes that the zero point is spatially invariant.
137  """
138  ConfigClass = ScaleZeroPointConfig
139  _DefaultName = "scaleZeroPoint"
140 
141  def __init__(self, *args, **kwargs):
142  """Construct a ScaleZeroPointTask
143  """
144  pipeBase.Task.__init__(self, *args, **kwargs)
145 
146  # flux at mag=0 is 10^(zeroPoint/2.5) because m = -2.5*log10(F/F0)
147  fluxMag0 = 10**(0.4 * self.config.zeroPoint)
149  self._calib.setFluxMag0(fluxMag0)
150 
151  def run(self, exposure, dataRef=None):
152  """Scale the specified exposure to the desired photometric zeropoint
153 
154  @param[in,out] exposure: exposure to scale; masked image is scaled in place
155  @param[in] dataRef: dataRef for exposure.
156  Not used, but in API so that users can switch between spatially variant
157  and invariant tasks
158  @return a pipeBase.Struct containing:
159  - imageScaler: the image scaling object used to scale exposure
160  """
161  imageScaler = self.computeImageScaler(exposure=exposure, dataRef=dataRef)
162  mi = exposure.getMaskedImage()
163  imageScaler.scaleMaskedImage(mi)
164  return pipeBase.Struct(
165  imageScaler=imageScaler,
166  )
167 
168  def computeImageScaler(self, exposure, dataRef=None):
169  """Compute image scaling object for a given exposure.
170 
171  @param[in] exposure: exposure for which scaling is desired
172  @param[in] dataRef: dataRef for exposure.
173  Not used, but in API so that users can switch between spatially variant
174  and invariant tasks
175  """
176  scale = self.scaleFromCalib(exposure.getCalib()).scale
177  return ImageScaler(scale)
178 
179  def getCalib(self):
180  """Get desired Calib
181 
182  @return calibration (lsst.afw.image.Calib) with fluxMag0 set appropriately for config.zeroPoint
183  """
184  return self._calib
185 
186  def scaleFromCalib(self, calib):
187  """Compute the scale for the specified Calib
188 
189  Compute scale, such that if pixelCalib describes the photometric zeropoint of a pixel
190  then the following scales that pixel to the photometric zeropoint specified by config.zeroPoint:
191  scale = computeScale(pixelCalib)
192  pixel *= scale
193 
194  @return a pipeBase.Struct containing:
195  - scale, as described above.
196 
197  @note: returns a struct to leave room for scaleErr in a future implementation.
198  """
199  fluxAtZeroPoint = calib.getFlux(self.config.zeroPoint)
200  return pipeBase.Struct(
201  scale=1.0 / fluxAtZeroPoint,
202  )
203 
204  def scaleFromFluxMag0(self, fluxMag0):
205  """Compute the scale for the specified fluxMag0
206 
207  This is a wrapper around scaleFromCalib, which see for more information
208 
209  @param[in] fluxMag0
210  @return a pipeBase.Struct containing:
211  - scale, as described in scaleFromCalib.
212  """
213  calib = afwImage.Calib()
214  calib.setFluxMag0(fluxMag0)
215  return self.scaleFromCalib(calib)
216 
217 
219  """Compute spatially varying scale factor to scale exposures to a desired photometric zero point
220  """
221  ConfigClass = SpatialScaleZeroPointConfig
222  _DefaultName = "scaleZeroPoint"
223 
224  def __init__(self, *args, **kwargs):
225  ScaleZeroPointTask.__init__(self, *args, **kwargs)
226  self.makeSubtask("selectFluxMag0")
227 
228  def run(self, exposure, dataRef):
229  """Scale the specified exposure to the desired photometric zeropoint
230 
231  @param[in,out] exposure: exposure to scale; masked image is scaled in place
232  @param[in] dataRef: dataRef for exposure
233 
234  @return a pipeBase.Struct containing:
235  - imageScaler: the image scaling object used to scale exposure
236  """
237  imageScaler = self.computeImageScaler(exposure=exposure, dataRef=dataRef)
238  mi = exposure.getMaskedImage()
239  imageScaler.scaleMaskedImage(mi)
240  return pipeBase.Struct(
241  imageScaler=imageScaler,
242  )
243 
244  def computeImageScaler(self, exposure, dataRef):
245  """Compute image scaling object for a given exposure.
246 
247  @param[in] exposure: exposure for which scaling is desired. Only wcs and bbox are used.
248  @param[in] dataRef: dataRef of exposure
249  dataRef.dataId used to retrieve all applicable fluxMag0's from a database.
250  @return a SpatialImageScaler
251  """
252 
253  wcs = exposure.getWcs()
254 
255  fluxMagInfoList = self.selectFluxMag0.run(dataRef.dataId).fluxMagInfoList
256 
257  xList = []
258  yList = []
259  scaleList = []
260 
261  for fluxMagInfo in fluxMagInfoList:
262  # find center of field in tract coordinates
263  if not fluxMagInfo.coordList:
264  raise RuntimeError("no x,y data for fluxMagInfo")
265  ctr = afwGeom.Extent2D()
266  for coord in fluxMagInfo.coordList:
267  # accumulate x, y
268  ctr += afwGeom.Extent2D(wcs.skyToPixel(coord))
269  # and find average x, y as the center of the chip
270  ctr = afwGeom.Point2D(ctr / len(fluxMagInfo.coordList))
271  xList.append(ctr.getX())
272  yList.append(ctr.getY())
273  scaleList.append(self.scaleFromFluxMag0(fluxMagInfo.fluxMag0).scale)
274 
275  self.log.info("Found %d flux scales for interpolation: %s" % (len(scaleList),
276  ["%0.4f"%(s) for s in scaleList]))
277  return SpatialImageScaler(
278  interpStyle=self.config.interpStyle,
279  xList=xList,
280  yList=yList,
281  scaleList=scaleList,
282  )
Describe an exposure&#39;s calibration.
Definition: Calib.h:82