LSSTApplications  18.1.0
LSSTDataManagementBasePackage
Public Member Functions | Public Attributes | Static Public Attributes | List of all members
lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask Class Reference

Use astrometry.net to match input sources with a reference catalog and solve for the Wcs. More...

Inheritance diagram for lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask:

Public Member Functions

def __init__ (self, schema, refObjLoader=None, kwds)
 Create the astrometric calibration task. More...
 
def run (self, exposure, sourceCat)
 Load reference objects, match sources and optionally fit a WCS. More...
 
def solve (self, exposure, sourceCat)
 Match with reference sources and calculate an astrometric solution. More...
 
def distort (self, sourceCat, exposure)
 Calculate distorted source positions. More...
 
def loadAndMatch (self, exposure, sourceCat, bbox=None)
 Load reference objects overlapping an exposure and match to sources detected on that exposure. More...
 
def refitWcs (self, sourceCat, exposure, matches)
 A final Wcs solution after matching and removing distortion. More...
 

Public Attributes

 distortedName
 
 centroidXKey
 
 centroidYKey
 
 centroidXErrKey
 
 centroidYErrKey
 
 centroidFlagKey
 
 centroidKey
 
 centroidErrKey
 
 solver
 

Static Public Attributes

 ConfigClass = ANetAstrometryConfig
 

Detailed Description

Use astrometry.net to match input sources with a reference catalog and solve for the Wcs.

The actual matching and solving is done by the 'solver'; this Task serves as a wrapper for taking into account the known optical distortion.

Contents

Description

Use astrometry.net to match input sources with a reference catalog and solve for the Wcs.

Task initialisation

Create the astrometric calibration task. Most arguments are simply passed onto pipe.base.Task.

Parameters
schemaAn lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog
refObjLoaderThe AstrometryTask constructor requires a refObjLoader. In order to make this task retargettable for AstrometryTask it needs to take the same arguments. This argument will be ignored since it uses its own internal loader.
**kwdskeyword arguments to be passed to the lsst.pipe.base.task.Task constructor

