37 """Config for ProcessImage"""
38 doCalibrate = pexConfig.Field(dtype=bool, default=
True, doc =
"Perform calibration?")
39 doDetection = pexConfig.Field(dtype=bool, default=
True, doc =
"Detect sources?")
41 doDeblend = pexConfig.Field(dtype=bool, default=
False, doc =
"Deblend sources?")
42 doMeasurement = pexConfig.Field(dtype=bool, default=
True, doc =
"Measure sources?")
43 doWriteCalibrate = pexConfig.Field(dtype=bool, default=
True, doc =
"Write calibration results?")
44 persistBackgroundModel = pexConfig.Field(dtype=bool, default=
True, doc =
"If True persist background model with background subtracted calexp. \
45 If False persist calexp with the background included.")
46 doWriteCalibrateMatches = pexConfig.Field(dtype=bool, default=
True,
47 doc =
"Write icSrc to reference matches?")
48 doWriteSources = pexConfig.Field(dtype=bool, default=
True, doc =
"Write sources?")
49 doWriteSourceMatches = pexConfig.Field(dtype=bool, default=
False,
50 doc =
"Compute and write src to reference matches?")
51 doWriteHeavyFootprintsInSources = pexConfig.Field(dtype=bool, default=
False,
52 doc =
"Include HeavyFootprint data in source table?")
53 calibrate = pexConfig.ConfigurableField(
54 target = CalibrateTask,
55 doc =
"Calibration (inc. high-threshold detection and measurement)",
57 detection = pexConfig.ConfigurableField(
58 target = SourceDetectionTask,
59 doc =
"Low-threshold detection for final measurement",
61 deblend = pexConfig.ConfigurableField(
62 target = SourceDeblendTask,
63 doc =
"Split blended sources into their components",
65 measurement = pexConfig.ConfigurableField(
66 target = SingleFrameMeasurementTask,
67 doc =
"Final source measurement on low-threshold detections",
71 pexConfig.Config.validate(self)
74 raise ValueError(
"Cannot run source measurement without source detection.")
75 if (
"skycoord" not in self.measurement.algorithms.names
76 and "base_SkyCoord" not in self.measurement.algorithms.names):
77 raise ValueError(
"If you run source measurement you must let it run the skycoord algorithm.")
79 raise ValueError(
"Cannot run source deblending without source detection.")
81 raise ValueError(
"Cannot write HeavyFootprints (doWriteHeavyFootprintsInSources) without doWriteSources")
84 """An abstract base class for tasks do simple calibration, detection, deblending, and measurement
87 Other command-line Process***Tasks (such as ProcessCcdTask and ProcessCoaddTask) rely on
88 ProcessImageTask for their main algorithmic code, and only need to add pre- and post- processing
91 Subclasses are responsible for meeting the requirements of CmdLineTask.
93 ConfigClass = ProcessImageConfig
97 pipeBase.CmdLineTask.__init__(self, **kwargs)
99 self.makeSubtask(
"calibrate")
102 calibSchema = self.calibrate.schema
104 self.schemaMapper.addMinimalSchema(afwTable.SourceTable.makeMinimalSchema(),
False)
108 afwTable.Field[
"Flag"](
"calib_detected",
"Source was detected as an icSrc")
111 for key
in self.calibrate.getCalibKeys():
112 self.schemaMapper.addMapping(key)
113 self.
schema = self.schemaMapper.getOutputSchema()
115 if self.config.doDetection:
116 self.makeSubtask(
"detection", schema=self.
schema)
117 if self.config.doDeblend:
118 self.makeSubtask(
"deblend", schema=self.
schema)
119 if self.config.doMeasurement:
120 self.makeSubtask(
"measurement", schema=self.
schema, algMetadata=self.
algMetadata)
123 raise NotImplementedError()
126 raise NotImplementedError()
129 def process(self, dataRef, inputExposure, enableWriteSources=True):
132 @param dataRef: data reference that corresponds to the input image
133 @param inputExposure: exposure to process
134 @param enableWriteSources: if True then writing sources is allowed.
135 Set False if you need to postprocess sources before writing them.
137 @return pipe_base Struct containing these fields:
138 - postIsrExposure: exposure after ISR performed if calib.doIsr or config.doCalibrate, else None
139 - exposure: calibrated exposure (calexp): as computed if config.doCalibrate,
140 else as upersisted and updated if config.doDetection, else None
141 - calib: object returned by calibration process if config.doCalibrate, else None
142 - sources: detected source if config.doPhotometry, else None
147 calExposure = inputExposure
150 backgrounds = afwMath.BackgroundList()
151 if self.config.doCalibrate:
152 calib = self.calibrate.run(inputExposure, idFactory=idFactory)
153 calExposure = calib.exposure
154 if self.config.doWriteCalibrate:
155 dataRef.put(calib.sources, self.
dataPrefix +
"icSrc")
156 if calib.matches
is not None and self.config.doWriteCalibrateMatches:
158 normalizedMatches.table.setMetadata(calib.matchMeta)
159 dataRef.put(normalizedMatches, self.
dataPrefix +
"icMatch")
161 for bg
in calib.backgrounds:
162 backgrounds.append(bg)
164 backgrounds.append(calib.backgrounds)
165 except AttributeError:
166 self.log.warn(
"The calibration task did not return any backgrounds. " +
167 "Any background subtracted in the calibration process cannot be persisted.")
171 if self.config.doDetection:
172 table = afwTable.SourceTable.make(self.
schema, idFactory)
174 detections = self.detection.run(table, calExposure)
175 sources = detections.sources
176 fpSets = detections.fpSets
177 if fpSets.background:
178 backgrounds.append(fpSets.background)
180 if self.config.doDeblend:
181 self.deblend.run(calExposure, sources, calExposure.getPsf())
183 if self.config.doMeasurement:
184 self.measurement.run(calExposure, sources, exposureId=self.
getExposureId(dataRef))
186 if self.config.doWriteCalibrate:
191 if calExposure
is None:
192 self.log.warn(
"calibrated exposure is None; cannot save it")
194 if self.config.persistBackgroundModel:
198 dataRef.put(calExposure, self.
dataPrefix +
"calexp")
200 if calib
is not None:
203 if sources
is not None and self.config.doWriteSources:
204 sourceWriteFlags = (0
if self.config.doWriteHeavyFootprintsInSources
205 else afwTable.SOURCE_IO_NO_HEAVY_FOOTPRINTS)
206 if enableWriteSources:
207 dataRef.put(sources, self.
dataPrefix +
'src', flags=sourceWriteFlags)
209 if self.config.doMeasurement
and self.config.doWriteSourceMatches:
210 self.log.info(
"Matching src to reference catalogue" % (dataRef.dataId))
211 srcMatches, srcMatchMeta = self.
matchSources(calExposure, sources)
214 normalizedSrcMatches.table.setMetadata(srcMatchMeta)
215 dataRef.put(normalizedSrcMatches, self.
dataPrefix +
"srcMatch")
217 srcMatches =
None; srcMatchMeta =
None
219 return pipeBase.Struct(
220 inputExposure = inputExposure,
221 exposure = calExposure,
224 matches = srcMatches,
225 matchMeta = srcMatchMeta,
226 backgrounds = backgrounds,
230 """Match the sources to the reference object loaded by the calibrate task"""
232 astrometer = self.calibrate.astrometry.astrometer
233 if astrometer
is None:
234 raise AttributeError(
"No astrometer")
235 except AttributeError:
236 self.log.warn(
"Failed to find an astrometer in calibrate's astronomy task")
239 astromRet = astrometer.useKnownWcs(sources, exposure=exposure)
241 return astromRet.matches, astromRet.matchMetadata
244 """Match the icSources and sources, and propagate Interesting Flags (e.g. PSF star) to the sources
246 self.log.info(
"Matching icSource and Source catalogs to propagate flags.")
247 if icSources
is None or sources
is None:
252 if self.config.doDeblend:
253 matched = [m
for m
in matched
if m[1].get(
"deblend_nChild") == 0]
259 for m0, m1, d
in matched:
261 if bestMatches.has_key(id0):
262 if d > bestMatches[id0][2]:
265 bestMatches[id0] = (m0, m1, d)
267 matched = bestMatches.values()
271 if len(set(m[0].getId()
for m
in matched)) != len(matched):
272 self.log.warn(
"At least one icSource is matched to more than one Source")
276 for ics, s, d
in matched:
283 """Return a dict of empty catalogs for each catalog dataset produced by this task."""
290 icSrc.getTable().setMetadata(self.calibrate.algMetadata)
291 except AttributeError:
293 if icSrc
is not None:
298 """Backgrounds are persisted via the butler
300 @param dataRef: Data reference
301 @param backgrounds: List of background models
303 self.log.warn(
"Persisting background models")
305 dataRef.put(backgrounds, self.
dataPrefix+
"calexpBackground")
308 """Add backgrounds back in to an exposure
310 @param exp: Exposure to which to add backgrounds
311 @param backgrounds: List of background models
313 mi = exp.getMaskedImage()
314 mi += backgrounds.getImage()
Class for storing ordered metadata with comments.
A mapping between the keys of two Schemas, used to copy data between them.
tuple doWriteHeavyFootprintsInSources
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
A description of a field in a table.
BaseCatalog packMatches(std::vector< Match< Record1, Record2 > > const &matches)
Return a table representation of a MatchVector that can be used to persist it.
std::vector< Match< typename Cat::Record, typename Cat::Record > > matchRaDec(Cat const &cat, Angle radius, bool symmetric=true)
tuple doDeblend
NOTE, default this to False until it is fully vetted; #2138.