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