A centroid field "centroid.distorted" (used internally during the Task's operation) will be added to the schema.

Invoking the Task

Load reference objects, match sources and optionally fit a WCS. This is a thin layer around solve or loadAndMatch, depending on config.forceKnownWcs

Parameters
[in,out]exposureexposure whose WCS is to be fit The following are read only:
  • bbox
  • photoCalib (may be absent)
  • filter (may be unset)
  • detector (if wcs is pure tangent; may be absent) The following are updated:
  • wcs (the initial value is used as an initial guess, and is required)
[in]sourceCatcatalog of sourceCat detected on the exposure (an lsst.afw.table.SourceCatalog)
Returns
an lsst.pipe.base.Struct with these fields:

Configuration parameters

See ANetAstrometryConfig

Debug variables

The command line task interface supports a flag -d to import debug.py from your PYTHONPATH; see Using lsstDebug to control debugging output for more about debug.py files.

The available variables in ANetAstrometryTask are:

display
If True call showAstrometry while iterating ANetAstrometryConfig.rejectIter times, and also after converging; and call displayAstrometry after applying the distortion correction.
frame
display frame to use in showAstrometry and displayAstrometry
pause
Pause after showAstrometry and displayAstrometry?

A complete example of using ANetAstrometryTask

See A complete example of using PhotoCalTask.

To investigate the Debug variables, put something like

import lsstDebug
def DebugInfo(name):
di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
if name in ("lsst.pipe.tasks.anetAstrometry", "lsst.pipe.tasks.anetBasicAstrometry"):
di.display = 1
di.frame = 1
di.pause = True
return di
lsstDebug.Info = DebugInfo

into your debug.py file and run photoCalTask.py with the –debug flag.

Definition at line 72 of file anetAstrometry.py.

Constructor & Destructor Documentation

◆ __init__()

def lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.__init__ (   self,
  schema,
  refObjLoader = None,
  kwds 
)

Create the astrometric calibration task.

Most arguments are simply passed onto pipe.base.Task.

Parameters
schemaAn lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog
refObjLoaderThe AstrometryTask constructor requires a refObjLoader. In order to make this task retargettable for AstrometryTask it needs to take the same arguments. This argument will be ignored since it uses its own internal loader.
**kwdskeyword arguments to be passed to the lsst.pipe.base.task.Task constructor

A centroid field "centroid.distorted" (used internally during the Task's operation) will be added to the schema.

Definition at line 145 of file anetAstrometry.py.

145  def __init__(self, schema, refObjLoader=None, **kwds):
146  r"""!Create the astrometric calibration task. Most arguments are simply passed onto pipe.base.Task.
147 
148  \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog
149  \param refObjLoader The AstrometryTask constructor requires a refObjLoader. In order to make this
150  task retargettable for AstrometryTask it needs to take the same arguments. This argument will be
151  ignored since it uses its own internal loader.
152  \param **kwds keyword arguments to be passed to the lsst.pipe.base.task.Task constructor
153 
154  A centroid field "centroid.distorted" (used internally during the Task's operation)
155  will be added to the schema.
156  """
157  pipeBase.Task.__init__(self, **kwds)
158  self.distortedName = "astrom_distorted"
159  self.centroidXKey = schema.addField(self.distortedName + "_x", type="D",
160  doc="centroid distorted for astrometry solver")
161  self.centroidYKey = schema.addField(self.distortedName + "_y", type="D",
162  doc="centroid distorted for astrometry solver")
163  self.centroidXErrKey = schema.addField(self.distortedName + "_xErr", type="F",
164  doc="centroid distorted err for astrometry solver")
165  self.centroidYErrKey = schema.addField(self.distortedName + "_yErr", type="F",
166  doc="centroid distorted err for astrometry solver")
167  self.centroidFlagKey = schema.addField(self.distortedName + "_flag", type="Flag",
168  doc="centroid distorted flag astrometry solver")
169  self.centroidKey = Point2DKey(self.centroidXKey, self.centroidYKey)
170  self.centroidErrKey = CovarianceMatrix2fKey((self.centroidXErrKey, self.centroidYErrKey))
171  # postpone making the solver subtask because it may not be needed and is expensive to create
172  self.solver = None
173 
PointKey< double > Point2DKey
Definition: aggregates.h:118
def __init__(self, minimum, dataRange, Q)

Member Function Documentation

◆ distort()

def lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.distort (   self,
  sourceCat,
  exposure 
)

Calculate distorted source positions.

CCD images are often affected by optical distortion that makes the astrometric solution higher order than linear. Unfortunately, most (all?) matching algorithms require that the distortion be small or zero, and so it must be removed. We do this by calculating (un-)distorted positions, based on a known optical distortion model in the Ccd.

The distortion correction moves sources, so we return the distorted bounding box.

Parameters
[in]exposureExposure to process
[in,out]sourceCatSourceCatalog; getX() and getY() will be used as inputs, with distorted points in "centroid.distorted" field.
Returns
bounding box of distorted exposure

Definition at line 236 of file anetAstrometry.py.

236  def distort(self, sourceCat, exposure):
237  r"""!Calculate distorted source positions
238 
239  CCD images are often affected by optical distortion that makes
240  the astrometric solution higher order than linear. Unfortunately,
241  most (all?) matching algorithms require that the distortion be
242  small or zero, and so it must be removed. We do this by calculating
243  (un-)distorted positions, based on a known optical distortion model
244  in the Ccd.
245 
246  The distortion correction moves sources, so we return the distorted bounding box.
247 
248  \param[in] exposure Exposure to process
249  \param[in,out] sourceCat SourceCatalog; getX() and getY() will be used as inputs,
250  with distorted points in "centroid.distorted" field.
251  \return bounding box of distorted exposure
252  """
253  detector = exposure.getDetector()
254  pixToTanXYTransform = None
255  if detector is None:
256  self.log.warn("No detector associated with exposure; assuming null distortion")
257  else:
258  pixToTanXYTransform = detector.getTransform(PIXELS, TAN_PIXELS)
259 
260  if pixToTanXYTransform is None:
261  self.log.info("Null distortion correction")
262  for s in sourceCat:
263  s.set(self.centroidKey, s.getCentroid())
264  s.set(self.centroidErrKey, s.getCentroidErr())
265  s.set(self.centroidFlagKey, s.getCentroidFlag())
266  return exposure.getBBox()
267 
268  # Distort source positions
269  self.log.info("Applying distortion correction")
270  for s in sourceCat:
271  centroid = pixToTanXYTransform.forwardTransform(s.getCentroid())
272  s.set(self.centroidKey, centroid)
273  s.set(self.centroidErrKey, s.getCentroidErr())
274  s.set(self.centroidFlagKey, s.getCentroidFlag())
275 
276  # Get distorted image size so that astrometry_net does not clip.
277  bboxD = afwGeom.Box2D()
278  for corner in detector.getCorners(TAN_PIXELS):
279  bboxD.include(corner)
280 
281  if lsstDebug.Info(__name__).display:
282  frame = lsstDebug.Info(__name__).frame
283  pause = lsstDebug.Info(__name__).pause
284  displayAstrometry(sourceCat=sourceCat, distortedCentroidKey=self.centroidKey,
285  exposure=exposure, frame=frame, pause=pause)
286 
287  return afwGeom.Box2I(bboxD)
288 
A floating-point coordinate rectangle geometry.
Definition: Box.h:305
def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, matches=None, frame=1, title="", pause=True)
Definition: display.py:34
An integer coordinate rectangle.
Definition: Box.h:54

◆ loadAndMatch()

def lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.loadAndMatch (   self,
  exposure,
  sourceCat,
  bbox = None 
)

Load reference objects overlapping an exposure and match to sources detected on that exposure.

Parameters
[in]exposureexposure whose WCS is to be fit
[in]sourceCatcatalog of sourceCat detected on the exposure (an lsst.afw.table.SourceCatalog)
[in]bboxbounding box go use for finding reference objects; if None, use exposure's bbox
Returns
an lsst.pipe.base.Struct with these fields:
Note
ignores config.forceKnownWcs

Definition at line 290 of file anetAstrometry.py.

290  def loadAndMatch(self, exposure, sourceCat, bbox=None):
291  """!Load reference objects overlapping an exposure and match to sources detected on that exposure
292 
293  @param[in] exposure exposure whose WCS is to be fit
294  @param[in] sourceCat catalog of sourceCat detected on the exposure (an lsst.afw.table.SourceCatalog)
295  @param[in] bbox bounding box go use for finding reference objects; if None, use exposure's bbox
296 
297  @return an lsst.pipe.base.Struct with these fields:
298  - refCat reference object catalog of objects that overlap the exposure (with some margin)
299  (an lsst::afw::table::SimpleCatalog)
300  - matches astrometric matches, a list of lsst.afw.table.ReferenceMatch
301  - matchMeta metadata about the field (an lsst.daf.base.PropertyList)
302 
303  @note ignores config.forceKnownWcs
304  """
305  bbox = exposure.getBBox()
306  if not self.solver:
307  self.makeSubtask("solver")
308 
309  astrom = self.solver.useKnownWcs(
310  sourceCat=sourceCat,
311  exposure=exposure,
312  bbox=bbox,
313  calculateSip=False,
314  )
315 
316  if astrom is None or astrom.getWcs() is None:
317  raise RuntimeError("Unable to solve astrometry")
318 
319  matches = astrom.getMatches()
320  matchMeta = astrom.getMatchMetadata()
321  if matches is None or len(matches) == 0:
322  raise RuntimeError("No astrometric matches")
323  self.log.info("%d astrometric matches", len(matches))
324 
325  if self._display:
326  frame = lsstDebug.Info(__name__).frame
327  displayAstrometry(exposure=exposure, sourceCat=sourceCat, matches=matches,
328  frame=frame, pause=False)
329 
330  return pipeBase.Struct(
331  refCat=astrom.refCat,
332  matches=matches,
333  matchMeta=matchMeta,
334  )
335 
def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, matches=None, frame=1, title="", pause=True)
Definition: display.py:34

