LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
LSST Data Management Base Package
Public Member Functions | Public Attributes | Static Public Attributes | List of all members
lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask Class Reference
Inheritance diagram for lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask:

Public Member Functions

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

Public Attributes

 outlierRejectionCtrl
 

Static Public Attributes

 ConfigClass = FitSipDistortionConfig
 

Detailed Description

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

Definition at line 101 of file fitSipDistortion.py.

Constructor & Destructor Documentation

◆ __init__()

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

Definition at line 107 of file fitSipDistortion.py.

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

Member Function Documentation

◆ display()

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

263  def display(self, revFitter, exposure=None, bbox=None, frame=0, pause=True):
264  """Display positions and outlier status overlaid on an image.
265 
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.
269 
270  Parameters
271  ----------
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
275  pixels.
276  exposure : :cpp:class:`lsst::afw::image::Exposure`
277  An Exposure or other displayable image on which matches can be
278  overplotted.
279  bbox : :cpp:class:`lsst::afw::geom::Box2I`
280  Bounding box of the region on which matches should be plotted.
281  """
282  data = revFitter.getData()
283  disp = lsst.afw.display.getDisplay(frame=frame)
284  if exposure is not None:
285  disp.mtv(exposure)
286  elif bbox is not None:
287  disp.mtv(exposure=lsst.afw.image.ExposureF(bbox))
288  else:
289  raise TypeError("At least one of 'exposure' and 'bbox' must be provided.")
290  data = revFitter.getData()
291  srcKey = lsst.afw.table.Point2DKey(data.schema["src"])
292  srcErrKey = lsst.afw.table.CovarianceMatrix2fKey(data.schema["src"], ["x", "y"])
293  refKey = lsst.afw.table.Point2DKey(data.schema["initial"])
294  modelKey = lsst.afw.table.Point2DKey(data.schema["model"])
295  rejectedKey = data.schema.find("rejected").key
296  with disp.Buffering():
297  for record in data:
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)
308  sEllipse = lsst.afw.geom.Quadrupole(sErr[0, 0], sErr[1, 1], sErr[0, 1])
309  disp.dot(sEllipse, sx, sy, ctype=colors[1])
310  if pause or pause is None: # default is to pause
311  print("Dropping into debugger to allow inspection of display. Type 'continue' when done.")
312  import pdb
313  pdb.set_trace()
314  return frame
315  else:
316  return frame + 1 # increment and return the frame for the next iteration.
317 
An ellipse core with quadrupole moments as parameters.
Definition: Quadrupole.h:47

