LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | List of all members
lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask Class Reference
Inheritance diagram for lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask:
lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelMapper

Public Member Functions

def __init__ (self, *args, **kwargs)
 
def computeVarianceMean (self, exposure)
 
def run (self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel, preConvKernel=None, xcen=None, ycen=None, svar=None, tvar=None, templateMatched=True, preConvMode=False, **kwargs)
 
def computeCommonShape (self, *shapes)
 
def computeDiffimCorrection (self, kappa, svar, tvar)
 
def computeScoreCorrection (self, kappa, svar, tvar, preConvArr)
 
def calculateVariancePlane (self, vplane1, vplane2, varMean1, varMean2, c1ft, c2ft)
 
def computeCorrectedDiffimPsf (self, corrft, psfOld)
 
def computeCorrectedImage (self, corrft, imgOld)
 

Static Public Member Functions

def padCenterOriginArray (A, tuple newShape, useInverse=False)
 
def estimateVariancePlane (vplane1, vplane2, c1ft, c2ft)
 

Public Attributes

 statsControl
 
 freqSpaceShape
 

Static Public Attributes

 ConfigClass = DecorrelateALKernelConfig
 

Detailed Description

Decorrelate the effect of convolution by Alard-Lupton matching kernel in image difference

Notes
-----

Pipe-task that removes the neighboring-pixel covariance in an
image difference that are added when the template image is
convolved with the Alard-Lupton PSF matching kernel.

The image differencing pipeline task @link
ip.diffim.psfMatch.PsfMatchTask PSFMatchTask@endlink and @link
ip.diffim.psfMatch.PsfMatchConfigAL PSFMatchConfigAL@endlink uses
the Alard and Lupton (1998) method for matching the PSFs of the
template and science exposures prior to subtraction. The
Alard-Lupton method identifies a matching kernel, which is then
(typically) convolved with the template image to perform PSF
matching. This convolution has the effect of adding covariance
between neighboring pixels in the template image, which is then
added to the image difference by subtraction.

The pixel covariance may be corrected by whitening the noise of
the image difference. This task performs such a decorrelation by
computing a decorrelation kernel (based upon the A&L matching
kernel and variances in the template and science images) and
convolving the image difference with it. This process is described
in detail in [DMTN-021](http://dmtn-021.lsst.io).

This task has no standalone example, however it is applied as a
subtask of pipe.tasks.imageDifference.ImageDifferenceTask.

Definition at line 59 of file imageDecorrelation.py.

Constructor & Destructor Documentation

◆ __init__()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.__init__ (   self,
args,
**  kwargs 
)
Create the image decorrelation Task

Parameters
----------
args :
    arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
kwargs :
    keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__``

Reimplemented in lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelMapper.

Definition at line 93 of file imageDecorrelation.py.

93  def __init__(self, *args, **kwargs):
94  """Create the image decorrelation Task
95 
96  Parameters
97  ----------
98  args :
99  arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
100  kwargs :
101  keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
102  """
103  pipeBase.Task.__init__(self, *args, **kwargs)
104 
105  self.statsControl = afwMath.StatisticsControl()
106  self.statsControl.setNumSigmaClip(3.)
107  self.statsControl.setNumIter(3)
108  self.statsControl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.ignoreMaskPlanes))
109 
Pass parameters to a Statistics object.
Definition: Statistics.h:92

Member Function Documentation

◆ calculateVariancePlane()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.calculateVariancePlane (   self,
  vplane1,
  vplane2,
  varMean1,
  varMean2,
  c1ft,
  c2ft 
)
Full propagation of the variance planes of the original exposures.

The original variance planes of independent pixels are convolved with the
image space square of the overall kernels.

Parameters
----------
vplane1, vplane2 : `numpy.ndarray` of `float`
    Variance planes of the original (before pre-convolution or matching)
    exposures.
varMean1, varMean2 : `float`
    Replacement average values for non-finite ``vplane1`` and ``vplane2`` values respectively.

c1ft, c2ft : `numpy.ndarray` of `complex`
    The overall convolution that includes the matching and the
    afterburner in frequency space. The result of either
    ``computeScoreCorrection`` or ``computeDiffimCorrection``.

Returns
-------
vplaneD : `numpy.ndarray` of `float`
  The variance plane of the difference/score images.

Notes
------
See DMTN-179 Section 5 about the variance plane calculations.

Infs and NaNs are allowed and kept in the returned array.

Definition at line 537 of file imageDecorrelation.py.