◆ refitWcs()

def lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.refitWcs (   self,
  sourceCat,
  exposure,
  matches 
)

A final Wcs solution after matching and removing distortion.

Specifically, fitting the non-linear part, since the linear part has been provided by the matching engine.

Parameters
sourceCatSources on exposure, an lsst.afw.table.SourceCatalog
exposureExposure of interest, an lsst.afw.image.ExposureF or D
matchesAstrometric matches, as a list of lsst.afw.table.ReferenceMatch
Returns
the resolved-Wcs object, or None if config.solver.calculateSip is False.

Definition at line 382 of file anetAstrometry.py.

382  def refitWcs(self, sourceCat, exposure, matches):
383  """!A final Wcs solution after matching and removing distortion
384 
385  Specifically, fitting the non-linear part, since the linear
386  part has been provided by the matching engine.
387 
388  @param sourceCat Sources on exposure, an lsst.afw.table.SourceCatalog
389  @param exposure Exposure of interest, an lsst.afw.image.ExposureF or D
390  @param matches Astrometric matches, as a list of lsst.afw.table.ReferenceMatch
391 
392  @return the resolved-Wcs object, or None if config.solver.calculateSip is False.
393  """
394  sip = None
395  if self.config.solver.calculateSip:
396  self.log.info("Refitting WCS")
397  origMatches = matches
398  wcs = exposure.getWcs()
399 
400  import lsstDebug
401  display = lsstDebug.Info(__name__).display
402  frame = lsstDebug.Info(__name__).frame
403  pause = lsstDebug.Info(__name__).pause
404 
405  def fitWcs(initialWcs, title=None):
406  """!Do the WCS fitting and display of the results"""
407  sip = makeCreateWcsWithSip(matches, initialWcs, self.config.solver.sipOrder)
408  resultWcs = sip.getNewWcs()
409  if display:
410  showAstrometry(exposure, resultWcs, origMatches, matches, frame=frame,
411  title=title, pause=pause)
412  return resultWcs, sip.getScatterOnSky()
413 
414  numRejected = 0
415  try:
416  for i in range(self.config.rejectIter):
417  wcs, scatter = fitWcs(wcs, title="Iteration %d" % i)
418 
419  ref = np.array([wcs.skyToPixel(m.first.getCoord()) for m in matches])
420  src = np.array([m.second.getCentroid() for m in matches])
421  diff = ref - src
422  rms = diff.std()
423  trimmed = []
424  for d, m in zip(diff, matches):
425  if np.all(np.abs(d) < self.config.rejectThresh*rms):
426  trimmed.append(m)
427  else:
428  numRejected += 1
429  if len(matches) == len(trimmed):
430  break
431  matches = trimmed
432 
433  # Final fit after rejection iterations
434  wcs, scatter = fitWcs(wcs, title="Final astrometry")
435 
437  self.log.warn("Unable to fit SIP: %s", e)
438 
439  self.log.info("Astrometric scatter: %f arcsec (%d matches, %d rejected)",
440  scatter.asArcseconds(), len(matches), numRejected)
441  exposure.setWcs(wcs)
442 
443  # Apply WCS to sources
444  updateSourceCoords(wcs, sourceCat)
445  else:
446  self.log.warn("Not calculating a SIP solution; matches may be suspect")
447 
448  if self._display:
449  frame = lsstDebug.Info(__name__).frame
450  displayAstrometry(exposure=exposure, sourceCat=sourceCat, matches=matches,
451  frame=frame, pause=False)
452 
453  return sip
454 
455 
Reports attempts to exceed implementation-defined length limits for some classes. ...
Definition: Runtime.h:76
def showAstrometry(exposure, wcs, allMatches, useMatches, frame=0, title=None, pause=False)
Show results of astrometry fitting.
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList)
Update sky coordinates in a collection of source objects.
Definition: wcsUtils.cc:96
def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, matches=None, frame=1, title="", pause=True)
Definition: display.py:34
CreateWcsWithSip< MatchT > makeCreateWcsWithSip(std::vector< MatchT > const &matches, afw::geom::SkyWcs const &linearWcs, int const order, geom::Box2I const &bbox=geom::Box2I(), int const ngrid=0)
Factory function for CreateWcsWithSip.

