22 from __future__
import absolute_import, division, print_function
23 from builtins
import object
25 import lsst.afw.geom
as afwGeom
26 import lsst.afw.image
as afwImage
27 import lsst.pex.config
as pexConfig
28 import lsst.pipe.base
as pipeBase
31 __all__ = [
"ImageScaler",
"SpatialImageScaler",
"ScaleZeroPointTask"]
35 """A class that scales an image
37 This version uses a single scalar. Fancier versions may use a spatially varying scale.
41 """Construct an ImageScaler
43 @param[in] scale: scale correction to apply (see scaleMaskedImage);
48 """Scale the specified image or masked image in place.
50 @param[in,out] maskedImage: masked image to scale
56 """Multiplicative image scaler using interpolation over a grid of points.
58 Contains the x, y positions in tract coordinates and the scale factors.
59 Interpolates only when scaleMaskedImage() or getInterpImage() is called.
61 Currently the only type of 'interpolation' implemented is CONSTANT which calculates the mean.
64 def __init__(self, interpStyle, xList, yList, scaleList):
67 @param[in] interpStyle: interpolation style (CONSTANT is only option)
68 @param[in] xList: list of X pixel positions
69 @param[in] yList: list of Y pixel positions
70 @param[in] scaleList: list of multiplicative scale factors at (x,y)
72 @raise RuntimeError if the lists have different lengths
74 if len(xList) != len(yList)
or len(xList) != len(scaleList):
76 "len(xList)=%s len(yList)=%s, len(scaleList)=%s but all lists must have the same length" %
77 (len(xList), len(yList), len(scaleList)))
85 """Apply scale correction to the specified masked image
87 @param[in,out] image to scale; scale is applied in place
93 """Return an image containing the scale correction with same bounding box as supplied.
95 @param[in] bbox: integer bounding box for image (afwGeom.Box2I)
100 raise RuntimeError(
"Cannot create scaling image. Found no fluxMag0s to interpolate")
102 image = afwImage.ImageF(bbox, numpy.mean(self.
_scaleList))
108 """Config for ScaleZeroPointTask
110 zeroPoint = pexConfig.Field(
112 doc=
"desired photometric zero point",
118 selectFluxMag0 = pexConfig.ConfigurableField(
119 doc=
"Task to select data to compute spatially varying photometric zeropoint",
120 target=BaseSelectImagesTask,
123 interpStyle = pexConfig.ChoiceField(
125 doc=
"Algorithm to interpolate the flux scalings;"
126 "Currently only one choice implemented",
129 "CONSTANT":
"Use a single constant value",
135 """Compute scale factor to scale exposures to a desired photometric zero point
137 This simple version assumes that the zero point is spatially invariant.
139 ConfigClass = ScaleZeroPointConfig
140 _DefaultName =
"scaleZeroPoint"
143 """Construct a ScaleZeroPointTask
145 pipeBase.Task.__init__(self, *args, **kwargs)
148 fluxMag0 = 10**(0.4 * self.config.zeroPoint)
149 self.
_calib = afwImage.Calib()
150 self._calib.setFluxMag0(fluxMag0)
152 def run(self, exposure, dataRef=None):
153 """Scale the specified exposure to the desired photometric zeropoint
155 @param[in,out] exposure: exposure to scale; masked image is scaled in place
156 @param[in] dataRef: dataRef for exposure.
157 Not used, but in API so that users can switch between spatially variant
159 @return a pipeBase.Struct containing:
160 - imageScaler: the image scaling object used to scale exposure
163 mi = exposure.getMaskedImage()
164 imageScaler.scaleMaskedImage(mi)
165 return pipeBase.Struct(
166 imageScaler=imageScaler,
170 """Compute image scaling object for a given exposure.
172 @param[in] exposure: exposure for which scaling is desired
173 @param[in] dataRef: dataRef for exposure.
174 Not used, but in API so that users can switch between spatially variant
183 @return calibration (lsst.afw.image.Calib) with fluxMag0 set appropriately for config.zeroPoint
188 """Compute the scale for the specified Calib
190 Compute scale, such that if pixelCalib describes the photometric zeropoint of a pixel
191 then the following scales that pixel to the photometric zeropoint specified by config.zeroPoint:
192 scale = computeScale(pixelCalib)
195 @return a pipeBase.Struct containing:
196 - scale, as described above.
198 @note: returns a struct to leave room for scaleErr in a future implementation.
200 fluxAtZeroPoint = calib.getFlux(self.config.zeroPoint)
201 return pipeBase.Struct(
202 scale=1.0 / fluxAtZeroPoint,
206 """Compute the scale for the specified fluxMag0
208 This is a wrapper around scaleFromCalib, which see for more information
211 @return a pipeBase.Struct containing:
212 - scale, as described in scaleFromCalib.
214 calib = afwImage.Calib()
215 calib.setFluxMag0(fluxMag0)
220 """Compute spatially varying scale factor to scale exposures to a desired photometric zero point
222 ConfigClass = SpatialScaleZeroPointConfig
223 _DefaultName =
"scaleZeroPoint"
226 ScaleZeroPointTask.__init__(self, *args, **kwargs)
227 self.makeSubtask(
"selectFluxMag0")
229 def run(self, exposure, dataRef):
230 """Scale the specified exposure to the desired photometric zeropoint
232 @param[in,out] exposure: exposure to scale; masked image is scaled in place
233 @param[in] dataRef: dataRef for exposure
235 @return a pipeBase.Struct containing:
236 - imageScaler: the image scaling object used to scale exposure
239 mi = exposure.getMaskedImage()
240 imageScaler.scaleMaskedImage(mi)
241 return pipeBase.Struct(
242 imageScaler=imageScaler,
246 """Compute image scaling object for a given exposure.
248 @param[in] exposure: exposure for which scaling is desired. Only wcs and bbox are used.
249 @param[in] dataRef: dataRef of exposure
250 dataRef.dataId used to retrieve all applicable fluxMag0's from a database.
251 @return a SpatialImageScaler
254 wcs = exposure.getWcs()
256 fluxMagInfoList = self.selectFluxMag0.run(dataRef.dataId).fluxMagInfoList
262 for fluxMagInfo
in fluxMagInfoList:
264 if not fluxMagInfo.coordList:
265 raise RuntimeError(
"no x,y data for fluxMagInfo")
266 ctr = afwGeom.Extent2D()
267 for coord
in fluxMagInfo.coordList:
269 ctr += afwGeom.Extent2D(wcs.skyToPixel(coord))
271 ctr = afwGeom.Point2D(ctr / len(fluxMagInfo.coordList))
272 xList.append(ctr.getX())
273 yList.append(ctr.getY())
276 self.log.info(
"Found %d flux scales for interpolation: %s" % (len(scaleList),
277 [
"%0.4f"%(s)
for s
in scaleList]))
279 interpStyle=self.config.interpStyle,