LSST Applications g0265f82a02+d6b5cd48b5,g02d81e74bb+a41d3748ce,g1470d8bcf6+6be6c9203b,g2079a07aa2+14824f138e,g212a7c68fe+a4f2ea4efa,g2305ad1205+72971fe858,g295015adf3+ab2c85acae,g2bbee38e9b+d6b5cd48b5,g337abbeb29+d6b5cd48b5,g3ddfee87b4+31b3a28dff,g487adcacf7+082e807817,g50ff169b8f+5929b3527e,g52b1c1532d+a6fc98d2e7,g591dd9f2cf+b2918d57ae,g5a732f18d5+66d966b544,g64a986408d+a41d3748ce,g858d7b2824+a41d3748ce,g8a8a8dda67+a6fc98d2e7,g99cad8db69+7fe4acdf18,g9ddcbc5298+d4bad12328,ga1e77700b3+246acaaf9c,ga8c6da7877+84af8b3ff8,gb0e22166c9+3863383f4c,gb6a65358fc+d6b5cd48b5,gba4ed39666+9664299f35,gbb8dafda3b+d8d527deb2,gc07e1c2157+b2dbe6b631,gc120e1dc64+61440b2abb,gc28159a63d+d6b5cd48b5,gcf0d15dbbd+31b3a28dff,gdaeeff99f8+a38ce5ea23,ge6526c86ff+39927bb362,ge79ae78c31+d6b5cd48b5,gee10cc3b42+a6fc98d2e7,gf1cff7945b+a41d3748ce,v24.1.5.rc1
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 103 of file fitSipDistortion.py.

Constructor & Destructor Documentation

◆ __init__()

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

Definition at line 109 of file fitSipDistortion.py.

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

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 264 of file fitSipDistortion.py.

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

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

319 def makeInitialWcs(self, matches, wcs):
320 """Generate a guess Wcs from the astrometric matches
321
322 We create a Wcs anchored at the center of the matches, with the scale
323 of the input Wcs. This is necessary because the Wcs may have a very
324 approximation position (as is common with telescoped-generated Wcs).
325 We're using the best of each: positions from the matches, and scale
326 from the input Wcs.
327
328 Parameters
329 ----------
330 matches : list of :cpp:class:`lsst::afw::table::ReferenceMatch`
331 A sequence of reference object/source matches.
332 The following fields are read:
333
334 - match.first (reference object) coord
335 - match.second (source) centroid
336
337 wcs : :cpp:class:`lsst::afw::geom::SkyWcs`
338 An initial WCS whose CD matrix is used as the CD matrix of the
339 result.
340
341 Returns
342 -------
343 newWcs : `lsst.afw.geom.SkyWcs`
344 A new WCS guess.
345 """
346 crpix = lsst.geom.Extent2D(0, 0)
347 crval = lsst.sphgeom.Vector3d(0, 0, 0)
348 for mm in matches:
349 crpix += lsst.geom.Extent2D(mm.second.getCentroid())
350 crval += mm.first.getCoord().getVector()
351 crpix /= len(matches)
352 crval /= len(matches)
353 cd = wcs.getCdMatrix()
354 newWcs = lsst.afw.geom.makeSkyWcs(crpix=lsst.geom.Point2D(crpix),
355 crval=lsst.geom.SpherePoint(crval),
356 cdMatrix=cd)
357 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 107 of file fitSipDistortion.py.

◆ ConfigClass

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

Definition at line 106 of file fitSipDistortion.py.

◆ outlierRejectionCtrl

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.outlierRejectionCtrl

Definition at line 111 of file fitSipDistortion.py.


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