1 from __future__
import absolute_import, division, print_function
3 __all__ = [
"matchOptimisticB",
"MatchOptimisticBTask",
"MatchOptimisticBConfig",
6 from builtins
import range
7 from builtins
import object
14 import lsst.pipe.base
as pipeBase
15 from lsst.meas.algorithms.sourceSelector
import sourceSelectorRegistry
17 from ..setMatchDistance
import setMatchDistance
18 from .
import matchOptimisticB, MatchOptimisticBControl
22 """ Stores match tolerances for use in AstrometryTask and later 23 iterations of the matcher. 27 maxMatchDist : lsst.afw.geom.Angle 31 """ MatchOptimsiticBTask relies on a maximum distance for matching 32 set by either the default in MatchOptimisticBConfig or the 2 sigma 33 scatter found after AstrometryTask has fit for a wcs. 39 """Configuration for MatchOptimisticBTask 41 maxMatchDistArcSec = pexConfig.RangeField(
42 doc=
"Maximum separation between reference objects and sources " 43 "beyond which they will not be considered a match (arcsec)",
48 numBrightStars = pexConfig.RangeField(
49 doc=
"Number of bright stars to use",
54 minMatchedPairs = pexConfig.RangeField(
55 doc=
"Minimum number of matched pairs; see also minFracMatchedPairs",
60 minFracMatchedPairs = pexConfig.RangeField(
61 doc=
"Minimum number of matched pairs as a fraction of the smaller of " 62 "the number of reference stars or the number of good sources; " 63 "the actual minimum is the smaller of this value or minMatchedPairs",
69 maxOffsetPix = pexConfig.RangeField(
70 doc=
"Maximum allowed shift of WCS, due to matching (pixel). " 71 "When changing this value, the LoadReferenceObjectsConfig.pixelMargin should also be updated.",
76 maxRotationDeg = pexConfig.RangeField(
77 doc=
"Rotation angle allowed between sources and position reference objects (degrees)",
82 allowedNonperpDeg = pexConfig.RangeField(
83 doc=
"Allowed non-perpendicularity of x and y (degree)",
88 numPointsForShape = pexConfig.Field(
89 doc=
"number of points to define a shape for matching",
93 maxDeterminant = pexConfig.Field(
94 doc=
"maximum determinant of linear transformation matrix for a usable solution",
98 sourceSelector = sourceSelectorRegistry.makeField(
99 doc=
"How to select sources for cross-matching",
105 sourceSelector.setDefaults()
118 """!Match sources to reference objects 120 @anchor MatchOptimisticBTask_ 122 @section meas_astrom_matchOptimisticB_Contents Contents 124 - @ref meas_astrom_matchOptimisticB_Purpose 125 - @ref meas_astrom_matchOptimisticB_Initialize 126 - @ref meas_astrom_matchOptimisticB_IO 127 - @ref meas_astrom_matchOptimisticB_Config 128 - @ref meas_astrom_matchOptimisticB_Example 129 - @ref meas_astrom_matchOptimisticB_Debug 131 @section meas_astrom_matchOptimisticB_Purpose Description 133 Match sources to reference objects. This is often done as a preliminary step to fitting an astrometric 134 or photometric solution. For details about the matching algorithm see matchOptimisticB.h 136 @section meas_astrom_matchOptimisticB_Initialize Task initialisation 138 @copydoc \_\_init\_\_ 140 @section meas_astrom_matchOptimisticB_IO Invoking the Task 142 @copydoc matchObjectsToSources 144 @section meas_astrom_matchOptimisticB_Config Configuration parameters 146 See @ref MatchOptimisticBConfig 148 To modify how usable sources are selected, specify a different source 149 selector in `config.sourceSelector`. 151 @section meas_astrom_matchOptimisticB_Example A complete example of using MatchOptimisticBTask 153 MatchOptimisticBTask is a subtask of AstrometryTask, which is called by PhotoCalTask. 154 See \ref pipe_tasks_photocal_Example. 156 @section meas_astrom_matchOptimisticB_Debug Debug variables 158 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 159 flag @c -d to import @b debug.py from your @c PYTHONPATH; see @ref baseDebug for more about 162 The available variables in MatchOptimisticBTask are: 164 <DT> @c verbose (bool) 165 <DD> If True then the matcher prints debug messages to stdout 168 To investigate the @ref meas_astrom_matchOptimisticB_Debug, put something like 172 debug = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 173 if name == "lsst.pipe.tasks.astrometry": 178 lsstDebug.Info = DebugInfo 180 into your debug.py file and run this task with the @c --debug flag. 182 ConfigClass = MatchOptimisticBConfig
183 _DefaultName =
"matchObjectsToSources" 186 pipeBase.Task.__init__(self, **kwargs)
187 self.makeSubtask(
"sourceSelector")
190 """Extra filtering pass; subclass if desired 196 match_tolerance=None):
197 """!Match sources to position reference stars 199 @param[in] refCat catalog of reference objects that overlap the exposure; reads fields for: 201 - the specified flux field 202 @param[in] sourceCat catalog of sources found on an exposure; reads fields for: 207 - aperture flux, if found, else PSF flux 208 @param[in] wcs estimated WCS 209 @param[in] refFluxField field of refCat to use for flux 210 @param[in] match_tolerance a MatchTolerance object for specifying 211 tolerances. Must at minimum contain a lsst.afw.geom.Angle 212 called maxMatchDist that communicates state between AstrometryTask 213 and the matcher Task. 214 @return an lsst.pipe.base.Struct with fields: 215 - matches a list of matches, each instance of lsst.afw.table.ReferenceMatch 216 - usableSourcCat a catalog of sources potentially usable for matching. 217 For this fitter usable sources include unresolved sources not too near the edge. 218 It includes saturated sources, even those these are removed from the final match list, 219 because saturated sources may be used to determine the match list. 224 preNumObj = len(refCat)
226 numRefObj = len(refCat)
229 self.log.info(
"filterStars purged %d reference stars, leaving %d stars" %
230 (preNumObj - numRefObj, numRefObj))
232 if match_tolerance
is None:
236 numSources = len(sourceCat)
237 selectedSources = self.sourceSelector.selectSources(sourceCat)
238 usableSourceCat = selectedSources.sourceCat
239 numUsableSources = len(usableSourceCat)
240 self.log.info(
"Purged %d unusable sources, leaving %d usable sources" %
241 (numSources - numUsableSources, numUsableSources))
243 if len(usableSourceCat) == 0:
244 raise pipeBase.TaskError(
"No sources are usable")
248 minMatchedPairs = min(self.config.minMatchedPairs,
249 int(self.config.minFracMatchedPairs * min([len(refCat), len(usableSourceCat)])))
254 sourceCat=usableSourceCat,
256 refFluxField=refFluxField,
257 numUsableSources=numUsableSources,
258 minMatchedPairs=minMatchedPairs,
259 maxMatchDist=match_tolerance.maxMatchDist,
260 sourceFluxField=self.sourceSelector.fluxField,
261 verbose=debug.verbose,
267 for match
in usableMatches:
270 matches.append(match)
272 self.log.debug(
"Found %d usable matches, of which %d had good sources",
273 len(usableMatches), len(matches))
275 if len(matches) == 0:
276 raise RuntimeError(
"Unable to match sources")
278 self.log.info(
"Matched %d sources" % len(matches))
279 if len(matches) < minMatchedPairs:
280 self.log.warn(
"Number of matches is smaller than request")
282 return pipeBase.Struct(
284 usableSourceCat=usableSourceCat,
285 match_tolerance=match_tolerance,
288 def _getIsGoodKeys(self, schema):
289 self.
edgeKey = schema[
"base_PixelFlags_flag_edge"].asKey()
293 def _isGoodTest(self, source):
295 This is a hard coded version of the isGood flag from the old SourceInfo class that used to be 296 part of this class. This is done current as the API for sourceSelector does not currently 299 return (
not source.get(self.
edgeKey)
and 304 def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs,
305 maxMatchDist, sourceFluxField, verbose):
306 """!Implementation of matching sources to position reference stars 308 Unlike matchObjectsToSources, this method does not check if the sources are suitable. 310 @param[in] refCat catalog of position reference stars that overlap an exposure 311 @param[in] sourceCat catalog of sources found on the exposure 312 @param[in] wcs estimated WCS of exposure 313 @param[in] refFluxField field of refCat to use for flux 314 @param[in] numUsableSources number of usable sources (sources with known centroid 315 that are not near the edge, but may be saturated) 316 @param[in] minMatchedPairs minimum number of matches 317 @param[in] maxMatchDist maximum on-sky distance between reference objects and sources 318 (an lsst.afw.geom.Angle); if specified then the smaller of config.maxMatchDistArcSec or 319 maxMatchDist is used; if None then config.maxMatchDistArcSec is used 320 @param[in] sourceFluxField Name of flux field in source catalog 321 @param[in] verbose true to print diagnostic information to std::cout 323 @return a list of matches, an instance of lsst.afw.table.ReferenceMatch 325 numSources = len(sourceCat)
326 posRefBegInd = numUsableSources - numSources
327 if maxMatchDist
is None:
328 maxMatchDistArcSec = self.config.maxMatchDistArcSec
330 maxMatchDistArcSec = min(maxMatchDist.asArcseconds(), self.config.maxMatchDistArcSec)
331 configMatchDistPix = maxMatchDistArcSec/wcs.getPixelScale().asArcseconds()
333 matchControl = MatchOptimisticBControl()
334 matchControl.refFluxField = refFluxField
335 matchControl.sourceFluxField = sourceFluxField
336 matchControl.numBrightStars = self.config.numBrightStars
337 matchControl.minMatchedPairs = self.config.minMatchedPairs
338 matchControl.maxOffsetPix = self.config.maxOffsetPix
339 matchControl.numPointsForShape = self.config.numPointsForShape
340 matchControl.maxDeterminant = self.config.maxDeterminant
342 for maxRotInd
in range(4):
343 matchControl.maxRotationDeg = self.config.maxRotationDeg * math.pow(2.0, 0.5*maxRotInd)
344 for matchRadInd
in range(3):
345 matchControl.matchingAllowancePix = configMatchDistPix * math.pow(1.25, matchRadInd)
347 for angleDiffInd
in range(3):
348 matchControl.allowedNonperpDeg = self.config.allowedNonperpDeg*(angleDiffInd+1)
357 if matches
is not None and len(matches) > 0:
def _isGoodTest(self, source)
def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs, maxMatchDist, sourceFluxField, verbose)
Implementation of matching sources to position reference stars.
def filterStars(self, refCat)
def __init__(self, kwargs)
def setMatchDistance(matches)
lsst::afw::table::ReferenceMatchVector matchOptimisticB(lsst::afw::table::SimpleCatalog const &posRefCat, lsst::afw::table::SourceCatalog const &sourceCat, MatchOptimisticBControl const &control, afw::geom::SkyWcs const &wcs, int posRefBegInd=0, bool verbose=false)
Match sources to stars in a position reference catalog using optimistic pattern matching B...
def __init__(self, maxMatchDist=None)
def matchObjectsToSources(self, refCat, sourceCat, wcs, refFluxField, match_tolerance=None)
Match sources to position reference stars.
Match sources to reference objects.
def _getIsGoodKeys(self, schema)