537  def calculateVariancePlane(self, vplane1, vplane2, varMean1, varMean2, c1ft, c2ft):
538  """Full propagation of the variance planes of the original exposures.
539 
540  The original variance planes of independent pixels are convolved with the
541  image space square of the overall kernels.
542 
543  Parameters
544  ----------
545  vplane1, vplane2 : `numpy.ndarray` of `float`
546  Variance planes of the original (before pre-convolution or matching)
547  exposures.
548  varMean1, varMean2 : `float`
549  Replacement average values for non-finite ``vplane1`` and ``vplane2`` values respectively.
550 
551  c1ft, c2ft : `numpy.ndarray` of `complex`
552  The overall convolution that includes the matching and the
553  afterburner in frequency space. The result of either
554  ``computeScoreCorrection`` or ``computeDiffimCorrection``.
555 
556  Returns
557  -------
558  vplaneD : `numpy.ndarray` of `float`
559  The variance plane of the difference/score images.
560 
561  Notes
562  ------
563  See DMTN-179 Section 5 about the variance plane calculations.
564 
565  Infs and NaNs are allowed and kept in the returned array.
566  """
567  D = np.real(np.fft.ifft2(c1ft))
568  c1SqFt = np.fft.fft2(D*D)
569 
570  v1shape = vplane1.shape
571  filtInf = np.isinf(vplane1)
572  filtNan = np.isnan(vplane1)
573  # This copy could be eliminated if inf/nan handling were go into padCenterOriginArray
574  vplane1 = np.copy(vplane1)
575  vplane1[filtInf | filtNan] = varMean1
576  D = self.padCenterOriginArray(vplane1, self.freqSpaceShape)
577  v1 = np.real(np.fft.ifft2(np.fft.fft2(D) * c1SqFt))
578  v1 = self.padCenterOriginArray(v1, v1shape, useInverse=True)
579  v1[filtNan] = np.nan
580  v1[filtInf] = np.inf
581 
582  D = np.real(np.fft.ifft2(c2ft))
583  c2ft = np.fft.fft2(D*D)
584 
585  v2shape = vplane2.shape
586  filtInf = np.isinf(vplane2)
587  filtNan = np.isnan(vplane2)
588  vplane2 = np.copy(vplane2)
589  vplane2[filtInf | filtNan] = varMean2
590  D = self.padCenterOriginArray(vplane2, self.freqSpaceShape)
591  v2 = np.real(np.fft.ifft2(np.fft.fft2(D) * c2ft))
592  v2 = self.padCenterOriginArray(v2, v2shape, useInverse=True)
593  v2[filtNan] = np.nan
594  v2[filtInf] = np.inf
595 
596  return v1 + v2
597 

◆ computeCommonShape()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.computeCommonShape (   self,
shapes 
)
Calculate the common shape for FFT operations. Set `self.freqSpaceShape`
internally.

Parameters
----------
shapes : one or more `tuple` of `int`
    Shapes of the arrays. All must have the same dimensionality.
    At least one shape must be provided.

Returns
-------
None.

Notes
-----
For each dimension, gets the smallest even number greater than or equal to
`N1+N2-1` where `N1` and `N2` are the two largest values.
In case of only one shape given, rounds up to even each dimension value.

Definition at line 330 of file imageDecorrelation.py.

330  def computeCommonShape(self, *shapes):
331  """Calculate the common shape for FFT operations. Set `self.freqSpaceShape`
332  internally.
333 
334  Parameters
335  ----------
336  shapes : one or more `tuple` of `int`
337  Shapes of the arrays. All must have the same dimensionality.
338  At least one shape must be provided.
339 
340  Returns
341  -------
342  None.
343 
344  Notes
345  -----
346  For each dimension, gets the smallest even number greater than or equal to
347  `N1+N2-1` where `N1` and `N2` are the two largest values.
348  In case of only one shape given, rounds up to even each dimension value.
349  """
350  S = np.array(shapes, dtype=int)
351  if len(shapes) > 2:
352  S.sort(axis=0)
353  S = S[-2:]
354  if len(shapes) > 1:
355  commonShape = np.sum(S, axis=0) - 1
356  else:
357  commonShape = S[0]
358  commonShape[commonShape % 2 != 0] += 1
359  self.freqSpaceShape = tuple(commonShape)
360  self.log.info("Common frequency space shape %s", self.freqSpaceShape)
361 

◆ computeCorrectedDiffimPsf()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.computeCorrectedDiffimPsf (   self,
  corrft,
  psfOld 
)
Compute the (decorrelated) difference image's new PSF.

Parameters
----------
corrft : `numpy.ndarray`
    The frequency space representation of the correction calculated by
    `computeCorrection`. Shape must be `self.freqSpaceShape`.
psfOld : `numpy.ndarray`
    The psf of the difference image to be corrected.

Returns
-------
psfNew : `numpy.ndarray`
    The corrected psf, same shape as `psfOld`, sum normed to 1.

Notes
-----
There is no algorithmic guarantee that the corrected psf can
meaningfully fit to the same size as the original one.

Definition at line 598 of file imageDecorrelation.py.

