22__all__ = (
"PsfexStarSelectorConfig",
"PsfexStarSelectorTask")
29 import matplotlib.pyplot
as plt
39from .psfex
import compute_fwhmrange
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 maxellip = pexConfig.Field(
70 doc=
"Maximum (A-B)/(A+B) ",
72 check=
lambda x: x >= 0.0,
74 minsn = pexConfig.Field(
76 doc=
"Minimum S/N for candidates",
78 check=
lambda x: x >= 0.0,
82 pexConfig.Config.validate(self)
87 msg = f
"fluxErrName ({self.fluxErrName}) doesn't correspond to fluxName ({self.fluxName})"
88 raise pexConfig.FieldValidationError(PsfexStarSelectorConfig.fluxName, self, msg)
91 raise pexConfig.FieldValidationError(PsfexStarSelectorConfig.minFwhm, self,
92 f
"minFwhm ({self.minFwhm}) > maxFwhm ({self.maxFwhm})")
96 "base_PixelFlags_flag_edge",
97 "base_PixelFlags_flag_saturatedCenter",
98 "base_PixelFlags_flag_crCenter",
99 "base_PixelFlags_flag_bad",
100 "base_PixelFlags_flag_suspectCenter",
107 """A class to handle key strokes with matplotlib displays
110 def __init__(self, axes, xs, ys, x, y, frames=[0]):
118 self.
cid = self.
axes.figure.canvas.mpl_connect(
'key_press_event', self)
121 if ev.inaxes != self.
axes:
124 if ev.key
and ev.key
in (
"p"):
125 dist = np.hypot(self.
xs - ev.xdata, self.
ys - ev.ydata)
126 dist[np.where(np.isnan(dist))] = 1e30
128 which = np.where(dist ==
min(dist))
133 disp = afwDisplay.Display(frame=frame)
140def plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-',
152 axes = fig.add_axes((0.1, 0.1, 0.85, 0.80))
154 xmin = sorted(mag)[int(0.05*len(mag))]
155 xmax = sorted(mag)[int(0.95*len(mag))]
157 axes.set_xlim(-17.5, -13)
158 axes.set_xlim(xmin - 0.1*(xmax - xmin), xmax + 0.1*(xmax - xmin))
161 colors = [
"r",
"g",
"b",
"c",
"m",
"k", ]
162 for k, mean
in enumerate(centers):
164 axes.plot(axes.get_xlim(), (mean, mean,),
"k%s" % ltype)
166 ll = (clusterId == k)
167 axes.plot(mag[ll], width[ll], marker, markersize=markersize, markeredgewidth=markeredgewidth,
168 color=colors[k%len(colors)])
170 ll = (clusterId == -1)
171 axes.plot(mag[ll], width[ll], marker, markersize=markersize, markeredgewidth=markeredgewidth,
175 axes.set_xlabel(
"model")
176 axes.set_ylabel(
r"$\sqrt{I_{xx} + I_{yy}}$")
188@pexConfig.registerConfigurable("psfex", sourceSelectorRegistry)
190 r"""A star selector whose algorithm is not yet documented.
192 @anchor PsfexStarSelectorTask_
194 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Contents Contents
196 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Purpose
197 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Initialize
198 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_IO
199 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Config
200 -
@ref meas_extensions_psfex_psfexStarSelectorStarSelector_Debug
202 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Purpose Description
204 A star selector whose algorithm
is not yet documented
206 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Initialize Task initialisation
208 @copydoc \_\_init\_\_
210 @section meas_extensions_psfex_psfexStarSelectorStarSelector_IO Invoking the Task
212 Like all star selectors, the main method
is `run`.
214 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Config Configuration parameters
216 See
@ref PsfexStarSelectorConfig
218 @section meas_extensions_psfex_psfexStarSelectorStarSelector_Debug Debug variables
220 PsfexStarSelectorTask has a debug dictionary
with the following keys:
223 <dd>bool;
if True display debug information
225 <dd>bool;
if True display the exposure
and spatial cells
226 <dt>plotFwhmHistogram
227 <dd>bool;
if True plot histogram of FWHM
229 <dd>bool:
if True plot the sources coloured by their flags
231 <dd>bool;
if True plot why sources are rejected
234 For example, put something like:
239 if name.endswith(
"objectSizeStarSelector"):
241 di.displayExposure =
True
242 di.plotFwhmHistogram =
True
248 into your `debug.py` file
and run your task
with the `--debug` flag.
250 ConfigClass = PsfexStarSelectorConfig
254 """Return a selection of psf-like objects.
259 Catalog of sources to select from.
260 This catalog must be contiguous
in memory.
262 Ignored by this source selector.
264 The exposure the catalog was built
from; used
for debug display.
268 struct : `lsst.pipe.base.Struct`
269 The struct contains the following data:
271 - selected : `numpy.ndarray` of `bool``
272 Boolean array of sources that were selected, same length
as
278 displayExposure = display
and \
280 plotFwhmHistogram = display
and plt
and \
282 plotFlags = display
and plt
and \
284 plotRejection = display
and plt
and \
286 afwDisplay.setDefaultMaskTransparency(75)
288 fluxName = self.config.fluxName
289 fluxErrName = self.config.fluxErrName
290 minFwhm = self.config.minFwhm
291 maxFwhm = self.config.maxFwhm
292 maxFwhmVariability = self.config.maxFwhmVariability
293 maxellip = self.config.maxellip
294 minsn = self.config.minsn
296 maxelong = (maxellip + 1.0)/(1.0 - maxellip)
if maxellip < 1.0
else 100
299 shape = sourceCat.getShapeDefinition()
300 ixx = sourceCat.get(
"%s.xx" % shape)
301 iyy = sourceCat.get(
"%s.yy" % shape)
303 fwhm = 2*np.sqrt(2*np.log(2))*np.sqrt(0.5*(ixx + iyy))
304 elong = 0.5*(ixx - iyy)/(ixx + iyy)
306 flux = sourceCat.get(fluxName)
307 fluxErr = sourceCat.get(fluxErrName)
308 sn = flux/np.where(fluxErr > 0, fluxErr, 1)
309 sn[fluxErr <= 0] = -psfexLib.BIG
312 for i, f
in enumerate(self.config.badFlags):
313 flags = np.bitwise_or(flags, np.where(sourceCat.get(f), 1 << i, 0))
317 good = np.logical_and(sn > minsn, np.logical_not(flags))
318 good = np.logical_and(good, elong < maxelong)
319 good = np.logical_and(good, fwhm >= minFwhm)
320 good = np.logical_and(good, fwhm < maxFwhm)
322 fwhmMode, fwhmMin, fwhmMax = compute_fwhmrange(fwhm[good], maxFwhmVariability, minFwhm, maxFwhm,
323 plot=dict(fwhmHistogram=plotFwhmHistogram))
335 selectionVectors = []
336 selectionVectors.append((bad,
"flags %d" % sum(bad)))
340 bad = np.logical_or(bad, dbad)
342 selectionVectors.append((dbad,
"S/N %d" % sum(dbad)))
344 dbad = fwhm < fwhmMin
346 bad = np.logical_or(bad, dbad)
348 selectionVectors.append((dbad,
"fwhmMin %d" % sum(dbad)))
350 dbad = fwhm > fwhmMax
352 bad = np.logical_or(bad, dbad)
354 selectionVectors.append((dbad,
"fwhmMax %d" % sum(dbad)))
356 dbad = elong > maxelong
358 bad = np.logical_or(bad, dbad)
360 selectionVectors.append((dbad,
"elong %d" % sum(dbad)))
362 good = np.logical_not(bad)
368 mi = exposure.getMaskedImage()
369 disp = afwDisplay.Display(frame=frame)
370 disp.mtv(mi, title=
"PSF candidates")
372 with disp.Buffering():
373 for i, source
in enumerate(sourceCat):
375 ctype = afwDisplay.GREEN
377 ctype = afwDisplay.RED
379 disp.dot(
"+", source.getX() - mi.getX0(), source.getY() - mi.getY0(),
382 if plotFlags
or plotRejection:
383 imag = -2.5*np.log10(flux)
388 isSet = np.where(flags == 0x0)[0]
389 plt.plot(imag[isSet], fwhm[isSet],
'o', alpha=alpha, label=
"good")
391 for i, f
in enumerate(self.config.badFlags):
393 isSet = np.where(np.bitwise_and(flags, mask))[0]
395 if np.isfinite(imag[isSet] + fwhm[isSet]).any():
396 label = re.sub(
r"\_flag",
"",
397 re.sub(
r"^base\_",
"",
398 re.sub(
r"^.*base\_PixelFlags\_flag\_",
"", f)))
399 plt.plot(imag[isSet], fwhm[isSet],
'o', alpha=alpha, label=label)
401 for bad, label
in selectionVectors:
402 plt.plot(imag[bad], fwhm[bad],
'o', alpha=alpha, label=label)
404 plt.plot(imag[good], fwhm[good],
'o', color=
"black", label=
"selected")
405 [plt.axhline(_, color=
'red')
for _
in [fwhmMin, fwhmMax]]
406 plt.xlim(np.median(imag[good]) + 5*np.array([-1, 1]))
407 plt.ylim(fwhm[np.where(np.isfinite(fwhm + imag))].
min(), 2*fwhmMax)
409 plt.xlabel(
"Instrumental %s Magnitude" % fluxName.split(
".")[-1].title())
411 title =
"PSFEX Star Selection"
412 plt.title(
"%s %d selected" % (title, sum(good)))
416 eventHandler =
EventHandler(plt.axes(), imag, fwhm, sourceCat.getX(), sourceCat.getY(),
419 if plotFlags
or plotRejection:
422 reply = input(
"continue? [y[es] h(elp) p(db) q(uit)] ").
strip()
431At this prompt, you can continue with almost any key; 'p' enters pdb,
432 'q' returns to the shell,
and
438If you put the cursor on a point in the matplotlib scatter plot and hit 'p' you'll see it in ds9.""")
439 elif reply[0] ==
"p":
442 elif reply[0] ==
'q':
447 return Struct(selected=good)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
__init__(self, axes, xs, ys, x, y, frames=[0])
selectSources(self, sourceCat, matches=None, exposure=None)
plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-', clear=True)
Lightweight representation of a geometric match between two records.