lsst.pipe.drivers  17.0.1-9-gcb87309
skyCorrection.py
Go to the documentation of this file.
1 # This file is part of pipe_drivers.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 from __future__ import absolute_import, division, print_function
22 
23 import lsst.afw.math as afwMath
24 import lsst.afw.image as afwImage
25 import lsst.pipe.base as pipeBase
26 
27 from lsst.pex.config import Config, Field, ConfigurableField, ConfigField
28 from lsst.ctrl.pool.pool import Pool
29 from lsst.ctrl.pool.parallel import BatchPoolTask
30 from lsst.pipe.drivers.background import (SkyMeasurementTask, FocalPlaneBackground,
31  FocalPlaneBackgroundConfig, MaskObjectsTask)
32 import lsst.pipe.drivers.visualizeVisit as visualizeVisit
33 
34 __all__ = ["SkyCorrectionConfig", "SkyCorrectionTask"]
35 
36 DEBUG = False # Debugging outputs?
37 
38 
39 def makeCameraImage(camera, exposures, filename=None, binning=8):
40  """Make and write an image of an entire focal plane
41 
42  Parameters
43  ----------
44  camera : `lsst.afw.cameraGeom.Camera`
45  Camera description.
46  exposures : `list` of `tuple` of `int` and `lsst.afw.image.Exposure`
47  List of detector ID and CCD exposures (binned by `binning`).
48  filename : `str`, optional
49  Output filename.
50  binning : `int`
51  Binning size that has been applied to images.
52  """
53  image = visualizeVisit.makeCameraImage(camera, dict(exp for exp in exposures if exp is not None), binning)
54  if filename is not None:
55  image.writeFits(filename)
56  return image
57 
58 
59 class SkyCorrectionConfig(pipeBase.PipelineTaskConfig, Config):
60  """Configuration for SkyCorrectionTask"""
61 
62  rawLinker = pipeBase.InputDatasetField(
63  doc="Raw data to provide exp-visit linkage to connect calExp inputs to camera/sky calibs.",
64  name="raw",
65  scalar=False,
66  storageClass="ExposureU",
67  dimensions=["instrument", "exposure", "detector"],
68  )
69 
70  calExpArray = pipeBase.InputDatasetField(
71  doc="Input exposures to process",
72  name="calexp",
73  scalar=False,
74  storageClass="ExposureF",
75  dimensions=["instrument", "visit", "detector"],
76  )
77  calBkgArray = pipeBase.InputDatasetField(
78  doc="Input background files to use",
79  name="calexpBackground",
80  scalar=False,
81  storageClass="Background",
82  dimensions=["instrument", "visit", "detector"],
83  )
84 
85  camera = pipeBase.InputDatasetField(
86  doc="Input camera to use.",
87  name="camera",
88  scalar=True,
89  storageClass="TablePersistableCamera",
90  dimensions=["instrument", "calibration_label"],
91  )
92  skyCalibs = pipeBase.InputDatasetField(
93  doc="Input sky calibrations to use.",
94  name="sky",
95  scalar=False,
96  storageClass="ExposureF",
97  dimensions=["instrument", "physical_filter", "detector", "calibration_label"],
98  )
99 
100  calExpCamera = pipeBase.OutputDatasetField(
101  doc="Output camera image.",
102  name='calexp_camera',
103  scalar=True,
104  storageClass="ImageF",
105  dimensions=["instrument", "visit"],
106  )
107  skyCorr = pipeBase.OutputDatasetField(
108  doc="Output sky corrected images.",
109  name='skyCorr',
110  scalar=False,
111  storageClass="Background",
112  dimensions=["instrument", "visit", "detector"],
113  )
114 
115  bgModel = ConfigField(dtype=FocalPlaneBackgroundConfig, doc="Background model")
116  bgModel2 = ConfigField(dtype=FocalPlaneBackgroundConfig, doc="2nd Background model")
117  sky = ConfigurableField(target=SkyMeasurementTask, doc="Sky measurement")
118  maskObjects = ConfigurableField(target=MaskObjectsTask, doc="Mask Objects")
119  doMaskObjects = Field(dtype=bool, default=True, doc="Mask objects to find good sky?")
120  doBgModel = Field(dtype=bool, default=True, doc="Do background model subtraction?")
121  doBgModel2 = Field(dtype=bool, default=True, doc="Do cleanup background model subtraction?")
122  doSky = Field(dtype=bool, default=True, doc="Do sky frame subtraction?")
123  binning = Field(dtype=int, default=8, doc="Binning factor for constructing focal-plane images")
124 
125  def setDefaults(self):
126  Config.setDefaults(self)
127  self.bgModel2.doSmooth = True
128  self.bgModel2.minFrac = 0.5
129  self.bgModel2.xSize = 256
130  self.bgModel2.ySize = 256
131  self.bgModel2.smoothScale = 1.0
132 
133  self.quantum.dimensions = ("instrument", "visit")
134 
135 
136 class SkyCorrectionTask(pipeBase.PipelineTask, BatchPoolTask):
137  """Correct sky over entire focal plane"""
138  ConfigClass = SkyCorrectionConfig
139  _DefaultName = "skyCorr"
140 
141  def adaptArgsAndRun(self, inputData, inputDataIds, outputDataIds, butler):
142  inputData.pop("rawLinker", None)
143  return super().adaptArgsAndRun(inputData, inputDataIds, outputDataIds, butler)
144 
145  @classmethod
146  def getPrerequisiteDatasetTypes(cls, config):
147  names = set()
148  names.add('skyCalibs')
149  names.add('camera')
150  return names
151 
152  @classmethod
153  def getPerDatasetTypeDimensions(cls, config):
154  return frozenset(["calibration_label"])
155 
156  def __init__(self, *args, **kwargs):
157  super().__init__(**kwargs)
158 
159  self.makeSubtask("sky")
160  self.makeSubtask("maskObjects")
161 
162  @classmethod
163  def _makeArgumentParser(cls, *args, **kwargs):
164  kwargs.pop("doBatch", False)
165  parser = pipeBase.ArgumentParser(name="skyCorr", *args, **kwargs)
166  parser.add_id_argument("--id", datasetType="calexp", level="visit",
167  help="data ID, e.g. --id visit=12345")
168  return parser
169 
170  @classmethod
171  def batchWallTime(cls, time, parsedCmd, numCores):
172  """Return walltime request for batch job
173 
174  Subclasses should override if the walltime should be calculated
175  differently (e.g., addition of some serial time).
176 
177  Parameters
178  ----------
179  time : `float`
180  Requested time per iteration.
181  parsedCmd : `argparse.Namespace`
182  Results of argument parsing.
183  numCores : `int`
184  Number of cores.
185  """
186  numTargets = len(cls.RunnerClass.getTargetList(parsedCmd))
187  return time*numTargets
188 
189  def runDataRef(self, expRef):
190  """Perform sky correction on an exposure
191 
192  We restore the original sky, and remove it again using multiple
193  algorithms. We optionally apply:
194 
195  1. A large-scale background model.
196  This step removes very-large-scale sky such as moonlight.
197  2. A sky frame.
198  3. A medium-scale background model.
199  This step removes residual sky (This is smooth on the focal plane).
200 
201  Only the master node executes this method. The data is held on
202  the slave nodes, which do all the hard work.
203 
204  Parameters
205  ----------
206  expRef : `lsst.daf.persistence.ButlerDataRef`
207  Data reference for exposure.
208 
209  See Also
210  --------
211  ~lsst.pipe.drivers.SkyCorrectionTask.run
212  """
213  if DEBUG:
214  extension = "-%(visit)d.fits" % expRef.dataId
215 
216  with self.logOperation("processing %s" % (expRef.dataId,)):
217  pool = Pool()
218  pool.cacheClear()
219  pool.storeSet(butler=expRef.getButler())
220  camera = expRef.get("camera")
221 
222  dataIdList = [ccdRef.dataId for ccdRef in expRef.subItems("ccd") if
223  ccdRef.datasetExists("calexp")]
224 
225  exposures = pool.map(self.loadImage, dataIdList)
226  if DEBUG:
227  makeCameraImage(camera, exposures, "restored" + extension)
228  exposures = pool.mapToPrevious(self.collectOriginal, dataIdList)
229  makeCameraImage(camera, exposures, "original" + extension)
230  exposures = pool.mapToPrevious(self.collectMask, dataIdList)
231  makeCameraImage(camera, exposures, "mask" + extension)
232 
233  if self.config.doBgModel:
234  exposures = self.focalPlaneBackground(camera, pool, dataIdList, self.config.bgModel)
235 
236  if self.config.doSky:
237  measScales = pool.mapToPrevious(self.measureSkyFrame, dataIdList)
238  scale = self.sky.solveScales(measScales)
239  self.log.info("Sky frame scale: %s" % (scale,))
240 
241  exposures = pool.mapToPrevious(self.subtractSkyFrame, dataIdList, scale)
242  if DEBUG:
243  makeCameraImage(camera, exposures, "skysub" + extension)
244  calibs = pool.mapToPrevious(self.collectSky, dataIdList)
245  makeCameraImage(camera, calibs, "sky" + extension)
246 
247  if self.config.doBgModel2:
248  exposures = self.focalPlaneBackground(camera, pool, dataIdList, self.config.bgModel2)
249 
250  # Persist camera-level image of calexp
251  image = makeCameraImage(camera, exposures)
252  expRef.put(image, "calexp_camera")
253 
254  pool.mapToPrevious(self.write, dataIdList)
255 
256  def focalPlaneBackground(self, camera, pool, dataIdList, config):
257  """Perform full focal-plane background subtraction
258 
259  This method runs on the master node.
260 
261  Parameters
262  ----------
263  camera : `lsst.afw.cameraGeom.Camera`
264  Camera description.
265  pool : `lsst.ctrl.pool.Pool`
266  Process pool.
267  dataIdList : iterable of `dict`
268  List of data identifiers for the CCDs.
269  config : `lsst.pipe.drivers.background.FocalPlaneBackgroundConfig`
270  Configuration to use for background subtraction.
271 
272  Returns
273  -------
274  exposures : `list` of `lsst.afw.image.Image`
275  List of binned images, for creating focal plane image.
276  """
277  bgModel = FocalPlaneBackground.fromCamera(config, camera)
278  data = [pipeBase.Struct(dataId=dataId, bgModel=bgModel.clone()) for dataId in dataIdList]
279  bgModelList = pool.mapToPrevious(self.accumulateModel, data)
280  for ii, bg in enumerate(bgModelList):
281  self.log.info("Background %d: %d pixels", ii, bg._numbers.array.sum())
282  bgModel.merge(bg)
283  return pool.mapToPrevious(self.subtractModel, dataIdList, bgModel)
284 
285  def focalPlaneBackgroundRun(self, camera, cacheExposures, idList, config):
286  """Perform full focal-plane background subtraction
287 
288  This method runs on the master node.
289 
290  Parameters
291  ----------
292  camera : `lsst.afw.cameraGeom.Camera`
293  Camera description.
294  cacheExposures : `list` of `lsst.afw.image.Exposures`
295  List of loaded and processed input calExp.
296  idList : `list` of `int`
297  List of detector ids to iterate over.
298  config : `lsst.pipe.drivers.background.FocalPlaneBackgroundConfig`
299  Configuration to use for background subtraction.
300 
301  Returns
302  -------
303  exposures : `list` of `lsst.afw.image.Image`
304  List of binned images, for creating focal plane image.
305  newCacheBgList : `list` of `lsst.afwMath.backgroundList`
306  Background lists generated.
307  cacheBgModel : `FocalPlaneBackground`
308  Full focal plane background model.
309  """
310  bgModel = FocalPlaneBackground.fromCamera(config, camera)
311  data = [pipeBase.Struct(id=id, bgModel=bgModel.clone()) for id in idList]
312 
313  bgModelList = []
314  for nodeData, cacheExp in zip(data, cacheExposures):
315  nodeData.bgModel.addCcd(cacheExp)
316  bgModelList.append(nodeData.bgModel)
317 
318  for ii, bg in enumerate(bgModelList):
319  self.log.info("Background %d: %d pixels", ii, bg._numbers.getArray().sum())
320  bgModel.merge(bg)
321 
322  exposures = []
323  newCacheBgList = []
324  cacheBgModel = []
325  for cacheExp in cacheExposures:
326  nodeExp, nodeBgModel, nodeBgList = self.subtractModelRun(cacheExp, bgModel)
327  exposures.append(afwMath.binImage(nodeExp.getMaskedImage(), self.config.binning))
328  cacheBgModel.append(nodeBgModel)
329  newCacheBgList.append(nodeBgList)
330 
331  return exposures, newCacheBgList, cacheBgModel
332 
333  def run(self, calExpArray, calBkgArray, skyCalibs, camera):
334  """Duplicate runDataRef method without ctrl_pool for Gen3.
335 
336  Parameters
337  ----------
338  calExpArray : `list` of `lsst.afw.image.Exposure`
339  Array of detector input calExp images for the exposure to
340  process.
341  calBkgArray : `list` of `lsst.afw.math.BackgroundList`
342  Array of detector input background lists matching the
343  calExps to process.
344  skyCalibs : `list` of `lsst.afw.image.Exposure`
345  Array of SKY calibrations for the input detectors to be
346  processed.
347  camera : `lsst.afw.cameraGeom.Camera`
348  Camera matching the input data to process.
349 
350  Returns
351  -------
352  results : `pipeBase.Struct` containing
353  calExpCamera : `lsst.afw.image.Exposure`
354  Full camera image of the sky-corrected data.
355  skyCorr : `list` of `lsst.afw.math.BackgroundList`
356  Detector-level sky-corrected background lists.
357 
358  See Also
359  --------
360  ~lsst.pipe.drivers.SkyCorrectionTask.runDataRef()
361  """
362  # To allow SkyCorrectionTask to run in the Gen3 butler
363  # environment, a new run() method was added that performs the
364  # same operations in a serial environment (pipetask processing
365  # does not support MPI processing as of 2019-05-03). Methods
366  # used in runDataRef() are used as appropriate in run(), but
367  # some have been rewritten in serial form. Please ensure that
368  # any updates to runDataRef() or the methods it calls with
369  # pool.mapToPrevious() are duplicated in run() and its
370  # methods.
371  #
372  # Variable names here should match those in runDataRef() as
373  # closely as possible. Variables matching data stored in the
374  # pool cache have a prefix indicating this. Variables that
375  # would be local to an MPI processing client have a prefix
376  # "node".
377  idList = [exp.getDetector().getId() for exp in calExpArray]
378 
379  # Construct arrays that match the cache in self.runDataRef() after
380  # self.loadImage() is map/reduced.
381  cacheExposures = []
382  cacheBgList = []
383  exposures = []
384  for calExp, calBgModel in zip(calExpArray, calBkgArray):
385  nodeExp, nodeBgList = self.loadImageRun(calExp, calBgModel)
386  cacheExposures.append(nodeExp)
387  cacheBgList.append(nodeBgList)
388  exposures.append(afwMath.binImage(nodeExp.getMaskedImage(), self.config.binning))
389 
390  if self.config.doBgModel:
391  # Generate focal plane background, updating backgrounds in the "cache".
392  exposures, newCacheBgList, cacheBgModel = self.focalPlaneBackgroundRun(
393  camera, cacheExposures, idList, self.config.bgModel
394  )
395  for cacheBg, newBg in zip(cacheBgList, newCacheBgList):
396  cacheBg.append(newBg)
397 
398  if self.config.doSky:
399  # Measure the sky frame scale on all inputs. Results in
400  # values equal to self.measureSkyFrame() and
401  # self.sky.solveScales() in runDataRef().
402  cacheSky = []
403  measScales = []
404  for cacheExp, skyCalib in zip(cacheExposures, skyCalibs):
405  skyExp = self.sky.exposureToBackground(skyCalib)
406  cacheSky.append(skyExp)
407  scale = self.sky.measureScale(cacheExp.getMaskedImage(), skyExp)
408  measScales.append(scale)
409 
410  scale = self.sky.solveScales(measScales)
411  self.log.info("Sky frame scale: %s" % (scale, ))
412 
413  # Subtract sky frame, as in self.subtractSkyFrame(), with
414  # appropriate scale from the "cache".
415  exposures = []
416  newBgList = []
417  for cacheExp, nodeSky, nodeBgList in zip(cacheExposures, cacheSky, cacheBgList):
418  self.sky.subtractSkyFrame(cacheExp.getMaskedImage(), nodeSky, scale, nodeBgList)
419  exposures.append(afwMath.binImage(cacheExp.getMaskedImage(), self.config.binning))
420 
421  if self.config.doBgModel2:
422  # As above, generate a focal plane background model and
423  # update the cache models.
424  exposures, newBgList, cacheBgModel = self.focalPlaneBackgroundRun(
425  camera, cacheExposures, idList, self.config.bgModel2
426  )
427  for cacheBg, newBg in zip(cacheBgList, newBgList):
428  cacheBg.append(newBg)
429 
430  # Generate camera-level image of calexp and return it along
431  # with the list of sky corrected background models.
432  image = makeCameraImage(camera, zip(idList, exposures))
433 
434  return pipeBase.Struct(
435  calExpCamera=image,
436  skyCorr=cacheBgList,
437  )
438 
439  def loadImage(self, cache, dataId):
440  """Load original image and restore the sky
441 
442  This method runs on the slave nodes.
443 
444  Parameters
445  ----------
446  cache : `lsst.pipe.base.Struct`
447  Process pool cache.
448  dataId : `dict`
449  Data identifier.
450 
451  Returns
452  -------
453  exposure : `lsst.afw.image.Exposure`
454  Resultant exposure.
455  """
456  cache.dataId = dataId
457  cache.exposure = cache.butler.get("calexp", dataId, immediate=True).clone()
458  bgOld = cache.butler.get("calexpBackground", dataId, immediate=True)
459  image = cache.exposure.getMaskedImage()
460 
461  # We're removing the old background, so change the sense of all its components
462  for bgData in bgOld:
463  statsImage = bgData[0].getStatsImage()
464  statsImage *= -1
465 
466  image -= bgOld.getImage()
467  cache.bgList = afwMath.BackgroundList()
468  for bgData in bgOld:
469  cache.bgList.append(bgData)
470 
471  if self.config.doMaskObjects:
472  self.maskObjects.findObjects(cache.exposure)
473 
474  return self.collect(cache)
475 
476  def loadImageRun(self, calExp, calExpBkg):
477  """Serial implementation of self.loadImage() for Gen3.
478 
479  Load and restore background to calExp and calExpBkg.
480 
481  Parameters
482  ----------
483  calExp : `lsst.afw.image.Exposure`
484  Detector level calExp image to process.
485  calExpBkg : `lsst.afw.math.BackgroundList`
486  Detector level background list associated with the calExp.
487 
488  Returns
489  -------
490  calExp : `lsst.afw.image.Exposure`
491  Background restored calExp.
492  bgList : `lsst.afw.math.BackgroundList`
493  New background list containing the restoration background.
494  """
495  image = calExp.getMaskedImage()
496 
497  for bgOld in calExpBkg:
498  statsImage = bgOld[0].getStatsImage()
499  statsImage *= -1
500 
501  image -= calExpBkg.getImage()
502  bgList = afwMath.BackgroundList()
503  for bgData in calExpBkg:
504  bgList.append(bgData)
505 
506  if self.config.doMaskObjects:
507  self.maskObjects.findObjects(calExp)
508 
509  return (calExp, bgList)
510 
511  def measureSkyFrame(self, cache, dataId):
512  """Measure scale for sky frame
513 
514  This method runs on the slave nodes.
515 
516  Parameters
517  ----------
518  cache : `lsst.pipe.base.Struct`
519  Process pool cache.
520  dataId : `dict`
521  Data identifier.
522 
523  Returns
524  -------
525  scale : `float`
526  Scale for sky frame.
527  """
528  assert cache.dataId == dataId
529  cache.sky = self.sky.getSkyData(cache.butler, dataId)
530  scale = self.sky.measureScale(cache.exposure.getMaskedImage(), cache.sky)
531  return scale
532 
533  def subtractSkyFrame(self, cache, dataId, scale):
534  """Subtract sky frame
535 
536  This method runs on the slave nodes.
537 
538  Parameters
539  ----------
540  cache : `lsst.pipe.base.Struct`
541  Process pool cache.
542  dataId : `dict`
543  Data identifier.
544  scale : `float`
545  Scale for sky frame.
546 
547  Returns
548  -------
549  exposure : `lsst.afw.image.Exposure`
550  Resultant exposure.
551  """
552  assert cache.dataId == dataId
553  self.sky.subtractSkyFrame(cache.exposure.getMaskedImage(), cache.sky, scale, cache.bgList)
554  return self.collect(cache)
555 
556  def accumulateModel(self, cache, data):
557  """Fit background model for CCD
558 
559  This method runs on the slave nodes.
560 
561  Parameters
562  ----------
563  cache : `lsst.pipe.base.Struct`
564  Process pool cache.
565  data : `lsst.pipe.base.Struct`
566  Data identifier, with `dataId` (data identifier) and `bgModel`
567  (background model) elements.
568 
569  Returns
570  -------
571  bgModel : `lsst.pipe.drivers.background.FocalPlaneBackground`
572  Background model.
573  """
574  assert cache.dataId == data.dataId
575  data.bgModel.addCcd(cache.exposure)
576  return data.bgModel
577 
578  def subtractModel(self, cache, dataId, bgModel):
579  """Subtract background model
580 
581  This method runs on the slave nodes.
582 
583  Parameters
584  ----------
585  cache : `lsst.pipe.base.Struct`
586  Process pool cache.
587  dataId : `dict`
588  Data identifier.
589  bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround`
590  Background model.
591 
592  Returns
593  -------
594  exposure : `lsst.afw.image.Exposure`
595  Resultant exposure.
596  """
597  assert cache.dataId == dataId
598  exposure = cache.exposure
599  image = exposure.getMaskedImage()
600  detector = exposure.getDetector()
601  bbox = image.getBBox()
602  cache.bgModel = bgModel.toCcdBackground(detector, bbox)
603  image -= cache.bgModel.getImage()
604  cache.bgList.append(cache.bgModel[0])
605  return self.collect(cache)
606 
607  def subtractModelRun(self, exposure, bgModel):
608  """Serial implementation of self.subtractModel() for Gen3.
609 
610  Load and restore background to calExp and calExpBkg.
611 
612  Parameters
613  ----------
614  exposure : `lsst.afw.image.Exposure`
615  Exposure to subtract the background model from.
616  bgModel : `lsst.pipe.drivers.background.FocalPlaneBackground`
617  Full camera level background model.
618 
619  Returns
620  -------
621  exposure : `lsst.afw.image.Exposure`
622  Background subtracted input exposure.
623  bgModelCcd : `lsst.afw.math.BackgroundList`
624  Detector level realization of the full background model.
625  bgModelMaskedImage : `lsst.afw.image.MaskedImage`
626  Background model from the bgModelCcd realization.
627  """
628  image = exposure.getMaskedImage()
629  detector = exposure.getDetector()
630  bbox = image.getBBox()
631  bgModelCcd = bgModel.toCcdBackground(detector, bbox)
632  image -= bgModelCcd.getImage()
633 
634  return (exposure, bgModelCcd, bgModelCcd[0])
635 
636  def realiseModel(self, cache, dataId, bgModel):
637  """Generate an image of the background model for visualisation
638 
639  Useful for debugging.
640 
641  Parameters
642  ----------
643  cache : `lsst.pipe.base.Struct`
644  Process pool cache.
645  dataId : `dict`
646  Data identifier.
647  bgModel : `lsst.pipe.drivers.background.FocalPlaneBackround`
648  Background model.
649 
650  Returns
651  -------
652  detId : `int`
653  Detector identifier.
654  image : `lsst.afw.image.MaskedImage`
655  Binned background model image.
656  """
657  assert cache.dataId == dataId
658  exposure = cache.exposure
659  detector = exposure.getDetector()
660  bbox = exposure.getMaskedImage().getBBox()
661  image = bgModel.toCcdBackground(detector, bbox).getImage()
662  return self.collectBinnedImage(exposure, image)
663 
664  def collectBinnedImage(self, exposure, image):
665  """Return the binned image required for visualization
666 
667  This method just helps to cut down on boilerplate.
668 
669  Parameters
670  ----------
671  image : `lsst.afw.image.MaskedImage`
672  Image to go into visualisation.
673 
674  Returns
675  -------
676  detId : `int`
677  Detector identifier.
678  image : `lsst.afw.image.MaskedImage`
679  Binned image.
680  """
681  return (exposure.getDetector().getId(), afwMath.binImage(image, self.config.binning))
682 
683  def collect(self, cache):
684  """Collect exposure for potential visualisation
685 
686  This method runs on the slave nodes.
687 
688  Parameters
689  ----------
690  cache : `lsst.pipe.base.Struct`
691  Process pool cache.
692 
693  Returns
694  -------
695  detId : `int`
696  Detector identifier.
697  image : `lsst.afw.image.MaskedImage`
698  Binned image.
699  """
700  return self.collectBinnedImage(cache.exposure, cache.exposure.maskedImage)
701 
702  def collectOriginal(self, cache, dataId):
703  """Collect original image for visualisation
704 
705  This method runs on the slave nodes.
706 
707  Parameters
708  ----------
709  cache : `lsst.pipe.base.Struct`
710  Process pool cache.
711  dataId : `dict`
712  Data identifier.
713 
714  Returns
715  -------
716  detId : `int`
717  Detector identifier.
718  image : `lsst.afw.image.MaskedImage`
719  Binned image.
720  """
721  exposure = cache.butler.get("calexp", dataId, immediate=True)
722  return self.collectBinnedImage(exposure, exposure.maskedImage)
723 
724  def collectSky(self, cache, dataId):
725  """Collect original image for visualisation
726 
727  This method runs on the slave nodes.
728 
729  Parameters
730  ----------
731  cache : `lsst.pipe.base.Struct`
732  Process pool cache.
733  dataId : `dict`
734  Data identifier.
735 
736  Returns
737  -------
738  detId : `int`
739  Detector identifier.
740  image : `lsst.afw.image.MaskedImage`
741  Binned image.
742  """
743  return self.collectBinnedImage(cache.exposure, cache.sky.getImage())
744 
745  def collectMask(self, cache, dataId):
746  """Collect mask for visualisation
747 
748  This method runs on the slave nodes.
749 
750  Parameters
751  ----------
752  cache : `lsst.pipe.base.Struct`
753  Process pool cache.
754  dataId : `dict`
755  Data identifier.
756 
757  Returns
758  -------
759  detId : `int`
760  Detector identifier.
761  image : `lsst.afw.image.Image`
762  Binned image.
763  """
764  # Convert Mask to floating-point image, because that's what's required for focal plane construction
765  image = afwImage.ImageF(cache.exposure.maskedImage.getBBox())
766  image.array[:] = cache.exposure.maskedImage.mask.array
767  return self.collectBinnedImage(cache.exposure, image)
768 
769  def write(self, cache, dataId):
770  """Write resultant background list
771 
772  This method runs on the slave nodes.
773 
774  Parameters
775  ----------
776  cache : `lsst.pipe.base.Struct`
777  Process pool cache.
778  dataId : `dict`
779  Data identifier.
780  """
781  cache.butler.put(cache.bgList, "skyCorr", dataId)
782 
783  def _getMetadataName(self):
784  """There's no metadata to write out"""
785  return None
def subtractModel(self, cache, dataId, bgModel)
def batchWallTime(cls, time, parsedCmd, numCores)
def makeCameraImage(camera, exposures, filename=None, binning=8)
def focalPlaneBackground(self, camera, pool, dataIdList, config)
def run(self, calExpArray, calBkgArray, skyCalibs, camera)
def realiseModel(self, cache, dataId, bgModel)
def focalPlaneBackgroundRun(self, camera, cacheExposures, idList, config)
def subtractSkyFrame(self, cache, dataId, scale)
def logOperation(self, operation, catch=False, trace=True)
def adaptArgsAndRun(self, inputData, inputDataIds, outputDataIds, butler)