598  def computeCorrectedDiffimPsf(self, corrft, psfOld):
599  """Compute the (decorrelated) difference image's new PSF.
600 
601  Parameters
602  ----------
603  corrft : `numpy.ndarray`
604  The frequency space representation of the correction calculated by
605  `computeCorrection`. Shape must be `self.freqSpaceShape`.
606  psfOld : `numpy.ndarray`
607  The psf of the difference image to be corrected.
608 
609  Returns
610  -------
611  psfNew : `numpy.ndarray`
612  The corrected psf, same shape as `psfOld`, sum normed to 1.
613 
614  Notes
615  -----
616  There is no algorithmic guarantee that the corrected psf can
617  meaningfully fit to the same size as the original one.
618  """
619  psfShape = psfOld.shape
620  psfNew = self.padCenterOriginArray(psfOld, self.freqSpaceShape)
621  psfNew = np.fft.fft2(psfNew)
622  psfNew *= corrft
623  psfNew = np.fft.ifft2(psfNew)
624  psfNew = psfNew.real
625  psfNew = self.padCenterOriginArray(psfNew, psfShape, useInverse=True)
626  psfNew = psfNew/psfNew.sum()
627  return psfNew
628 

◆ computeCorrectedImage()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.computeCorrectedImage (   self,
  corrft,
  imgOld 
)
Compute the decorrelated difference image.

Parameters
----------
corrft : `numpy.ndarray`
    The frequency space representation of the correction calculated by
    `computeCorrection`. Shape must be `self.freqSpaceShape`.
imgOld : `numpy.ndarray`
    The difference image to be corrected.

Returns
-------
imgNew : `numpy.ndarray`
    The corrected image, same size as the input.

Definition at line 629 of file imageDecorrelation.py.

629  def computeCorrectedImage(self, corrft, imgOld):
630  """Compute the decorrelated difference image.
631 
632  Parameters
633  ----------
634  corrft : `numpy.ndarray`
635  The frequency space representation of the correction calculated by
636  `computeCorrection`. Shape must be `self.freqSpaceShape`.
637  imgOld : `numpy.ndarray`
638  The difference image to be corrected.
639 
640  Returns
641  -------
642  imgNew : `numpy.ndarray`
643  The corrected image, same size as the input.
644  """
645  expShape = imgOld.shape
646  imgNew = np.copy(imgOld)
647  filtInf = np.isinf(imgNew)
648  filtNan = np.isnan(imgNew)
649  imgNew[filtInf] = np.nan
650  imgNew[filtInf | filtNan] = np.nanmean(imgNew)
651  imgNew = self.padCenterOriginArray(imgNew, self.freqSpaceShape)
652  imgNew = np.fft.fft2(imgNew)
653  imgNew *= corrft
654  imgNew = np.fft.ifft2(imgNew)
655  imgNew = imgNew.real
656  imgNew = self.padCenterOriginArray(imgNew, expShape, useInverse=True)
657  imgNew[filtNan] = np.nan
658  imgNew[filtInf] = np.inf
659  return imgNew
660 
661 

◆ computeDiffimCorrection()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.computeDiffimCorrection (   self,
  kappa,
  svar,
  tvar 
)
Compute the Lupton decorrelation post-convolution kernel for decorrelating an
image difference, based on the PSF-matching kernel.

Parameters
----------
kappa : `numpy.ndarray` of `float`
    A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
svar : `float` > 0.
    Average variance of science image used for PSF matching.
tvar : `float` > 0.
    Average variance of the template (matched) image used for PSF matching.

Returns
-------
corrft : `numpy.ndarray` of `float`
    The frequency space representation of the correction. The array is real (dtype float).
    Shape is `self.freqSpaceShape`.

cnft, crft : `numpy.ndarray` of `complex`
    The overall convolution (pre-conv, PSF matching, noise correction) kernel
    for the science and template images, respectively for the variance plane
    calculations. These are intermediate results in frequency space.

Notes
-----
The maximum correction factor converges to `sqrt(tvar/svar)` towards high frequencies.
This should be a plausible value.

Definition at line 410 of file imageDecorrelation.py.

410  def computeDiffimCorrection(self, kappa, svar, tvar):
411  """Compute the Lupton decorrelation post-convolution kernel for decorrelating an
412  image difference, based on the PSF-matching kernel.
413 
414  Parameters
415  ----------
416  kappa : `numpy.ndarray` of `float`
417  A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
418  svar : `float` > 0.
419  Average variance of science image used for PSF matching.
420  tvar : `float` > 0.
421  Average variance of the template (matched) image used for PSF matching.
422 
423  Returns
424  -------
425  corrft : `numpy.ndarray` of `float`
426  The frequency space representation of the correction. The array is real (dtype float).
427  Shape is `self.freqSpaceShape`.
428 
429  cnft, crft : `numpy.ndarray` of `complex`
430  The overall convolution (pre-conv, PSF matching, noise correction) kernel
431  for the science and template images, respectively for the variance plane
432  calculations. These are intermediate results in frequency space.
433 
434  Notes
435  -----
436  The maximum correction factor converges to `sqrt(tvar/svar)` towards high frequencies.
437  This should be a plausible value.
438  """
439  kSum = np.sum(kappa) # We scale the decorrelation to preserve fluxes
440  kappa = self.padCenterOriginArray(kappa, self.freqSpaceShape)
441  kft = np.fft.fft2(kappa)
442  kftAbsSq = np.real(np.conj(kft) * kft)
443 
444  denom = svar + tvar * kftAbsSq
445  corrft = np.sqrt((svar + tvar * kSum*kSum) / denom)
446  cnft = corrft
447  crft = kft*corrft
448  return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
449 

