1 from __future__
import absolute_import, division, print_function
11 from lsst.pex.config import Config, Field, ConfigurableField, ConfigField
22 """Make and write an image of an entire focal plane 26 camera : `lsst.afw.cameraGeom.Camera` 28 exposures : `list` of `tuple` of `int` and `lsst.afw.image.Exposure` 29 List of detector ID and CCD exposures (binned by `binning`). 30 filename : `str`, optional 33 Binning size that has been applied to images. 35 image = visualizeVisit.makeCameraImage(camera, dict(exp
for exp
in exposures
if exp
is not None), binning)
36 if filename
is not None:
37 image.writeFits(filename)
42 """Configuration for SkyCorrectionTask""" 43 bgModel = ConfigField(dtype=FocalPlaneBackgroundConfig, doc=
"Background model")
44 sky = ConfigurableField(target=SkyMeasurementTask, doc=
"Sky measurement")
45 detection = ConfigurableField(target=measAlg.SourceDetectionTask, doc=
"Detection configuration")
46 detectSigma = Field(dtype=float, default=2.0, doc=
"Detection PSF gaussian sigma")
47 doBgModel = Field(dtype=bool, default=
True, doc=
"Do background model subtraction?")
48 doSky = Field(dtype=bool, default=
True, doc=
"Do sky frame subtraction?")
52 """Correct sky over entire focal plane""" 53 ConfigClass = SkyCorrectionConfig
54 _DefaultName =
"skyCorr" 57 BatchPoolTask.__init__(self, *args, **kwargs)
58 self.makeSubtask(
"sky")
59 self.makeSubtask(
"detection")
62 def _makeArgumentParser(cls, *args, **kwargs):
63 kwargs.pop(
"doBatch",
False)
64 parser = ArgumentParser(name=
"skyCorr", *args, **kwargs)
65 parser.add_id_argument(
"--id", datasetType=
"calexp", level=
"visit",
66 help=
"data ID, e.g. --id visit=12345")
71 """Return walltime request for batch job 73 Subclasses should override if the walltime should be calculated 74 differently (e.g., addition of some serial time). 79 Requested time per iteration. 80 parsedCmd : `argparse.Namespace` 81 Results of argument parsing. 85 numTargets = len(cls.RunnerClass.getTargetList(parsedCmd))
86 return time*numTargets
88 def run(self, expRef):
89 """Perform sky correction on an exposure 91 We restore the original sky, and remove it again using multiple 92 algorithms. We optionally apply: 94 1. A large-scale background model. 97 Only the master node executes this method. The data is held on 98 the slave nodes, which do all the hard work. 102 expRef : `lsst.daf.persistence.ButlerDataRef` 103 Data reference for exposure. 106 extension =
"-%(visit)d.fits" % expRef.dataId
108 with self.
logOperation(
"processing %s" % (expRef.dataId,)):
111 pool.storeSet(butler=expRef.getButler())
112 camera = expRef.get(
"camera")
114 dataIdList = [ccdRef.dataId
for ccdRef
in expRef.subItems(
"ccd")
if 115 ccdRef.datasetExists(
"calexp")]
117 exposures = pool.map(self.
loadImage, dataIdList)
123 if self.config.doBgModel:
124 bgModel = FocalPlaneBackground.fromCamera(self.config.bgModel, camera)
125 data = [Struct(dataId=dataId, bgModel=bgModel.clone())
for dataId
in dataIdList]
127 for ii, bg
in enumerate(bgModelList):
128 self.log.info(
"Background %d: %d pixels", ii, bg._numbers.getArray().sum())
132 bgModel.getStatsImage().writeFits(
"bgModel" + extension)
133 bgImages = pool.mapToPrevious(self.
realiseModel, dataIdList, bgModel)
136 exposures = pool.mapToPrevious(self.
subtractModel, dataIdList, bgModel)
140 if self.config.doSky:
142 scale = self.sky.solveScales(measScales)
143 self.log.info(
"Sky frame scale: %s" % (scale,))
147 calibs = pool.mapToPrevious(self.
collectSky, dataIdList)
152 expRef.put(image,
"calexp_camera")
154 pool.mapToPrevious(self.
write, dataIdList)
157 """Load original image and restore the sky 159 This method runs on the slave nodes. 163 cache : `lsst.pipe.base.Struct` 170 exposure : `lsst.afw.image.Exposure` 173 cache.dataId = dataId
174 cache.exposure = cache.butler.get(
"calexp", dataId, immediate=
True).clone()
175 bgOld = cache.butler.get(
"calexpBackground", dataId, immediate=
True)
176 image = cache.exposure.getMaskedImage()
180 statsImage = bgData[0].getStatsImage()
183 image -= bgOld.getImage()
184 cache.bgList = afwMath.BackgroundList()
186 cache.bgList.append(bgData)
191 """Measure scale for sky frame 193 This method runs on the slave nodes. 197 cache : `lsst.pipe.base.Struct` 207 assert cache.dataId == dataId
208 cache.sky = self.sky.getSkyData(cache.butler, dataId)
209 scale = self.sky.measureScale(cache.exposure.getMaskedImage(), cache.sky)
213 """Subtract sky frame 215 This method runs on the slave nodes. 219 cache : `lsst.pipe.base.Struct` 228 exposure : `lsst.afw.image.Exposure` 231 assert cache.dataId == dataId
232 self.sky.
subtractSkyFrame(cache.exposure.getMaskedImage(), cache.sky, scale, cache.bgList)
236 """Fit background model for CCD 238 This method runs on the slave nodes. 242 cache : `lsst.pipe.base.Struct` 244 data : `lsst.pipe.base.Struct` 245 Data identifier, with `dataId` (data identifier) and `bgModel` 246 (background model) elements. 250 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackground` 253 assert cache.dataId == data.dataId
254 data.bgModel.addCcd(cache.exposure)
258 """Subtract background model 260 This method runs on the slave nodes. 264 cache : `lsst.pipe.base.Struct` 268 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround` 273 exposure : `lsst.afw.image.Exposure` 276 assert cache.dataId == dataId
277 exposure = cache.exposure
278 image = exposure.getMaskedImage()
279 detector = exposure.getDetector()
280 bbox = image.getBBox()
281 cache.bgModel = bgModel.toCcdBackground(detector, bbox)
282 image -= cache.bgModel.getImage()
283 cache.bgList.append(cache.bgModel[0])
287 """Generate an image of the background model for visualisation 289 Useful for debugging. 293 cache : `lsst.pipe.base.Struct` 297 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround` 304 image : `lsst.afw.image.MaskedImage` 305 Binned background model image. 307 assert cache.dataId == dataId
308 exposure = cache.exposure
309 detector = exposure.getDetector()
310 bbox = exposure.getMaskedImage().getBBox()
311 image = bgModel.toCcdBackground(detector, bbox).getImage()
312 return (detector.getId(), afwMath.binImage(image, BINNING))
315 """Collect exposure for potential visualisation 317 This method runs on the slave nodes. 321 cache : `lsst.pipe.base.Struct` 328 image : `lsst.afw.image.MaskedImage` 331 return (cache.exposure.getDetector().getId(),
332 afwMath.binImage(cache.exposure.getMaskedImage(), BINNING))
335 """Collect original image for visualisation 337 This method runs on the slave nodes. 341 cache : `lsst.pipe.base.Struct` 350 image : `lsst.afw.image.MaskedImage` 353 exposure = cache.butler.get(
"calexp", dataId, immediate=
True)
354 return (exposure.getDetector().getId(),
355 afwMath.binImage(exposure.getMaskedImage(), BINNING))
358 """Collect original image for visualisation 360 This method runs on the slave nodes. 364 cache : `lsst.pipe.base.Struct` 373 image : `lsst.afw.image.MaskedImage` 376 return (cache.exposure.getDetector().getId(), afwMath.binImage(cache.sky.getImage(), BINNING))
379 """Write resultant background list 381 This method runs on the slave nodes. 385 cache : `lsst.pipe.base.Struct` 390 cache.butler.put(cache.bgList,
"skyCorr", dataId)
392 def _getMetadataName(self):
393 """There's no metadata to write out"""
def subtractModel(self, cache, dataId, bgModel)
def batchWallTime(cls, time, parsedCmd, numCores)
def makeCameraImage(camera, exposures, filename=None, binning=8)
def accumulateModel(self, cache, data)
def loadImage(self, cache, dataId)
def realiseModel(self, cache, dataId, bgModel)
def collectOriginal(self, cache, dataId)
def subtractSkyFrame(self, cache, dataId, scale)
def write(self, cache, dataId)
def logOperation(self, operation, catch=False, trace=True)
def __init__(self, args, kwargs)
def collectSky(self, cache, dataId)
def measureSkyFrame(self, cache, dataId)