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