1 from __future__
import absolute_import, division, print_function
2 from builtins
import zip
3 from builtins
import input
4 from builtins
import object
31 import matplotlib.pyplot
as plt
36 from lsst.afw.table
import SourceCatalog
37 from lsst.pipe.base
import Struct
38 import lsst.pex.config
as pexConfig
39 import lsst.afw.display.ds9
as ds9
40 from lsst.meas.algorithms
import BaseStarSelectorTask, starSelectorRegistry
41 from .
import psfexLib
42 from .psfex
import compute_fwhmrange
44 __all__ = [
"PsfexStarSelectorConfig",
"PsfexStarSelectorTask"]
48 fluxName = pexConfig.Field(
50 doc=
"Name of photometric flux key ",
51 default=
"base_PsfFlux",
53 fluxErrName = pexConfig.Field(
55 doc=
"Name of phot. flux err. key",
58 minFwhm = pexConfig.Field(
60 doc=
"Maximum allowed FWHM ",
63 maxFwhm = pexConfig.Field(
65 doc=
"Minimum allowed FWHM ",
68 maxFwhmVariability = pexConfig.Field(
70 doc=
"Allowed FWHM variability (1.0 = 100%)",
73 maxbad = pexConfig.Field(
75 doc=
"Max number of bad pixels ",
77 check=
lambda x: x >= 0,
79 maxbadflag = pexConfig.Field(
81 doc=
"Filter bad pixels? ",
84 maxellip = pexConfig.Field(
86 doc=
"Maximum (A-B)/(A+B) ",
88 check=
lambda x: x >= 0.0,
90 minsn = pexConfig.Field(
92 doc=
"Minimum S/N for candidates",
94 check=
lambda x: x >= 0.0,
98 pexConfig.Config.validate(self)
103 raise pexConfig.FieldValidationError(
"fluxErrName (%s) doesn't correspond to fluxName (%s)" 107 raise pexConfig.FieldValidationError(
"minFwhm (%f) > maxFwhm (%f)" % (self.
minFwhm, self.
maxFwhm))
111 "base_PixelFlags_flag_edge",
112 "base_PixelFlags_flag_saturatedCenter",
113 "base_PixelFlags_flag_crCenter",
114 "base_PixelFlags_flag_bad",
115 "base_PixelFlags_flag_suspectCenter",
122 """A class to handle key strokes with matplotlib displays""" 124 def __init__(self, axes, xs, ys, x, y, frames=[0]):
132 self.
cid = self.
axes.figure.canvas.mpl_connect(
'key_press_event', self)
135 if ev.inaxes != self.
axes:
138 if ev.key
and ev.key
in (
"p"):
139 dist = np.hypot(self.
xs - ev.xdata, self.
ys - ev.ydata)
140 dist[np.where(np.isnan(dist))] = 1e30
142 which = np.where(dist == min(dist))
147 ds9.pan(x, y, frame=frame)
148 ds9.cmdBuffer.flush()
155 def plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-',
167 axes = fig.add_axes((0.1, 0.1, 0.85, 0.80))
169 xmin = sorted(mag)[int(0.05*len(mag))]
170 xmax = sorted(mag)[int(0.95*len(mag))]
172 axes.set_xlim(-17.5, -13)
173 axes.set_xlim(xmin - 0.1*(xmax - xmin), xmax + 0.1*(xmax - xmin))
176 colors = [
"r", "g", "b", "c", "m", "k", ]
177 for k, mean
in enumerate(centers):
179 axes.plot(axes.get_xlim(), (mean, mean,),
"k%s" % ltype)
182 axes.plot(mag[l], width[l], marker, markersize=markersize, markeredgewidth=markeredgewidth,
183 color=colors[k%len(colors)])
185 l = (clusterId == -1)
186 axes.plot(mag[l], width[l], marker, markersize=markersize, markeredgewidth=markeredgewidth,
190 axes.set_xlabel(
"model")
191 axes.set_ylabel(
r"$\sqrt{I_{xx} + I_{yy}}$")
204 """!A star selector whose algorithm is not yet documented 206 @anchor PsfexStarSelectorTask_ 208 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Contents Contents 210 - @ref meas_extensions_psfex_psfexStarSelectorStarSelector_Purpose 211 - @ref meas_extensions_psfex_psfexStarSelectorStarSelector_Initialize 212 - @ref meas_extensions_psfex_psfexStarSelectorStarSelector_IO 213 - @ref meas_extensions_psfex_psfexStarSelectorStarSelector_Config 214 - @ref meas_extensions_psfex_psfexStarSelectorStarSelector_Debug 216 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Purpose Description 218 A star selector whose algorithm is not yet documented 220 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Initialize Task initialisation 222 @copydoc \_\_init\_\_ 224 @section meas_extensions_psfex_psfexStarSelectorStarSelector_IO Invoking the Task 226 Like all star selectors, the main method is `run`. 228 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Config Configuration parameters 230 See @ref PsfexStarSelectorConfig 232 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Debug Debug variables 234 PsfexStarSelectorTask has a debug dictionary with the following keys: 237 <dd>bool; if True display debug information 239 <dd>bool; if True display the exposure and spatial cells 240 <dt>plotFwhmHistogram 241 <dd>bool; if True plot histogram of FWHM 243 <dd>bool: if True plot the sources coloured by their flags 245 <dd>bool; if True plot why sources are rejected 248 For example, put something like: 252 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 253 if name.endswith("objectSizeStarSelector"): 255 di.displayExposure = True 256 di.plotFwhmHistogram = True 260 lsstDebug.Info = DebugInfo 262 into your `debug.py` file and run your task with the `--debug` flag. 264 ConfigClass = PsfexStarSelectorConfig
268 """!Select stars from source catalog 270 @param[in] exposure the exposure containing the sources 271 @param[in] sourceCat catalog of sources that may be stars (an lsst.afw.table.SourceCatalog) 272 @param[in] matches astrometric matches; ignored by this star selector 274 @return a Struct containing: 275 - starCat a subset of sourceCat containing the selected stars 278 display = lsstDebug.Info(__name__).display
280 displayExposure = display
and \
281 lsstDebug.Info(__name__).displayExposure
282 plotFwhmHistogram = display
and plt
and \
283 lsstDebug.Info(__name__).plotFwhmHistogram
284 plotFlags = display
and plt
and \
285 lsstDebug.Info(__name__).plotFlags
286 plotRejection = display
and plt
and \
287 lsstDebug.Info(__name__).plotRejection
291 fluxName = self.config.fluxName
292 fluxErrName = self.config.fluxErrName
293 minFwhm = self.config.minFwhm
294 maxFwhm = self.config.maxFwhm
295 maxFwhmVariability = self.config.maxFwhmVariability
296 maxbad = self.config.maxbad
297 maxbadflag = self.config.maxbadflag
298 maxellip = self.config.maxellip
299 minsn = self.config.minsn
301 maxelong = (maxellip + 1.0)/(1.0 - maxellip)
if maxellip < 1.0
else 100
304 shape = sourceCat.getShapeDefinition()
305 ixx = sourceCat.get(
"%s.xx" % shape)
306 iyy = sourceCat.get(
"%s.yy" % shape)
308 fwhm = 2*np.sqrt(2*np.log(2))*np.sqrt(0.5*(ixx + iyy))
309 elong = 0.5*(ixx - iyy)/(ixx + iyy)
311 flux = sourceCat.get(fluxName)
312 fluxErr = sourceCat.get(fluxErrName)
313 sn = flux/np.where(fluxErr > 0, fluxErr, 1)
314 sn[fluxErr <= 0] = -psfexLib.BIG
317 for i, f
in enumerate(self.config.badFlags):
318 flags = np.bitwise_or(flags, np.where(sourceCat.get(f), 1 << i, 0))
322 good = np.logical_and(sn > minsn, np.logical_not(flags))
323 good = np.logical_and(good, elong < maxelong)
324 good = np.logical_and(good, fwhm >= minFwhm)
325 good = np.logical_and(good, fwhm < maxFwhm)
327 fwhmMode, fwhmMin, fwhmMax =
compute_fwhmrange(fwhm[good], maxFwhmVariability, minFwhm, maxFwhm,
328 plot=dict(fwhmHistogram=plotFwhmHistogram))
340 selectionVectors = []
341 selectionVectors.append((bad,
"flags %d" % sum(bad)))
345 bad = np.logical_or(bad, dbad)
347 selectionVectors.append((dbad,
"S/N %d" % sum(dbad)))
349 dbad = fwhm < fwhmMin
351 bad = np.logical_or(bad, dbad)
353 selectionVectors.append((dbad,
"fwhmMin %d" % sum(dbad)))
355 dbad = fwhm > fwhmMax
357 bad = np.logical_or(bad, dbad)
359 selectionVectors.append((dbad,
"fwhmMax %d" % sum(dbad)))
361 dbad = elong > maxelong
363 bad = np.logical_or(bad, dbad)
365 selectionVectors.append((dbad,
"elong %d" % sum(dbad)))
369 nbad = np.array([(v <= -psfexLib.BIG).sum()
for v
in vignet])
372 bad = np.logical_or(bad, dbad)
374 selectionVectors.append((dbad,
"badpix %d" % sum(dbad)))
376 good = np.logical_not(bad)
382 mi = exposure.getMaskedImage()
384 ds9.mtv(mi, frame=frame, title=
"PSF candidates")
386 with ds9.Buffering():
387 for i, source
in enumerate(sourceCat):
393 ds9.dot(
"+", source.getX() - mi.getX0(), source.getY() - mi.getY0(),
394 frame=frame, ctype=ctype)
396 if plotFlags
or plotRejection:
397 imag = -2.5*np.log10(flux)
402 isSet = np.where(flags == 0x0)[0]
403 plt.plot(imag[isSet], fwhm[isSet],
'o', alpha=alpha, label=
"good")
405 for i, f
in enumerate(self.config.badFlags):
407 isSet = np.where(np.bitwise_and(flags, mask))[0]
409 if np.isfinite(imag[isSet] + fwhm[isSet]).any():
410 label = re.sub(
r"\_flag",
"",
411 re.sub(
r"^base\_",
"",
412 re.sub(
r"^.*base\_PixelFlags\_flag\_",
"", f)))
413 plt.plot(imag[isSet], fwhm[isSet],
'o', alpha=alpha, label=label)
415 for bad, label
in selectionVectors:
416 plt.plot(imag[bad], fwhm[bad],
'o', alpha=alpha, label=label)
418 plt.plot(imag[good], fwhm[good],
'o', color=
"black", label=
"selected")
419 [plt.axhline(_, color=
'red')
for _
in [fwhmMin, fwhmMax]]
420 plt.xlim(np.median(imag[good]) + 5*np.array([-1, 1]))
421 plt.ylim(fwhm[np.where(np.isfinite(fwhm + imag))].min(), 2*fwhmMax)
423 plt.xlabel(
"Instrumental %s Magnitude" % fluxName.split(
".")[-1].title())
425 title =
"PSFEX Star Selection" 426 plt.title(
"%s %d selected" % (title, sum(good)))
430 eventHandler =
EventHandler(plt.axes(), imag, fwhm, sourceCat.getX(), sourceCat.getY(),
433 if plotFlags
or plotRejection:
436 reply = input(
"continue? [y[es] h(elp) p(db) q(uit)] ").strip()
445 At this prompt, you can continue with almost any key; 'p' enters pdb, 446 'q' returns to the shell, and 452 If you put the cursor on a point in the matplotlib scatter plot and hit 'p' you'll see it in ds9.""")
453 elif reply[0] ==
"p":
456 elif reply[0] ==
'q':
461 starCat = SourceCatalog(sourceCat.schema)
462 for source, isGood
in zip(sourceCat, good):
464 starCat.append(source)
470 starSelectorRegistry.register(
"psfex", PsfexStarSelectorTask)
def selectStars(self, exposure, sourceCat, matches=None)
Select stars from source catalog.
def compute_fwhmrange(fwhm, maxvar, minin, maxin, plot=dict(fwhmHistogram=False))
def __init__(self, axes, xs, ys, x, y, frames=[0])
A star selector whose algorithm is not yet documented.
def plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-', clear=True)