LSST Applications g0265f82a02+0e5473021a,g02d81e74bb+0dd8ce4237,g1470d8bcf6+3ea6592b6f,g2079a07aa2+86d27d4dc4,g2305ad1205+5ca4c0b359,g295015adf3+d10818ec9d,g2a9a014e59+6f9be1b9cd,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g3ddfee87b4+703ba97ebf,g487adcacf7+4fa16da234,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+ffa42b374e,g5a732f18d5+53520f316c,g64a986408d+0dd8ce4237,g858d7b2824+0dd8ce4237,g8a8a8dda67+585e252eca,g99cad8db69+d39438377f,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,ga8c6da7877+f1d96605c8,gb0e22166c9+60f28cb32d,gb6a65358fc+0e5473021a,gba4ed39666+c2a2e4ac27,gbb8dafda3b+e5339d463f,gc120e1dc64+da31e9920e,gc28159a63d+0e5473021a,gcf0d15dbbd+703ba97ebf,gdaeeff99f8+f9a426f77a,ge6526c86ff+889fc9d533,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gf18bd8381d+7268b93478,gff1a9f87cc+0dd8ce4237,w.2024.16
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Static Public Attributes | Static Protected Attributes | List of all members
lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask Class Reference
Inheritance diagram for lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask:

Public Member Functions

 __init__ (self, **kwargs)
 
 fitWcs (self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None)
 
 display (self, revFitter, exposure=None, bbox=None, frame=0, pause=True)
 
 makeInitialWcs (self, matches, wcs)
 

Public Attributes

 outlierRejectionCtrl
 

Static Public Attributes

 ConfigClass = FitSipDistortionConfig
 

Static Protected Attributes

str _DefaultName = "fitWcs"
 

Detailed Description

Fit a TAN-SIP WCS given a list of reference object/source matches.

Definition at line 104 of file fitSipDistortion.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.__init__ ( self,
** kwargs )

Definition at line 110 of file fitSipDistortion.py.

110 def __init__(self, **kwargs):
111 lsst.pipe.base.Task.__init__(self, **kwargs)
112 self.outlierRejectionCtrl = OutlierRejectionControl()
113 self.outlierRejectionCtrl.nClipMin = self.config.nClipMin
114 self.outlierRejectionCtrl.nClipMax = self.config.nClipMax
115 self.outlierRejectionCtrl.nSigma = self.config.rejSigma
116

Member Function Documentation

◆ display()

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.display ( self,
revFitter,
exposure = None,
bbox = None,
frame = 0,
pause = True )
Display positions and outlier status overlaid on an image.

This method is called by fitWcs when display debugging is enabled.  It
always drops into pdb before returning to allow interactive inspection,
and hence it should never be called in non-interactive contexts.

Parameters
----------
revFitter : :cpp:class:`lsst::meas::astrom::ScaledPolynomialTransformFitter`
    Fitter object initialized with `fromMatches` for fitting a "reverse"
    distortion: the mapping from intermediate world coordinates to
    pixels.
exposure : :cpp:class:`lsst::afw::image::Exposure`
    An Exposure or other displayable image on which matches can be
    overplotted.
bbox : :cpp:class:`lsst::afw::geom::Box2I`
    Bounding box of the region on which matches should be plotted.

Definition at line 265 of file fitSipDistortion.py.