◆ run()

def lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.run (   self,
  exposure,
  sourceCat 
)

Load reference objects, match sources and optionally fit a WCS.

This is a thin layer around solve or loadAndMatch, depending on config.forceKnownWcs

Parameters
[in,out]exposureexposure whose WCS is to be fit The following are read only:
  • bbox
  • photoCalib (may be absent)
  • filter (may be unset)
  • detector (if wcs is pure tangent; may be absent) The following are updated:
  • wcs (the initial value is used as an initial guess, and is required)
[in]sourceCatcatalog of sourceCat detected on the exposure (an lsst.afw.table.SourceCatalog)
Returns
an lsst.pipe.base.Struct with these fields:

Definition at line 175 of file anetAstrometry.py.

175  def run(self, exposure, sourceCat):
176  """!Load reference objects, match sources and optionally fit a WCS
177 
178  This is a thin layer around solve or loadAndMatch, depending on config.forceKnownWcs
179 
180  @param[in,out] exposure exposure whose WCS is to be fit
181  The following are read only:
182  - bbox
183  - photoCalib (may be absent)
184  - filter (may be unset)
185  - detector (if wcs is pure tangent; may be absent)
186  The following are updated:
187  - wcs (the initial value is used as an initial guess, and is required)
188  @param[in] sourceCat catalog of sourceCat detected on the exposure (an lsst.afw.table.SourceCatalog)
189  @return an lsst.pipe.base.Struct with these fields:
190  - refCat reference object catalog of objects that overlap the exposure (with some margin)
191  (an lsst::afw::table::SimpleCatalog)
192  - matches astrometric matches, a list of lsst.afw.table.ReferenceMatch
193  - matchMeta metadata about the field (an lsst.daf.base.PropertyList)
194  """
195  if self.config.forceKnownWcs:
196  return self.loadAndMatch(exposure=exposure, sourceCat=sourceCat)
197  else:
198  return self.solve(exposure=exposure, sourceCat=sourceCat)
199 

