27 import matplotlib.pyplot
as plt
37from .psfex
import compute_fwhmrange
39__all__ = [
"PsfexStarSelectorConfig",
"PsfexStarSelectorTask"]
43 fluxName = pexConfig.Field(
45 doc=
"Name of photometric flux key ",
46 default=
"base_PsfFlux",
48 fluxErrName = pexConfig.Field(
50 doc=
"Name of phot. flux err. key",
53 minFwhm = pexConfig.Field(
55 doc=
"Maximum allowed FWHM ",
58 maxFwhm = pexConfig.Field(
60 doc=
"Minimum allowed FWHM ",
63 maxFwhmVariability = pexConfig.Field(
65 doc=
"Allowed FWHM variability (1.0 = 100%)",
68 maxbad = pexConfig.Field(
70 doc=
"Max number of bad pixels ",
72 check=
lambda x: x >= 0,
73 deprecated=
"This field has never worked and its code is gone. Will be removed after v21."
75 maxbadflag = pexConfig.Field(
77 doc=
"Filter bad pixels? ",
79 deprecated=
"This field has never worked and its code is gone. Will be removed after v21."
81 maxellip = pexConfig.Field(
83 doc=
"Maximum (A-B)/(A+B) ",
85 check=
lambda x: x >= 0.0,
87 minsn = pexConfig.Field(
89 doc=
"Minimum S/N for candidates",
91 check=
lambda x: x >= 0.0,
95 pexConfig.Config.validate(self)
100 msg = f
"fluxErrName ({self.fluxErrName}) doesn't correspond to fluxName ({self.fluxName})"
101 raise pexConfig.FieldValidationError(PsfexStarSelectorConfig.fluxName, self, msg)
104 raise pexConfig.FieldValidationError(PsfexStarSelectorConfig.minFwhm, self,
105 f
"minFwhm ({self.minFwhm}) > maxFwhm ({self.maxFwhm})")
109 "base_PixelFlags_flag_edge",
110 "base_PixelFlags_flag_saturatedCenter",
111 "base_PixelFlags_flag_crCenter",
112 "base_PixelFlags_flag_bad",
113 "base_PixelFlags_flag_suspectCenter",
120 """A class to handle key strokes with matplotlib displays
123 def __init__(self, axes, xs, ys, x, y, frames=[0]):
131 self.
cid = self.
axes.figure.canvas.mpl_connect(
'key_press_event', self)
134 if ev.inaxes != self.
axes:
137 if ev.key
and ev.key
in (
"p"):
138 dist = np.hypot(self.
xs - ev.xdata, self.
ys - ev.ydata)
139 dist[np.where(np.isnan(dist))] = 1e30
141 which = np.where(dist ==
min(dist))
146 disp = afwDisplay.Display(frame=frame)
153def plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-',
165 axes = fig.add_axes((0.1, 0.1, 0.85, 0.80))
167 xmin = sorted(mag)[int(0.05*len(mag))]
168 xmax = sorted(mag)[int(0.95*len(mag))]
170 axes.set_xlim(-17.5, -13)
171 axes.set_xlim(xmin - 0.1*(xmax - xmin), xmax + 0.1*(xmax - xmin))
174 colors = [
"r",
"g",
"b",
"c",
"m",
"k", ]
175 for k, mean
in enumerate(centers):
177 axes.plot(axes.get_xlim(), (mean, mean,),
"k%s" % ltype)
179 ll = (clusterId == k)
180 axes.plot(mag[ll], width[ll], marker, markersize=markersize, markeredgewidth=markeredgewidth,
181 color=colors[k%len(colors)])
183 ll = (clusterId == -1)
184 axes.plot(mag[ll], width[ll], marker, markersize=markersize, markeredgewidth=markeredgewidth,
188 axes.set_xlabel(
"model")
189 axes.set_ylabel(
r"$\sqrt{I_{xx} + I_{yy}}$")
201@pexConfig.registerConfigurable("psfex", sourceSelectorRegistry)
203 r"""A star selector whose algorithm is not yet documented.
205 @anchor PsfexStarSelectorTask_
207 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Contents Contents
209 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Purpose
210 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Initialize
211 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_IO
212 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Config
213 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Debug
215 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Purpose Description
217 A star selector whose algorithm
is not yet documented
219 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Initialize Task initialisation
221 @copydoc \_\_init\_\_
223 @section meas_extensions_psfex_psfexStarSelectorStarSelector_IO Invoking the Task
225 Like all star selectors, the main method
is `run`.
227 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Config Configuration parameters
229 See
@ref PsfexStarSelectorConfig
231 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Debug Debug variables
233 PsfexStarSelectorTask has a debug dictionary
with the following keys:
236 <dd>bool;
if True display debug information
238 <dd>bool;
if True display the exposure
and spatial cells
239 <dt>plotFwhmHistogram
240 <dd>bool;
if True plot histogram of FWHM
242 <dd>bool:
if True plot the sources coloured by their flags
244 <dd>bool;
if True plot why sources are rejected
247 For example, put something like:
252 if name.endswith(
"objectSizeStarSelector"):
254 di.displayExposure =
True
255 di.plotFwhmHistogram =
True
261 into your `debug.py` file
and run your task
with the `--debug` flag.
263 ConfigClass = PsfexStarSelectorConfig
267 """Return a selection of psf-like objects.
272 Catalog of sources to select from.
273 This catalog must be contiguous
in memory.
275 Ignored by this source selector.
277 The exposure the catalog was built
from; used
for debug display.
281 struct : `lsst.pipe.base.Struct`
282 The struct contains the following data:
284 - selected : `numpy.ndarray` of `bool``
285 Boolean array of sources that were selected, same length
as
291 displayExposure = display
and \
293 plotFwhmHistogram = display
and plt
and \
295 plotFlags = display
and plt
and \
297 plotRejection = display
and plt
and \
299 afwDisplay.setDefaultMaskTransparency(75)
301 fluxName = self.config.fluxName
302 fluxErrName = self.config.fluxErrName
303 minFwhm = self.config.minFwhm
304 maxFwhm = self.config.maxFwhm
305 maxFwhmVariability = self.config.maxFwhmVariability
306 maxellip = self.config.maxellip
307 minsn = self.config.minsn
309 maxelong = (maxellip + 1.0)/(1.0 - maxellip)
if maxellip < 1.0
else 100
312 shape = sourceCat.getShapeDefinition()
313 ixx = sourceCat.get(
"%s.xx" % shape)
314 iyy = sourceCat.get(
"%s.yy" % shape)
316 fwhm = 2*np.sqrt(2*np.log(2))*np.sqrt(0.5*(ixx + iyy))
317 elong = 0.5*(ixx - iyy)/(ixx + iyy)
319 flux = sourceCat.get(fluxName)
320 fluxErr = sourceCat.get(fluxErrName)
321 sn = flux/np.where(fluxErr > 0, fluxErr, 1)
322 sn[fluxErr <= 0] = -psfexLib.BIG
325 for i, f
in enumerate(self.config.badFlags):
326 flags = np.bitwise_or(flags, np.where(sourceCat.get(f), 1 << i, 0))
330 good = np.logical_and(sn > minsn, np.logical_not(flags))
331 good = np.logical_and(good, elong < maxelong)
332 good = np.logical_and(good, fwhm >= minFwhm)
333 good = np.logical_and(good, fwhm < maxFwhm)
335 fwhmMode, fwhmMin, fwhmMax = compute_fwhmrange(fwhm[good], maxFwhmVariability, minFwhm, maxFwhm,
336 plot=dict(fwhmHistogram=plotFwhmHistogram))
348 selectionVectors = []
349 selectionVectors.append((bad,
"flags %d" % sum(bad)))
353 bad = np.logical_or(bad, dbad)
355 selectionVectors.append((dbad,
"S/N %d" % sum(dbad)))
357 dbad = fwhm < fwhmMin
359 bad = np.logical_or(bad, dbad)
361 selectionVectors.append((dbad,
"fwhmMin %d" % sum(dbad)))
363 dbad = fwhm > fwhmMax
365 bad = np.logical_or(bad, dbad)
367 selectionVectors.append((dbad,
"fwhmMax %d" % sum(dbad)))
369 dbad = elong > maxelong
371 bad = np.logical_or(bad, dbad)
373 selectionVectors.append((dbad,
"elong %d" % sum(dbad)))
375 good = np.logical_not(bad)
381 mi = exposure.getMaskedImage()
382 disp = afwDisplay.Display(frame=frame)
383 disp.mtv(mi, title=
"PSF candidates")
385 with disp.Buffering():
386 for i, source
in enumerate(sourceCat):
388 ctype = afwDisplay.GREEN
390 ctype = afwDisplay.RED
392 disp.dot(
"+", source.getX() - mi.getX0(), source.getY() - mi.getY0(),
395 if plotFlags
or plotRejection:
396 imag = -2.5*np.log10(flux)
401 isSet = np.where(flags == 0x0)[0]
402 plt.plot(imag[isSet], fwhm[isSet],
'o', alpha=alpha, label=
"good")
404 for i, f
in enumerate(self.config.badFlags):
406 isSet = np.where(np.bitwise_and(flags, mask))[0]
408 if np.isfinite(imag[isSet] + fwhm[isSet]).any():
409 label = re.sub(
r"\_flag",
"",
410 re.sub(
r"^base\_",
"",
411 re.sub(
r"^.*base\_PixelFlags\_flag\_",
"", f)))
412 plt.plot(imag[isSet], fwhm[isSet],
'o', alpha=alpha, label=label)
414 for bad, label
in selectionVectors:
415 plt.plot(imag[bad], fwhm[bad],
'o', alpha=alpha, label=label)
417 plt.plot(imag[good], fwhm[good],
'o', color=
"black", label=
"selected")
418 [plt.axhline(_, color=
'red')
for _
in [fwhmMin, fwhmMax]]
419 plt.xlim(np.median(imag[good]) + 5*np.array([-1, 1]))
420 plt.ylim(fwhm[np.where(np.isfinite(fwhm + imag))].
min(), 2*fwhmMax)
422 plt.xlabel(
"Instrumental %s Magnitude" % fluxName.split(
".")[-1].title())
424 title =
"PSFEX Star Selection"
425 plt.title(
"%s %d selected" % (title, sum(good)))
429 eventHandler =
EventHandler(plt.axes(), imag, fwhm, sourceCat.getX(), sourceCat.getY(),
432 if plotFlags
or plotRejection:
435 reply = input(
"continue? [y[es] h(elp) p(db) q(uit)] ").
strip()
444At this prompt, you can continue with almost any key; 'p' enters pdb,
445 'q' returns to the shell,
and
451If you put the cursor on a point in the matplotlib scatter plot and hit 'p' you'll see it in ds9.""")
452 elif reply[0] ==
"p":
455 elif reply[0] ==
'q':
460 return Struct(selected=good)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
def __init__(self, axes, xs, ys, x, y, frames=[0])
def selectSources(self, sourceCat, matches=None, exposure=None)
def plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-', clear=True)
Lightweight representation of a geometric match between two records.