◆ computeScoreCorrection()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.computeScoreCorrection (   self,
  kappa,
  svar,
  tvar,
  preConvArr 
)
Compute the correction kernel for a score image.

Parameters
----------
kappa : `numpy.ndarray`
    A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
svar : `float`
    Average variance of science image used for PSF matching (before pre-convolution).
tvar : `float`
    Average variance of the template (matched) image used for PSF matching.
preConvArr : `numpy.ndarray`
    The pre-convolution kernel of the science image. It should be the PSF
    of the science image or an approximation of it. It must be normed to sum 1.

Returns
-------
corrft : `numpy.ndarray` of `float`
    The frequency space representation of the correction. The array is real (dtype float).
    Shape is `self.freqSpaceShape`.
cnft, crft : `numpy.ndarray` of `complex`
    The overall convolution (pre-conv, PSF matching, noise correction) kernel
    for the science and template images, respectively for the variance plane
    calculations. These are intermediate results in frequency space.

Notes
-----
To be precise, the science image should be _correlated_ by ``preConvArray`` but this
does not matter for this calculation.

``cnft``, ``crft`` contain the scaling factor as well.

Definition at line 450 of file imageDecorrelation.py.

450  def computeScoreCorrection(self, kappa, svar, tvar, preConvArr):
451  """Compute the correction kernel for a score image.
452 
453  Parameters
454  ----------
455  kappa : `numpy.ndarray`
456  A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
457  svar : `float`
458  Average variance of science image used for PSF matching (before pre-convolution).
459  tvar : `float`
460  Average variance of the template (matched) image used for PSF matching.
461  preConvArr : `numpy.ndarray`
462  The pre-convolution kernel of the science image. It should be the PSF
463  of the science image or an approximation of it. It must be normed to sum 1.
464 
465  Returns
466  -------
467  corrft : `numpy.ndarray` of `float`
468  The frequency space representation of the correction. The array is real (dtype float).
469  Shape is `self.freqSpaceShape`.
470  cnft, crft : `numpy.ndarray` of `complex`
471  The overall convolution (pre-conv, PSF matching, noise correction) kernel
472  for the science and template images, respectively for the variance plane
473  calculations. These are intermediate results in frequency space.
474 
475  Notes
476  -----
477  To be precise, the science image should be _correlated_ by ``preConvArray`` but this
478  does not matter for this calculation.
479 
480  ``cnft``, ``crft`` contain the scaling factor as well.
481 
482  """
483  kSum = np.sum(kappa)
484  kappa = self.padCenterOriginArray(kappa, self.freqSpaceShape)
485  kft = np.fft.fft2(kappa)
486  preConvArr = self.padCenterOriginArray(preConvArr, self.freqSpaceShape)
487  preFt = np.fft.fft2(preConvArr)
488  preFtAbsSq = np.real(np.conj(preFt) * preFt)
489  kftAbsSq = np.real(np.conj(kft) * kft)
490  # Avoid zero division, though we don't normally approach `tiny`.
491  # We have numerical noise instead.
492  tiny = np.finfo(preFtAbsSq.dtype).tiny * 1000.
493  flt = preFtAbsSq < tiny
494  # If we pre-convolve to avoid deconvolution in AL, then kftAbsSq / preFtAbsSq
495  # theoretically expected to diverge to +inf. But we don't care about the convergence
496  # properties here, S goes to 0 at these frequencies anyway.
497  preFtAbsSq[flt] = tiny
498  denom = svar + tvar * kftAbsSq / preFtAbsSq
499  corrft = (svar + tvar * kSum*kSum) / denom
500  cnft = np.conj(preFt)*corrft
501  crft = kft*corrft
502  return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
503 

◆ computeVarianceMean()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.computeVarianceMean (   self,
  exposure 
)

Definition at line 110 of file imageDecorrelation.py.

110  def computeVarianceMean(self, exposure):
111  statObj = afwMath.makeStatistics(exposure.getMaskedImage().getVariance(),
112  exposure.getMaskedImage().getMask(),
113  afwMath.MEANCLIP, self.statsControl)
114  var = statObj.getValue(afwMath.MEANCLIP)
115  return var
116 
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition: Statistics.h:359

◆ estimateVariancePlane()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.estimateVariancePlane (   vplane1,
  vplane2,
  c1ft,
  c2ft 
)
static
Estimate the variance planes.

The estimation assumes that around each pixel the surrounding
pixels' sigmas within the convolution kernel are the same.

Parameters
----------
vplane1, vplane2 : `numpy.ndarray` of `float`
    Variance planes of the original (before pre-convolution or matching)
    exposures.