265 def display(self, revFitter, exposure=None, bbox=None, frame=0, pause=True):
266 """Display positions and outlier status overlaid on an image.
267
268 This method is called by fitWcs when display debugging is enabled. It
269 always drops into pdb before returning to allow interactive inspection,
270 and hence it should never be called in non-interactive contexts.
271
272 Parameters
273 ----------
274 revFitter : :cpp:class:`lsst::meas::astrom::ScaledPolynomialTransformFitter`
275 Fitter object initialized with `fromMatches` for fitting a "reverse"
276 distortion: the mapping from intermediate world coordinates to
277 pixels.
278 exposure : :cpp:class:`lsst::afw::image::Exposure`
279 An Exposure or other displayable image on which matches can be
280 overplotted.
281 bbox : :cpp:class:`lsst::afw::geom::Box2I`
282 Bounding box of the region on which matches should be plotted.
283 """
284 data = revFitter.getData()
285 disp = lsst.afw.display.getDisplay(frame=frame)
286 if exposure is not None:
287 disp.mtv(exposure)
288 elif bbox is not None:
289 disp.mtv(exposure=lsst.afw.image.ExposureF(bbox))
290 else:
291 raise TypeError("At least one of 'exposure' and 'bbox' must be provided.")
292 data = revFitter.getData()
293 srcKey = lsst.afw.table.Point2DKey(data.schema["src"])
294 srcErrKey = lsst.afw.table.CovarianceMatrix2fKey(data.schema["src"], ["x", "y"])
295 refKey = lsst.afw.table.Point2DKey(data.schema["initial"])
296 modelKey = lsst.afw.table.Point2DKey(data.schema["model"])
297 rejectedKey = data.schema.find("rejected").key
298 with disp.Buffering():
299 for record in data:
300 colors = ((lsst.afw.display.RED, lsst.afw.display.GREEN)
301 if not record.get(rejectedKey) else
302 (lsst.afw.display.MAGENTA, lsst.afw.display.CYAN))
303 rx, ry = record.get(refKey)
304 disp.dot("x", rx, ry, size=10, ctype=colors[0])
305 mx, my = record.get(modelKey)
306 disp.dot("o", mx, my, size=10, ctype=colors[0])
307 disp.line([(rx, ry), (mx, my)], ctype=colors[0])
308 sx, sy = record.get(srcKey)
309 sErr = record.get(srcErrKey)
310 sEllipse = lsst.afw.geom.Quadrupole(sErr[0, 0], sErr[1, 1], sErr[0, 1])
311 disp.dot(sEllipse, sx, sy, ctype=colors[1])
312 if pause or pause is None: # default is to pause
313 print("Dropping into debugger to allow inspection of display. Type 'continue' when done.")
314 import pdb
315 pdb.set_trace()
316 return frame
317 else:
318 return frame + 1 # increment and return the frame for the next iteration.
319
An ellipse core with quadrupole moments as parameters.
Definition Quadrupole.h:47

◆ fitWcs()

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.fitWcs ( self,
matches,
initWcs,
bbox = None,
refCat = None,
sourceCat = None,
exposure = None )
Fit a TAN-SIP WCS from a list of reference object/source matches.

Parameters
----------
matches : `list` of `lsst.afw.table.ReferenceMatch`
    A sequence of reference object/source matches.
    The following fields are read:
    - match.first (reference object) coord
    - match.second (source) centroid

    The following fields are written:
    - match.first (reference object) centroid
    - match.second (source) centroid
    - match.distance (on sky separation, in radians)

initWcs : `lsst.afw.geom.SkyWcs`
    An initial WCS whose CD matrix is used as the final CD matrix.
bbox : `lsst.geom.Box2I`
    The region over which the WCS will be valid (PARENT pixel coordinates);
    if `None` or an empty box then computed from matches
refCat : `lsst.afw.table.SimpleCatalog`
    Reference object catalog, or `None`.
    If provided then all centroids are updated with the new WCS,
    otherwise only the centroids for ref objects in matches are updated.
    Required fields are "centroid_x", "centroid_y", "coord_ra", and "coord_dec".
sourceCat : `lsst.afw.table.SourceCatalog`
    Source catalog, or `None`.
    If provided then coords are updated with the new WCS;
    otherwise only the coords for sources in matches are updated.
    Required input fields are "slot_Centroid_x", "slot_Centroid_y",
    "slot_Centroid_xErr", "slot_Centroid_yErr", and optionally
    "slot_Centroid_x_y_Cov".  The "coord_ra" and "coord_dec" fields
    will be updated but are not used as input.
exposure : `lsst.afw.image.Exposure`
    An Exposure or other displayable image on which matches can be
    overplotted.  Ignored (and may be `None`) if display-based debugging
    is not enabled via lsstDebug.

Returns
-------
An lsst.pipe.base.Struct with the following fields:
    - wcs : `lsst.afw.geom.SkyWcs`
        The best-fit WCS.
    - scatterOnSky : `lsst.geom.Angle`
        The median on-sky separation between reference objects and
        sources in "matches", as an `lsst.geom.Angle`

Definition at line 118 of file fitSipDistortion.py.

118 def fitWcs(self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None):
119 """Fit a TAN-SIP WCS from a list of reference object/source matches.
120
121 Parameters
122 ----------
123 matches : `list` of `lsst.afw.table.ReferenceMatch`
124 A sequence of reference object/source matches.
125 The following fields are read:
126 - match.first (reference object) coord
127 - match.second (source) centroid
128
129 The following fields are written:
130 - match.first (reference object) centroid
131 - match.second (source) centroid
132 - match.distance (on sky separation, in radians)
133
134 initWcs : `lsst.afw.geom.SkyWcs`
135 An initial WCS whose CD matrix is used as the final CD matrix.
136 bbox : `lsst.geom.Box2I`
137 The region over which the WCS will be valid (PARENT pixel coordinates);
138 if `None` or an empty box then computed from matches
139 refCat : `lsst.afw.table.SimpleCatalog`
140 Reference object catalog, or `None`.
141 If provided then all centroids are updated with the new WCS,
142 otherwise only the centroids for ref objects in matches are updated.
143 Required fields are "centroid_x", "centroid_y", "coord_ra", and "coord_dec".
144 sourceCat : `lsst.afw.table.SourceCatalog`
145 Source catalog, or `None`.
146 If provided then coords are updated with the new WCS;
147 otherwise only the coords for sources in matches are updated.
148 Required input fields are "slot_Centroid_x", "slot_Centroid_y",
149 "slot_Centroid_xErr", "slot_Centroid_yErr", and optionally
150 "slot_Centroid_x_y_Cov". The "coord_ra" and "coord_dec" fields
151 will be updated but are not used as input.
152 exposure : `lsst.afw.image.Exposure`
153 An Exposure or other displayable image on which matches can be
154 overplotted. Ignored (and may be `None`) if display-based debugging
155 is not enabled via lsstDebug.
156
157 Returns
158 -------
159 An lsst.pipe.base.Struct with the following fields:
160 - wcs : `lsst.afw.geom.SkyWcs`
161 The best-fit WCS.
162 - scatterOnSky : `lsst.geom.Angle`
163 The median on-sky separation between reference objects and
164 sources in "matches", as an `lsst.geom.Angle`
165 """
166 import lsstDebug
167 display = lsstDebug.Info(__name__).display
168 displayFrame = lsstDebug.Info(__name__).frame
169 displayPause = lsstDebug.Info(__name__).pause
170
171 if bbox is None:
172 bbox = lsst.geom.Box2D()
173 for match in matches:
174 bbox.include(match.second.getCentroid())
175 bbox = lsst.geom.Box2I(bbox)
176
177 wcs = self.makeInitialWcs(matches, initWcs)
178 cdMatrix = lsst.geom.LinearTransform(wcs.getCdMatrix())
179
180 # Fit the "reverse" mapping from intermediate world coordinates to
181 # pixels, rejecting outliers. Fitting in this direction first makes it
182 # easier to handle the case where we have uncertainty on source
183 # positions but not reference positions. That's the case we have
184 # right now for purely bookeeeping reasons, and it may be the case we
185 # have in the future when we us Gaia as the reference catalog.
186 revFitter = ScaledPolynomialTransformFitter.fromMatches(self.config.order, matches, wcs,
187 self.config.refUncertainty)
188 revFitter.fit()
189 for nIter in range(self.config.numRejIter):
190 revFitter.updateModel()
191 intrinsicScatter = revFitter.updateIntrinsicScatter()
192 clippedSigma, nRejected = revFitter.rejectOutliers(self.outlierRejectionCtrl)
193 self.log.debug(
194 "Iteration %s: intrinsic scatter is %4.3f pixels, "
195 "rejected %d outliers at %3.2f sigma.",
196 nIter+1, intrinsicScatter, nRejected, clippedSigma
197 )
198 if display:
199 displayFrame = self.display(revFitter, exposure=exposure, bbox=bbox,
200 frame=displayFrame, displayPause=displayPause)
201 revFitter.fit()
202 revScaledPoly = revFitter.getTransform()
203 # Convert the generic ScaledPolynomialTransform result to SIP form
204 # with given CRPIX and CD (this is an exact conversion, up to
205 # floating-point round-off error)
206 sipReverse = SipReverseTransform.convert(revScaledPoly, wcs.getPixelOrigin(), cdMatrix)
207
208 # Fit the forward mapping to a grid of points created from the reverse
209 # transform. Because that grid needs to be defined in intermediate
210 # world coordinates, and we don't have a good way to get from pixels to
211 # intermediate world coordinates yet (that's what we're fitting), we'll
212 # first grow the box to make it conservatively large...
213 gridBBoxPix = lsst.geom.Box2D(bbox)
214 gridBBoxPix.grow(self.config.gridBorder)
215 # ...and then we'll transform using just the CRPIX offset and CD matrix
216 # linear transform, which is the TAN-only (no SIP distortion, and
217 # hence approximate) mapping from pixels to intermediate world
218 # coordinates.
219 gridBBoxIwc = lsst.geom.Box2D()
220 for point in gridBBoxPix.getCorners():
221 point -= lsst.geom.Extent2D(wcs.getPixelOrigin())
222 gridBBoxIwc.include(cdMatrix(point))
223 fwdFitter = ScaledPolynomialTransformFitter.fromGrid(self.config.order, gridBBoxIwc,
224 self.config.nGridX, self.config.nGridY,
225 revScaledPoly)
226 fwdFitter.fit()
227 # Convert to SIP forward form.
228 fwdScaledPoly = fwdFitter.getTransform()
229 sipForward = SipForwardTransform.convert(fwdScaledPoly, wcs.getPixelOrigin(), cdMatrix)
230
231 # Make a new WCS from the SIP transform objects and the CRVAL in the
232 # initial WCS.
233 wcs = makeWcs(sipForward, sipReverse, wcs.getSkyOrigin())
234
235 if refCat is not None:
236 self.log.debug("Updating centroids in refCat")
237 lsst.afw.table.updateRefCentroids(wcs, refList=refCat)
238 else:
239 self.log.warning("Updating reference object centroids in match list; refCat is None")
240 lsst.afw.table.updateRefCentroids(wcs, refList=[match.first for match in matches])
241
242 if sourceCat is not None:
243 self.log.debug("Updating coords in sourceCat")
244 lsst.afw.table.updateSourceCoords(wcs, sourceList=sourceCat)
245 else:
246 self.log.warning("Updating source coords in match list; sourceCat is None")
247 lsst.afw.table.updateSourceCoords(wcs, sourceList=[match.second for match in matches])
248
249 self.log.debug("Updating distance in match list")
250 setMatchDistance(matches)
251
252 stats = makeMatchStatisticsInRadians(wcs, matches, lsst.afw.math.MEDIAN)
253 scatterOnSky = stats.getValue()*lsst.geom.radians
254
255 if scatterOnSky.asArcseconds() > self.config.maxScatterArcsec:
256 raise exceptions.AstrometryFitFailure(
257 "Fit failed: median scatter on sky = %0.3f arcsec > %0.3f config.maxScatterArcsec" %
258 (scatterOnSky.asArcseconds(), self.config.maxScatterArcsec))
259
260 return lsst.pipe.base.Struct(
261 wcs=wcs,
262 scatterOnSky=scatterOnSky,
263 )
264
A floating-point coordinate rectangle geometry.
Definition Box.h:413
An integer coordinate rectangle.
Definition Box.h:55
A 2D linear coordinate transformation.
void updateRefCentroids(geom::SkyWcs const &wcs, ReferenceCollection &refList)
Update centroids in a collection of reference objects.
Definition wcsUtils.cc:73
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList, bool include_covariance=true)
Update sky coordinates in a collection of source objects.
Definition wcsUtils.cc:125

