LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
LSST Data Management Base Package
Public Member Functions | Static Public Attributes | List of all members
lsst.ip.diffim.imageMapReduce.ImageReducer Class Reference
Inheritance diagram for lsst.ip.diffim.imageMapReduce.ImageReducer:

Public Member Functions

def run (self, mapperResults, exposure, **kwargs)
 

Static Public Attributes

 ConfigClass = ImageReducerConfig
 

Detailed Description

Base class for any 'reduce' task that is to be
used as `ImageMapReduceConfig.reducer`.

Basic reduce operations are provided by the `run` method
of this class, to be selected by its config.

Definition at line 180 of file imageMapReduce.py.

Member Function Documentation

◆ run()

def lsst.ip.diffim.imageMapReduce.ImageReducer.run (   self,
  mapperResults,
  exposure,
**  kwargs 
)
Reduce a list of items produced by `ImageMapper`.

Either stitch the passed `mapperResults` list
together into a new Exposure (default) or pass it through
(if `self.config.reduceOperation` is 'none').

If `self.config.reduceOperation` is not 'none', then expect
that the `pipeBase.Struct`s in the `mapperResults` list
contain sub-exposures named 'subExposure', to be stitched back
into a single Exposure with the same dimensions, PSF, and mask
as the input `exposure`. Otherwise, the `mapperResults` list
is simply returned directly.

Parameters
----------
mapperResults : `list`
    list of `lsst.pipe.base.Struct` returned by `ImageMapper.run`.
exposure : `lsst.afw.image.Exposure`
    the original exposure which is cloned to use as the
    basis for the resulting exposure (if
    ``self.config.mapper.reduceOperation`` is not 'None')
kwargs :
    additional keyword arguments propagated from
    `ImageMapReduceTask.run`.

Returns
-------
A `lsst.pipe.base.Struct` containing either an `lsst.afw.image.Exposure`
(named 'exposure') or a list (named 'result'),
depending on `config.reduceOperation`.

Notes
-----
1. This currently correctly handles overlapping sub-exposures.
   For overlapping sub-exposures, use `config.reduceOperation='average'`.
2. This correctly handles varying PSFs, constructing the resulting
   exposure's PSF via CoaddPsf (DM-9629).

Known issues

1. To be done: correct handling of masks (nearly there)
2. This logic currently makes *two* copies of the original exposure
   (one here and one in `mapper.run()`). Possibly of concern
   for large images on memory-constrained systems.

Definition at line 190 of file imageMapReduce.py.

