LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
dipoleMeasurement.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2015 AURA/LSST.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 import numpy as np
23 import lsst.afw.geom as afwGeom
24 import lsst.afw.image as afwImage
25 import lsst.afw.detection as afwDetect
26 import lsst.pipe.base as pipeBase
27 import lsst.pex.logging as pexLog
28 import lsst.pex.config as pexConfig
29 import lsst.meas.deblender.baseline as deblendBaseline
30 from lsst.meas.base import SingleFrameMeasurementTask, SingleFrameMeasurementConfig
31 import lsst.afw.display.ds9 as ds9
32 
33 __all__ = ("DipoleMeasurementConfig", "DipoleMeasurementTask", "DipoleAnalysis", "DipoleDeblender")
34 
35 
36 class DipoleClassificationConfig(pexConfig.Config):
37  """!Classification of detected diaSources as dipole or not"""
38  minSn = pexConfig.Field(
39  doc="Minimum quadrature sum of positive+negative lobe S/N to be considered a dipole",
40  dtype=float, default=np.sqrt(2) * 5.0,
41  )
42  maxFluxRatio = pexConfig.Field(
43  doc = "Maximum flux ratio in either lobe to be considered a dipole",
44  dtype = float, default = 0.65
45  )
46 
47 class DipoleMeasurementConfig(SingleFrameMeasurementConfig):
48  """!Measurement of detected diaSources as dipoles"""
49  classification = pexConfig.ConfigField(
50  dtype=DipoleClassificationConfig,
51  doc="Dipole classification config"
52  )
53 
54  def setDefaults(self):
55  SingleFrameMeasurementConfig.setDefaults(self)
56  self.plugins = ["ip_diffim_NaiveDipoleCentroid",
57  "ip_diffim_NaiveDipoleFlux",
58  "ip_diffim_PsfDipoleFlux"]
59  self.slots.psfFlux = None
60  self.slots.calibFlux = None
61  self.slots.modelFlux = None
62  self.slots.apFlux = None
63  self.slots.instFlux = None
64  self.slots.shape = None
65  self.slots.centroid = "ip_diffim_NaiveDipoleCentroid_pos"
66  self.doReplaceWithNoise = False
67 
68 ## \addtogroup LSST_task_documentation
69 ## \{
70 ## \page DipoleMeasurementTask
71 ## \ref DipoleMeasurementTask_ "DipoleMeasurementTask"
72 ## \copybrief DipoleMeasurementTask
73 ## \}
74 class DipoleMeasurementTask(SingleFrameMeasurementTask):
75  """!
76 \anchor DipoleMeasurementTask_
77 
78 \brief Measurement of Sources, specifically ones from difference images, for characterization as dipoles
79 
80 \section ip_diffim_dipolemeas_Contents Contents
81 
82  - \ref ip_diffim_dipolemeas_Purpose
83  - \ref ip_diffim_dipolemeas_Initialize
84  - \ref ip_diffim_dipolemeas_IO
85  - \ref ip_diffim_dipolemeas_Config
86  - \ref ip_diffim_dipolemeas_Metadata
87  - \ref ip_diffim_dipolemeas_Debug
88  - \ref ip_diffim_dipolemeas_Example
89 
90 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
91 
92 \section ip_diffim_dipolemeas_Purpose Description
93 
94 This class provies a specialized set of Source measurements that allow the user to test the hypothesis that
95 the Source is a dipole. This includes a set of measurements derived from intermediate base classes
96 DipoleCentroidAlgorithm and DipoleFluxAlgorithm. Their respective algorithm control classes are defined in
97 DipoleCentroidControl and DipoleFluxControl. Each centroid and flux measurement will have .neg (negative)
98 and .pos (positive lobe) fields.
99 
100 The first set of measurements uses a "naive" alrogithm for centroid and flux measurements, implemented in
101 NaiveDipoleCentroidControl and NaiveDipoleFluxControl. The algorithm uses a naive 3x3 weighted moment around
102 the nominal centroids of each peak in the Source Footprint. These algorithms fill the table fields
103 centroid.dipole.naive.* and flux.dipole.naive.*.
104 
105 The second set of measurements undertakes a joint-Psf model on the negative and positive lobe simultaneously.
106 This fit simultaneously solves for the negative and positive lobe centroids and fluxes using non-linear
107 least squares minimization. The fields are stored in table elements flux.dipole.psf.*.
108 
109 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
110 
111 \section ip_diffim_dipolemeas_Initialize Task initialization
112 
113 \copydoc \_\_init\_\_
114 
115 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
116 
117 \section ip_diffim_dipolemeas_IO Invoking the Task
118 
119 \copydoc run
120 
121 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
122 
123 \section ip_diffim_dipolemeas_Config Configuration parameters
124 
125 See \ref DipoleMeasurementConfig and \ref DipoleClassificationConfig
126 
127 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
128 
129 \section ip_diffim_dipolemeas_Metadata Quantities set in Metadata
130 
131 No specific values are set in the Task metadata. However, the Source schema are modified to store the
132 results of the dipole-specific measurements. These additional fields include:
133 
134  . classification.dipole : probability of being a dipole
135 
136  . centroid.dipole.naive.pos : unweighted 3x3 first moment centroid
137 
138  . centroid.dipole.naive.pos.err : covariance matrix for centroid.dipole.naive.pos
139 
140  . centroid.dipole.naive.pos.flags : set if the centroid.dipole.naive.pos measurement did not fully succeed
141 
142  . centroid.dipole.naive.neg : unweighted 3x3 first moment centroid
143 
144  . centroid.dipole.naive.neg.err : covariance matrix for centroid.dipole.naive.neg
145 
146  . centroid.dipole.naive.neg.flags : set if the centroid.dipole.naive.neg measurement did not fully succeed
147 
148  . flux.dipole.naive.pos : raw flux counts
149 
150  . flux.dipole.naive.pos.err : uncertainty for flux.dipole.naive.pos
151 
152  . flux.dipole.naive.pos.flags : set if the flux.dipole.naive.pos measurement failed
153 
154  . flux.dipole.naive.neg : raw flux counts
155 
156  . flux.dipole.naive.neg.err : uncertainty for flux.dipole.naive.neg
157 
158  . flux.dipole.naive.neg.flags : set if the flux.dipole.naive.neg measurement failed
159 
160  . flux.dipole.naive.npos : number of positive pixels
161 
162  . flux.dipole.naive.nneg : number of negative pixels
163 
164  . flux.dipole.psf.pos : jointly fitted psf flux counts
165 
166  . flux.dipole.psf.pos.err : uncertainty for flux.dipole.psf.pos
167 
168  . flux.dipole.psf.pos.flags : set if the flux.dipole.psf.pos measurement failed
169 
170  . flux.dipole.psf.neg : jointly fitted psf flux counts
171 
172  . flux.dipole.psf.neg.err : uncertainty for flux.dipole.psf.neg
173 
174  . flux.dipole.psf.neg.flags : set if the flux.dipole.psf.neg measurement failed
175 
176  . flux.dipole.psf.chi2dof : chi2 per degree of freedom of fit
177 
178  . flux.dipole.psf.centroid : average of the postive and negative lobe positions
179 
180  . flux.dipole.psf.centroid.err : covariance matrix for flux.dipole.psf.centroid
181 
182  . flux.dipole.psf.centroid.flags : set if the flux.dipole.psf.centroid measurement did not fully succeed
183 
184  . flux.dipole.psf.neg.centroid : psf fitted center of negative lobe
185 
186  . flux.dipole.psf.neg.centroid.err : covariance matrix for flux.dipole.psf.neg.centroid
187 
188  . flux.dipole.psf.neg.centroid.flags : set if the flux.dipole.psf.neg.centroid measurement did not fully succeed
189 
190  . flux.dipole.psf.pos.centroid : psf fitted center of positive lobe
191 
192  . flux.dipole.psf.pos.centroid.err : covariance matrix for flux.dipole.psf.pos.centroid
193 
194  . flux.dipole.psf.pos.centroid.flags : set if the flux.dipole.psf.pos.centroid measurement did not fully succeed
195 
196  . flux.dipole.psf.flags.maxpix : set if too large a footprint was sent to the algorithm
197 
198 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
199 
200 \section ip_diffim_dipolemeas_Debug Debug variables
201 
202 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
203 flag \c -d/--debug to import \b debug.py from your \c PYTHONPATH. The relevant contents of debug.py
204 for this Task include:
205 
206 \code{.py}
207  import sys
208  import lsstDebug
209  def DebugInfo(name):
210  di = lsstDebug.getInfo(name)
211  if name == "lsst.ip.diffim.dipoleMeasurement":
212  di.display = True # enable debug output
213  di.maskTransparency = 90 # ds9 mask transparency
214  di.displayDiaSources = True # show exposure with dipole results
215  return di
216  lsstDebug.Info = DebugInfo
217  lsstDebug.frame = 1
218 \endcode
219 
220 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
221 
222 \section ip_diffim_dipolemeas_Example A complete example of using DipoleMeasurementTask
223 
224 This code is dipoleMeasTask.py in the examples directory, and can be run as \em e.g.
225 \code
226 examples/dipoleMeasTask.py
227 examples/dipoleMeasTask.py --debug
228 examples/dipoleMeasTask.py --debug --image /path/to/image.fits
229 \endcode
230 
231 \dontinclude dipoleMeasTask.py
232 Start the processing by parsing the command line, where the user has the option of enabling debugging output
233 and/or sending their own image for demonstration (in case they have not downloaded the afwdata package).
234 \skip main
235 \until run
236 
237 \dontinclude dipoleMeasTask.py
238 The processing occurs in the run function. We first extract an exposure from disk or afwdata, displaying
239 it if requested:
240 \skip args
241 \until mtv
242 
243 Create a default source schema that we will append fields to as we add more algorithms:
244 \skip makeMinimalSchema
245 \until makeMinimalSchema
246 
247 Create the detection and measurement Tasks, with some minor tweaking of their configs:
248 \skip Create
249 \until measureTask
250 
251 Having fully initialied the schema, we create a Source table from it:
252 \skip output
253 \until SourceTable
254 
255 Run detection:
256 \skip Process
257 \until detectionTask
258 
259 Because we are looking for dipoles, we need to merge the positive and negative detections:
260 \skip Merge
261 \until numNeg
262 
263 Finally, perform measurement (both standard and dipole-specialized) on the merged sources:
264 \skip measureTask
265 \until measureTask
266 
267 Optionally display debugging information:
268 \skip Display
269 \until displayDipoles
270 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
271 
272  """
273  ConfigClass = DipoleMeasurementConfig
274  _DefaultName = "dipoleMeasurement"
275  _ClassificationFlag = "classification_dipole"
276 
277  def __init__(self, schema, algMetadata=None, **kwds):
278  """!Create the Task, and add Task-specific fields to the provided measurement table schema.
279 
280  @param[in,out] schema Schema object for measurement fields; modified in-place.
281  @param[in,out] algMetadata Passed to MeasureSources object to be filled with
282  metadata by algorithms (e.g. radii for aperture photometry).
283  @param **kwds Passed to Task.__init__.
284  """
285  SingleFrameMeasurementTask.__init__(self, schema, algMetadata, **kwds)
287 
288  @pipeBase.timeMethod
289  def classify(self, sources):
290  """!Create the records needed for dipole classification, and run classifier
291 
292  @param[in,out] sources The diaSources to run classification on; modified in in-place
293  """
294 
295  self.log.log(self.log.INFO, "Classifying %d sources" % len(sources))
296  if not sources:
297  return
298 
299  ctrl = self.config.classification
300  try:
301  key = sources[0].getSchema().find(self._ClassificationFlag).getKey()
302  except Exception:
303  self.log.warn("Key %s not found in table" % (self._ClassificationFlag))
304  return
305 
306  for source in sources:
307  passesSn = self.dipoleAnalysis.getSn(source) > ctrl.minSn
308 
309  negFlux = np.abs(source.get("ip_diffim_PsfDipoleFlux_neg_flux"))
310  posFlux = np.abs(source.get("ip_diffim_PsfDipoleFlux_pos_flux"))
311  totalFlux = negFlux + posFlux
312  passesFluxNeg = (negFlux / totalFlux) < ctrl.maxFluxRatio
313  passesFluxPos = (posFlux / totalFlux) < ctrl.maxFluxRatio
314  if (passesSn and passesFluxPos and passesFluxNeg):
315  val = 1.0
316  else:
317  val = 0.0
318 
319  source.set(key, val)
320 
321  def run(self, sources, exposure, **kwds):
322  """!Run dipole measurement and classification
323  @param sources diaSources that will be measured using dipole measurement
324  @param exposure Exposure on which the diaSources were detected
325  @param **kwds Sent to SingleFrameMeasurementTask
326  """
327 
328  SingleFrameMeasurementTask.run(self, sources, exposure, **kwds)
329  self.classify(sources)
330 
331 #########
332 # Other Support classs
333 #########
334 
335 class SourceFlagChecker(object):
336  """!Functor class to check whether a diaSource has flags set that should cause it to be labeled bad."""
337  def __init__(self, sources, badFlags=None):
338  """!Constructor
339 
340  @param sources Sources that will be measured
341  @param badFlags A list of flags that will be used to determine if there was a measurement problem
342 
343  The list of badFlags will be used to make a list of keys to check for measurement flags on. By
344  default the centroid keys are added to this list"""
345 
346  self.badFlags = ['base_PixelFlags_flag_edge', 'base_PixelFlags_flag_interpolatedCenter', 'base_PixelFlags_flag_saturatedCenter']
347  if badFlags is not None:
348  for flag in badFlags:
349  self.badFlags.append(flag)
350  self.keys = [sources.getSchema().find(name).key for name in self.badFlags]
351  self.keys.append(sources.table.getCentroidFlagKey())
352 
353  def __call__(self, source):
354  """!Call the source flag checker on a single Source
355 
356  @param source Source that will be examined"""
357  for k in self.keys:
358  if source.get(k):
359  return False
360  return True
361 
362 class DipoleAnalysis(object):
363  """!Functor class that provides (S/N, position, orientation) of measured dipoles"""
364  def __init__(self):
365  """!Constructor"""
366  pass
367 
368  def __call__(self, source):
369  """!Parse information returned from dipole measurement
370 
371  @param source The source that will be examined"""
372  return self.getSn(source), self.getCentroid(source), self.getOrientation(source)
373 
374  def getSn(self, source):
375  """!Get the total signal-to-noise of the dipole; total S/N is from positive and negative lobe
376 
377  @param source The source that will be examined"""
378 
379  posflux = source.get("ip_diffim_PsfDipoleFlux_pos_flux")
380  posfluxErr = source.get("ip_diffim_PsfDipoleFlux_pos_fluxSigma")
381  negflux = source.get("ip_diffim_PsfDipoleFlux_neg_flux")
382  negfluxErr = source.get("ip_diffim_PsfDipoleFlux_neg_fluxSigma")
383 
384  # Not a dipole!
385  if (posflux < 0) is (negflux < 0):
386  return 0
387 
388  return np.sqrt((posflux/posfluxErr)**2 + (negflux/negfluxErr)**2)
389 
390  def getCentroid(self, source):
391  """!Get the centroid of the dipole; average of positive and negative lobe
392 
393  @param source The source that will be examined"""
394 
395  negCenX = source.get("ip_diffim_PsfDipoleFlux_neg_centroid_x")
396  negCenY = source.get("ip_diffim_PsfDipoleFlux_neg_centroid_y")
397  posCenX = source.get("ip_diffim_PsfDipoleFlux_pos_centroid_x")
398  posCenY = source.get("ip_diffim_PsfDipoleFlux_pos_centroid_y")
399  if (np.isinf(negCenX) or np.isinf(negCenY) or np.isinf(posCenX) or np.isinf(posCenY)):
400  return None
401 
402  center = afwGeom.Point2D(0.5*(negCenX+posCenX),
403  0.5*(negCenY+posCenY))
404  return center
405 
406  def getOrientation(self, source):
407  """!Calculate the orientation of dipole; vector from negative to positive lobe
408 
409  @param source The source that will be examined"""
410 
411  negCenX = source.get("ip_diffim_PsfDipoleFlux_neg_centroid_x")
412  negCenY = source.get("ip_diffim_PsfDipoleFlux_neg_centroid_y")
413  posCenX = source.get("ip_diffim_PsfDipoleFlux_pos_centroid_x")
414  posCenY = source.get("ip_diffim_PsfDipoleFlux_pos_centroid_y")
415  if (np.isinf(negCenX) or np.isinf(negCenY) or np.isinf(posCenX) or np.isinf(posCenY)):
416  return None
417 
418  dx, dy = posCenX-negCenX, posCenY-negCenY
419  angle = afwGeom.Angle(np.arctan2(dx, dy), afwGeom.radians)
420  return angle
421 
422  def displayDipoles(self, exposure, sources):
423  """!Display debugging information on the detected dipoles
424 
425  @param exposure Image the dipoles were measured on
426  @param sources The set of diaSources that were measured"""
427 
428  import lsstDebug
429  display = lsstDebug.Info(__name__).display
430  displayDiaSources = lsstDebug.Info(__name__).displayDiaSources
431  maskTransparency = lsstDebug.Info(__name__).maskTransparency
432  if not maskTransparency:
433  maskTransparency = 90
434  ds9.setMaskTransparency(maskTransparency)
435  ds9.mtv(exposure, frame=lsstDebug.frame)
436 
437  if display and displayDiaSources:
438  with ds9.Buffering():
439  for source in sources:
440  cenX, cenY = source.get("ipdiffim_DipolePsfFlux_centroid")
441  if np.isinf(cenX) or np.isinf(cenY):
442  cenX, cenY = source.getCentroid()
443 
444  isdipole = source.get("classification.dipole")
445  if isdipole and np.isfinite(isdipole):
446  # Dipole
447  ctype= "green"
448  else:
449  # Not dipole
450  ctype = "red"
451 
452  ds9.dot("o", cenX, cenY, size=2, ctype=ctype, frame=lsstDebug.frame)
453 
454  negCenX = source.get("ip_diffim_PsfDipoleFlux_neg_centroid_x")
455  negCenY = source.get("ip_diffim_PsfDipoleFlux_neg_centroid_y")
456  posCenX = source.get("ip_diffim_PsfDipoleFlux_pos_centroid_x")
457  posCenY = source.get("ip_diffim_PsfDipoleFlux_pos_centroid_y")
458  if (np.isinf(negCenX) or np.isinf(negCenY) or np.isinf(posCenX) or np.isinf(posCenY)):
459  continue
460 
461  ds9.line([(negCenX, negCenY), (posCenX, posCenY)], ctype="yellow", frame=lsstDebug.frame)
462 
463  lsstDebug.frame += 1
464 
465 
466 
467 class DipoleDeblender(object):
468  """!Functor to deblend a source as a dipole, and return a new source with deblended footprints.
469 
470  This necessarily overrides some of the functionality from
471  meas_algorithms/python/lsst/meas/algorithms/deblend.py since we
472  need a single source that contains the blended peaks, not
473  multiple children sources. This directly calls the core
474  deblending code deblendBaseline.deblend (optionally _fitPsf for
475  debugging).
476 
477  Not actively being used, but there is a unit test for it in
478  dipoleAlgorithm.py.
479  """
480  def __init__(self):
481  # Set up defaults to send to deblender
482 
483  # Always deblend as Psf
484  self.psfChisqCut1 = self.psfChisqCut2 = self.psfChisqCut2b = np.inf
485  self.log = pexLog.Log(pexLog.Log.getDefaultLog(),
486  'lsst.ip.diffim.DipoleDeblender', pexLog.Log.INFO)
487  self.sigma2fwhm = 2. * np.sqrt(2. * np.log(2.))
488 
489  def __call__(self, source, exposure):
490  fp = source.getFootprint()
491  peaks = fp.getPeaks()
492  peaksF = [pk.getF() for pk in peaks]
493  fbb = fp.getBBox()
494  fmask = afwImage.MaskU(fbb)
495  fmask.setXY0(fbb.getMinX(), fbb.getMinY())
496  afwDetect.setMaskFromFootprint(fmask, fp, 1)
497 
498  psf = exposure.getPsf()
499  psfSigPix = psf.computeShape().getDeterminantRadius()
500  psfFwhmPix = psfSigPix * self.sigma2fwhm
501  subimage = afwImage.ExposureF(exposure, fbb, True)
502  cpsf = deblendBaseline.CachingPsf(psf)
503 
504  # if fewer than 2 peaks, just return a copy of the source
505  if len(peaks) < 2:
506  return source.getTable().copyRecord(source)
507 
508  # make sure you only deblend 2 peaks; take the brighest and faintest
509  speaks = [(p.getPeakValue(), p) for p in peaks]
510  speaks.sort()
511  dpeaks = [speaks[0][1], speaks[-1][1]]
512 
513  # and only set these peaks in the footprint (peaks is mutable)
514  peaks.clear()
515  for peak in dpeaks:
516  peaks.append(peak)
517 
518  if True:
519  # Call top-level deblend task
520  fpres = deblendBaseline.deblend(fp, exposure.getMaskedImage(), psf, psfFwhmPix,
521  log = self.log,
522  psfChisqCut1 = self.psfChisqCut1,
523  psfChisqCut2 = self.psfChisqCut2,
524  psfChisqCut2b = self.psfChisqCut2b)
525  else:
526  # Call lower-level _fit_psf task
527 
528  # Prepare results structure
529  fpres = deblendBaseline.PerFootprint()
530  fpres.peaks = []
531  for pki,pk in enumerate(dpeaks):
532  pkres = deblendBaseline.PerPeak()
533  pkres.peak = pk
534  pkres.pki = pki
535  fpres.peaks.append(pkres)
536 
537  for pki,(pk,pkres,pkF) in enumerate(zip(dpeaks, fpres.peaks, peaksF)):
538  self.log.logdebug('Peak %i' % pki)
539  deblendBaseline._fitPsf(fp, fmask, pk, pkF, pkres, fbb, dpeaks, peaksF, self.log,
540  cpsf, psfFwhmPix,
541  subimage.getMaskedImage().getImage(),
542  subimage.getMaskedImage().getVariance(),
543  self.psfChisqCut1, self.psfChisqCut2, self.psfChisqCut2b)
544 
545 
546  deblendedSource = source.getTable().copyRecord(source)
547  deblendedSource.setParent(source.getId())
548  peakList = deblendedSource.getFootprint().getPeaks()
549  peakList.clear()
550 
551  for i, peak in enumerate(fpres.peaks):
552  if peak.psfFitFlux > 0:
553  suffix = "pos"
554  else:
555  suffix = "neg"
556  c = peak.psfFitCenter
557  self.log.info("deblended.centroid.dipole.psf.%s %f %f" % (
558  suffix, c[0], c[1]))
559  self.log.info("deblended.chi2dof.dipole.%s %f" % (
560  suffix, peak.psfFitChisq / peak.psfFitDof))
561  self.log.info("deblended.flux.dipole.psf.%s %f" % (
562  suffix, peak.psfFitFlux * np.sum(peak.templateImage.getArray())))
563  peakList.append(peak.peak)
564  return deblendedSource
def getCentroid
Get the centroid of the dipole; average of positive and negative lobe.
Classification of detected diaSources as dipole or not.
def displayDipoles
Display debugging information on the detected dipoles.
def classify
Create the records needed for dipole classification, and run classifier.
a place to record messages and descriptions of the state of processing.
Definition: Log.h:154
def run
Run dipole measurement and classification.
Measurement of Sources, specifically ones from difference images, for characterization as dipoles...
def __init__
Create the Task, and add Task-specific fields to the provided measurement table schema.
Functor class that provides (S/N, position, orientation) of measured dipoles.
Functor to deblend a source as a dipole, and return a new source with deblended footprints.
def __call__
Parse information returned from dipole measurement.
MaskT setMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
OR bitmask into all the Mask&#39;s pixels that are in the Footprint.
Functor class to check whether a diaSource has flags set that should cause it to be labeled bad...
Measurement of detected diaSources as dipoles.
def getOrientation
Calculate the orientation of dipole; vector from negative to positive lobe.
def getSn
Get the total signal-to-noise of the dipole; total S/N is from positive and negative lobe...
def __call__
Call the source flag checker on a single Source.