◆ makeInitialWcs()

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.makeInitialWcs ( self,
matches,
wcs )
Generate a guess Wcs from the astrometric matches

We create a Wcs anchored at the center of the matches, with the scale
of the input Wcs.  This is necessary because the Wcs may have a very
approximation position (as is common with telescoped-generated Wcs).
We're using the best of each: positions from the matches, and scale
from the input Wcs.

Parameters
----------
matches : list of :cpp:class:`lsst::afw::table::ReferenceMatch`
    A sequence of reference object/source matches.
    The following fields are read:

    - match.first (reference object) coord
    - match.second (source) centroid

wcs : :cpp:class:`lsst::afw::geom::SkyWcs`
    An initial WCS whose CD matrix is used as the CD matrix of the
    result.

Returns
-------
newWcs : `lsst.afw.geom.SkyWcs`
    A new WCS guess.

Definition at line 320 of file fitSipDistortion.py.

320 def makeInitialWcs(self, matches, wcs):
321 """Generate a guess Wcs from the astrometric matches
322
323 We create a Wcs anchored at the center of the matches, with the scale
324 of the input Wcs. This is necessary because the Wcs may have a very
325 approximation position (as is common with telescoped-generated Wcs).
326 We're using the best of each: positions from the matches, and scale
327 from the input Wcs.
328
329 Parameters
330 ----------
331 matches : list of :cpp:class:`lsst::afw::table::ReferenceMatch`
332 A sequence of reference object/source matches.
333 The following fields are read:
334
335 - match.first (reference object) coord
336 - match.second (source) centroid
337
338 wcs : :cpp:class:`lsst::afw::geom::SkyWcs`
339 An initial WCS whose CD matrix is used as the CD matrix of the
340 result.
341
342 Returns
343 -------
344 newWcs : `lsst.afw.geom.SkyWcs`
345 A new WCS guess.
346 """
347 crpix = lsst.geom.Extent2D(0, 0)
348 crval = lsst.sphgeom.Vector3d(0, 0, 0)
349 for mm in matches:
350 crpix += lsst.geom.Extent2D(mm.second.getCentroid())
351 crval += mm.first.getCoord().getVector()
352 crpix /= len(matches)
353 crval /= len(matches)
354 cd = wcs.getCdMatrix()
355 newWcs = lsst.afw.geom.makeSkyWcs(crpix=lsst.geom.Point2D(crpix),
356 crval=lsst.geom.SpherePoint(crval),
357 cdMatrix=cd)
358 return newWcs
Point in an unspecified spherical coordinate system.
Definition SpherePoint.h:57
Vector3d is a vector in ℝ³ with components stored in double precision.
Definition Vector3d.h:51
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition SkyWcs.cc:521

Member Data Documentation

◆ _DefaultName

str lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask._DefaultName = "fitWcs"
staticprotected

Definition at line 108 of file fitSipDistortion.py.

◆ ConfigClass

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.ConfigClass = FitSipDistortionConfig
static

Definition at line 107 of file fitSipDistortion.py.

◆ outlierRejectionCtrl

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.outlierRejectionCtrl

Definition at line 112 of file fitSipDistortion.py.


The documentation for this class was generated from the following file: