2__all__ = [
"MatchOptimisticBTask",
"MatchOptimisticBConfig",
10from .setMatchDistance
import setMatchDistance
11from .matchOptimisticB
import matchOptimisticB, MatchOptimisticBControl
15 """Stores match tolerances for use in `lsst.meas.astrom.AstrometryTask` and
16 later iterations of the matcher.
18 MatchOptimsiticBTask relies on a maximum distance for matching
19 set by either the default
in MatchOptimisticBConfig
or the 2 sigma
20 scatter found after AstrometryTask has fit
for a wcs.
25 Current maximum distance to consider a match.
33 """Configuration for MatchOptimisticBTask
35 maxMatchDistArcSec = pexConfig.RangeField(
36 doc="Maximum separation between reference objects and sources "
37 "beyond which they will not be considered a match (arcsec)",
42 numBrightStars = pexConfig.RangeField(
43 doc=
"Number of bright stars to use",
48 minMatchedPairs = pexConfig.RangeField(
49 doc=
"Minimum number of matched pairs; see also minFracMatchedPairs",
54 minFracMatchedPairs = pexConfig.RangeField(
55 doc=
"Minimum number of matched pairs as a fraction of the smaller of "
56 "the number of reference stars or the number of good sources; "
57 "the actual minimum is the smaller of this value or minMatchedPairs",
63 maxOffsetPix = pexConfig.RangeField(
64 doc=
"Maximum allowed shift of WCS, due to matching (pixel). "
65 "When changing this value, the LoadReferenceObjectsConfig.pixelMargin should also be updated.",
70 maxRotationDeg = pexConfig.RangeField(
71 doc=
"Rotation angle allowed between sources and position reference objects (degrees)",
76 allowedNonperpDeg = pexConfig.RangeField(
77 doc=
"Allowed non-perpendicularity of x and y (degree)",
82 numPointsForShape = pexConfig.Field(
83 doc=
"number of points to define a shape for matching",
87 maxDeterminant = pexConfig.Field(
88 doc=
"maximum determinant of linear transformation matrix for a usable solution",
104 """Match sources to reference objects using the Optimistic Pattern Matcher
105 B algorithm of Tabur 2007.
107 ConfigClass = MatchOptimisticBConfig
108 _DefaultName = "matchObjectsToSources"
111 pipeBase.Task.__init__(self, **kwargs)
114 """Extra filtering pass; subclass if desired.
119 Catalog of reference objects.
124 Reference catalog with some filtering applied. Currently no
125 filtering
is applied.
131 match_tolerance=None):
132 """Match sources to position reference stars.
137 Reference catalog to match.
139 Catalog of sources found on an exposure. This should already be
140 down-selected to "good"/
"usable" sources
in the calling Task.
142 Current WCS of the exposure containing the sources.
143 sourceFluxField : `str`
144 Field of the sourceCat to use
for flux
146 Field of the refCat to use
for flux
148 Object containing information
from previous
150 matching. If `
None`
is config defaults.
154 matchResult : `lsst.pipe.base.Struct`
155 Result struct
with components
157 - ``matches`` : List of matches
with distance below the maximum match
159 - ``useableSourceCat`` : Catalog of sources matched
and suited
for
161 - ``match_tolerance`` : MatchTolerance object updated
from this
167 preNumObj = len(refCat)
169 numRefObj = len(refCat)
172 self.log.info(
"filterStars purged %d reference stars, leaving %d stars" %
173 (preNumObj - numRefObj, numRefObj))
175 if match_tolerance
is None:
180 usableSourceCat = sourceCat
182 numUsableSources = len(usableSourceCat)
184 if len(usableSourceCat) == 0:
185 raise pipeBase.TaskError(
"No sources are usable")
187 minMatchedPairs = min(self.config.minMatchedPairs,
188 int(self.config.minFracMatchedPairs * min([len(refCat), len(usableSourceCat)])))
193 sourceCat=usableSourceCat,
195 refFluxField=refFluxField,
196 numUsableSources=numUsableSources,
197 minMatchedPairs=minMatchedPairs,
198 maxMatchDist=match_tolerance.maxMatchDist,
199 sourceFluxField=sourceFluxField,
200 verbose=debug.verbose,
206 for match
in usableMatches:
209 matches.append(match)
211 self.log.debug(
"Found %d usable matches, of which %d had good sources",
212 len(usableMatches), len(matches))
214 if len(matches) == 0:
215 raise RuntimeError(
"Unable to match sources")
217 self.log.info(
"Matched %d sources" % len(matches))
218 if len(matches) < minMatchedPairs:
219 self.log.warn(
"Number of matches is smaller than request")
221 return pipeBase.Struct(
223 usableSourceCat=usableSourceCat,
224 match_tolerance=match_tolerance,
227 def _getIsGoodKeys(self, schema):
228 """Retrieve the keys needed for the isGoodTest from the source catalog
236 self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey()
240 def _isGoodTest(self, source):
241 """Test that an object is good for use in the WCS fitter.
243 This is a hard coded version of the isGood flag
from the old SourceInfo
244 class that used to be part of this class.
254 Source passes CCD edge
and saturated tests.
256 return (
not source.get(self.
edgeKey)
261 def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs,
262 maxMatchDist, sourceFluxField, verbose):
263 """Implementation of matching sources to position reference stars.
265 Unlike matchObjectsToSources, this method does not check
if the sources
271 Catalog of reference objects.
273 Catalog of detected sources.
275 Current best WCS of the image.
276 refFluxFioeld : `str`
277 Name of flux field
in refCat to use.
278 numUsableSources : `int`
279 Total number of source usable
for matching.
280 mintMatchPairs : `int`
281 Minimum number of objects to match between the refCat
and sourceCat
282 to consider a valid match.
284 Maximum separation to considering a reference
and a source a match.
285 sourceFluxField : `str`
286 Name of source catalog flux field.
288 Print diagnostic information std::cout
294 numSources = len(sourceCat)
295 posRefBegInd = numUsableSources - numSources
296 if maxMatchDist
is None:
297 maxMatchDistArcSec = self.config.maxMatchDistArcSec
299 maxMatchDistArcSec = min(maxMatchDist.asArcseconds(), self.config.maxMatchDistArcSec)
300 configMatchDistPix = maxMatchDistArcSec/wcs.getPixelScale().asArcseconds()
302 matchControl = MatchOptimisticBControl()
303 matchControl.refFluxField = refFluxField
304 matchControl.sourceFluxField = sourceFluxField
305 matchControl.numBrightStars = self.config.numBrightStars
306 matchControl.minMatchedPairs = self.config.minMatchedPairs
307 matchControl.maxOffsetPix = self.config.maxOffsetPix
308 matchControl.numPointsForShape = self.config.numPointsForShape
309 matchControl.maxDeterminant = self.config.maxDeterminant
311 for maxRotInd
in range(4):
312 matchControl.maxRotationDeg = self.config.maxRotationDeg * math.pow(2.0, 0.5*maxRotInd)
313 for matchRadInd
in range(3):
314 matchControl.matchingAllowancePix = configMatchDistPix * math.pow(1.25, matchRadInd)
316 for angleDiffInd
in range(3):
317 matchControl.allowedNonperpDeg = self.config.allowedNonperpDeg*(angleDiffInd+1)
318 matches = matchOptimisticB(
326 if matches
is not None and len(matches) > 0:
327 setMatchDistance(matches)
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)