2 __all__ = [
"MatchOptimisticBTask",
"MatchOptimisticBConfig",
10 from .setMatchDistance
import setMatchDistance
11 from .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.
24 maxMatchDist : `lsst.geom.Angle`
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.
118 refCat : `lsst.afw.table.SimpleCatalog`
119 Catalog of reference objects.
123 trimmedRefCat : `lsst.afw.table.SimpleCatalog`
124 Reference catalog with some filtering applied. Currently no
125 filtering is applied.
131 match_tolerance=None):
132 """Match sources to position reference stars.
136 refCat : `lsst.afw.table.SimpleCatalog`
137 Reference catalog to match.
138 sourceCat : `lsst.afw.table.SourceCatalog`
139 Catalog of sources found on an exposure. This should already be
140 down-selected to "good"/"usable" sources in the calling Task.
141 wcs : `lsst.afw.geom.SkyWcs`
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
147 match_tolerance : `lsst.meas.astrom.MatchTolerance`
148 Object containing information from previous
149 `lsst.meas.astrom.AstrometryTask` match/fit cycles for use in
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
158 distance (`list` of `lsst.afw.table.ReferenceMatch`).
159 - ``useableSourceCat`` : Catalog of sources matched and suited for
160 WCS fitting (`lsst.afw.table.SourceCatalog`).
161 - ``match_tolerance`` : MatchTolerance object updated from this
162 match iteration (`lsst.meas.astrom.MatchTolerance`).
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)])))
191 usableMatches = self.
_doMatch_doMatch(
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
233 schema : `lsst.afw.table.Schema`
234 Source schema to retrieve `lsst.afw.table.Key` s from.
236 self.
edgeKeyedgeKey = schema[
"base_PixelFlags_flag_edge"].asKey()
238 self.
saturatedKeysaturatedKey = schema[
"base_PixelFlags_flag_saturated"].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.
248 source : `lsst.afw.table.SourceRecord`
254 Source passes CCD edge and saturated tests.
256 return (
not source.get(self.
edgeKeyedgeKey)
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
270 refCat : `lsst.afw.table.SimpleCatalog`
271 Catalog of reference objects.
272 sourceCat : `lsst.afw.table.SourceCatalog`
273 Catalog of detected sources.
274 wcs : `lsst.afw.geom.SkyWcs`
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.
283 maxMatchDist : `lsst.geom.Angle`
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
292 matches : `list` of `lsst.afw.table.ReferenceMatch`
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)
326 if matches
is not None and len(matches) > 0:
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)
def setMatchDistance(matches)
afw::table::ReferenceMatchVector matchOptimisticB(afw::table::SimpleCatalog const &posRefCat, 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.