c1ft, c2ft : `numpy.ndarray` of `complex`
    The overall convolution that includes the matching and the
    afterburner in frequency space. The result of either
    ``computeScoreCorrection`` or ``computeDiffimCorrection``.

Returns
-------
vplaneD : `numpy.ndarray` of `float`
  The estimated variance plane of the difference/score image
  as a weighted sum of the input variances.

Notes
------
See DMTN-179 Section 5 about the variance plane calculations.

Definition at line 505 of file imageDecorrelation.py.

505  def estimateVariancePlane(vplane1, vplane2, c1ft, c2ft):
506  """Estimate the variance planes.
507 
508  The estimation assumes that around each pixel the surrounding
509  pixels' sigmas within the convolution kernel are the same.
510 
511  Parameters
512  ----------
513  vplane1, vplane2 : `numpy.ndarray` of `float`
514  Variance planes of the original (before pre-convolution or matching)
515  exposures.
516  c1ft, c2ft : `numpy.ndarray` of `complex`
517  The overall convolution that includes the matching and the
518  afterburner in frequency space. The result of either
519  ``computeScoreCorrection`` or ``computeDiffimCorrection``.
520 
521  Returns
522  -------
523  vplaneD : `numpy.ndarray` of `float`
524  The estimated variance plane of the difference/score image
525  as a weighted sum of the input variances.
526 
527  Notes
528  ------
529  See DMTN-179 Section 5 about the variance plane calculations.
530  """
531  w1 = np.sum(np.real(np.conj(c1ft)*c1ft)) / c1ft.size
532  w2 = np.sum(np.real(np.conj(c2ft)*c2ft)) / c2ft.size
533  # w1, w2: the frequency space sum of abs(c1)^2 is the same as in image
534  # space.
535  return vplane1*w1 + vplane2*w2
536 

◆ padCenterOriginArray()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.padCenterOriginArray (   A,
tuple  newShape,
  useInverse = False 
)
static
Zero pad an image where the origin is at the center and replace the
origin to the corner as required by the periodic input of FFT. Implement also
the inverse operation, crop the padding and re-center data.

Parameters
----------
A : `numpy.ndarray`
    An array to copy from.
newShape : `tuple` of `int`
    The dimensions of the resulting array. For padding, the resulting array
    must be larger than A in each dimension. For the inverse operation this
    must be the original, before padding size of the array.
useInverse : bool, optional
    Selector of forward, add padding, operation (False)
    or its inverse, crop padding, operation (True).

Returns
-------
R : `numpy.ndarray`
    The padded or unpadded array with shape of `newShape` and the same dtype as A.

Notes
-----
For odd dimensions, the splitting is rounded to
put the center pixel into the new corner origin (0,0). This is to be consistent
e.g. for a dirac delta kernel that is originally located at the center pixel.

Definition at line 363 of file imageDecorrelation.py.

