LSSTApplications  19.0.0-14-gb0260a2+72efe9b372,20.0.0+7927753e06,20.0.0+8829bf0056,20.0.0+995114c5d2,20.0.0+b6f4b2abd1,20.0.0+bddc4f4cbe,20.0.0-1-g253301a+8829bf0056,20.0.0-1-g2b7511a+0d71a2d77f,20.0.0-1-g5b95a8c+7461dd0434,20.0.0-12-g321c96ea+23efe4bbff,20.0.0-16-gfab17e72e+fdf35455f6,20.0.0-2-g0070d88+ba3ffc8f0b,20.0.0-2-g4dae9ad+ee58a624b3,20.0.0-2-g61b8584+5d3db074ba,20.0.0-2-gb780d76+d529cf1a41,20.0.0-2-ged6426c+226a441f5f,20.0.0-2-gf072044+8829bf0056,20.0.0-2-gf1f7952+ee58a624b3,20.0.0-20-geae50cf+e37fec0aee,20.0.0-25-g3dcad98+544a109665,20.0.0-25-g5eafb0f+ee58a624b3,20.0.0-27-g64178ef+f1f297b00a,20.0.0-3-g4cc78c6+e0676b0dc8,20.0.0-3-g8f21e14+4fd2c12c9a,20.0.0-3-gbd60e8c+187b78b4b8,20.0.0-3-gbecbe05+48431fa087,20.0.0-38-ge4adf513+a12e1f8e37,20.0.0-4-g97dc21a+544a109665,20.0.0-4-gb4befbc+087873070b,20.0.0-4-gf910f65+5d3db074ba,20.0.0-5-gdfe0fee+199202a608,20.0.0-5-gfbfe500+d529cf1a41,20.0.0-6-g64f541c+d529cf1a41,20.0.0-6-g9a5b7a1+a1cd37312e,20.0.0-68-ga3f3dda+5fca18c6a4,20.0.0-9-g4aef684+e18322736b,w.2020.45
LSSTDataManagementBasePackage
exposureAssembler.py
Go to the documentation of this file.
1 # This file is part of obs_base.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (http://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 <http://www.gnu.org/licenses/>.
21 
22 """Support for assembling and disassembling afw Exposures."""
23 
24 # Need to enable PSFs to be instantiated
25 import lsst.afw.detection # noqa: F401
26 from lsst.afw.image import makeExposure, makeMaskedImage, Filter, stripFilterKeywords
27 
28 from lsst.daf.butler import StorageClassDelegate
29 
30 
31 class ExposureAssembler(StorageClassDelegate):
32 
33  EXPOSURE_COMPONENTS = set(("image", "variance", "mask", "wcs", "psf"))
34  EXPOSURE_INFO_COMPONENTS = set(("apCorrMap", "coaddInputs", "photoCalib", "metadata",
35  "filter", "transmissionCurve", "visitInfo",
36  "detector", "validPolygon"))
37  EXPOSURE_READ_COMPONENTS = {"bbox", "dimensions", "xy0"}
38 
39  COMPONENT_MAP = {"bbox": "BBox", "xy0": "XY0"}
40  """Map component name to actual getter name."""
41 
42  def _groupRequestedComponents(self):
43  """Group requested components into top level and ExposureInfo.
44 
45  Returns
46  -------
47  expComps : `dict`
48  Components associated with the top level Exposure.
49  expInfoComps : `dict`
50  Components associated with the ExposureInfo
51 
52  Raises
53  ------
54  ValueError
55  There are components defined in the storage class that are not
56  expected by this assembler.
57  """
58  requested = set(self.storageClass.components.keys())
59 
60  # Check that we are requesting something that we support
61  unknown = requested - (self.EXPOSURE_COMPONENTS | self.EXPOSURE_INFO_COMPONENTS)
62  if unknown:
63  raise ValueError("Asking for unrecognized component: {}".format(unknown))
64 
65  expItems = requested & self.EXPOSURE_COMPONENTS
66  expInfoItems = requested & self.EXPOSURE_INFO_COMPONENTS
67  return expItems, expInfoItems
68 
69  def getComponent(self, composite, componentName):
70  """Get a component from an Exposure
71 
72  Parameters
73  ----------
74  composite : `~lsst.afw.image.Exposure`
75  `Exposure` to access component.
76  componentName : `str`
77  Name of component to retrieve.
78 
79  Returns
80  -------
81  component : `object`
82  The component. Can be None.
83 
84  Raises
85  ------
86  AttributeError
87  The component can not be found.
88  """
89  if componentName in self.EXPOSURE_COMPONENTS or componentName in self.EXPOSURE_READ_COMPONENTS:
90  # Use getter translation if relevant or the name itself
91  return super().getComponent(composite, self.COMPONENT_MAP.get(componentName, componentName))
92  elif componentName in self.EXPOSURE_INFO_COMPONENTS:
93  if hasattr(composite, "getInfo"):
94  # it is possible for this method to be called with
95  # an ExposureInfo composite so trap for that and only get
96  # the ExposureInfo if the method is supported
97  composite = composite.getInfo()
98  return super().getComponent(composite, componentName)
99  else:
100  raise AttributeError("Do not know how to retrieve component {} from {}".format(componentName,
101  type(composite)))
102 
103  def getValidComponents(self, composite):
104  """Extract all non-None components from a composite.
105 
106  Parameters
107  ----------
108  composite : `object`
109  Composite from which to extract components.
110 
111  Returns
112  -------
113  comps : `dict`
114  Non-None components extracted from the composite, indexed by the
115  component name as derived from the `self.storageClass`.
116  """
117  # For Exposure we call the generic version twice: once for top level
118  # components, and again for ExposureInfo.
119  expItems, expInfoItems = self._groupRequestedComponents()
120 
121  components = super().getValidComponents(composite)
122  infoComps = super().getValidComponents(composite.getInfo())
123  components.update(infoComps)
124  return components
125 
126  def disassemble(self, composite):
127  """Disassemble an afw Exposure.
128 
129  This implementation attempts to extract components from the parent
130  by looking for attributes of the same name or getter methods derived
131  from the component name.
132 
133  Parameters
134  ----------
135  composite : `~lsst.afw.image.Exposure`
136  `Exposure` composite object consisting of components to be
137  extracted.
138 
139  Returns
140  -------
141  components : `dict`
142  `dict` with keys matching the components defined in
143  `self.storageClass` and values being `DatasetComponent` instances
144  describing the component.
145 
146  Raises
147  ------
148  ValueError
149  A requested component can not be found in the parent using generic
150  lookups.
151  TypeError
152  The parent object does not match the supplied `self.storageClass`.
153  """
154  if not self.storageClass.validateInstance(composite):
155  raise TypeError("Unexpected type mismatch between parent and StorageClass"
156  " ({} != {})".format(type(composite), self.storageClass.pytype))
157 
158  # Only look for components that are defined by the StorageClass
159  components = {}
160  expItems, expInfoItems = self._groupRequestedComponents()
161 
162  fromExposure = super().disassemble(composite, subset=expItems)
163  components.update(fromExposure)
164 
165  fromExposureInfo = super().disassemble(composite,
166  subset=expInfoItems, override=composite.getInfo())
167  components.update(fromExposureInfo)
168 
169  # We must reproduce some of the metadata manipulation that occurs
170  # in ExposureInfo::FitsWriteData.
171 
172  # Force the FILTER header to be overwritten
173  if "filter" in components and "metadata" in components:
174  md = components["metadata"].component
175  md["FILTER"] = components["filter"].component.getName()
176 
177  return components
178 
179  def assemble(self, components):
180  """Construct an Exposure from components.
181 
182  Parameters
183  ----------
184  components : `dict`
185  All the components from which to construct the Exposure.
186  Some can be missing.
187 
188  Returns
189  -------
190  exposure : `~lsst.afw.image.Exposure`
191  Assembled exposure.
192 
193  Raises
194  ------
195  ValueError
196  Some supplied components are not recognized.
197  """
198  components = components.copy()
199  maskedImageComponents = {}
200  hasMaskedImage = False
201  for component in ("image", "variance", "mask"):
202  value = None
203  if component in components:
204  hasMaskedImage = True
205  value = components.pop(component)
206  maskedImageComponents[component] = value
207 
208  wcs = None
209  if "wcs" in components:
210  wcs = components.pop("wcs")
211 
212  pytype = self.storageClass.pytype
213  if hasMaskedImage:
214  maskedImage = makeMaskedImage(**maskedImageComponents)
215  exposure = makeExposure(maskedImage, wcs=wcs)
216 
217  if not isinstance(exposure, pytype):
218  raise RuntimeError("Unexpected type created in assembly;"
219  " was {} expected {}".format(type(exposure), pytype))
220 
221  else:
222  exposure = pytype()
223  if wcs is not None:
224  exposure.setWcs(wcs)
225 
226  # Set other components
227  exposure.setPsf(components.pop("psf", None))
228  exposure.setPhotoCalib(components.pop("photoCalib", None))
229 
230  info = exposure.getInfo()
231  if "visitInfo" in components:
232  info.setVisitInfo(components.pop("visitInfo"))
233  info.setApCorrMap(components.pop("apCorrMap", None))
234  info.setCoaddInputs(components.pop("coaddInputs", None))
235  info.setMetadata(components.pop("metadata", None))
236  info.setValidPolygon(components.pop("validPolygon", None))
237  info.setDetector(components.pop("detector", None))
238  info.setTransmissionCurve(components.pop("transmissionCurve", None))
239 
240  # Filter needs to be updated specially to match Exposure behavior
241  # from ExposureFitsReader::MetadataReader
242 
243  # Override the serialized filter knowing that we are using FILTER
244  md = info.getMetadata()
245  if "filter" in components and "FILTER" in md:
246  filter = Filter(md, True)
248  if filter.getName() != components["filter"].getName():
249  components["filter"] = filter
250 
251  info.setFilter(components.pop("filter", None))
252 
253  # If we have some components left over that is a problem
254  if components:
255  raise ValueError("The following components were not understood:"
256  " {}".format(list(components.keys())))
257 
258  return exposure
259 
260  def handleParameters(self, inMemoryDataset, parameters=None):
261  """Modify the in-memory dataset using the supplied parameters,
262  returning a possibly new object.
263 
264  Parameters
265  ----------
266  inMemoryDataset : `object`
267  Object to modify based on the parameters.
268  parameters : `dict`, optional
269  Parameters to apply. Values are specific to the parameter.
270  Supported parameters are defined in the associated
271  `StorageClass`. If no relevant parameters are specified the
272  inMemoryDataset will be return unchanged.
273 
274  Returns
275  -------
276  inMemoryDataset : `object`
277  Updated form of supplied in-memory dataset, after parameters
278  have been used.
279  """
280  # Understood by *this* subset command
281  understood = ("bbox", "origin")
282  use = self.storageClass.filterParameters(parameters, subset=understood)
283  if use:
284  inMemoryDataset = inMemoryDataset.subset(**use)
285 
286  return inMemoryDataset
287 
288  @classmethod
289  def selectResponsibleComponent(cls, readComponent: str, fromComponents) -> str:
290  imageComponents = ["mask", "image", "variance"]
291  forwarderMap = {
292  "bbox": imageComponents,
293  "dimensions": imageComponents,
294  "xy0": imageComponents,
295  }
296  forwarder = forwarderMap.get(readComponent)
297  if forwarder is not None:
298  for c in forwarder:
299  if c in fromComponents:
300  return c
301  raise ValueError(f"Can not calculate read component {readComponent} from {fromComponents}")
lsst::afw::image
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: imageAlgorithm.dox:1
lsst::afw::image::detail::stripFilterKeywords
int stripFilterKeywords(std::shared_ptr< lsst::daf::base::PropertySet > metadata)
Remove Filter-related keywords from the metadata.
Definition: Filter.cc:132
lsst::afw::image::Filter
Holds an integer identifier for an LSST filter.
Definition: Filter.h:141
lsst::afw::image::makeExposure
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
Definition: Exposure.h:442
lsst.obs.base.exposureAssembler.ExposureAssembler
Definition: exposureAssembler.py:31
lsst.obs.base.exposureAssembler.ExposureAssembler.EXPOSURE_READ_COMPONENTS
dictionary EXPOSURE_READ_COMPONENTS
Definition: exposureAssembler.py:37
lsst.obs.base.exposureAssembler.ExposureAssembler.EXPOSURE_INFO_COMPONENTS
EXPOSURE_INFO_COMPONENTS
Definition: exposureAssembler.py:34
lsst.pex.config.history.format
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
lsst.obs.base.exposureAssembler.ExposureAssembler._groupRequestedComponents
def _groupRequestedComponents(self)
Definition: exposureAssembler.py:42
lsst.obs.base.exposureAssembler.ExposureAssembler.getComponent
def getComponent(self, composite, componentName)
Definition: exposureAssembler.py:69
lsst.obs.base.exposureAssembler.ExposureAssembler.getValidComponents
def getValidComponents(self, composite)
Definition: exposureAssembler.py:103
lsst.obs.base.exposureAssembler.ExposureAssembler.selectResponsibleComponent
str selectResponsibleComponent(cls, str readComponent, fromComponents)
Definition: exposureAssembler.py:289
lsst.obs.base.exposureAssembler.ExposureAssembler.disassemble
def disassemble(self, composite)
Definition: exposureAssembler.py:126
lsst.obs.base.exposureAssembler.ExposureAssembler.handleParameters
def handleParameters(self, inMemoryDataset, parameters=None)
Definition: exposureAssembler.py:260
lsst::afw::detection
Definition: Footprint.h:50
lsst.obs.base.exposureAssembler.ExposureAssembler.EXPOSURE_COMPONENTS
EXPOSURE_COMPONENTS
Definition: exposureAssembler.py:33
list
daf::base::PropertyList * list
Definition: fits.cc:913
type
table::Key< int > type
Definition: Detector.cc:163
lsst::afw::image::makeMaskedImage
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
Definition: MaskedImage.h:1268
lsst.obs.base.exposureAssembler.ExposureAssembler.assemble
def assemble(self, components)
Definition: exposureAssembler.py:179
set
daf::base::PropertySet * set
Definition: fits.cc:912
lsst.obs.base.exposureAssembler.ExposureAssembler.COMPONENT_MAP
dictionary COMPONENT_MAP
Definition: exposureAssembler.py:39