◆ fitWcs()

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

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.
117 
118  Parameters
119  ----------
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
125 
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)
130 
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.
153 
154  Returns
155  -------
156  An lsst.pipe.base.Struct with the following fields:
157  - wcs : `lsst.afw.geom.SkyWcs`
158  The best-fit WCS.
159  - scatterOnSky : `lsst.geom.Angle`
160  The median on-sky separation between reference objects and
161  sources in "matches", as an `lsst.geom.Angle`
162  """
163  import lsstDebug
164  display = lsstDebug.Info(__name__).display
165  displayFrame = lsstDebug.Info(__name__).frame
166  displayPause = lsstDebug.Info(__name__).pause
167 
168  if bbox is None:
169  bbox = lsst.geom.Box2D()
170  for match in matches:
171  bbox.include(match.second.getCentroid())
172  bbox = lsst.geom.Box2I(bbox)
173 
174  wcs = self.makeInitialWcs(matches, initWcs)
175  cdMatrix = lsst.geom.LinearTransform(wcs.getCdMatrix())
176 
177  # Fit the "reverse" mapping from intermediate world coordinates to
178  # pixels, rejecting outliers. Fitting in this direction first makes it
179  # easier to handle the case where we have uncertainty on source
180  # positions but not reference positions. That's the case we have
181  # right now for purely bookeeeping reasons, and it may be the case we
182  # have in the future when we us Gaia as the reference catalog.
183  revFitter = ScaledPolynomialTransformFitter.fromMatches(self.config.order, matches, wcs,
184  self.config.refUncertainty)
185  revFitter.fit()
186  for nIter in range(self.config.numRejIter):
187  revFitter.updateModel()
188  intrinsicScatter = revFitter.updateIntrinsicScatter()
189  clippedSigma, nRejected = revFitter.rejectOutliers(self.outlierRejectionCtrl)
190  self.log.debug(
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
194  )
195  )
196  if display:
197  displayFrame = self.display(revFitter, exposure=exposure, bbox=bbox,
198  frame=displayFrame, displayPause=displayPause)
199  revFitter.fit()
200  revScaledPoly = revFitter.getTransform()
201  # Convert the generic ScaledPolynomialTransform result to SIP form
202  # with given CRPIX and CD (this is an exact conversion, up to
203  # floating-point round-off error)
204  sipReverse = SipReverseTransform.convert(revScaledPoly, wcs.getPixelOrigin(), cdMatrix)
205 
206  # Fit the forward mapping to a grid of points created from the reverse
207  # transform. Because that grid needs to be defined in intermediate
208  # world coordinates, and we don't have a good way to get from pixels to
209  # intermediate world coordinates yet (that's what we're fitting), we'll
210  # first grow the box to make it conservatively large...
211  gridBBoxPix = lsst.geom.Box2D(bbox)
212  gridBBoxPix.grow(self.config.gridBorder)
213  # ...and then we'll transform using just the CRPIX offset and CD matrix
214  # linear transform, which is the TAN-only (no SIP distortion, and
215  # hence approximate) mapping from pixels to intermediate world
216  # coordinates.
217  gridBBoxIwc = lsst.geom.Box2D()
218  for point in gridBBoxPix.getCorners():
219  point -= lsst.geom.Extent2D(wcs.getPixelOrigin())
220  gridBBoxIwc.include(cdMatrix(point))
221  fwdFitter = ScaledPolynomialTransformFitter.fromGrid(self.config.order, gridBBoxIwc,
222  self.config.nGridX, self.config.nGridY,
223  revScaledPoly)
224  fwdFitter.fit()
225  # Convert to SIP forward form.
226  fwdScaledPoly = fwdFitter.getTransform()
227  sipForward = SipForwardTransform.convert(fwdScaledPoly, wcs.getPixelOrigin(), cdMatrix)
228 
229  # Make a new WCS from the SIP transform objects and the CRVAL in the
230  # initial WCS.
231  wcs = makeWcs(sipForward, sipReverse, wcs.getSkyOrigin())
232 
233  if refCat is not None:
234  self.log.debug("Updating centroids in refCat")
235  lsst.afw.table.updateRefCentroids(wcs, refList=refCat)
236  else:
237  self.log.warn("Updating reference object centroids in match list; refCat is None")
238  lsst.afw.table.updateRefCentroids(wcs, refList=[match.first for match in matches])
239 
240  if sourceCat is not None:
241  self.log.debug("Updating coords in sourceCat")
242  lsst.afw.table.updateSourceCoords(wcs, sourceList=sourceCat)
243  else:
244  self.log.warn("Updating source coords in match list; sourceCat is None")
245  lsst.afw.table.updateSourceCoords(wcs, sourceList=[match.second for match in matches])
246 
247  self.log.debug("Updating distance in match list")
248  setMatchDistance(matches)
249 
250  stats = makeMatchStatisticsInRadians(wcs, matches, lsst.afw.math.MEDIAN)
251  scatterOnSky = stats.getValue()*lsst.geom.radians
252 
253  if scatterOnSky.asArcseconds() > self.config.maxScatterArcsec:
254  raise lsst.pipe.base.TaskError(
255  "Fit failed: median scatter on sky = %0.3f arcsec > %0.3f config.maxScatterArcsec" %
256  (scatterOnSky.asArcseconds(), self.config.maxScatterArcsec))
257 
258  return lsst.pipe.base.Struct(
259  wcs=wcs,
260  scatterOnSky=scatterOnSky,
261  )
262 
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:72
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList)
Update sky coordinates in a collection of source objects.
Definition: wcsUtils.cc:95
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.
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.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174

◆ makeInitialWcs()

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

318  def makeInitialWcs(self, matches, wcs):
319  """Generate a guess Wcs from the astrometric matches
320 
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
325  from the input Wcs.
326 
327  Parameters
328  ----------
329  matches : list of :cpp:class:`lsst::afw::table::ReferenceMatch`
330  A sequence of reference object/source matches.
331  The following fields are read:
332 
333  - match.first (reference object) coord
334  - match.second (source) centroid
335 
336  wcs : :cpp:class:`lsst::afw::geom::SkyWcs`
337  An initial WCS whose CD matrix is used as the CD matrix of the
338  result.
339 
340  Returns
341  -------
342  newWcs : `lsst.afw.geom.SkyWcs`
343  A new WCS guess.
344  """
345  crpix = lsst.geom.Extent2D(0, 0)
346  crval = lsst.sphgeom.Vector3d(0, 0, 0)
347  for mm in matches:
348  crpix += lsst.geom.Extent2D(mm.second.getCentroid())
349  crval += mm.first.getCoord().getVector()
350  crpix /= len(matches)
351  crval /= len(matches)
352  cd = wcs.getCdMatrix()
353  newWcs = lsst.afw.geom.makeSkyWcs(crpix=lsst.geom.Point2D(crpix),
354  crval=lsst.geom.SpherePoint(crval),
355  cdMatrix=cd)
356  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:44
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

◆ ConfigClass

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

Definition at line 104 of file fitSipDistortion.py.

◆ outlierRejectionCtrl

lsst.meas.astrom.fitSipDistortion.FitSipDistortionTask.outlierRejectionCtrl

Definition at line 109 of file fitSipDistortion.py.


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