190  def run(self, mapperResults, exposure, **kwargs):
191  """Reduce a list of items produced by `ImageMapper`.
192 
193  Either stitch the passed `mapperResults` list
194  together into a new Exposure (default) or pass it through
195  (if `self.config.reduceOperation` is 'none').
196 
197  If `self.config.reduceOperation` is not 'none', then expect
198  that the `pipeBase.Struct`s in the `mapperResults` list
199  contain sub-exposures named 'subExposure', to be stitched back
200  into a single Exposure with the same dimensions, PSF, and mask
201  as the input `exposure`. Otherwise, the `mapperResults` list
202  is simply returned directly.
203 
204  Parameters
205  ----------
206  mapperResults : `list`
207  list of `lsst.pipe.base.Struct` returned by `ImageMapper.run`.
208  exposure : `lsst.afw.image.Exposure`
209  the original exposure which is cloned to use as the
210  basis for the resulting exposure (if
211  ``self.config.mapper.reduceOperation`` is not 'None')
212  kwargs :
213  additional keyword arguments propagated from
214  `ImageMapReduceTask.run`.
215 
216  Returns
217  -------
218  A `lsst.pipe.base.Struct` containing either an `lsst.afw.image.Exposure`
219  (named 'exposure') or a list (named 'result'),
220  depending on `config.reduceOperation`.
221 
222  Notes
223  -----
224  1. This currently correctly handles overlapping sub-exposures.
225  For overlapping sub-exposures, use `config.reduceOperation='average'`.
226  2. This correctly handles varying PSFs, constructing the resulting
227  exposure's PSF via CoaddPsf (DM-9629).
228 
229  Known issues
230 
231  1. To be done: correct handling of masks (nearly there)
232  2. This logic currently makes *two* copies of the original exposure
233  (one here and one in `mapper.run()`). Possibly of concern
234  for large images on memory-constrained systems.
235  """
236  # No-op; simply pass mapperResults directly to ImageMapReduceTask.run
237  if self.config.reduceOperation == 'none':
238  return pipeBase.Struct(result=mapperResults)
239 
240  if self.config.reduceOperation == 'coaddPsf':
241  # Each element of `mapperResults` should contain 'psf' and 'bbox'
242  coaddPsf = self._constructPsf(mapperResults, exposure)
243  return pipeBase.Struct(result=coaddPsf)
244 
245  newExp = exposure.clone()
246  newMI = newExp.getMaskedImage()
247 
248  reduceOp = self.config.reduceOperation
249  if reduceOp == 'copy':
250  weights = None
251  newMI.getImage()[:, :] = np.nan
252  newMI.getVariance()[:, :] = np.nan
253  else:
254  newMI.getImage()[:, :] = 0.
255  newMI.getVariance()[:, :] = 0.
256  if reduceOp == 'average': # make an array to keep track of weights
257  weights = afwImage.ImageI(newMI.getBBox())
258 
259  for item in mapperResults:
260  item = item.subExposure # Expected named value in the pipeBase.Struct
261  if not (isinstance(item, afwImage.ExposureF) or isinstance(item, afwImage.ExposureI)
262  or isinstance(item, afwImage.ExposureU) or isinstance(item, afwImage.ExposureD)):
263  raise TypeError("""Expecting an Exposure type, got %s.
264  Consider using `reduceOperation="none".""" % str(type(item)))
265  subExp = newExp.Factory(newExp, item.getBBox())
266  subMI = subExp.getMaskedImage()
267  patchMI = item.getMaskedImage()
268  isValid = ~np.isnan(patchMI.getImage().getArray() * patchMI.getVariance().getArray())
269 
270  if reduceOp == 'copy':
271  subMI.getImage().getArray()[isValid] = patchMI.getImage().getArray()[isValid]
272  subMI.getVariance().getArray()[isValid] = patchMI.getVariance().getArray()[isValid]
273  subMI.getMask().getArray()[:, :] |= patchMI.getMask().getArray()
274 
275  if reduceOp == 'sum' or reduceOp == 'average': # much of these two options is the same
276  subMI.getImage().getArray()[isValid] += patchMI.getImage().getArray()[isValid]
277  subMI.getVariance().getArray()[isValid] += patchMI.getVariance().getArray()[isValid]
278  subMI.getMask().getArray()[:, :] |= patchMI.getMask().getArray()
279  if reduceOp == 'average':
280  # wtsView is a view into the `weights` Image
281  wtsView = afwImage.ImageI(weights, item.getBBox())
282  wtsView.getArray()[isValid] += 1
283 
284  # New mask plane - for debugging map-reduced images
285  mask = newMI.getMask()
286  for m in self.config.badMaskPlanes:
287  mask.addMaskPlane(m)
288  bad = mask.getPlaneBitMask(self.config.badMaskPlanes)
289 
290  isNan = np.where(np.isnan(newMI.getImage().getArray() * newMI.getVariance().getArray()))
291  if len(isNan[0]) > 0:
292  # set mask to INVALID for pixels where produced exposure is NaN
293  mask.getArray()[isNan[0], isNan[1]] |= bad
294 
295  if reduceOp == 'average':
296  wts = weights.getArray().astype(np.float)
297  self.log.info('AVERAGE: Maximum overlap: %f', np.nanmax(wts))
298  self.log.info('AVERAGE: Average overlap: %f', np.nanmean(wts))
299  self.log.info('AVERAGE: Minimum overlap: %f', np.nanmin(wts))
300  wtsZero = np.equal(wts, 0.)
301  wtsZeroInds = np.where(wtsZero)
302  wtsZeroSum = len(wtsZeroInds[0])
303  self.log.info('AVERAGE: Number of zero pixels: %f (%f%%)', wtsZeroSum,
304  wtsZeroSum * 100. / wtsZero.size)
305  notWtsZero = ~wtsZero
306  tmp = newMI.getImage().getArray()
307  np.divide(tmp, wts, out=tmp, where=notWtsZero)
308  tmp = newMI.getVariance().getArray()
309  np.divide(tmp, wts, out=tmp, where=notWtsZero)
310  if len(wtsZeroInds[0]) > 0:
311  newMI.getImage().getArray()[wtsZeroInds] = np.nan
312  newMI.getVariance().getArray()[wtsZeroInds] = np.nan
313  # set mask to something for pixels where wts == 0.
314  # happens sometimes if operation failed on a certain subexposure
315  mask.getArray()[wtsZeroInds] |= bad
316 
317  # Not sure how to construct a PSF when reduceOp=='copy'...
318  if reduceOp == 'sum' or reduceOp == 'average':
319  psf = self._constructPsf(mapperResults, exposure)
320  newExp.setPsf(psf)
321 
322  return pipeBase.Struct(exposure=newExp)
323 
table::Key< int > type
Definition: Detector.cc:163
def run(self, coaddExposures, bbox, wcs)
Definition: getTemplate.py:603

Member Data Documentation

◆ ConfigClass

lsst.ip.diffim.imageMapReduce.ImageReducer.ConfigClass = ImageReducerConfig
static

Definition at line 187 of file imageMapReduce.py.


The documentation for this class was generated from the following file: