LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
diaSourceAnalysis.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 #
4 # LSST Data Management System
5 # Copyright 2008-2016 LSST Corporation.
6 #
7 # This product includes software developed by the
8 # LSST Project (http://www.lsst.org/).
9 #
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the LSST License Statement and
21 # the GNU General Public License along with this program. If not,
22 # see <http://www.lsstcorp.org/LegalNotices/>.
23 #
24 from __future__ import division
25 from builtins import input
26 from builtins import range
27 from builtins import object
28 
29 from optparse import OptionParser
30 import lsst.afw.image as afwImage
31 import lsst.afw.geom as afwGeom
32 import lsst.afw.detection as afwDet
33 from lsst.log import Log
34 import lsst.pex.policy as pexPolicy
35 import lsst.daf.persistence as dafPersist
36 import lsst.daf.base as dafBase
37 import numpy as num
38 import lsst.afw.display.ds9 as ds9
39 import lsst.pex.config as pexConfig
40 
41 scaling = 5
42 
43 
45  """Parse the command line options."""
46  parser = OptionParser(
47  usage="""%prog cdDiffSources crDiffExposure
48 
49 Read in sources and test for junk""")
50  options, args = parser.parse_args()
51  if len(args) != 2:
52  parser.error("incorrect number of arguments")
53  return options, args
54 
55 
56 def readSourceSet(boostFile):
57  loc = dafPersist.LogicalLocation(boostFile)
58  storageList = dafPersist.StorageList()
59  additionalData = dafBase.PropertySet()
60  persistence = dafPersist.Persistence.getPersistence(pexPolicy.Policy())
61  storageList.append(persistence.getRetrieveStorage("BoostStorage", loc))
62  psvptr = persistence.unsafeRetrieve("PersistableSourceVector", storageList, additionalData)
63  psv = afwDet.PersistableSourceVector.swigConvert(psvptr)
64  return psv.getSources()
65 
66 
67 class DiaSourceAnalystConfig(pexConfig.Config):
68  srcBadMaskPlanes = pexConfig.ListField(
69  dtype=str,
70  doc="""Mask planes that lead to an invalid detection.
71  Options: NO_DATA EDGE SAT BAD CR INTRP
72  E.g. : NO_DATA SAT BAD allows CR-masked and interpolated pixels""",
73  default=("NO_DATA", "EDGE", "SAT", "BAD")
74  )
75  fBadPixels = pexConfig.Field(
76  dtype=float,
77  doc="Fraction of bad pixels allowed in footprint",
78  default=0.1
79  )
80  fluxPolarityRatio = pexConfig.Field(
81  dtype=float,
82  doc="Minimum fraction of flux in correct-polarity pixels",
83  default=0.75
84  )
85  nPolarityRatio = pexConfig.Field(
86  dtype=float,
87  doc="Minimum fraction of correct-polarity pixels in unmasked subset",
88  default=0.7
89  )
90  nMaskedRatio = pexConfig.Field(
91  dtype=float,
92  doc="Minimum fraction of correct-polarity unmasked to masked pixels",
93  default=0.6,
94  )
95  nGoodRatio = pexConfig.Field(
96  dtype=float,
97  doc="Minimum fraction of correct-polarity unmasked to all pixels",
98  default=0.5
99  )
100 
101 
102 class DiaSourceAnalyst(object):
103 
104  def __init__(self, config):
105  self.config = config
106  self.log = Log.getLogger("ip.diffim.DiaSourceAnalysis")
107 
108  self.bitMask = 0
109  srcBadMaskPlanes = self.config.srcBadMaskPlanes
110  for maskPlane in srcBadMaskPlanes:
111  self.bitMask |= afwImage.MaskU_getPlaneBitMask(maskPlane)
112 
113  def countDetected(self, mask):
114  idxP = num.where(mask & afwImage.MaskU_getPlaneBitMask("DETECTED"))
115  idxN = num.where(mask & afwImage.MaskU_getPlaneBitMask("DETECTED_NEGATIVE"))
116  return len(idxP[0]), len(idxN[0])
117 
118  def countMasked(self, mask):
119  idxM = num.where(mask & self.bitMask)
120  return len(idxM[0])
121 
122  def countPolarity(self, mask, pixels):
123  unmasked = ((mask & self.bitMask) == 0)
124  idxP = num.where((pixels >= 0) & unmasked)
125  idxN = num.where((pixels < 0) & unmasked)
126  fluxP = num.sum(pixels[idxP])
127  fluxN = num.sum(pixels[idxN])
128  #import pdb; pdb.set_trace()
129  return len(idxP[0]), len(idxN[0]), fluxP, fluxN
130 
131  def testSource(self, source, subMi):
132  imArr, maArr, varArr = subMi.getArrays()
133  flux = source.getApFlux()
134 
135  nPixels = subMi.getWidth() * subMi.getHeight()
136  nPos, nNeg, fPos, fNeg = self.countPolarity(maArr, imArr)
137  nDetPos, nDetNeg = self.countDetected(maArr)
138  nMasked = self.countMasked(maArr)
139  assert(nPixels == (nMasked + nPos + nNeg))
140 
141  # 1) Too many pixels in the detection are masked
142  fMasked = (nMasked / nPixels)
143  fMaskedTol = self.config.fBadPixels
144  if fMasked > fMaskedTol:
145  self.log.debug("Candidate %d : BAD fBadPixels %.2f > %.2f", source.getId(), fMasked, fMaskedTol)
146  return False
147 
148  if flux > 0:
149  # positive-going source
150  fluxRatio = fPos / (fPos + abs(fNeg))
151  ngoodRatio = nPos / nPixels
152  maskRatio = nPos / (nPos + nMasked)
153  npolRatio = nPos / (nPos + nNeg)
154  else:
155  # negative-going source
156  fluxRatio = abs(fNeg) / (fPos + abs(fNeg))
157  ngoodRatio = nNeg / nPixels
158  maskRatio = nNeg / (nNeg + nMasked)
159  npolRatio = nNeg / (nNeg + nPos)
160 
161  # 2) Not enough flux in unmasked correct-polarity pixels
162  fluxRatioTolerance = self.config.fluxPolarityRatio
163  if fluxRatio < fluxRatioTolerance:
164  self.log.debug("Candidate %d : BAD flux polarity %.2f < %.2f (pos=%.2f neg=%.2f)",
165  source.getId(), fluxRatio, fluxRatioTolerance, fPos, fNeg)
166  return False
167 
168  # 3) Not enough unmasked pixels of correct polarity
169  polarityTolerance = self.config.nPolarityRatio
170  if npolRatio < polarityTolerance:
171  self.log.debug("Candidate %d : BAD polarity count %.2f < %.2f (pos=%d neg=%d)",
172  source.getId(), npolRatio, polarityTolerance, nPos, nNeg)
173  return False
174 
175  # 4) Too many masked vs. correct polarity pixels
176  maskedTolerance = self.config.nMaskedRatio
177  if maskRatio < maskedTolerance:
178  self.log.debug("Candidate %d : BAD unmasked count %.2f < %.2f (pos=%d neg=%d mask=%d)",
179  source.getId(), maskRatio, maskedTolerance, nPos, nNeg, nMasked)
180  return False
181 
182  # 5) Too few unmasked, correct polarity pixels
183  ngoodTolerance = self.config.nGoodRatio
184  if ngoodRatio < ngoodTolerance:
185  self.log.debug("Candidate %d : BAD good pixel count %.2f < %.2f (pos=%d neg=%d tot=%d)",
186  source.getId(), ngoodRatio, ngoodTolerance, nPos, nNeg, nPixels)
187  return False
188 
189  self.log.debug("Candidate %d : OK flux=%.2f nPos=%d nNeg=%d nTot=%d nDetPos=%d nDetNeg=%d fPos=%.2f fNeg=%2f",
190  source.getId(), flux, nPos, nNeg, nPixels, nDetPos, nDetNeg, fPos, fNeg)
191  return True
192 
193 
194 def main():
195  """Main program"""
196  options, args = parseOptions()
197  (crDiffSourceFile, crDiffExposureFile) = args
198 
199  crDiffSources = readSourceSet(crDiffSourceFile)
200  crDiffExposure = afwImage.ExposureF(crDiffExposureFile)
201  #import pdb; pdb.set_trace()
202 
203  analyst = DiaSourceAnalyst()
204 
205  expX0 = crDiffExposure.getX0()
206  expY0 = crDiffExposure.getY0()
207  expX1 = expX0 + crDiffExposure.getWidth() - 1
208  expY1 = expY0 + crDiffExposure.getHeight() - 1
209 
210  for i in range(crDiffSources.size()):
211  crDiffSource = crDiffSources[i]
212 
213  # This segfaults; revisit once the stack stabilizes
214  #footprint = crDiffSource.getFootprint()
215  #bbox = footprint.getBBox()
216 
217  xAstrom = crDiffSource.getXAstrom()
218  yAstrom = crDiffSource.getYAstrom()
219  Ixx = max(1.0, crDiffSource.getIxx())
220  Iyy = max(1.0, crDiffSource.getIyy())
221  x0 = max(expX0, int(xAstrom - scaling * Ixx))
222  x1 = min(expX1, int(xAstrom + scaling * Ixx))
223  y0 = max(expY0, int(yAstrom - scaling * Iyy))
224  y1 = min(expY1, int(yAstrom + scaling * Iyy))
225  bbox = afwGeom.Box2I(afwGeom.Point2I(x0, y0),
226  afwGeom.Point2I(x1, y1))
227  subExp = afwImage.ExposureF(crDiffExposure, bbox)
228  subMi = subExp.getMaskedImage()
229  imArr, maArr, varArr = subMi.getArrays()
230 
231  if analyst.testSource(crDiffSource, subMi):
232  ds9.mtv(subExp, frame=1)
233  input('Next: ')
234 if __name__ == "__main__":
235  main()
Class for logical location of a persisted Persistable instance.
a container for holding hierarchical configuration data in memory.
Definition: Policy.h:169
An integer coordinate rectangle.
Definition: Box.h:53
Class for storing generic metadata.
Definition: PropertySet.h:82
metadata input