LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
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, 2009, 2010 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 
26 import os
27 from optparse import OptionParser
28 import lsst.afw.image as afwImage
29 import lsst.afw.geom as afwGeom
30 import lsst.afw.detection as afwDet
31 import lsst.pex.policy as pexPolicy
32 import lsst.pex.logging as pexLog
33 import lsst.daf.persistence as dafPersist
34 import lsst.daf.base as dafBase
35 import numpy as num
36 import lsst.afw.display.ds9 as ds9
37 import lsst.pex.config as pexConfig
38 
39 scaling = 5
40 
42  """Parse the command line options."""
43  parser = OptionParser(
44  usage="""%prog cdDiffSources crDiffExposure
45 
46 Read in sources and test for junk""")
47  options, args = parser.parse_args()
48  if len(args) != 2:
49  parser.error("incorrect number of arguments")
50  return options, args
51 
52 def readSourceSet(boostFile):
53  loc = dafPersist.LogicalLocation(boostFile)
54  storageList = dafPersist.StorageList()
55  additionalData = dafBase.PropertySet()
56  persistence = dafPersist.Persistence.getPersistence(pexPolicy.Policy())
57  storageList.append(persistence.getRetrieveStorage("BoostStorage", loc))
58  psvptr = persistence.unsafeRetrieve("PersistableSourceVector", storageList, additionalData)
59  psv = afwDet.PersistableSourceVector.swigConvert(psvptr)
60  return psv.getSources()
61 
62 class DiaSourceAnalystConfig(pexConfig.Config):
63  srcBadMaskPlanes = pexConfig.ListField(
64  dtype = str,
65  doc = """Mask planes that lead to an invalid detection.
66  Options: EDGE SAT BAD CR INTRP
67  E.g. : EDGE SAT BAD allows CR-masked and interpolated pixels""",
68  default = ("EDGE", "SAT", "BAD")
69  )
70  fBadPixels = pexConfig.Field(
71  dtype = float,
72  doc = "Fraction of bad pixels allowed in footprint",
73  default = 0.1
74  )
75  fluxPolarityRatio = pexConfig.Field(
76  dtype = float,
77  doc = "Minimum fraction of flux in correct-polarity pixels",
78  default = 0.75
79  )
80  nPolarityRatio = pexConfig.Field(
81  dtype = float,
82  doc = "Minimum fraction of correct-polarity pixels in unmasked subset",
83  default = 0.7
84  )
85  nMaskedRatio = pexConfig.Field(
86  dtype = float,
87  doc = "Minimum fraction of correct-polarity unmasked to masked pixels",
88  default = 0.6,
89  )
90  nGoodRatio = pexConfig.Field(
91  dtype = float,
92  doc = "Minimum fraction of correct-polarity unmasked to all pixels",
93  default = 0.5
94  )
95 
96 
97 class DiaSourceAnalyst(object):
98  def __init__(self, config):
99  self.config = config
100 
101  self.bitMask = 0
102  srcBadMaskPlanes = self.config.srcBadMaskPlanes
103  for maskPlane in srcBadMaskPlanes:
104  self.bitMask |= afwImage.MaskU_getPlaneBitMask(maskPlane)
105 
106  def countDetected(self, mask):
107  idxP = num.where(mask & afwImage.MaskU_getPlaneBitMask("DETECTED"))
108  idxN = num.where(mask & afwImage.MaskU_getPlaneBitMask("DETECTED_NEGATIVE"))
109  return len(idxP[0]), len(idxN[0])
110 
111  def countMasked(self, mask):
112  idxM = num.where(mask & self.bitMask)
113  return len(idxM[0])
114 
115  def countPolarity(self, mask, pixels):
116  unmasked = ((mask & self.bitMask) == 0)
117  idxP = num.where( (pixels >= 0) & unmasked)
118  idxN = num.where( (pixels < 0) & unmasked)
119  fluxP = num.sum(pixels[idxP])
120  fluxN = num.sum(pixels[idxN])
121  #import pdb; pdb.set_trace()
122  return len(idxP[0]), len(idxN[0]), fluxP, fluxN
123 
124  def testSource(self, source, subMi):
125  imArr, maArr, varArr = subMi.getArrays()
126  flux = source.getApFlux()
127 
128  nPixels = subMi.getWidth() * subMi.getHeight()
129  nPos, nNeg, fPos, fNeg = self.countPolarity(maArr, imArr)
130  nDetPos, nDetNeg = self.countDetected(maArr)
131  nMasked = self.countMasked(maArr)
132  assert (nPixels == (nMasked + nPos + nNeg))
133 
134  # 1) Too many pixels in the detection are masked
135  fMasked = (nMasked / nPixels)
136  fMaskedTol = self.config.fBadPixels
137  if fMasked > fMaskedTol:
138  pexLog.Trace("lsst.ip.diffim.DiaSourceAnalysis", 1,
139  "Candidate %d : BAD fBadPixels %.2f > %.2f" % (source.getId(), fMasked, fMaskedTol))
140  return False
141 
142  if flux > 0:
143  # positive-going source
144  fluxRatio = fPos / (fPos + abs(fNeg))
145  ngoodRatio = nPos / nPixels
146  maskRatio = nPos / (nPos + nMasked)
147  npolRatio = nPos / (nPos + nNeg)
148  else:
149  # negative-going source
150  fluxRatio = abs(fNeg)/ (fPos + abs(fNeg))
151  ngoodRatio = nNeg / nPixels
152  maskRatio = nNeg / (nNeg + nMasked)
153  npolRatio = nNeg / (nNeg + nPos)
154 
155  # 2) Not enough flux in unmasked correct-polarity pixels
156  fluxRatioTolerance = self.config.fluxPolarityRatio
157  if fluxRatio < fluxRatioTolerance:
158  pexLog.Trace("lsst.ip.diffim.DiaSourceAnalysis", 1,
159  "Candidate %d : BAD flux polarity %.2f < %.2f (pos=%.2f neg=%.2f)" % (source.getId(),
160  fluxRatio, fluxRatioTolerance, fPos, fNeg))
161  return False
162 
163  # 3) Not enough unmasked pixels of correct polarity
164  polarityTolerance = self.config.nPolarityRatio
165  if npolRatio < polarityTolerance:
166  pexLog.Trace("lsst.ip.diffim.DiaSourceAnalysis", 1,
167  "Candidate %d : BAD polarity count %.2f < %.2f (pos=%d neg=%d)" % (source.getId(),
168  npolRatio, polarityTolerance, nPos, nNeg))
169  return False
170 
171  # 4) Too many masked vs. correct polarity pixels
172  maskedTolerance = self.config.nMaskedRatio
173  if maskRatio < maskedTolerance:
174  pexLog.Trace("lsst.ip.diffim.DiaSourceAnalysis", 1,
175  "Candidate %d : BAD unmasked count %.2f < %.2f (pos=%d neg=%d mask=%d)" % (source.getId(),
176  maskRatio, maskedTolerance, nPos, nNeg, nMasked))
177  return False
178 
179  # 5) Too few unmasked, correct polarity pixels
180  ngoodTolerance = self.config.nGoodRatio
181  if ngoodRatio < ngoodTolerance:
182  pexLog.Trace("lsst.ip.diffim.DiaSourceAnalysis", 1,
183  "Candidate %d : BAD good pixel count %.2f < %.2f (pos=%d neg=%d tot=%d)" % (source.getId(),
184  ngoodRatio, ngoodTolerance, nPos, nNeg, nPixels))
185  return False
186 
187  pexLog.Trace("lsst.ip.diffim.DiaSourceAnalysis", 1,
188  "Candidate %d : OK flux=%.2f nPos=%d nNeg=%d nTot=%d nDetPos=%d nDetNeg=%d fPos=%.2f fNeg=%2f" % (source.getId(),
189  flux, nPos, nNeg, nPixels, nDetPos, nDetNeg, fPos, fNeg))
190  return True
191 
192 
193 def main():
194  """Main program"""
195  options, args = parseOptions()
196  (crDiffSourceFile, crDiffExposureFile) = args
197 
198  crDiffSources = readSourceSet(crDiffSourceFile)
199  crDiffExposure = afwImage.ExposureF(crDiffExposureFile)
200  #import pdb; pdb.set_trace()
201 
202  analyst = DiaSourceAnalyst()
203 
204  expX0 = crDiffExposure.getX0()
205  expY0 = crDiffExposure.getY0()
206  expX1 = expX0 + crDiffExposure.getWidth() - 1
207  expY1 = expY0 + crDiffExposure.getHeight() - 1
208 
209  for i in range(crDiffSources.size()):
210  crDiffSource = crDiffSources[i]
211 
212  # This segfaults; revisit once the stack stabilizes
213  #footprint = crDiffSource.getFootprint()
214  #bbox = footprint.getBBox()
215 
216  xAstrom = crDiffSource.getXAstrom()
217  yAstrom = crDiffSource.getYAstrom()
218  Ixx = max(1.0, crDiffSource.getIxx())
219  Iyy = max(1.0, crDiffSource.getIyy())
220  x0 = max(expX0, int(xAstrom - scaling * Ixx))
221  x1 = min(expX1, int(xAstrom + scaling * Ixx))
222  y0 = max(expY0, int(yAstrom - scaling * Iyy))
223  y1 = min(expY1, int(yAstrom + scaling * Iyy))
224  bbox = afwGeom.Box2I(afwGeom.Point2I(x0, y0),
225  afwGeom.Point2I(x1, y1))
226  subExp = afwImage.ExposureF(crDiffExposure, bbox)
227  subMi = subExp.getMaskedImage()
228  imArr, maArr, varArr = subMi.getArrays()
229 
230  if analyst.testSource(crDiffSource, subMi):
231  ds9.mtv(subExp, frame=1)
232  raw_input('Next: ')
233 if __name__ == "__main__":
234  main()
Class for logical location of a persisted Persistable instance.
a container for holding hierarchical configuration data in memory.
Definition: Policy.h:169
limited backward compatibility to the DC2 run-time trace facilities
Definition: Trace.h:93
An integer coordinate rectangle.
Definition: Box.h:53
double min
Definition: attributes.cc:216
double max
Definition: attributes.cc:218
Class for storing generic metadata.
Definition: PropertySet.h:82