1 from __future__
import absolute_import, division, print_function
7 from lsst.pex.config import Config, Field, ConfigurableField, ConfigField
11 FocalPlaneBackgroundConfig, MaskObjectsTask)
18 """Make and write an image of an entire focal plane 22 camera : `lsst.afw.cameraGeom.Camera` 24 exposures : `list` of `tuple` of `int` and `lsst.afw.image.Exposure` 25 List of detector ID and CCD exposures (binned by `binning`). 26 filename : `str`, optional 29 Binning size that has been applied to images. 31 image = visualizeVisit.makeCameraImage(camera, dict(exp
for exp
in exposures
if exp
is not None), binning)
32 if filename
is not None:
33 image.writeFits(filename)
38 """Configuration for SkyCorrectionTask""" 39 bgModel = ConfigField(dtype=FocalPlaneBackgroundConfig, doc=
"Background model")
40 bgModel2 = ConfigField(dtype=FocalPlaneBackgroundConfig, doc=
"2nd Background model")
41 sky = ConfigurableField(target=SkyMeasurementTask, doc=
"Sky measurement")
42 maskObjects = ConfigurableField(target=MaskObjectsTask, doc=
"Mask Objects")
43 doMaskObjects = Field(dtype=bool, default=
True, doc=
"Mask objects to find good sky?")
44 doBgModel = Field(dtype=bool, default=
True, doc=
"Do background model subtraction?")
45 doBgModel2 = Field(dtype=bool, default=
True, doc=
"Do cleanup background model subtraction?")
46 doSky = Field(dtype=bool, default=
True, doc=
"Do sky frame subtraction?")
47 binning = Field(dtype=int, default=8, doc=
"Binning factor for constructing focal-plane images")
50 Config.setDefaults(self)
59 """Correct sky over entire focal plane""" 60 ConfigClass = SkyCorrectionConfig
61 _DefaultName =
"skyCorr" 64 BatchPoolTask.__init__(self, *args, **kwargs)
65 self.makeSubtask(
"maskObjects")
66 self.makeSubtask(
"sky")
69 def _makeArgumentParser(cls, *args, **kwargs):
70 kwargs.pop(
"doBatch",
False)
71 parser = ArgumentParser(name=
"skyCorr", *args, **kwargs)
72 parser.add_id_argument(
"--id", datasetType=
"calexp", level=
"visit",
73 help=
"data ID, e.g. --id visit=12345")
78 """Return walltime request for batch job 80 Subclasses should override if the walltime should be calculated 81 differently (e.g., addition of some serial time). 86 Requested time per iteration. 87 parsedCmd : `argparse.Namespace` 88 Results of argument parsing. 92 numTargets = len(cls.RunnerClass.getTargetList(parsedCmd))
93 return time*numTargets
96 """Perform sky correction on an exposure 98 We restore the original sky, and remove it again using multiple 99 algorithms. We optionally apply: 101 1. A large-scale background model. 102 This step removes very-large-scale sky such as moonlight. 104 3. A medium-scale background model. 105 This step removes residual sky (This is smooth on the focal plane). 107 Only the master node executes this method. The data is held on 108 the slave nodes, which do all the hard work. 112 expRef : `lsst.daf.persistence.ButlerDataRef` 113 Data reference for exposure. 116 extension =
"-%(visit)d.fits" % expRef.dataId
118 with self.
logOperation(
"processing %s" % (expRef.dataId,)):
121 pool.storeSet(butler=expRef.getButler())
122 camera = expRef.get(
"camera")
124 dataIdList = [ccdRef.dataId
for ccdRef
in expRef.subItems(
"ccd")
if 125 ccdRef.datasetExists(
"calexp")]
127 exposures = pool.map(self.
loadImage, dataIdList)
132 exposures = pool.mapToPrevious(self.
collectMask, dataIdList)
135 if self.config.doBgModel:
138 if self.config.doSky:
140 scale = self.sky.solveScales(measScales)
141 self.log.info(
"Sky frame scale: %s" % (scale,))
145 calibs = pool.mapToPrevious(self.
collectSky, dataIdList)
148 if self.config.doBgModel2:
153 expRef.put(image,
"calexp_camera")
155 pool.mapToPrevious(self.
write, dataIdList)
158 """Perform full focal-plane background subtraction 160 This method runs on the master node. 164 camera : `lsst.afw.cameraGeom.Camera` 166 pool : `lsst.ctrl.pool.Pool` 168 dataIdList : iterable of `dict` 169 List of data identifiers for the CCDs. 170 config : `lsst.pipe.drivers.background.FocalPlaneBackgroundConfig` 171 Configuration to use for background subtraction. 175 exposures : `list` of `lsst.afw.image.Image` 176 List of binned images, for creating focal plane image. 178 bgModel = FocalPlaneBackground.fromCamera(config, camera)
179 data = [Struct(dataId=dataId, bgModel=bgModel.clone())
for dataId
in dataIdList]
181 for ii, bg
in enumerate(bgModelList):
182 self.log.info(
"Background %d: %d pixels", ii, bg._numbers.array.sum())
184 return pool.mapToPrevious(self.
subtractModel, dataIdList, bgModel)
187 """Load original image and restore the sky 189 This method runs on the slave nodes. 193 cache : `lsst.pipe.base.Struct` 200 exposure : `lsst.afw.image.Exposure` 203 cache.dataId = dataId
204 cache.exposure = cache.butler.get(
"calexp", dataId, immediate=
True).clone()
205 bgOld = cache.butler.get(
"calexpBackground", dataId, immediate=
True)
206 image = cache.exposure.getMaskedImage()
210 statsImage = bgData[0].getStatsImage()
213 image -= bgOld.getImage()
214 cache.bgList = afwMath.BackgroundList()
216 cache.bgList.append(bgData)
218 if self.config.doMaskObjects:
219 self.maskObjects.findObjects(cache.exposure)
224 """Measure scale for sky frame 226 This method runs on the slave nodes. 230 cache : `lsst.pipe.base.Struct` 240 assert cache.dataId == dataId
241 cache.sky = self.sky.getSkyData(cache.butler, dataId)
242 scale = self.sky.measureScale(cache.exposure.getMaskedImage(), cache.sky)
246 """Subtract sky frame 248 This method runs on the slave nodes. 252 cache : `lsst.pipe.base.Struct` 261 exposure : `lsst.afw.image.Exposure` 264 assert cache.dataId == dataId
265 self.sky.
subtractSkyFrame(cache.exposure.getMaskedImage(), cache.sky, scale, cache.bgList)
269 """Fit background model for CCD 271 This method runs on the slave nodes. 275 cache : `lsst.pipe.base.Struct` 277 data : `lsst.pipe.base.Struct` 278 Data identifier, with `dataId` (data identifier) and `bgModel` 279 (background model) elements. 283 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackground` 286 assert cache.dataId == data.dataId
287 data.bgModel.addCcd(cache.exposure)
291 """Subtract background model 293 This method runs on the slave nodes. 297 cache : `lsst.pipe.base.Struct` 301 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround` 306 exposure : `lsst.afw.image.Exposure` 309 assert cache.dataId == dataId
310 exposure = cache.exposure
311 image = exposure.getMaskedImage()
312 detector = exposure.getDetector()
313 bbox = image.getBBox()
314 cache.bgModel = bgModel.toCcdBackground(detector, bbox)
315 image -= cache.bgModel.getImage()
316 cache.bgList.append(cache.bgModel[0])
320 """Generate an image of the background model for visualisation 322 Useful for debugging. 326 cache : `lsst.pipe.base.Struct` 330 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround` 337 image : `lsst.afw.image.MaskedImage` 338 Binned background model image. 340 assert cache.dataId == dataId
341 exposure = cache.exposure
342 detector = exposure.getDetector()
343 bbox = exposure.getMaskedImage().getBBox()
344 image = bgModel.toCcdBackground(detector, bbox).getImage()
348 """Return the binned image required for visualization 350 This method just helps to cut down on boilerplate. 354 image : `lsst.afw.image.MaskedImage` 355 Image to go into visualisation. 361 image : `lsst.afw.image.MaskedImage` 364 return (exposure.getDetector().getId(), afwMath.binImage(image, self.config.binning))
367 """Collect exposure for potential visualisation 369 This method runs on the slave nodes. 373 cache : `lsst.pipe.base.Struct` 380 image : `lsst.afw.image.MaskedImage` 386 """Collect original image for visualisation 388 This method runs on the slave nodes. 392 cache : `lsst.pipe.base.Struct` 401 image : `lsst.afw.image.MaskedImage` 404 exposure = cache.butler.get(
"calexp", dataId, immediate=
True)
408 """Collect original image for visualisation 410 This method runs on the slave nodes. 414 cache : `lsst.pipe.base.Struct` 423 image : `lsst.afw.image.MaskedImage` 429 """Collect mask for visualisation 431 This method runs on the slave nodes. 435 cache : `lsst.pipe.base.Struct` 444 image : `lsst.afw.image.Image` 448 image = afwImage.ImageF(cache.exposure.maskedImage.getBBox())
449 image.array[:] = cache.exposure.maskedImage.mask.array
453 """Write resultant background list 455 This method runs on the slave nodes. 459 cache : `lsst.pipe.base.Struct` 464 cache.butler.put(cache.bgList,
"skyCorr", dataId)
466 def _getMetadataName(self):
467 """There's no metadata to write out"""
def subtractModel(self, cache, dataId, bgModel)
def batchWallTime(cls, time, parsedCmd, numCores)
def runDataRef(self, expRef)
def makeCameraImage(camera, exposures, filename=None, binning=8)
def focalPlaneBackground(self, camera, pool, dataIdList, config)
def accumulateModel(self, cache, data)
def loadImage(self, cache, dataId)
def collectBinnedImage(self, exposure, image)
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 collectMask(self, cache, dataId)
def measureSkyFrame(self, cache, dataId)