LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
debugger.py
Go to the documentation of this file.
1 """This is a useful module for debugging measurements.
2 
3 It relies on the presence of a catalog already produced by running
4 measurements in the regular context. You provide the image and
5 catalog on the command-line, a list of source identifiers and the
6 measurement configuration in the config; the module reads the inputs,
7 subsets the catalog to contain only the sources of interest, and measures
8 those. This reduces the frustration of waiting for image processing
9 and the measurements to run on many other sources, greatly increasing
10 debugging efficiency.
11 """
12 
13 import sys
14 from argparse import ArgumentParser, Namespace
15 from lsst.log import Log
16 from lsst.pex.config import Config, ConfigurableField, Field, ListField
17 from lsst.pipe.base import CmdLineTask, ConfigValueAction, ConfigFileAction, TaskRunner, Struct
18 import lsst.afw.image as afwImage
19 import lsst.afw.table as afwTable
20 
21 from lsst.meas.algorithms.measurement import SourceMeasurementTask
22 
23 
25  sourceId = ListField(dtype=int, default=[], doc="List of source identifiers to measure")
26  outputName = Field(dtype=str, default="catalog.fits", doc="Output name for source catalog")
27  measurement = ConfigurableField(target=SourceMeasurementTask, doc="Measurements")
28 
29 
30 class MeasurementDebuggerRunner(TaskRunner):
31  """Provide the image and catalog names to the Task
32 
33  We provide a dummy dataRef only to avoid further overrides
34  of this class.
35  """
36  @staticmethod
37  def getTargetList(parsedCmd, **kwargs):
38  kwargs["image"] = parsedCmd.image
39  kwargs["catalog"] = parsedCmd.catalog
40  return [(Struct(dataId="<none>"), kwargs)]
41 
42 
43 class MeasurementDebuggerArgumentParser(ArgumentParser):
44  """A stripped down version of the pipe_base ArgumentParser
45 
46  We don't care about the butler, just the config, and log.
47  """
48 
49  def __init__(self, *args, **kwargs):
50  super(MeasurementDebuggerArgumentParser, self).__init__(*args, **kwargs)
51  self.add_argument("image", help="Name of image to measure")
52  self.add_argument("catalog", help="Name of catalog to measure")
53  self.add_argument("-c", "--config", nargs="*", action=ConfigValueAction,
54  help="config override(s), e.g. -c foo=newfoo bar.baz=3", metavar="NAME=VALUE")
55  self.add_argument("-C", "--configfile", dest="configfile", nargs="*", action=ConfigFileAction,
56  help="config override file(s)")
57  self.add_argument("--doraise", action="store_true",
58  help="raise an exception on error (else log a message and continue)?")
59  self.add_argument("--logdest", help="logging destination")
60 
61  def parse_args(self, config, args=None, log=None, override=None):
62  if args is None:
63  args = sys.argv[1:]
64  namespace = Namespace()
65  namespace.config = config
66  namespace.clobberConfig = False
67  namespace.butler = None
68  namespace.log = log if log is not None else Log.getDefaultLogger()
69  namespace = super(MeasurementDebuggerArgumentParser, self).parse_args(args=args, namespace=namespace)
70  del namespace.configfile
71  return namespace
72 
73 
74 class MeasurementDebuggerTask(CmdLineTask):
75  _DefaultName = "debugger"
76  ConfigClass = MeasurementDebuggerConfig
77  RunnerClass = MeasurementDebuggerRunner
78 
79  def __init__(self, schema=None, **kwargs):
80  super(MeasurementDebuggerTask, self).__init__(**kwargs)
81  if schema is None:
82  schema = afwTable.SourceTable.makeMinimalSchema()
83  self.schema = schema
84  self.makeSubtask("measurement", schema=schema)
85 
86  @classmethod
89 
90  def run(self, dataRef, image, catalog):
91  exp = self.readImage(image)
92  sources = self.readSources(catalog)
93  sources = self.subsetSources(sources)
94  sources = self.mapSchemas(sources)
95  self.measurement.measure(exp, sources)
96  self.writeSources(sources)
97  return Struct(exp=exp, sources=sources)
98 
99  def readImage(self, image):
100  exp = afwImage.ExposureF(image)
101  self.log.info("Read %dx%d image", exp.getWidth(), exp.getHeight())
102  return exp
103 
104  def readSources(self, catalog):
105  sources = afwTable.SourceCatalog.readFits(catalog)
106  self.log.info("Read %d sources", len(sources))
107  return sources
108 
109  def mapSchemas(self, sources):
110  catalog = afwTable.SourceCatalog(self.schema)
111  for ss in sources:
112  new = catalog.addNew()
113  new.setFootprint(ss.getFootprint())
114  for name in self.schema.getNames():
115  if name in ss.schema:
116  new.set(name, ss.get(name))
117  return catalog
118 
119  def subsetSources(self, sources):
120  """Return a subset of the input catalog
121 
122  The full catalog is used if the 'sourceId' list is empty.
123 
124  Parent sources (in the deblending sense) are also added to the
125  subset so that they can be removed (via replaceWithNoise).
126  """
127  if not self.config.sourceId:
128  return sources
129 
130  identifiers = set(self.config.sourceId)
131  subset = afwTable.SourceCatalog(sources.table)
132  while len(identifiers) > 0:
133  ident = identifiers.pop()
134  ss = sources.find(ident)
135  if ss is None:
136  raise RuntimeError("Unable to find id=%d in catalog" % ident)
137  subset.append(ss)
138  parent = ss.getParent()
139  if parent:
140  identifiers.add(parent)
141  self.log.info("Subset to %d sources", len(subset))
142  return subset
143 
144  def writeSources(self, sources):
145  sources.writeFits(self.config.outputName)
146  self.log.info("Wrote %s", self.config.outputName)
147 
148  def writeConfig(self, *args, **kwargs):
149  pass
150 
151  def writeMetadata(self, *args, **kwargs):
152  pass
153 
154  def writeSchemas(self, *args, **kwargs):
155  pass
Definition: Log.h:716
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Definition: fwd.h:55