363  def padCenterOriginArray(A, newShape: tuple, useInverse=False):
364  """Zero pad an image where the origin is at the center and replace the
365  origin to the corner as required by the periodic input of FFT. Implement also
366  the inverse operation, crop the padding and re-center data.
367 
368  Parameters
369  ----------
370  A : `numpy.ndarray`
371  An array to copy from.
372  newShape : `tuple` of `int`
373  The dimensions of the resulting array. For padding, the resulting array
374  must be larger than A in each dimension. For the inverse operation this
375  must be the original, before padding size of the array.
376  useInverse : bool, optional
377  Selector of forward, add padding, operation (False)
378  or its inverse, crop padding, operation (True).
379 
380  Returns
381  -------
382  R : `numpy.ndarray`
383  The padded or unpadded array with shape of `newShape` and the same dtype as A.
384 
385  Notes
386  -----
387  For odd dimensions, the splitting is rounded to
388  put the center pixel into the new corner origin (0,0). This is to be consistent
389  e.g. for a dirac delta kernel that is originally located at the center pixel.
390  """
391 
392  # The forward and inverse operations should round odd dimension halves at the opposite
393  # sides to get the pixels back to their original positions.
394  if not useInverse:
395  # Forward operation: First and second halves with respect to the axes of A.
396  firstHalves = [x//2 for x in A.shape]
397  secondHalves = [x-y for x, y in zip(A.shape, firstHalves)]
398  else:
399  # Inverse operation: Opposite rounding
400  secondHalves = [x//2 for x in newShape]
401  firstHalves = [x-y for x, y in zip(newShape, secondHalves)]
402 
403  R = np.zeros_like(A, shape=newShape)
404  R[-firstHalves[0]:, -firstHalves[1]:] = A[:firstHalves[0], :firstHalves[1]]
405  R[:secondHalves[0], -firstHalves[1]:] = A[-secondHalves[0]:, :firstHalves[1]]
406  R[:secondHalves[0], :secondHalves[1]] = A[-secondHalves[0]:, -secondHalves[1]:]
407  R[-firstHalves[0]:, :secondHalves[1]] = A[:firstHalves[0], -secondHalves[1]:]
408  return R
409 

◆ run()

def lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.run (   self,
  scienceExposure,
  templateExposure,
  subtractedExposure,
  psfMatchingKernel,
  preConvKernel = None,
  xcen = None,
  ycen = None,
  svar = None,
  tvar = None,
  templateMatched = True,
  preConvMode = False,
**  kwargs 
)
Perform decorrelation of an image difference or of a score difference exposure.

Corrects the difference or score image due to the convolution of the
templateExposure with the A&L PSF matching kernel.
See [DMTN-021, Equation 1](http://dmtn-021.lsst.io/#equation-1) and
[DMTN-179](http://dmtn-179.lsst.io/) for details.

Parameters
----------
scienceExposure : `lsst.afw.image.Exposure`
    The original science exposure (before pre-convolution, if ``preConvMode==True``).
templateExposure : `lsst.afw.image.Exposure`
    The original template exposure warped into the science exposure dimensions.
subtractedExposure : `lsst.afw.image.Exposure`
    the subtracted exposure produced by
    `ip_diffim.ImagePsfMatchTask.subtractExposures()`. The `subtractedExposure` must
    inherit its PSF from `exposure`, see notes below.
psfMatchingKernel : `lsst.afw.detection.Psf`
    An (optionally spatially-varying) PSF matching kernel produced
    by `ip_diffim.ImagePsfMatchTask.subtractExposures()`.
preConvKernel : `lsst.afw.math.Kernel`, optional
    If not `None`, then the `scienceExposure` was pre-convolved with (the reflection of)
    this kernel. Must be normalized to sum to 1.
    Allowed only if ``templateMatched==True`` and ``preConvMode==True``.
    Defaults to the PSF of the science exposure at the image center.
xcen : `float`, optional
    X-pixel coordinate to use for computing constant matching kernel to use
    If `None` (default), then use the center of the image.
ycen : `float`, optional
    Y-pixel coordinate to use for computing constant matching kernel to use
    If `None` (default), then use the center of the image.
svar : `float`, optional
    Image variance for science image
    If `None` (default) then compute the variance over the entire input science image.
tvar : `float`, optional
    Image variance for template image
    If `None` (default) then compute the variance over the entire input template image.
templateMatched : `bool`, optional
    If True, the template exposure was matched (convolved) to the science exposure.
    See also notes below.
preConvMode : `bool`, optional
    If True, ``subtractedExposure`` is assumed to be a likelihood difference image
    and will be noise corrected as a likelihood image.
**kwargs
    Additional keyword arguments propagated from DecorrelateALKernelSpatialTask.

Returns
-------
result : `lsst.pipe.base.Struct`
    - ``correctedExposure`` : the decorrelated diffim

Notes
-----
If ``preConvMode==True``, ``subtractedExposure`` is assumed to be a
score image and the noise correction for likelihood images
is applied. The resulting image is an optimal detection likelihood image
when the templateExposure has noise. (See DMTN-179) If ``preConvKernel`` is
not specified, the PSF of ``scienceExposure`` is assumed as pre-convolution kernel.

The ``subtractedExposure`` is NOT updated. The returned ``correctedExposure``
has an updated but spatially fixed PSF. It is calculated as the center of
image PSF corrected by the center of image matching kernel.

If ``templateMatched==True``, the templateExposure was matched (convolved)
to the ``scienceExposure`` by ``psfMatchingKernel``. Otherwise the ``scienceExposure``
was matched (convolved) by ``psfMatchingKernel``.

This task discards the variance plane of ``subtractedExposure`` and re-computes
it from the variance planes of ``scienceExposure`` and ``templateExposure``.
The image plane of ``subtractedExposure`` must be at the photometric level
set by the AL PSF matching in `ImagePsfMatchTask.subtractExposures`.
The assumptions about the photometric level are controlled by the
`templateMatched` option in this task.

Here we currently convert a spatially-varying matching kernel into a constant kernel,
just by computing it at the center of the image (tickets DM-6243, DM-6244).

We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute
the decorrelation kernel.

TODO DM-23857 As part of the spatially varying correction implementation
consider whether returning a Struct is still necessary.

Definition at line 118 of file imageDecorrelation.py.

120  templateMatched=True, preConvMode=False, **kwargs):
121  """Perform decorrelation of an image difference or of a score difference exposure.
122 
123  Corrects the difference or score image due to the convolution of the
124  templateExposure with the A&L PSF matching kernel.
125  See [DMTN-021, Equation 1](http://dmtn-021.lsst.io/#equation-1) and
126  [DMTN-179](http://dmtn-179.lsst.io/) for details.
127 
128  Parameters
129  ----------
130  scienceExposure : `lsst.afw.image.Exposure`
131  The original science exposure (before pre-convolution, if ``preConvMode==True``).
132  templateExposure : `lsst.afw.image.Exposure`
133  The original template exposure warped into the science exposure dimensions.
134  subtractedExposure : `lsst.afw.image.Exposure`
135  the subtracted exposure produced by
136  `ip_diffim.ImagePsfMatchTask.subtractExposures()`. The `subtractedExposure` must
137  inherit its PSF from `exposure`, see notes below.
138  psfMatchingKernel : `lsst.afw.detection.Psf`
139  An (optionally spatially-varying) PSF matching kernel produced
140  by `ip_diffim.ImagePsfMatchTask.subtractExposures()`.
141  preConvKernel : `lsst.afw.math.Kernel`, optional
142  If not `None`, then the `scienceExposure` was pre-convolved with (the reflection of)
143  this kernel. Must be normalized to sum to 1.
144  Allowed only if ``templateMatched==True`` and ``preConvMode==True``.
145  Defaults to the PSF of the science exposure at the image center.
146  xcen : `float`, optional
147  X-pixel coordinate to use for computing constant matching kernel to use
148  If `None` (default), then use the center of the image.
149  ycen : `float`, optional
150  Y-pixel coordinate to use for computing constant matching kernel to use
151  If `None` (default), then use the center of the image.
152  svar : `float`, optional
153  Image variance for science image
154  If `None` (default) then compute the variance over the entire input science image.
155  tvar : `float`, optional
156  Image variance for template image
157  If `None` (default) then compute the variance over the entire input template image.
158  templateMatched : `bool`, optional
159  If True, the template exposure was matched (convolved) to the science exposure.
160  See also notes below.
161  preConvMode : `bool`, optional
162  If True, ``subtractedExposure`` is assumed to be a likelihood difference image
163  and will be noise corrected as a likelihood image.
164  **kwargs
165  Additional keyword arguments propagated from DecorrelateALKernelSpatialTask.
166 
167  Returns
168  -------
169  result : `lsst.pipe.base.Struct`
170  - ``correctedExposure`` : the decorrelated diffim
171 
172  Notes
173  -----
174  If ``preConvMode==True``, ``subtractedExposure`` is assumed to be a
175  score image and the noise correction for likelihood images
176  is applied. The resulting image is an optimal detection likelihood image
177  when the templateExposure has noise. (See DMTN-179) If ``preConvKernel`` is
178  not specified, the PSF of ``scienceExposure`` is assumed as pre-convolution kernel.
179 
180  The ``subtractedExposure`` is NOT updated. The returned ``correctedExposure``
181  has an updated but spatially fixed PSF. It is calculated as the center of
182  image PSF corrected by the center of image matching kernel.
183 
184  If ``templateMatched==True``, the templateExposure was matched (convolved)
185  to the ``scienceExposure`` by ``psfMatchingKernel``. Otherwise the ``scienceExposure``
186  was matched (convolved) by ``psfMatchingKernel``.
187 
188  This task discards the variance plane of ``subtractedExposure`` and re-computes
189  it from the variance planes of ``scienceExposure`` and ``templateExposure``.
190  The image plane of ``subtractedExposure`` must be at the photometric level
191  set by the AL PSF matching in `ImagePsfMatchTask.subtractExposures`.
192  The assumptions about the photometric level are controlled by the
193  `templateMatched` option in this task.
194 
195  Here we currently convert a spatially-varying matching kernel into a constant kernel,
196  just by computing it at the center of the image (tickets DM-6243, DM-6244).
197 
198  We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute
199  the decorrelation kernel.
200 
201  TODO DM-23857 As part of the spatially varying correction implementation
202  consider whether returning a Struct is still necessary.
203  """
204  if preConvKernel is not None and not (templateMatched and preConvMode):
205  raise ValueError("Pre-convolution kernel is allowed only if "
206  "preConvMode==True and templateMatched==True.")
207 
208  spatialKernel = psfMatchingKernel
209  kimg = afwImage.ImageD(spatialKernel.getDimensions())
210  bbox = subtractedExposure.getBBox()
211  if xcen is None:
212  xcen = (bbox.getBeginX() + bbox.getEndX()) / 2.
213  if ycen is None:
214  ycen = (bbox.getBeginY() + bbox.getEndY()) / 2.
215  self.log.info("Using matching kernel computed at (%d, %d)", xcen, ycen)
216  spatialKernel.computeImage(kimg, False, xcen, ycen)
217 
218  preConvImg = None
219  if preConvMode:
220  if preConvKernel is None:
221  preConvKernel = scienceExposure.getPsf().getLocalKernel() # at average position
222  preConvImg = afwImage.ImageD(preConvKernel.getDimensions())
223  preConvKernel.computeImage(preConvImg, True)
224 
225  if svar is None:
226  svar = self.computeVarianceMean(scienceExposure)
227  if tvar is None:
228  tvar = self.computeVarianceMean(templateExposure)
229  self.log.info("Original variance plane means. Science:%.5e, warped template:%.5e)",
230  svar, tvar)
231 
232  if templateMatched:
233  # Regular subtraction, we convolved the template
234  self.log.info("Decorrelation after template image convolution")
235  expVar = svar
236  matchedVar = tvar
237  exposure = scienceExposure
238  matchedExposure = templateExposure
239  else:
240  # We convolved the science image
241  self.log.info("Decorrelation after science image convolution")
242  expVar = tvar
243  matchedVar = svar
244  exposure = templateExposure
245  matchedExposure = scienceExposure
246 
247  # Should not happen unless entire image has been masked, which could happen
248  # if this is a small subimage of the main exposure. In this case, just return a full NaN
249  # exposure
250  if np.isnan(expVar) or np.isnan(matchedVar):
251  # Double check that one of the exposures is all NaNs
252  if (np.all(np.isnan(exposure.image.array))
253  or np.all(np.isnan(matchedExposure.image.array))):
254  self.log.warning('Template or science image is entirely NaNs: skipping decorrelation.')
255  outExposure = subtractedExposure.clone()
256  return pipeBase.Struct(correctedExposure=outExposure, )
257 
258  # The maximal correction value converges to sqrt(matchedVar/expVar).
259  # Correction divergence warning if the correction exceeds 4 orders of magnitude.
260  mOverExpVar = matchedVar/expVar
261  if mOverExpVar > 1e8:
262  self.log.warning("Diverging correction: matched image variance is "
263  " much larger than the unconvolved one's"
264  ", matchedVar/expVar:%.2e", mOverExpVar)
265 
266  oldVarMean = self.computeVarianceMean(subtractedExposure)
267  self.log.info("Variance plane mean of uncorrected diffim: %f", oldVarMean)
268 
269  kArr = kimg.array
270  diffExpArr = subtractedExposure.image.array
271  psfImg = subtractedExposure.getPsf().computeKernelImage(geom.Point2D(xcen, ycen))
272  psfDim = psfImg.getDimensions()
273  psfArr = psfImg.array
274 
275  # Determine the common shape
276  kSum = np.sum(kArr)
277  kSumSq = kSum*kSum
278  self.log.debug("Matching kernel sum: %.3e", kSum)
279 
280  if preConvMode:
281  self.log.info("Decorrelation of likelihood image")
282  self.computeCommonShape(preConvImg.array.shape, kArr.shape,
283  psfArr.shape, diffExpArr.shape)
284  corr = self.computeScoreCorrection(kArr, expVar, matchedVar, preConvImg.array)
285  else:
286  self.log.info("Decorrelation of difference image")
287  self.computeCommonShape(kArr.shape, psfArr.shape, diffExpArr.shape)
288  corr = self.computeDiffimCorrection(kArr, expVar, matchedVar)
289 
290  diffExpArr = self.computeCorrectedImage(corr.corrft, diffExpArr)
291 
292  corrPsfArr = self.computeCorrectedDiffimPsf(corr.corrft, psfArr)
293  psfcI = afwImage.ImageD(psfDim)
294  psfcI.array = corrPsfArr
295  psfcK = afwMath.FixedKernel(psfcI)
296  psfNew = measAlg.KernelPsf(psfcK)
297 
298  correctedExposure = subtractedExposure.clone()
299  correctedExposure.image.array[...] = diffExpArr # Allow for numpy type casting
300  # The subtracted exposure variance plane is already correlated, we cannot propagate
301  # it through another convolution; instead we need to use the uncorrelated originals
302  # The whitening should scale it to expVar + matchedVar on average
303  if self.config.completeVarPlanePropagation:
304  self.log.debug("Using full variance plane calculation in decorrelation")
305  newVarArr = self.calculateVariancePlane(
306  exposure.variance.array, matchedExposure.variance.array,
307  expVar, matchedVar, corr.cnft, corr.crft)
308  else:
309  self.log.debug("Using estimated variance plane calculation in decorrelation")
310  newVarArr = self.estimateVariancePlane(
311  exposure.variance.array, matchedExposure.variance.array,
312  corr.cnft, corr.crft)
313 
314  corrExpVarArr = correctedExposure.variance.array
315  corrExpVarArr[...] = newVarArr # Allow for numpy type casting
316 
317  if not templateMatched:
318  # ImagePsfMatch.subtractExposures re-scales the difference in
319  # the science image convolution mode
320  corrExpVarArr /= kSumSq
321  correctedExposure.setPsf(psfNew)
322 
323  newVarMean = self.computeVarianceMean(correctedExposure)
324  self.log.info("Variance plane mean of corrected diffim: %.5e", newVarMean)
325 
326  # TODO DM-23857 As part of the spatially varying correction implementation
327  # consider whether returning a Struct is still necessary.
328  return pipeBase.Struct(correctedExposure=correctedExposure, )
329 
A kernel created from an Image.
Definition: Kernel.h:471

Member Data Documentation

◆ ConfigClass

lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.ConfigClass = DecorrelateALKernelConfig
static

Definition at line 90 of file imageDecorrelation.py.

◆ freqSpaceShape

lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.freqSpaceShape

Definition at line 359 of file imageDecorrelation.py.

◆ statsControl

lsst.ip.diffim.imageDecorrelation.DecorrelateALKernelTask.statsControl

Definition at line 105 of file imageDecorrelation.py.


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