◆ solve()

def lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.solve (   self,
  exposure,
  sourceCat 
)

Match with reference sources and calculate an astrometric solution.

Parameters
[in,out]exposureExposure to calibrate; wcs is updated
[in]sourceCatcatalog of measured sources (an lsst.afw.table.SourceCatalog)
Returns
a pipeBase.Struct with fields:

The reference catalog actually used is up to the implementation of the solver; it will be manifested in the returned matches as a list of lsst.afw.table.ReferenceMatch objects (i.e. of lsst.afw.table.Match with first being of type lsst.afw.table.SimpleRecord and second type lsst.afw.table.SourceRecord — the reference object and matched object respectively).

Note
The input sources have the centroid slot moved to a new column "centroid.distorted" which has the positions corrected for any known optical distortion; the 'solver' (which is instantiated in the 'astrometry' member) should therefore simply use the centroids provided by calling afw.table.Source.getCentroid() on the individual source records. This column must be present in the sources table.
ignores config.forceKnownWcs

Definition at line 201 of file anetAstrometry.py.

201  def solve(self, exposure, sourceCat):
202  r"""!Match with reference sources and calculate an astrometric solution
203 
204  \param[in,out] exposure Exposure to calibrate; wcs is updated
205  \param[in] sourceCat catalog of measured sources (an lsst.afw.table.SourceCatalog)
206  \return a pipeBase.Struct with fields:
207  - refCat reference object catalog of objects that overlap the exposure (with some margin)
208  (an lsst::afw::table::SimpleCatalog)
209  - matches astrometric matches, a list of lsst.afw.table.ReferenceMatch
210  - matchMeta metadata about the field (an lsst.daf.base.PropertyList)
211 
212  The reference catalog actually used is up to the implementation
213  of the solver; it will be manifested in the returned matches as
214  a list of lsst.afw.table.ReferenceMatch objects (\em i.e. of lsst.afw.table.Match with
215  \c first being of type lsst.afw.table.SimpleRecord and \c second type lsst.afw.table.SourceRecord ---
216  the reference object and matched object respectively).
217 
218  \note
219  The input sources have the centroid slot moved to a new column "centroid.distorted"
220  which has the positions corrected for any known optical distortion;
221  the 'solver' (which is instantiated in the 'astrometry' member)
222  should therefore simply use the centroids provided by calling
223  afw.table.Source.getCentroid() on the individual source records. This column \em must
224  be present in the sources table.
225 
226  \note ignores config.forceKnownWcs
227  """
228  results = self._astrometry(sourceCat=sourceCat, exposure=exposure)
229 
230  if results.matches:
231  self.refitWcs(sourceCat=sourceCat, exposure=exposure, matches=results.matches)
232 
233  return results
234 

Member Data Documentation

◆ centroidErrKey

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.centroidErrKey

Definition at line 170 of file anetAstrometry.py.

◆ centroidFlagKey

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.centroidFlagKey

Definition at line 167 of file anetAstrometry.py.

◆ centroidKey

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.centroidKey

Definition at line 169 of file anetAstrometry.py.

◆ centroidXErrKey

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.centroidXErrKey

Definition at line 163 of file anetAstrometry.py.

◆ centroidXKey

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.centroidXKey

Definition at line 159 of file anetAstrometry.py.

◆ centroidYErrKey

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.centroidYErrKey

Definition at line 165 of file anetAstrometry.py.

◆ centroidYKey

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.centroidYKey

Definition at line 161 of file anetAstrometry.py.

◆ ConfigClass

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.ConfigClass = ANetAstrometryConfig
static

Definition at line 142 of file anetAstrometry.py.

◆ distortedName

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.distortedName

Definition at line 158 of file anetAstrometry.py.

◆ solver

lsst.meas.extensions.astrometryNet.anetAstrometry.ANetAstrometryTask.solver

Definition at line 172 of file anetAstrometry.py.


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