24 from .multiBandUtils
import (CullPeaksConfig, MergeSourcesRunner, _makeMakeIdFactory, makeMergeArgumentParser,
25 getInputSchema, getShortFilterName, readCatalog)
33 from lsst.pex.config import Config, Field, ListField, ConfigurableField, ConfigField
40 @anchor MergeDetectionsConfig_ 42 @brief Configuration parameters for the MergeDetectionsTask. 44 minNewPeak = Field(dtype=float, default=1,
45 doc=
"Minimum distance from closest peak to create a new one (in arcsec).")
47 maxSamePeak = Field(dtype=float, default=0.3,
48 doc=
"When adding new catalogs to the merge, all peaks less than this distance " 49 " (in arcsec) to an existing peak will be flagged as detected in that catalog.")
50 cullPeaks = ConfigField(dtype=CullPeaksConfig, doc=
"Configuration for how to cull peaks.")
52 skyFilterName = Field(dtype=str, default=
"sky",
53 doc=
"Name of `filter' used to label sky objects (e.g. flag merge_peak_sky is set)\n" 54 "(N.b. should be in MergeMeasurementsConfig.pseudoFilterList)")
55 skyObjects = ConfigurableField(target=SkyObjectsTask, doc=
"Generate sky objects")
56 priorityList = ListField(dtype=str, default=[],
57 doc=
"Priority-ordered list of bands for the merge.")
58 coaddName = Field(dtype=str, default=
"deep", doc=
"Name of coadd")
61 Config.setDefaults(self)
67 raise RuntimeError(
"No priority list provided")
72 @anchor MergeDetectionsTask_ 74 @brief Merge coadd detections from multiple bands. 76 @section pipe_tasks_multiBand_Contents Contents 78 - @ref pipe_tasks_multiBand_MergeDetectionsTask_Purpose 79 - @ref pipe_tasks_multiBand_MergeDetectionsTask_Init 80 - @ref pipe_tasks_multiBand_MergeDetectionsTask_Run 81 - @ref pipe_tasks_multiBand_MergeDetectionsTask_Config 82 - @ref pipe_tasks_multiBand_MergeDetectionsTask_Debug 83 - @ref pipe_tasks_multiband_MergeDetectionsTask_Example 85 @section pipe_tasks_multiBand_MergeDetectionsTask_Purpose Description 87 Command-line task that merges sources detected in coadds of exposures obtained with different filters. 89 To perform photometry consistently across coadds in multiple filter bands, we create a master catalog of 90 sources from all bands by merging the sources (peaks & footprints) detected in each coadd, while keeping 91 track of which band each source originates in. 93 The catalog merge is performed by @ref getMergedSourceCatalog. Spurious peaks detected around bright 94 objects are culled as described in @ref CullPeaksConfig_. 97 deepCoadd_det{tract,patch,filter}: SourceCatalog (only parent Footprints) 99 deepCoadd_mergeDet{tract,patch}: SourceCatalog (only parent Footprints) 103 @section pipe_tasks_multiBand_MergeDetectionsTask_Init Task initialisation 105 @copydoc \_\_init\_\_ 107 @section pipe_tasks_multiBand_MergeDetectionsTask_Run Invoking the Task 111 @section pipe_tasks_multiBand_MergeDetectionsTask_Config Configuration parameters 113 See @ref MergeDetectionsConfig_ 115 @section pipe_tasks_multiBand_MergeDetectionsTask_Debug Debug variables 117 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a flag @c -d 118 to import @b debug.py from your @c PYTHONPATH; see @ref baseDebug for more about @b debug.py files. 120 MergeDetectionsTask has no debug variables. 122 @section pipe_tasks_multiband_MergeDetectionsTask_Example A complete example of using MergeDetectionsTask 124 MergeDetectionsTask is meant to be run after detecting sources in coadds generated for the chosen subset 125 of the available bands. 126 The purpose of the task is to merge sources (peaks & footprints) detected in the coadds generated from the 127 chosen subset of filters. 128 Subsequent tasks in the multi-band processing procedure will deblend the generated master list of sources 129 and, eventually, perform forced photometry. 130 Command-line usage of MergeDetectionsTask expects data references for all the coadds to be processed. 131 A list of the available optional arguments can be obtained by calling mergeCoaddDetections.py with the 132 `--help` command line argument: 134 mergeCoaddDetections.py --help 137 To demonstrate usage of the DetectCoaddSourcesTask in the larger context of multi-band processing, we 138 will process HSC data in the [ci_hsc](https://github.com/lsst/ci_hsc) package. Assuming one has finished 139 step 5 at @ref pipeTasks_multiBand, one may merge the catalogs of sources from each coadd as follows: 141 mergeCoaddDetections.py $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-I^HSC-R 143 This will merge the HSC-I & -R band parent source catalogs and write the results to 144 `$CI_HSC_DIR/DATA/deepCoadd-results/merged/0/5,4/mergeDet-0-5,4.fits`. 146 The next step in the multi-band processing procedure is 147 @ref MeasureMergedCoaddSourcesTask_ "MeasureMergedCoaddSourcesTask" 149 ConfigClass = MergeDetectionsConfig
150 RunnerClass = MergeSourcesRunner
151 _DefaultName =
"mergeCoaddDetections" 153 outputDataset =
"mergeDet" 154 makeIdFactory = _makeMakeIdFactory(
"MergedCoaddId")
157 def _makeArgumentParser(cls):
163 def __init__(self, butler=None, schema=None, **kwargs):
165 @brief Initialize the merge detections task. 167 A @ref FootprintMergeList_ "FootprintMergeList" will be used to 168 merge the source catalogs. 170 @param[in] schema the schema of the detection catalogs used as input to this one 171 @param[in] butler a butler used to read the input schema from disk, if schema is None 172 @param[in] **kwargs keyword arguments to be passed to CmdLineTask.__init__ 174 The task will set its own self.schema attribute to the schema of the output merged catalog. 176 CmdLineTask.__init__(self, **kwargs)
177 self.makeSubtask(
"skyObjects")
181 filterNames += [self.config.skyFilterName]
185 catalogs = dict(
readCatalog(self, patchRef)
for patchRef
in patchRefList)
186 mergeCatalog = self.
run(catalogs, patchRefList[0])
187 self.
write(patchRefList[0], mergeCatalog)
189 def run(self, catalogs, patchRef):
191 @brief Merge multiple catalogs. 193 After ordering the catalogs and filters in priority order, 194 @ref getMergedSourceCatalog of the @ref FootprintMergeList_ "FootprintMergeList" created by 195 @ref \_\_init\_\_ is used to perform the actual merging. Finally, @ref cullPeaks is used to remove 196 garbage peaks detected around bright objects. 200 @param[out] mergedList 204 skyInfo =
getSkyInfo(coaddName=self.config.coaddName, patchRef=patchRef)
205 tractWcs = skyInfo.wcs
206 peakDistance = self.config.minNewPeak / tractWcs.getPixelScale().asArcseconds()
207 samePeakDistance = self.config.maxSamePeak / tractWcs.getPixelScale().asArcseconds()
210 orderedCatalogs = [catalogs[band]
for band
in self.config.priorityList
if band
in catalogs.keys()]
212 if band
in catalogs.keys()]
214 mergedList = self.
merged.getMergedSourceCatalog(orderedCatalogs, orderedBands, peakDistance,
221 skySeed = patchRef.get(self.config.coaddName +
"MergedCoaddId")
223 if skySourceFootprints:
224 key = mergedList.schema.find(
"merge_footprint_%s" % self.config.skyFilterName).key
225 for foot
in skySourceFootprints:
226 s = mergedList.addNew()
231 for record
in mergedList:
232 record.getFootprint().sortPeaks()
233 self.log.info(
"Merged to %d sources" % len(mergedList))
240 @brief Attempt to remove garbage peaks (mostly on the outskirts of large blends). 242 @param[in] catalog Source catalog 244 keys = [item.key
for item
in self.
merged.getPeakSchema().extract(
"merge_peak_*").values()]
245 assert len(keys) > 0,
"Error finding flags that associate peaks with their detection bands." 248 for parentSource
in catalog:
251 keptPeaks = parentSource.getFootprint().getPeaks()
252 oldPeaks = list(keptPeaks)
254 familySize = len(oldPeaks)
255 totalPeaks += familySize
256 for rank, peak
in enumerate(oldPeaks):
257 if ((rank < self.config.cullPeaks.rankSufficient)
or 258 (sum([peak.get(k)
for k
in keys]) >= self.config.cullPeaks.nBandsSufficient)
or 259 (rank < self.config.cullPeaks.rankConsidered
and 260 rank < self.config.cullPeaks.rankNormalizedConsidered * familySize)):
261 keptPeaks.append(peak)
264 self.log.info(
"Culled %d of %d peaks" % (culledPeaks, totalPeaks))
268 Return a dict of empty catalogs for each catalog dataset produced by this task. 270 @param[out] dictionary of empty catalogs 272 mergeDet = afwTable.SourceCatalog(self.
schema)
273 peak = afwDetect.PeakCatalog(self.
merged.getPeakSchema())
274 return {self.config.coaddName +
"Coadd_mergeDet": mergeDet,
275 self.config.coaddName +
"Coadd_peak": peak}
279 @brief Return a list of Footprints of sky objects which don't overlap with anything in mergedList 281 @param mergedList The merged Footprints from all the input bands 282 @param skyInfo A description of the patch 283 @param seed Seed for the random number generator 285 mask = afwImage.Mask(skyInfo.patchInfo.getOuterBBox())
286 detected = mask.getPlaneBitMask(
"DETECTED")
288 s.getFootprint().spans.setMask(mask, detected)
290 footprints = self.skyObjects.
run(mask, seed)
295 schema = self.
merged.getPeakSchema()
296 mergeKey = schema.find(
"merge_peak_%s" % self.config.skyFilterName).key
298 for oldFoot
in footprints:
299 assert len(oldFoot.getPeaks()) == 1,
"Should be a single peak only" 300 peak = oldFoot.getPeaks()[0]
301 newFoot = afwDetect.Footprint(oldFoot.spans, schema)
302 newFoot.addPeak(peak.getFx(), peak.getFy(), peak.getPeakValue())
303 newFoot.getPeaks()[0].set(mergeKey,
True)
304 converted.append(newFoot)
310 @brief Write the output. 312 @param[in] patchRef data reference for patch 313 @param[in] catalog catalog 315 We write as the dataset provided by the 'outputDataset' 318 patchRef.put(catalog, self.config.coaddName +
"Coadd_" + self.
outputDataset)
321 mergeDataId = patchRef.dataId.copy()
322 del mergeDataId[
"filter"]
323 self.log.info(
"Wrote merged catalog: %s" % (mergeDataId,))
327 @brief No metadata to write, and not sure how to write it for a list of dataRefs. def getSchemaCatalogs(self)
Return a dict of empty catalogs for each catalog dataset produced by this task.
def makeMergeArgumentParser(name, dataset)
Create a suitable ArgumentParser.
Merge coadd detections from multiple bands.
def readCatalog(task, patchRef)
Read input catalog.
def run(self, catalogs, patchRef)
Merge multiple catalogs.
def __init__(self, butler=None, schema=None, kwargs)
Initialize the merge detections task.
Configuration parameters for the MergeDetectionsTask.
def getShortFilterName(name)
def getSkyInfo(coaddName, patchRef)
Return the SkyMap, tract and patch information, wcs, and outer bbox of the patch to be coadded...
def cullPeaks(self, catalog)
Attempt to remove garbage peaks (mostly on the outskirts of large blends).
def write(self, patchRef, catalog)
Write the output.
def getInputSchema(self, butler=None, schema=None)
def runDataRef(self, patchRefList)
def writeMetadata(self, dataRefList)
No metadata to write, and not sure how to write it for a list of dataRefs.
def getSkySourceFootprints(self, mergedList, skyInfo, seed)
Return a list of Footprints of sky objects which don't overlap with anything in mergedList.