22 __all__ = [
"FitSipDistortionTask",
"FitSipDistortionConfig"]
32 from .scaledPolynomialTransformFitter
import ScaledPolynomialTransformFitter, OutlierRejectionControl
33 from .sipTransform
import SipForwardTransform, SipReverseTransform, makeWcs
34 from .makeMatchStatistics
import makeMatchStatisticsInRadians
36 from .setMatchDistance
import setMatchDistance
40 """Config for FitSipDistortionTask""" 41 order = lsst.pex.config.RangeField(
42 doc=
"Order of SIP polynomial",
47 numRejIter = lsst.pex.config.RangeField(
48 doc=
"Number of rejection iterations",
53 rejSigma = lsst.pex.config.RangeField(
54 doc=
"Number of standard deviations for clipping level",
59 nClipMin = lsst.pex.config.Field(
60 doc=
"Minimum number of matches to reject when sigma-clipping",
64 nClipMax = lsst.pex.config.Field(
65 doc=
"Maximum number of matches to reject when sigma-clipping",
69 maxScatterArcsec = lsst.pex.config.RangeField(
70 doc=
"Maximum median scatter of a WCS fit beyond which the fit fails (arcsec); " 71 "be generous, as this is only intended to catch catastrophic failures",
76 refUncertainty = lsst.pex.config.Field(
77 doc=
"RMS uncertainty in reference catalog positions, in pixels. Will be added " 78 "in quadrature with measured uncertainties in the fit.",
82 nGridX = lsst.pex.config.Field(
83 doc=
"Number of X grid points used to invert the SIP reverse transform.",
87 nGridY = lsst.pex.config.Field(
88 doc=
"Number of Y grid points used to invert the SIP reverse transform.",
92 gridBorder = lsst.pex.config.Field(
93 doc=
"When setting the gird region, how much to extend the image " 94 "bounding box (in pixels) before transforming it to intermediate " 95 "world coordinates using the initial WCS.",
102 """Fit a TAN-SIP WCS given a list of reference object/source matches. 104 ConfigClass = FitSipDistortionConfig
105 _DefaultName =
"fitWcs" 108 lsst.pipe.base.Task.__init__(self, **kwargs)
114 @lsst.pipe.base.timeMethod
115 def fitWcs(self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None):
116 """Fit a TAN-SIP WCS from a list of reference object/source matches. 120 matches : `list` of `lsst.afw.table.ReferenceMatch` 121 A sequence of reference object/source matches. 122 The following fields are read: 123 - match.first (reference object) coord 124 - match.second (source) centroid 126 The following fields are written: 127 - match.first (reference object) centroid 128 - match.second (source) centroid 129 - match.distance (on sky separation, in radians) 131 initWcs : `lsst.afw.geom.SkyWcs` 132 An initial WCS whose CD matrix is used as the final CD matrix. 133 bbox : `lsst.geom.Box2I` 134 The region over which the WCS will be valid (PARENT pixel coordinates); 135 if `None` or an empty box then computed from matches 136 refCat : `lsst.afw.table.SimpleCatalog` 137 Reference object catalog, or `None`. 138 If provided then all centroids are updated with the new WCS, 139 otherwise only the centroids for ref objects in matches are updated. 140 Required fields are "centroid_x", "centroid_y", "coord_ra", and "coord_dec". 141 sourceCat : `lsst.afw.table.SourceCatalog` 142 Source catalog, or `None`. 143 If provided then coords are updated with the new WCS; 144 otherwise only the coords for sources in matches are updated. 145 Required input fields are "slot_Centroid_x", "slot_Centroid_y", 146 "slot_Centroid_xErr", "slot_Centroid_yErr", and optionally 147 "slot_Centroid_x_y_Cov". The "coord_ra" and "coord_dec" fields 148 will be updated but are not used as input. 149 exposure : `lsst.afw.image.Exposure` 150 An Exposure or other displayable image on which matches can be 151 overplotted. Ignored (and may be `None`) if display-based debugging 152 is not enabled via lsstDebug. 156 An lsst.pipe.base.Struct with the following fields: 157 - wcs : `lsst.afw.geom.SkyWcs` 159 - scatterOnSky : `lsst.geom.Angle` 160 The median on-sky separation between reference objects and 161 sources in "matches", as an `lsst.geom.Angle` 170 for match
in matches:
171 bbox.include(match.second.getCentroid())
183 revFitter = ScaledPolynomialTransformFitter.fromMatches(self.
config.order, matches, wcs,
184 self.
config.refUncertainty)
186 for nIter
in range(self.
config.numRejIter):
187 revFitter.updateModel()
188 intrinsicScatter = revFitter.updateIntrinsicScatter()
191 "Iteration {0}: intrinsic scatter is {1:4.3f} pixels, " 192 "rejected {2} outliers at {3:3.2f} sigma.".
format(
193 nIter+1, intrinsicScatter, nRejected, clippedSigma
197 displayFrame = self.
display(revFitter, exposure=exposure, bbox=bbox,
198 frame=displayFrame, displayPause=displayPause)
200 revScaledPoly = revFitter.getTransform()
204 sipReverse = SipReverseTransform.convert(revScaledPoly, wcs.getPixelOrigin(), cdMatrix)
212 gridBBoxPix.grow(self.
config.gridBorder)
218 for point
in gridBBoxPix.getCorners():
220 gridBBoxIwc.include(cdMatrix(point))
221 fwdFitter = ScaledPolynomialTransformFitter.fromGrid(self.
config.order, gridBBoxIwc,
226 fwdScaledPoly = fwdFitter.getTransform()
227 sipForward = SipForwardTransform.convert(fwdScaledPoly, wcs.getPixelOrigin(), cdMatrix)
231 wcs =
makeWcs(sipForward, sipReverse, wcs.getSkyOrigin())
233 if refCat
is not None:
234 self.
log.
debug(
"Updating centroids in refCat")
237 self.
log.
warn(
"Updating reference object centroids in match list; refCat is None")
240 if sourceCat
is not None:
241 self.
log.
debug(
"Updating coords in sourceCat")
244 self.
log.
warn(
"Updating source coords in match list; sourceCat is None")
247 self.
log.
debug(
"Updating distance in match list")
251 scatterOnSky = stats.getValue()*lsst.geom.radians
253 if scatterOnSky.asArcseconds() > self.
config.maxScatterArcsec:
255 "Fit failed: median scatter on sky = %0.3f arcsec > %0.3f config.maxScatterArcsec" %
256 (scatterOnSky.asArcseconds(), self.
config.maxScatterArcsec))
260 scatterOnSky=scatterOnSky,
263 def display(self, revFitter, exposure=None, bbox=None, frame=0, pause=True):
264 """Display positions and outlier status overlaid on an image. 266 This method is called by fitWcs when display debugging is enabled. It 267 always drops into pdb before returning to allow interactive inspection, 268 and hence it should never be called in non-interactive contexts. 272 revFitter : :cpp:class:`lsst::meas::astrom::ScaledPolynomialTransformFitter` 273 Fitter object initialized with `fromMatches` for fitting a "reverse" 274 distortion: the mapping from intermediate world coordinates to 276 exposure : :cpp:class:`lsst::afw::image::Exposure` 277 An Exposure or other displayable image on which matches can be 279 bbox : :cpp:class:`lsst::afw::geom::Box2I` 280 Bounding box of the region on which matches should be plotted. 282 data = revFitter.getData()
283 disp = lsst.afw.display.getDisplay(frame=frame)
284 if exposure
is not None:
286 elif bbox
is not None:
287 disp.mtv(exposure=lsst.afw.image.ExposureF(bbox))
289 raise TypeError(
"At least one of 'exposure' and 'bbox' must be provided.")
290 data = revFitter.getData()
292 srcErrKey = lsst.afw.table.CovarianceMatrix2fKey(data.schema[
"src"], [
"x",
"y"])
295 rejectedKey = data.schema.find(
"rejected").key
296 with disp.Buffering():
298 colors = ((lsst.afw.display.RED, lsst.afw.display.GREEN)
299 if not record.get(rejectedKey)
else 300 (lsst.afw.display.MAGENTA, lsst.afw.display.CYAN))
301 rx, ry = record.get(refKey)
302 disp.dot(
"x", rx, ry, size=10, ctype=colors[0])
303 mx, my = record.get(modelKey)
304 disp.dot(
"o", mx, my, size=10, ctype=colors[0])
305 disp.line([(rx, ry), (mx, my)], ctype=colors[0])
306 sx, sy = record.get(srcKey)
307 sErr = record.get(srcErrKey)
309 disp.dot(sEllipse, sx, sy, ctype=colors[1])
310 if pause
or pause
is None:
311 print(
"Dropping into debugger to allow inspection of display. Type 'continue' when done.")
319 """Generate a guess Wcs from the astrometric matches 321 We create a Wcs anchored at the center of the matches, with the scale 322 of the input Wcs. This is necessary because the Wcs may have a very 323 approximation position (as is common with telescoped-generated Wcs). 324 We're using the best of each: positions from the matches, and scale 329 matches : list of :cpp:class:`lsst::afw::table::ReferenceMatch` 330 A sequence of reference object/source matches. 331 The following fields are read: 333 - match.first (reference object) coord 334 - match.second (source) centroid 336 wcs : :cpp:class:`lsst::afw::geom::SkyWcs` 337 An initial WCS whose CD matrix is used as the CD matrix of the 342 newWcs : `lsst.afw.geom.SkyWcs` 349 crval += mm.first.getCoord().getVector()
350 crpix /= len(matches)
351 crval /= len(matches)
352 cd = wcs.getCdMatrix()
An ellipse core with quadrupole moments as parameters.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def fitWcs(self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None)
A floating-point coordinate rectangle geometry.
def display(self, revFitter, exposure=None, bbox=None, frame=0, pause=True)
def __init__(self, kwargs)
Vector3d is a vector in ℝ³ with components stored in double precision.
void updateRefCentroids(geom::SkyWcs const &wcs, ReferenceCollection &refList)
Update centroids in a collection of reference objects.
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList)
Update sky coordinates in a collection of source objects.
def makeInitialWcs(self, matches, wcs)
std::shared_ptr< afw::geom::SkyWcs > makeWcs(SipForwardTransform const &sipForward, SipReverseTransform const &sipReverse, geom::SpherePoint const &skyOrigin)
Create a new TAN SIP Wcs from a pair of SIP transforms and the sky origin.
afw::math::Statistics makeMatchStatisticsInRadians(afw::geom::SkyWcs const &wcs, std::vector< MatchT > const &matchList, int const flags, afw::math::StatisticsControl const &sctrl=afw::math::StatisticsControl())
Compute statistics of on-sky radial separation for a match list, in radians.
def setMatchDistance(matches)
Point in an unspecified spherical coordinate system.
std::shared_ptr< SkyWcs > makeSkyWcs(TransformPoint2ToPoint2 const &pixelsToFieldAngle, lsst::geom::Angle const &orientation, bool flipX, lsst::geom::SpherePoint const &boresight, std::string const &projection="TAN")
Construct a FITS SkyWcs from camera geometry.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
An integer coordinate rectangle.