2__all__ = [
"MatchOptimisticBTask",
"MatchOptimisticBConfig",
9from lsst.utils.timer
import timeMethod
11from .setMatchDistance
import setMatchDistance
12from .matchOptimisticB
import matchOptimisticB, MatchOptimisticBControl
16 """Stores match tolerances for use in `lsst.meas.astrom.AstrometryTask` and
17 later iterations of the matcher.
19 MatchOptimsiticBTask relies on a maximum distance for matching
20 set by either the default
in MatchOptimisticBConfig
or the 2 sigma
21 scatter found after AstrometryTask has fit
for a wcs.
26 Current maximum distance to consider a match.
34 """Configuration for MatchOptimisticBTask
36 maxMatchDistArcSec = pexConfig.RangeField(
37 doc="Maximum separation between reference objects and sources "
38 "beyond which they will not be considered a match (arcsec)",
43 numBrightStars = pexConfig.RangeField(
44 doc=
"Number of bright stars to use",
49 minMatchedPairs = pexConfig.RangeField(
50 doc=
"Minimum number of matched pairs; see also minFracMatchedPairs",
55 minFracMatchedPairs = pexConfig.RangeField(
56 doc=
"Minimum number of matched pairs as a fraction of the smaller of "
57 "the number of reference stars or the number of good sources; "
58 "the actual minimum is the smaller of this value or minMatchedPairs",
64 maxOffsetPix = pexConfig.RangeField(
65 doc=
"Maximum allowed shift of WCS, due to matching (pixel). "
66 "When changing this value, the LoadReferenceObjectsConfig.pixelMargin should also be updated.",
71 maxRotationDeg = pexConfig.RangeField(
72 doc=
"Rotation angle allowed between sources and position reference objects (degrees)",
77 allowedNonperpDeg = pexConfig.RangeField(
78 doc=
"Allowed non-perpendicularity of x and y (degree)",
83 numPointsForShape = pexConfig.Field(
84 doc=
"number of points to define a shape for matching",
88 maxDeterminant = pexConfig.Field(
89 doc=
"maximum determinant of linear transformation matrix for a usable solution",
105 """Match sources to reference objects using the Optimistic Pattern Matcher
106 B algorithm of Tabur 2007.
108 ConfigClass = MatchOptimisticBConfig
109 _DefaultName = "matchObjectsToSources"
112 pipeBase.Task.__init__(self, **kwargs)
115 """Extra filtering pass; subclass if desired.
120 Catalog of reference objects.
125 Reference catalog with some filtering applied. Currently no
126 filtering
is applied.
132 match_tolerance=None):
133 """Match sources to position reference stars.
138 Reference catalog to match.
140 Catalog of sources found on an exposure. This should already be
141 down-selected to "good"/
"usable" sources
in the calling Task.
143 Current WCS of the exposure containing the sources.
144 sourceFluxField : `str`
145 Field of the sourceCat to use
for flux
147 Field of the refCat to use
for flux
149 Object containing information
from previous
151 matching. If `
None`
is config defaults.
155 matchResult : `lsst.pipe.base.Struct`
156 Result struct
with components
158 - ``matches`` : List of matches
with distance below the maximum match
160 - ``useableSourceCat`` : Catalog of sources matched
and suited
for
162 - ``match_tolerance`` : MatchTolerance object updated
from this
168 preNumObj = len(refCat)
170 numRefObj = len(refCat)
173 self.log.info(
"filterStars purged %d reference stars, leaving %d stars",
174 preNumObj - numRefObj, numRefObj)
176 if match_tolerance
is None:
181 usableSourceCat = sourceCat
183 numUsableSources = len(usableSourceCat)
185 if len(usableSourceCat) == 0:
186 raise pipeBase.TaskError(
"No sources are usable")
188 minMatchedPairs =
min(self.config.minMatchedPairs,
189 int(self.config.minFracMatchedPairs *
min([len(refCat), len(usableSourceCat)])))
194 sourceCat=usableSourceCat,
196 refFluxField=refFluxField,
197 numUsableSources=numUsableSources,
198 minMatchedPairs=minMatchedPairs,
199 maxMatchDist=match_tolerance.maxMatchDist,
200 sourceFluxField=sourceFluxField,
201 verbose=debug.verbose,
207 for match
in usableMatches:
210 matches.append(match)
212 self.log.debug(
"Found %d usable matches, of which %d had good sources",
213 len(usableMatches), len(matches))
215 if len(matches) == 0:
216 raise RuntimeError(
"Unable to match sources")
218 self.log.info(
"Matched %d sources", len(matches))
219 if len(matches) < minMatchedPairs:
220 self.log.warning(
"Number of matches is smaller than request")
222 return pipeBase.Struct(
224 usableSourceCat=usableSourceCat,
225 match_tolerance=match_tolerance,
228 def _getIsGoodKeys(self, schema):
229 """Retrieve the keys needed for the isGoodTest from the source catalog
237 self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey()
241 def _isGoodTest(self, source):
242 """Test that an object is good for use in the WCS fitter.
244 This is a hard coded version of the isGood flag
from the old SourceInfo
245 class that used
to be part of this class.
255 Source passes CCD edge
and saturated tests.
257 return (
not source.get(self.
edgeKey)
262 def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs,
263 maxMatchDist, sourceFluxField, verbose):
264 """Implementation of matching sources to position reference stars.
266 Unlike matchObjectsToSources, this method does not check
if the sources
272 Catalog of reference objects.
274 Catalog of detected sources.
276 Current best WCS of the image.
277 refFluxFioeld : `str`
278 Name of flux field
in refCat to use.
279 numUsableSources : `int`
280 Total number of source usable
for matching.
281 mintMatchPairs : `int`
282 Minimum number of objects to match between the refCat
and sourceCat
283 to consider a valid match.
285 Maximum separation to considering a reference
and a source a match.
286 sourceFluxField : `str`
287 Name of source catalog flux field.
289 Print diagnostic information std::cout
295 numSources = len(sourceCat)
296 posRefBegInd = numUsableSources - numSources
297 if maxMatchDist
is None:
298 maxMatchDistArcSec = self.config.maxMatchDistArcSec
300 maxMatchDistArcSec =
min(maxMatchDist.asArcseconds(), self.config.maxMatchDistArcSec)
301 configMatchDistPix = maxMatchDistArcSec/wcs.getPixelScale().asArcseconds()
303 matchControl = MatchOptimisticBControl()
304 matchControl.refFluxField = refFluxField
305 matchControl.sourceFluxField = sourceFluxField
306 matchControl.numBrightStars = self.config.numBrightStars
307 matchControl.minMatchedPairs = self.config.minMatchedPairs
308 matchControl.maxOffsetPix = self.config.maxOffsetPix
309 matchControl.numPointsForShape = self.config.numPointsForShape
310 matchControl.maxDeterminant = self.config.maxDeterminant
312 for maxRotInd
in range(4):
313 matchControl.maxRotationDeg = self.config.maxRotationDeg * math.pow(2.0, 0.5*maxRotInd)
314 for matchRadInd
in range(3):
315 matchControl.matchingAllowancePix = configMatchDistPix * math.pow(1.25, matchRadInd)
317 for angleDiffInd
in range(3):
318 matchControl.allowedNonperpDeg = self.config.allowedNonperpDeg*(angleDiffInd+1)
319 matches = matchOptimisticB(
327 if matches
is not None and len(matches) > 0:
328 setMatchDistance(matches)
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
A class used as a handle to a particular field in a table.
Defines the fields and offsets for a table.
Custom catalog class for record/table subclasses that are guaranteed to have an ID,...
Record class that contains measurements made on a single exposure.
A class representing an angle.
def __init__(self, **kwargs)
def matchObjectsToSources(self, refCat, sourceCat, wcs, sourceFluxField, refFluxField, match_tolerance=None)
def _isGoodTest(self, source)
def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs, maxMatchDist, sourceFluxField, verbose)
def _getIsGoodKeys(self, schema)
def filterStars(self, refCat)
def __init__(self, maxMatchDist=None)
Lightweight representation of a geometric match between two records.