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 : `dict` mapping detector ID to `lsst.afw.image.Exposure` 29 CCD exposures, binned by `binning`. 30 filename : `str`, optional 33 Binning size that has been applied to images. 35 class ImageSource(object):
36 """Source of images for makeImageFromCamera""" 37 def __init__(self, exposures):
42 exposures : `dict` mapping detector ID to `lsst.afw.image.Exposure` 43 CCD exposures, already binned. 46 self.exposures = exposures
47 self.background = numpy.nan
49 def getCcdImage(self, detector, imageFactory, binSize):
50 """Provide image of CCD to makeImageFromCamera""" 51 if detector.getId()
not in self.exposures:
52 return imageFactory(1, 1), detector
53 image = self.exposures[detector.getId()]
54 if hasattr(image,
"getMaskedImage"):
55 image = image.getMaskedImage()
56 if hasattr(image,
"getMask"):
57 mask = image.getMask()
58 isBad = mask.getArray() & mask.getPlaneBitMask(
"NO_DATA") > 0
60 image.getImage().getArray()[isBad] = numpy.nan
61 if hasattr(image,
"getImage"):
62 image = image.getImage()
63 return image, detector
65 image = makeImageFromCamera(
67 imageSource=ImageSource(dict(exp
for exp
in exposures
if exp
is not None)),
68 imageFactory=afwImage.ImageF,
71 if filename
is not None:
72 image.writeFits(filename)
77 """Configuration for SkyCorrectionTask""" 78 bgModel = ConfigField(dtype=FocalPlaneBackgroundConfig, doc=
"Background model")
79 sky = ConfigurableField(target=SkyMeasurementTask, doc=
"Sky measurement")
80 detection = ConfigurableField(target=measAlg.SourceDetectionTask, doc=
"Detection configuration")
81 detectSigma = Field(dtype=float, default=2.0, doc=
"Detection PSF gaussian sigma")
82 doBgModel = Field(dtype=bool, default=
True, doc=
"Do background model subtraction?")
83 doSky = Field(dtype=bool, default=
True, doc=
"Do sky frame subtraction?")
87 """Correct sky over entire focal plane""" 88 ConfigClass = SkyCorrectionConfig
89 _DefaultName =
"skyCorr" 92 BatchPoolTask.__init__(self, *args, **kwargs)
93 self.makeSubtask(
"sky")
94 self.makeSubtask(
"detection")
97 def _makeArgumentParser(cls, *args, **kwargs):
98 kwargs.pop(
"doBatch",
False)
99 parser = ArgumentParser(name=
"skyCorr", *args, **kwargs)
100 parser.add_id_argument(
"--id", datasetType=
"calexp", level=
"visit",
101 help=
"data ID, e.g. --id visit=12345")
106 """Return walltime request for batch job 108 Subclasses should override if the walltime should be calculated 109 differently (e.g., addition of some serial time). 114 Requested time per iteration. 115 parsedCmd : `argparse.Namespace` 116 Results of argument parsing. 120 numTargets = len(cls.RunnerClass.getTargetList(parsedCmd))
121 return time*numTargets
124 """Perform sky correction on an exposure 126 We restore the original sky, and remove it again using multiple 127 algorithms. We optionally apply: 129 1. A large-scale background model. 132 Only the master node executes this method. The data is held on 133 the slave nodes, which do all the hard work. 137 expRef : `lsst.daf.persistence.ButlerDataRef` 138 Data reference for exposure. 141 extension =
"-%(visit)d.fits" % expRef.dataId
143 with self.
logOperation(
"processing %s" % (expRef.dataId,)):
146 pool.storeSet(butler=expRef.getButler())
147 camera = expRef.get(
"camera")
149 dataIdList = [ccdRef.dataId
for ccdRef
in expRef.subItems(
"ccd")
if 150 ccdRef.datasetExists(
"calexp")]
152 exposures = pool.map(self.
loadImage, dataIdList)
158 if self.config.doBgModel:
159 bgModel = FocalPlaneBackground.fromCamera(self.config.bgModel, camera)
160 data = [Struct(dataId=dataId, bgModel=bgModel.clone())
for dataId
in dataIdList]
162 for ii, bg
in enumerate(bgModelList):
163 self.log.info(
"Background %d: %d pixels", ii, bg._numbers.getArray().sum())
167 bgModel.getStatsImage().writeFits(
"bgModel" + extension)
168 bgImages = pool.mapToPrevious(self.
realiseModel, dataIdList, bgModel)
171 exposures = pool.mapToPrevious(self.
subtractModel, dataIdList, bgModel)
175 if self.config.doSky:
177 scale = self.sky.solveScales(measScales)
178 self.log.info(
"Sky frame scale: %s" % (scale,))
182 calibs = pool.mapToPrevious(self.
collectSky, dataIdList)
187 expRef.put(image,
"calexp_camera")
189 pool.mapToPrevious(self.
write, dataIdList)
192 """Load original image and restore the sky 194 This method runs on the slave nodes. 198 cache : `lsst.pipe.base.Struct` 205 exposure : `lsst.afw.image.Exposure` 208 cache.dataId = dataId
209 cache.exposure = cache.butler.get(
"calexp", dataId, immediate=
True).clone()
210 bgOld = cache.butler.get(
"calexpBackground", dataId, immediate=
True)
211 image = cache.exposure.getMaskedImage()
215 statsImage = bgData[0].getStatsImage()
218 image -= bgOld.getImage()
219 cache.bgList = afwMath.BackgroundList()
221 cache.bgList.append(bgData)
226 """Measure scale for sky frame 228 This method runs on the slave nodes. 232 cache : `lsst.pipe.base.Struct` 242 assert cache.dataId == dataId
243 cache.sky = self.sky.getSkyData(cache.butler, dataId)
244 scale = self.sky.measureScale(cache.exposure.getMaskedImage(), cache.sky)
248 """Subtract sky frame 250 This method runs on the slave nodes. 254 cache : `lsst.pipe.base.Struct` 263 exposure : `lsst.afw.image.Exposure` 266 assert cache.dataId == dataId
267 self.sky.
subtractSkyFrame(cache.exposure.getMaskedImage(), cache.sky, scale, cache.bgList)
271 """Fit background model for CCD 273 This method runs on the slave nodes. 277 cache : `lsst.pipe.base.Struct` 279 data : `lsst.pipe.base.Struct` 280 Data identifier, with `dataId` (data identifier) and `bgModel` 281 (background model) elements. 285 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackground` 288 assert cache.dataId == data.dataId
289 data.bgModel.addCcd(cache.exposure)
293 """Subtract background model 295 This method runs on the slave nodes. 299 cache : `lsst.pipe.base.Struct` 303 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround` 308 exposure : `lsst.afw.image.Exposure` 311 assert cache.dataId == dataId
312 exposure = cache.exposure
313 image = exposure.getMaskedImage()
314 detector = exposure.getDetector()
315 bbox = image.getBBox()
316 cache.bgModel = bgModel.toCcdBackground(detector, bbox)
317 image -= cache.bgModel.getImage()
318 cache.bgList.append(cache.bgModel[0])
322 """Generate an image of the background model for visualisation 324 Useful for debugging. 328 cache : `lsst.pipe.base.Struct` 332 bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround` 339 image : `lsst.afw.image.MaskedImage` 340 Binned background model image. 342 assert cache.dataId == dataId
343 exposure = cache.exposure
344 detector = exposure.getDetector()
345 bbox = exposure.getMaskedImage().getBBox()
346 image = bgModel.toCcdBackground(detector, bbox).getImage()
347 return (detector.getId(), afwMath.binImage(image, BINNING))
350 """Collect exposure for potential visualisation 352 This method runs on the slave nodes. 356 cache : `lsst.pipe.base.Struct` 363 image : `lsst.afw.image.MaskedImage` 366 return (cache.exposure.getDetector().getId(),
367 afwMath.binImage(cache.exposure.getMaskedImage(), BINNING))
370 """Collect original image for visualisation 372 This method runs on the slave nodes. 376 cache : `lsst.pipe.base.Struct` 385 image : `lsst.afw.image.MaskedImage` 388 exposure = cache.butler.get(
"calexp", dataId, immediate=
True)
389 return (exposure.getDetector().getId(),
390 afwMath.binImage(exposure.getMaskedImage(), BINNING))
393 """Collect original image for visualisation 395 This method runs on the slave nodes. 399 cache : `lsst.pipe.base.Struct` 408 image : `lsst.afw.image.MaskedImage` 411 return (cache.exposure.getDetector().getId(), afwMath.binImage(cache.sky.getImage(), BINNING))
414 """Write resultant background list 416 This method runs on the slave nodes. 420 cache : `lsst.pipe.base.Struct` 425 cache.butler.put(cache.bgList,
"skyCorr", dataId)
427 def _getMetadataName(self):
428 """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)