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