LSSTApplications  19.0.0-13-g16625d3+12,20.0.0+1,20.0.0+22,20.0.0+23,20.0.0+26,20.0.0+28,20.0.0+3,20.0.0+4,20.0.0+5,20.0.0+7,20.0.0+8,20.0.0-1-g10df615+22,20.0.0-1-g253301a+6,20.0.0-1-g4d801e7,20.0.0-1-g596936a+28,20.0.0-1-g8a53f90+2,20.0.0-1-gc96f8cb+27,20.0.0-1-gd1c87d7+2,20.0.0-1-gdb27ee5+6,20.0.0-11-gda4966f+11,20.0.0-14-g7cb2d310,20.0.0-2-gec03fae+4,20.0.0-24-g0eb5a41+1,20.0.0-3-gd2e950e,20.0.0-4-g4a2362f,20.0.0-4-gde602ef96+5,20.0.0-4-ge48a6ca+22,20.0.0-5-gc4485221+1,20.0.0-6-g9bcb941+3,20.0.0-7-g3c4151b+4,20.0.0-7-gb92c176+1,w.2020.31
LSSTDataManagementBasePackage
Public Member Functions | Public Attributes | Static Public Attributes | List of all members
lsst.ip.diffim.zogy.ZogyMapper Class Reference
Inheritance diagram for lsst.ip.diffim.zogy.ZogyMapper:
lsst.ip.diffim.zogy.ZogyTask lsst.ip.diffim.imageMapReduce.ImageMapper

Public Member Functions

def __init__ (self, *args, **kwargs)
 
def run (self, subExposure, expandedSubExposure, fullBBox, template, **kwargs)
 
def setup (self, templateExposure=None, scienceExposure=None, sig1=None, sig2=None, psf1=None, psf2=None, correctBackground=False, *args, **kwargs)
 
def computePrereqs (self, psf1=None, psf2=None, padSize=0)
 
def computeDiffimFourierSpace (self, debug=False, returnMatchedTemplate=False, **kwargs)
 
def computeDiffimImageSpace (self, padSize=None, debug=False, **kwargs)
 
def computeDiffim (self, inImageSpace=None, padSize=None, returnMatchedTemplate=False, **kwargs)
 
def computeDiffimPsf (self, padSize=0, keepFourier=False, psf1=None, psf2=None)
 
def computeScorrFourierSpace (self, xVarAst=0., yVarAst=0., **kwargs)
 
def computeScorrImageSpace (self, xVarAst=0., yVarAst=0., padSize=None, **kwargs)
 
def computeScorr (self, xVarAst=0., yVarAst=0., inImageSpace=None, padSize=0, **kwargs)
 
def run (self, subExposure, expandedSubExposure, fullBBox, **kwargs)
 

Public Attributes

 template
 
 science
 
 statsControl
 
 im1
 
 im2
 
 im1_var
 
 im2_var
 
 im1_psf
 
 im2_psf
 
 sig1
 
 sig2
 
 Fr
 
 Fn
 
 padSize
 

Static Public Attributes

 ConfigClass = ZogyConfig
 

Detailed Description

Task to be used as an ImageMapper for performing
ZOGY image subtraction on a grid of subimages.

Definition at line 927 of file zogy.py.

Constructor & Destructor Documentation

◆ __init__()

def lsst.ip.diffim.zogy.ZogyMapper.__init__ (   self,
args,
**  kwargs 
)

Definition at line 934 of file zogy.py.

934  def __init__(self, *args, **kwargs):
935  ImageMapper.__init__(self, *args, **kwargs)
936 

Member Function Documentation

◆ computeDiffim()

def lsst.ip.diffim.zogy.ZogyTask.computeDiffim (   self,
  inImageSpace = None,
  padSize = None,
  returnMatchedTemplate = False,
**  kwargs 
)
inherited
Wrapper method to compute ZOGY proper diffim

This method should be used as the public interface for
computing the ZOGY diffim.

Parameters
----------
inImageSpace : `bool`
   Override config `inImageSpace` parameter
padSize : `int`
   Override config `padSize` parameter
returnMatchedTemplate : `bool`
   Include the PSF-matched template in the results Struct
**kwargs
    additional keyword arguments to be passed to
    `computeDiffimFourierSpace` or `computeDiffimImageSpace`.

Returns
-------
An lsst.pipe.base.Struct containing:
   - D : `lsst.afw.Exposure`
       the proper image difference, including correct variance,
       masks, and PSF
   - R : `lsst.afw.Exposure`
       If `returnMatchedTemplate` is True, the PSF-matched template
       exposure

Definition at line 610 of file zogy.py.

610  def computeDiffim(self, inImageSpace=None, padSize=None,
611  returnMatchedTemplate=False, **kwargs):
612  """Wrapper method to compute ZOGY proper diffim
613 
614  This method should be used as the public interface for
615  computing the ZOGY diffim.
616 
617  Parameters
618  ----------
619  inImageSpace : `bool`
620  Override config `inImageSpace` parameter
621  padSize : `int`
622  Override config `padSize` parameter
623  returnMatchedTemplate : `bool`
624  Include the PSF-matched template in the results Struct
625  **kwargs
626  additional keyword arguments to be passed to
627  `computeDiffimFourierSpace` or `computeDiffimImageSpace`.
628 
629  Returns
630  -------
631  An lsst.pipe.base.Struct containing:
632  - D : `lsst.afw.Exposure`
633  the proper image difference, including correct variance,
634  masks, and PSF
635  - R : `lsst.afw.Exposure`
636  If `returnMatchedTemplate` is True, the PSF-matched template
637  exposure
638  """
639  R = None
640  inImageSpace = self.config.inImageSpace if inImageSpace is None else inImageSpace
641  if inImageSpace:
642  padSize = self.padSize if padSize is None else padSize
643  res = self.computeDiffimImageSpace(padSize=padSize, **kwargs)
644  D = res.D
645  if returnMatchedTemplate:
646  R = res.R
647  else:
648  res = self.computeDiffimFourierSpace(**kwargs)
649  D = self.science.clone()
650  D.getMaskedImage().getImage().getArray()[:, :] = res.D
651  D.getMaskedImage().getVariance().getArray()[:, :] = res.D_var
652  if returnMatchedTemplate:
653  R = self.science.clone()
654  R.getMaskedImage().getImage().getArray()[:, :] = res.R
655  R.getMaskedImage().getVariance().getArray()[:, :] = res.R_var
656 
657  psf = self.computeDiffimPsf()
658  D = self._setNewPsf(D, psf)
659  return pipeBase.Struct(D=D, R=R)
660 

◆ computeDiffimFourierSpace()

def lsst.ip.diffim.zogy.ZogyTask.computeDiffimFourierSpace (   self,
  debug = False,
  returnMatchedTemplate = False,
**  kwargs 
)
inherited
Compute ZOGY diffim `D` as proscribed in ZOGY (2016) manuscript

Parameters
----------
debug : `bool`, optional
    If set to True, filter the kernels by setting the edges to zero.
returnMatchedTemplate : `bool`, optional
    Calculate the template image.
    If not set, the returned template will be None.

Notes
-----
In all functions, im1 is R (reference, or template) and im2 is N (new, or science)
Compute the ZOGY eqn. (13):

.. math::

    \widehat{D} = \frac{Fr\widehat{Pr}\widehat{N} -
    F_n\widehat{Pn}\widehat{R}}{\sqrt{\sigma_n^2 Fr^2
    \|\widehat{Pr}\|^2 + \sigma_r^2 F_n^2 \|\widehat{Pn}\|^2}}

where :math:`D` is the optimal difference image, :math:`R` and :math:`N` are the
reference and "new" image, respectively, :math:`Pr` and :math:`P_n` are their
PSFs, :math:`Fr` and :math:`Fn` are their flux-based zero-points (which we
will set to one here), :math:`\sigma_r^2` and :math:`\sigma_n^2` are their
variance, and :math:`\widehat{D}` denotes the FT of :math:`D`.

Returns
-------
result : `lsst.pipe.base.Struct`
    Result struct with components:

    - ``D`` : 2D `numpy.array`, the proper image difference
    - ``D_var`` : 2D `numpy.array`, the variance image for `D`

Definition at line 406 of file zogy.py.

406  def computeDiffimFourierSpace(self, debug=False, returnMatchedTemplate=False, **kwargs):
407  r"""Compute ZOGY diffim `D` as proscribed in ZOGY (2016) manuscript
408 
409  Parameters
410  ----------
411  debug : `bool`, optional
412  If set to True, filter the kernels by setting the edges to zero.
413  returnMatchedTemplate : `bool`, optional
414  Calculate the template image.
415  If not set, the returned template will be None.
416 
417  Notes
418  -----
419  In all functions, im1 is R (reference, or template) and im2 is N (new, or science)
420  Compute the ZOGY eqn. (13):
421 
422  .. math::
423 
424  \widehat{D} = \frac{Fr\widehat{Pr}\widehat{N} -
425  F_n\widehat{Pn}\widehat{R}}{\sqrt{\sigma_n^2 Fr^2
426  \|\widehat{Pr}\|^2 + \sigma_r^2 F_n^2 \|\widehat{Pn}\|^2}}
427 
428  where :math:`D` is the optimal difference image, :math:`R` and :math:`N` are the
429  reference and "new" image, respectively, :math:`Pr` and :math:`P_n` are their
430  PSFs, :math:`Fr` and :math:`Fn` are their flux-based zero-points (which we
431  will set to one here), :math:`\sigma_r^2` and :math:`\sigma_n^2` are their
432  variance, and :math:`\widehat{D}` denotes the FT of :math:`D`.
433 
434  Returns
435  -------
436  result : `lsst.pipe.base.Struct`
437  Result struct with components:
438 
439  - ``D`` : 2D `numpy.array`, the proper image difference
440  - ``D_var`` : 2D `numpy.array`, the variance image for `D`
441  """
442  # Do all in fourier space (needs image-sized PSFs)
443  psf1 = ZogyTask._padPsfToSize(self.im1_psf, self.im1.shape)
444  psf2 = ZogyTask._padPsfToSize(self.im2_psf, self.im2.shape)
445 
446  preqs = self.computePrereqs(psf1, psf2, padSize=0) # already padded the PSFs
447 
448  def _filterKernel(K, trim_amount):
449  # Filter the wings of Kn, Kr, set to zero
450  ps = trim_amount
451  K[:ps, :] = K[-ps:, :] = 0
452  K[:, :ps] = K[:, -ps:] = 0
453  return K
454 
455  Kr_hat = self.Fr * preqs.Pr_hat / preqs.denom
456  Kn_hat = self.Fn * preqs.Pn_hat / preqs.denom
457  if debug and self.config.doTrimKernels: # default False
458  # Suggestion from Barak to trim Kr and Kn to remove artifacts
459  # Here we just filter them (in image space) to keep them the same size
460  ps = (Kn_hat.shape[1] - 80)//2
461  Kn = _filterKernel(np.fft.ifft2(Kn_hat), ps)
462  Kn_hat = np.fft.fft2(Kn)
463  Kr = _filterKernel(np.fft.ifft2(Kr_hat), ps)
464  Kr_hat = np.fft.fft2(Kr)
465 
466  def processImages(im1, im2, doAdd=False):
467  # Some masked regions are NaN or infinite!, and FFTs no likey.
468  im1[np.isinf(im1)] = np.nan
469  im1[np.isnan(im1)] = np.nanmean(im1)
470  im2[np.isinf(im2)] = np.nan
471  im2[np.isnan(im2)] = np.nanmean(im2)
472 
473  R_hat = np.fft.fft2(im1)
474  N_hat = np.fft.fft2(im2)
475 
476  D_hat = Kr_hat * N_hat
477  D_hat_R = Kn_hat * R_hat
478  if not doAdd:
479  D_hat -= D_hat_R
480  else:
481  D_hat += D_hat_R
482 
483  D = np.fft.ifft2(D_hat)
484  D = np.fft.ifftshift(D.real) / preqs.Fd
485 
486  R = None
487  if returnMatchedTemplate:
488  R = np.fft.ifft2(D_hat_R)
489  R = np.fft.ifftshift(R.real) / preqs.Fd
490 
491  return D, R
492 
493  # First do the image
494  D, R = processImages(self.im1, self.im2, doAdd=False)
495  # Do the exact same thing to the var images, except add them
496  D_var, R_var = processImages(self.im1_var, self.im2_var, doAdd=True)
497 
498  return pipeBase.Struct(D=D, D_var=D_var, R=R, R_var=R_var)
499 

◆ computeDiffimImageSpace()

def lsst.ip.diffim.zogy.ZogyTask.computeDiffimImageSpace (   self,
  padSize = None,
  debug = False,
**  kwargs 
)
inherited
Compute ZOGY diffim `D` using image-space convlutions

This method is still being debugged as it results in artifacts
when the PSFs are noisy (see module-level docstring). Thus
there are several options still enabled by the `debug` flag,
which are disabled by defult.

Parameters
----------
padSize : `int`
   The amount to pad the PSFs by
debug : `bool`
   Flag to enable debugging tests and options

Returns
-------
D : `lsst.afw.Exposure`
   the proper image difference, including correct variance,
   masks, and PSF

Definition at line 540 of file zogy.py.

540  def computeDiffimImageSpace(self, padSize=None, debug=False, **kwargs):
541  """Compute ZOGY diffim `D` using image-space convlutions
542 
543  This method is still being debugged as it results in artifacts
544  when the PSFs are noisy (see module-level docstring). Thus
545  there are several options still enabled by the `debug` flag,
546  which are disabled by defult.
547 
548  Parameters
549  ----------
550  padSize : `int`
551  The amount to pad the PSFs by
552  debug : `bool`
553  Flag to enable debugging tests and options
554 
555  Returns
556  -------
557  D : `lsst.afw.Exposure`
558  the proper image difference, including correct variance,
559  masks, and PSF
560  """
561  preqs = self.computePrereqs(padSize=padSize)
562 
563  delta = 0.
564  if debug:
565  delta = 1. # Regularize the ratio, a possible option to remove artifacts
566  Kr_hat = (preqs.Pr_hat + delta) / (preqs.denom + delta)
567  Kn_hat = (preqs.Pn_hat + delta) / (preqs.denom + delta)
568  Kr = np.fft.ifft2(Kr_hat).real
569  Kr = np.roll(np.roll(Kr, -1, 0), -1, 1)
570  Kn = np.fft.ifft2(Kn_hat).real
571  Kn = np.roll(np.roll(Kn, -1, 0), -1, 1)
572 
573  def _trimKernel(self, K, trim_amount):
574  # Trim out the wings of Kn, Kr (see notebook #15)
575  # only necessary if it's from a measured psf and PsfEx seems to always make PSFs of size 41x41
576  ps = trim_amount
577  K = K[ps:-ps, ps:-ps]
578  return K
579 
580  padSize = self.padSize if padSize is None else padSize
581  # Enabling this block (debug=True) makes it slightly faster, but ~25% worse artifacts:
582  if debug and self.config.doTrimKernels: # default False
583  # Filtering also makes it slightly faster (zeros are ignored in convolution)
584  # but a bit worse. Filter the wings of Kn, Kr (see notebook #15)
585  Kn = _trimKernel(Kn, padSize)
586  Kr = _trimKernel(Kr, padSize)
587 
588  # Note these are reverse-labelled, this is CORRECT!
589  exp1, _ = self._doConvolve(self.template, Kn)
590  exp2, _ = self._doConvolve(self.science, Kr)
591  D = exp2
592  tmp = D.getMaskedImage()
593  tmp -= exp1.getMaskedImage()
594  tmp /= preqs.Fd
595  return pipeBase.Struct(D=D, R=exp1)
596 

◆ computeDiffimPsf()

def lsst.ip.diffim.zogy.ZogyTask.computeDiffimPsf (   self,
  padSize = 0,
  keepFourier = False,
  psf1 = None,
  psf2 = None 
)
inherited
Compute the ZOGY diffim PSF (ZOGY manuscript eq. 14)

Parameters
----------
padSize : `int`
   Override config `padSize` parameter
keepFourier : `bool`
   Return the FFT of the diffim PSF (do not inverse-FFT it)
psf1 : 2D `numpy.array`
    (Optional) Input psf of template, override if already padded
psf2 : 2D `numpy.array`
    (Optional) Input psf of science image, override if already padded

Returns
-------
Pd : 2D `numpy.array`
    The diffim PSF (or FFT of PSF if `keepFourier=True`)

Definition at line 661 of file zogy.py.

661  def computeDiffimPsf(self, padSize=0, keepFourier=False, psf1=None, psf2=None):
662  """Compute the ZOGY diffim PSF (ZOGY manuscript eq. 14)
663 
664  Parameters
665  ----------
666  padSize : `int`
667  Override config `padSize` parameter
668  keepFourier : `bool`
669  Return the FFT of the diffim PSF (do not inverse-FFT it)
670  psf1 : 2D `numpy.array`
671  (Optional) Input psf of template, override if already padded
672  psf2 : 2D `numpy.array`
673  (Optional) Input psf of science image, override if already padded
674 
675  Returns
676  -------
677  Pd : 2D `numpy.array`
678  The diffim PSF (or FFT of PSF if `keepFourier=True`)
679  """
680  preqs = self.computePrereqs(psf1=psf1, psf2=psf2, padSize=padSize)
681 
682  Pd_hat_numerator = (self.Fr * self.Fn * preqs.Pr_hat * preqs.Pn_hat)
683  Pd_hat = Pd_hat_numerator / (preqs.Fd * preqs.denom)
684 
685  if keepFourier:
686  return Pd_hat
687 
688  Pd = np.fft.ifft2(Pd_hat)
689  Pd = np.fft.ifftshift(Pd).real
690 
691  return Pd
692 

◆ computePrereqs()

def lsst.ip.diffim.zogy.ZogyTask.computePrereqs (   self,
  psf1 = None,
  psf2 = None,
  padSize = 0 
)
inherited
Compute standard ZOGY quantities used by (nearly) all methods.

Many of the ZOGY calculations require similar quantities, including
FFTs of the PSFs, and the "denominator" term (e.g. in eq. 13 of
ZOGY manuscript (2016). This function consolidates many of those
operations.

Parameters
----------
psf1 : 2D `numpy.array`
    (Optional) Input psf of template, override if already padded
psf2 : 2D `numpy.array`
    (Optional) Input psf of science image, override if already padded
padSize : `int`, optional
    Number of pixels to pad the image on each side with zeroes.

Returns
-------
A `lsst.pipe.base.Struct` containing:
- Pr : 2D `numpy.array`, the (possibly zero-padded) template PSF
- Pn : 2D `numpy.array`, the (possibly zero-padded) science PSF
- Pr_hat : 2D `numpy.array`, the FFT of `Pr`
- Pn_hat : 2D `numpy.array`, the FFT of `Pn`
- denom : 2D `numpy.array`, the denominator of equation (13) in ZOGY (2016) manuscript
- Fd : `float`, the relative flux scaling factor between science and template

Definition at line 355 of file zogy.py.

355  def computePrereqs(self, psf1=None, psf2=None, padSize=0):
356  """Compute standard ZOGY quantities used by (nearly) all methods.
357 
358  Many of the ZOGY calculations require similar quantities, including
359  FFTs of the PSFs, and the "denominator" term (e.g. in eq. 13 of
360  ZOGY manuscript (2016). This function consolidates many of those
361  operations.
362 
363  Parameters
364  ----------
365  psf1 : 2D `numpy.array`
366  (Optional) Input psf of template, override if already padded
367  psf2 : 2D `numpy.array`
368  (Optional) Input psf of science image, override if already padded
369  padSize : `int`, optional
370  Number of pixels to pad the image on each side with zeroes.
371 
372  Returns
373  -------
374  A `lsst.pipe.base.Struct` containing:
375  - Pr : 2D `numpy.array`, the (possibly zero-padded) template PSF
376  - Pn : 2D `numpy.array`, the (possibly zero-padded) science PSF
377  - Pr_hat : 2D `numpy.array`, the FFT of `Pr`
378  - Pn_hat : 2D `numpy.array`, the FFT of `Pn`
379  - denom : 2D `numpy.array`, the denominator of equation (13) in ZOGY (2016) manuscript
380  - Fd : `float`, the relative flux scaling factor between science and template
381  """
382  psf1 = self.im1_psf if psf1 is None else psf1
383  psf2 = self.im2_psf if psf2 is None else psf2
384  padSize = self.padSize if padSize is None else padSize
385  Pr, Pn = psf1, psf2
386  if padSize > 0:
387  Pr = ZogyTask._padPsfToSize(psf1, (psf1.shape[0] + padSize, psf1.shape[1] + padSize))
388  Pn = ZogyTask._padPsfToSize(psf2, (psf2.shape[0] + padSize, psf2.shape[1] + padSize))
389  # Make sure there are no div-by-zeros
390  psf1[np.abs(psf1) <= MIN_KERNEL] = MIN_KERNEL
391  psf2[np.abs(psf2) <= MIN_KERNEL] = MIN_KERNEL
392 
393  sigR, sigN = self.sig1, self.sig2
394  Pr_hat = np.fft.fft2(Pr)
395  Pr_hat2 = np.conj(Pr_hat) * Pr_hat
396  Pn_hat = np.fft.fft2(Pn)
397  Pn_hat2 = np.conj(Pn_hat) * Pn_hat
398  denom = np.sqrt((sigN**2 * self.Fr**2 * Pr_hat2) + (sigR**2 * self.Fn**2 * Pn_hat2))
399  Fd = self.Fr * self.Fn / np.sqrt(sigN**2 * self.Fr**2 + sigR**2 * self.Fn**2)
400 
401  res = pipeBase.Struct(
402  Pr=Pr, Pn=Pn, Pr_hat=Pr_hat, Pn_hat=Pn_hat, denom=denom, Fd=Fd
403  )
404  return res
405 

◆ computeScorr()

def lsst.ip.diffim.zogy.ZogyTask.computeScorr (   self,
  xVarAst = 0.,
  yVarAst = 0.,
  inImageSpace = None,
  padSize = 0,
**  kwargs 
)
inherited
Wrapper method to compute ZOGY corrected likelihood image, optimal for
source detection

This method should be used as the public interface for
computing the ZOGY S_corr.

Parameters
----------
xVarAst, yVarAst : `float`
   estimated astrometric noise (variance of astrometric registration errors)
inImageSpace : `bool`
   Override config `inImageSpace` parameter
padSize : `int`
   Override config `padSize` parameter

Returns
-------
S : `lsst.afw.image.Exposure`
    The likelihood exposure S (eq. 12 of ZOGY (2016)),
    including corrected variance, masks, and PSF

Definition at line 890 of file zogy.py.

890  def computeScorr(self, xVarAst=0., yVarAst=0., inImageSpace=None, padSize=0, **kwargs):
891  """Wrapper method to compute ZOGY corrected likelihood image, optimal for
892  source detection
893 
894  This method should be used as the public interface for
895  computing the ZOGY S_corr.
896 
897  Parameters
898  ----------
899  xVarAst, yVarAst : `float`
900  estimated astrometric noise (variance of astrometric registration errors)
901  inImageSpace : `bool`
902  Override config `inImageSpace` parameter
903  padSize : `int`
904  Override config `padSize` parameter
905 
906  Returns
907  -------
908  S : `lsst.afw.image.Exposure`
909  The likelihood exposure S (eq. 12 of ZOGY (2016)),
910  including corrected variance, masks, and PSF
911  """
912  inImageSpace = self.config.inImageSpace if inImageSpace is None else inImageSpace
913  if inImageSpace:
914  res = self.computeScorrImageSpace(xVarAst=xVarAst, yVarAst=yVarAst, padSize=padSize)
915  S = res.S
916  else:
917  res = self.computeScorrFourierSpace(xVarAst=xVarAst, yVarAst=yVarAst)
918 
919  S = self.science.clone()
920  S.getMaskedImage().getImage().getArray()[:, :] = res.S
921  S.getMaskedImage().getVariance().getArray()[:, :] = res.S_var
922  S = self._setNewPsf(S, res.Dpsf)
923 
924  return pipeBase.Struct(S=S)
925 
926 

◆ computeScorrFourierSpace()

def lsst.ip.diffim.zogy.ZogyTask.computeScorrFourierSpace (   self,
  xVarAst = 0.,
  yVarAst = 0.,
**  kwargs 
)
inherited
Compute corrected likelihood image, optimal for source detection

Compute ZOGY S_corr image. This image can be thresholded for
detection without optimal filtering, and the variance image is
corrected to account for astrometric noise (errors in
astrometric registration whether systematic or due to effects
such as DCR). The calculations here are all performed in
Fourier space, as proscribed in ZOGY (2016).

Parameters
----------
xVarAst, yVarAst : `float`
   estimated astrometric noise (variance of astrometric registration errors)

Returns
-------
result : `lsst.pipe.base.Struct`
    Result struct with components:

    - ``S`` : `numpy.array`, the likelihood image S (eq. 12 of ZOGY (2016))
    - ``S_var`` : the corrected variance image (denominator of eq. 25 of ZOGY (2016))
    - ``Dpsf`` : the PSF of the diffim D, likely never to be used.

Definition at line 748 of file zogy.py.

748  def computeScorrFourierSpace(self, xVarAst=0., yVarAst=0., **kwargs):
749  """Compute corrected likelihood image, optimal for source detection
750 
751  Compute ZOGY S_corr image. This image can be thresholded for
752  detection without optimal filtering, and the variance image is
753  corrected to account for astrometric noise (errors in
754  astrometric registration whether systematic or due to effects
755  such as DCR). The calculations here are all performed in
756  Fourier space, as proscribed in ZOGY (2016).
757 
758  Parameters
759  ----------
760  xVarAst, yVarAst : `float`
761  estimated astrometric noise (variance of astrometric registration errors)
762 
763  Returns
764  -------
765  result : `lsst.pipe.base.Struct`
766  Result struct with components:
767 
768  - ``S`` : `numpy.array`, the likelihood image S (eq. 12 of ZOGY (2016))
769  - ``S_var`` : the corrected variance image (denominator of eq. 25 of ZOGY (2016))
770  - ``Dpsf`` : the PSF of the diffim D, likely never to be used.
771  """
772  # Some masked regions are NaN or infinite!, and FFTs no likey.
773  def fix_nans(im):
774  """Replace any NaNs or Infs with the mean of the image."""
775  isbad = ~np.isfinite(im)
776  if np.any(isbad):
777  im[isbad] = np.nan
778  im[isbad] = np.nanmean(im)
779  return im
780 
781  self.im1 = fix_nans(self.im1)
782  self.im2 = fix_nans(self.im2)
783  self.im1_var = fix_nans(self.im1_var)
784  self.im2_var = fix_nans(self.im2_var)
785 
786  # Do all in fourier space (needs image-sized PSFs)
787  psf1 = ZogyTask._padPsfToSize(self.im1_psf, self.im1.shape)
788  psf2 = ZogyTask._padPsfToSize(self.im2_psf, self.im2.shape)
789 
790  preqs = self.computePrereqs(psf1, psf2, padSize=0) # already padded the PSFs
791 
792  # Compute D_hat here (don't need D then, for speed)
793  R_hat = np.fft.fft2(self.im1)
794  N_hat = np.fft.fft2(self.im2)
795  D_hat = self.Fr * preqs.Pr_hat * N_hat - self.Fn * preqs.Pn_hat * R_hat
796  D_hat /= preqs.denom
797 
798  Pd_hat = self.computeDiffimPsf(padSize=0, keepFourier=True, psf1=psf1, psf2=psf2)
799  Pd_bar = np.conj(Pd_hat)
800  S = np.fft.ifft2(D_hat * Pd_bar)
801 
802  # Adjust the variance planes of the two images to contribute to the final detection
803  # (eq's 26-29).
804  Pn_hat2 = np.conj(preqs.Pn_hat) * preqs.Pn_hat
805  Kr_hat = self.Fr * self.Fn**2. * np.conj(preqs.Pr_hat) * Pn_hat2 / preqs.denom**2.
806  Pr_hat2 = np.conj(preqs.Pr_hat) * preqs.Pr_hat
807  Kn_hat = self.Fn * self.Fr**2. * np.conj(preqs.Pn_hat) * Pr_hat2 / preqs.denom**2.
808 
809  Kr_hat2 = np.fft.fft2(np.fft.ifft2(Kr_hat)**2.)
810  Kn_hat2 = np.fft.fft2(np.fft.ifft2(Kn_hat)**2.)
811  var1c_hat = Kr_hat2 * np.fft.fft2(self.im1_var)
812  var2c_hat = Kn_hat2 * np.fft.fft2(self.im2_var)
813 
814  # Do the astrometric variance correction
815  fGradR, fGradN = self._computeVarAstGradients(xVarAst, yVarAst, inImageSpace=False,
816  R_hat=R_hat, Kr_hat=Kr_hat,
817  N_hat=N_hat, Kn_hat=Kn_hat)
818 
819  S_var = np.sqrt(np.fft.ifftshift(np.fft.ifft2(var1c_hat + var2c_hat)) + fGradR + fGradN)
820  S_var *= preqs.Fd
821 
822  S = np.fft.ifftshift(np.fft.ifft2(Kn_hat * N_hat - Kr_hat * R_hat))
823  S *= preqs.Fd
824 
825  Pd = self.computeDiffimPsf(padSize=0)
826  return pipeBase.Struct(S=S.real, S_var=S_var.real, Dpsf=Pd)
827 

◆ computeScorrImageSpace()

def lsst.ip.diffim.zogy.ZogyTask.computeScorrImageSpace (   self,
  xVarAst = 0.,
  yVarAst = 0.,
  padSize = None,
**  kwargs 
)
inherited
Compute corrected likelihood image, optimal for source detection

Compute ZOGY S_corr image. This image can be thresholded for
detection without optimal filtering, and the variance image is
corrected to account for astrometric noise (errors in
astrometric registration whether systematic or due to effects
such as DCR). The calculations here are all performed in
Real (image) space.

Parameters
----------
xVarAst, yVarAst : `float`
   estimated astrometric noise (variance of astrometric registration errors)

Returns
-------
A `lsst.pipe.base.Struct` containing:
- S : `lsst.afw.image.Exposure`, the likelihood exposure S (eq. 12 of ZOGY (2016)),
    including corrected variance, masks, and PSF
- D : `lsst.afw.image.Exposure`, the proper image difference, including correct
    variance, masks, and PSF

Definition at line 828 of file zogy.py.

828  def computeScorrImageSpace(self, xVarAst=0., yVarAst=0., padSize=None, **kwargs):
829  """Compute corrected likelihood image, optimal for source detection
830 
831  Compute ZOGY S_corr image. This image can be thresholded for
832  detection without optimal filtering, and the variance image is
833  corrected to account for astrometric noise (errors in
834  astrometric registration whether systematic or due to effects
835  such as DCR). The calculations here are all performed in
836  Real (image) space.
837 
838  Parameters
839  ----------
840  xVarAst, yVarAst : `float`
841  estimated astrometric noise (variance of astrometric registration errors)
842 
843  Returns
844  -------
845  A `lsst.pipe.base.Struct` containing:
846  - S : `lsst.afw.image.Exposure`, the likelihood exposure S (eq. 12 of ZOGY (2016)),
847  including corrected variance, masks, and PSF
848  - D : `lsst.afw.image.Exposure`, the proper image difference, including correct
849  variance, masks, and PSF
850  """
851  # Do convolutions in image space
852  preqs = self.computePrereqs(padSize=0)
853 
854  padSize = self.padSize if padSize is None else padSize
855  D = self.computeDiffimImageSpace(padSize=padSize).D
856  Pd = self.computeDiffimPsf()
857  D = self._setNewPsf(D, Pd)
858  Pd_bar = np.fliplr(np.flipud(Pd))
859  S, _ = self._doConvolve(D, Pd_bar)
860  tmp = S.getMaskedImage()
861  tmp *= preqs.Fd
862 
863  # Adjust the variance planes of the two images to contribute to the final detection
864  # (eq's 26-29).
865  Pn_hat2 = np.conj(preqs.Pn_hat) * preqs.Pn_hat
866  Kr_hat = self.Fr * self.Fn**2. * np.conj(preqs.Pr_hat) * Pn_hat2 / preqs.denom**2.
867  Pr_hat2 = np.conj(preqs.Pr_hat) * preqs.Pr_hat
868  Kn_hat = self.Fn * self.Fr**2. * np.conj(preqs.Pn_hat) * Pr_hat2 / preqs.denom**2.
869 
870  Kr = np.fft.ifft2(Kr_hat).real
871  Kr = np.roll(np.roll(Kr, -1, 0), -1, 1)
872  Kn = np.fft.ifft2(Kn_hat).real
873  Kn = np.roll(np.roll(Kn, -1, 0), -1, 1)
874  var1c, _ = self._doConvolve(self.template.getMaskedImage().getVariance(), Kr**2.)
875  var2c, _ = self._doConvolve(self.science.getMaskedImage().getVariance(), Kn**2.)
876 
877  # Do the astrometric variance correction
878  fGradR, fGradN = self._computeVarAstGradients(xVarAst, yVarAst, inImageSpace=True,
879  Kr=Kr, Kn=Kn)
880 
881  Smi = S.getMaskedImage()
882  Smi *= preqs.Fd
883  S_var = np.sqrt(var1c.getArray() + var2c.getArray() + fGradR + fGradN)
884  S.getMaskedImage().getVariance().getArray()[:, :] = S_var
885  S = self._setNewPsf(S, Pd)
886 
887  # also return diffim since it was calculated and might be desired
888  return pipeBase.Struct(S=S, D=D)
889 

◆ run() [1/2]

def lsst.ip.diffim.imageMapReduce.ImageMapper.run (   self,
  subExposure,
  expandedSubExposure,
  fullBBox,
**  kwargs 
)
inherited
Perform operation on `subExposure`.

To be implemented by subclasses. See class docstring for more
details. This method is given the `subExposure` which
is to be operated upon, and an `expandedSubExposure` which
will contain `subExposure` with additional surrounding
pixels. This allows for, for example, convolutions (which
should be performed on `expandedSubExposure`), to prevent the
returned sub-exposure from containing invalid pixels.

This method may return a new, processed sub-exposure which can
be be "stitched" back into a new resulting larger exposure
(depending on the paired, configured `ImageReducer`);
otherwise if it does not return an lsst.afw.image.Exposure, then the
`ImageReducer.config.mapper.reduceOperation`
should be set to 'none' and the result will be propagated
as-is.

Parameters
----------
subExposure : `lsst.afw.image.Exposure`
    the sub-exposure upon which to operate
expandedSubExposure : `lsst.afw.image.Exposure`
    the expanded sub-exposure upon which to operate
fullBBox : `lsst.geom.Box2I`
    the bounding box of the original exposure
kwargs :
    additional keyword arguments propagated from
    `ImageMapReduceTask.run`.

Returns
-------
result : `lsst.pipe.base.Struct`
    A structure containing the result of the `subExposure` processing,
    which may itself be of any type. See above for details. If it is an
    `lsst.afw.image.Exposure` (processed sub-exposure), then the name in
    the Struct should be 'subExposure'. This is implemented here as a
    pass-through example only.

Definition at line 109 of file imageMapReduce.py.

109  def run(self, subExposure, expandedSubExposure, fullBBox, **kwargs):
110  """Perform operation on `subExposure`.
111 
112  To be implemented by subclasses. See class docstring for more
113  details. This method is given the `subExposure` which
114  is to be operated upon, and an `expandedSubExposure` which
115  will contain `subExposure` with additional surrounding
116  pixels. This allows for, for example, convolutions (which
117  should be performed on `expandedSubExposure`), to prevent the
118  returned sub-exposure from containing invalid pixels.
119 
120  This method may return a new, processed sub-exposure which can
121  be be "stitched" back into a new resulting larger exposure
122  (depending on the paired, configured `ImageReducer`);
123  otherwise if it does not return an lsst.afw.image.Exposure, then the
124  `ImageReducer.config.mapper.reduceOperation`
125  should be set to 'none' and the result will be propagated
126  as-is.
127 
128  Parameters
129  ----------
130  subExposure : `lsst.afw.image.Exposure`
131  the sub-exposure upon which to operate
132  expandedSubExposure : `lsst.afw.image.Exposure`
133  the expanded sub-exposure upon which to operate
134  fullBBox : `lsst.geom.Box2I`
135  the bounding box of the original exposure
136  kwargs :
137  additional keyword arguments propagated from
138  `ImageMapReduceTask.run`.
139 
140  Returns
141  -------
142  result : `lsst.pipe.base.Struct`
143  A structure containing the result of the `subExposure` processing,
144  which may itself be of any type. See above for details. If it is an
145  `lsst.afw.image.Exposure` (processed sub-exposure), then the name in
146  the Struct should be 'subExposure'. This is implemented here as a
147  pass-through example only.
148  """
149  return pipeBase.Struct(subExposure=subExposure)
150 
151 

◆ run() [2/2]

def lsst.ip.diffim.zogy.ZogyMapper.run (   self,
  subExposure,
  expandedSubExposure,
  fullBBox,
  template,
**  kwargs 
)
Perform ZOGY proper image subtraction on sub-images

This method performs ZOGY proper image subtraction on
`subExposure` using local measures for image variances and
PSF. `subExposure` is a sub-exposure of the science image. It
also requires the corresponding sub-exposures of the template
(`template`). The operations are actually performed on
`expandedSubExposure` to allow for invalid edge pixels arising
from convolutions, which are then removed.

Parameters
----------
subExposure : `lsst.afw.image.Exposure`
    the sub-exposure of the diffim
expandedSubExposure : `lsst.afw.image.Exposure`
    the expanded sub-exposure upon which to operate
fullBBox : `lsst.geom.Box2I`
    the bounding box of the original exposure
template : `lsst.afw.image.Exposure`
    the template exposure, from which a corresponding sub-exposure
    is extracted
**kwargs
    additional keyword arguments propagated from
    `ImageMapReduceTask.run`. These include:

    ``doScorr`` : `bool`
Compute and return the corrected likelihood image S_corr
rather than the proper image difference
    ``inImageSpace`` : `bool`
Perform all convolutions in real (image) space rather than
in Fourier space. This option currently leads to artifacts
when using real (measured and noisy) PSFs, thus it is set
to `False` by default.
These kwargs may also include arguments to be propagated to
`ZogyTask.computeDiffim` and `ZogyTask.computeScorr`.

Returns
-------
result : `lsst.pipe.base.Struct`
    Result struct with components:

``subExposure``: Either the subExposure of the proper image difference ``D``,
    or (if `doScorr==True`) the corrected likelihood exposure ``S``.

Notes
-----
This `run` method accepts parameters identical to those of
`ImageMapper.run`, since it is called from the
`ImageMapperTask`. See that class for more information.

Definition at line 937 of file zogy.py.

937  def run(self, subExposure, expandedSubExposure, fullBBox, template,
938  **kwargs):
939  """Perform ZOGY proper image subtraction on sub-images
940 
941  This method performs ZOGY proper image subtraction on
942  `subExposure` using local measures for image variances and
943  PSF. `subExposure` is a sub-exposure of the science image. It
944  also requires the corresponding sub-exposures of the template
945  (`template`). The operations are actually performed on
946  `expandedSubExposure` to allow for invalid edge pixels arising
947  from convolutions, which are then removed.
948 
949  Parameters
950  ----------
951  subExposure : `lsst.afw.image.Exposure`
952  the sub-exposure of the diffim
953  expandedSubExposure : `lsst.afw.image.Exposure`
954  the expanded sub-exposure upon which to operate
955  fullBBox : `lsst.geom.Box2I`
956  the bounding box of the original exposure
957  template : `lsst.afw.image.Exposure`
958  the template exposure, from which a corresponding sub-exposure
959  is extracted
960  **kwargs
961  additional keyword arguments propagated from
962  `ImageMapReduceTask.run`. These include:
963 
964  ``doScorr`` : `bool`
965  Compute and return the corrected likelihood image S_corr
966  rather than the proper image difference
967  ``inImageSpace`` : `bool`
968  Perform all convolutions in real (image) space rather than
969  in Fourier space. This option currently leads to artifacts
970  when using real (measured and noisy) PSFs, thus it is set
971  to `False` by default.
972  These kwargs may also include arguments to be propagated to
973  `ZogyTask.computeDiffim` and `ZogyTask.computeScorr`.
974 
975  Returns
976  -------
977  result : `lsst.pipe.base.Struct`
978  Result struct with components:
979 
980  ``subExposure``: Either the subExposure of the proper image difference ``D``,
981  or (if `doScorr==True`) the corrected likelihood exposure ``S``.
982 
983  Notes
984  -----
985  This `run` method accepts parameters identical to those of
986  `ImageMapper.run`, since it is called from the
987  `ImageMapperTask`. See that class for more information.
988  """
989  bbox = subExposure.getBBox()
990  center = ((bbox.getBeginX() + bbox.getEndX()) // 2., (bbox.getBeginY() + bbox.getEndY()) // 2.)
991  center = geom.Point2D(center[0], center[1])
992 
993  imageSpace = kwargs.pop('inImageSpace', False)
994  doScorr = kwargs.pop('doScorr', False)
995  sigmas = kwargs.pop('sigmas', None)
996  padSize = kwargs.pop('padSize', 7)
997 
998  # Psf and image for science img (index 2)
999  subExp2 = expandedSubExposure
1000 
1001  # Psf and image for template img (index 1)
1002  subExp1 = template.Factory(template, expandedSubExposure.getBBox())
1003 
1004  if sigmas is None:
1005  sig1 = sig2 = None
1006  else:
1007  # for testing, can use the input sigma (e.g., global value for entire exposure)
1008  sig1, sig2 = sigmas[0], sigmas[1]
1009 
1010  def _makePsfSquare(psf):
1011  # Sometimes CoaddPsf does this. Make it square.
1012  if psf.shape[0] < psf.shape[1]:
1013  psf = np.pad(psf, ((0, psf.shape[1] - psf.shape[0]), (0, 0)), mode='constant',
1014  constant_values=0.)
1015  elif psf.shape[0] > psf.shape[1]:
1016  psf = np.pad(psf, ((0, 0), (0, psf.shape[0] - psf.shape[1])), mode='constant',
1017  constant_values=0.)
1018  return psf
1019 
1020  psf2 = subExp2.getPsf().computeKernelImage(center).getArray()
1021  psf2 = _makePsfSquare(psf2)
1022 
1023  psf1 = template.getPsf().computeKernelImage(center).getArray()
1024  psf1 = _makePsfSquare(psf1)
1025 
1026  # from diffimTests.diffimTests ...
1027  if subExp1.getDimensions()[0] < psf1.shape[0] or subExp1.getDimensions()[1] < psf1.shape[1]:
1028  return pipeBase.Struct(subExposure=subExposure)
1029 
1030  def _filterPsf(psf):
1031  """Filter a noisy Psf to remove artifacts. Subject of future research."""
1032  # only necessary if it's from a measured psf and PsfEx seems to always make PSFs of size 41x41
1033  if psf.shape[0] == 41: # its from a measured psf
1034  psf = psf.copy()
1035  psf[psf < 0] = 0
1036  psf[0:10, :] = psf[:, 0:10] = psf[31:41, :] = psf[:, 31:41] = 0
1037  psf /= psf.sum()
1038 
1039  return psf
1040 
1041  psf1b = psf2b = None
1042  if self.config.doFilterPsfs: # default True
1043  # Note this *really* helps for measured psfs.
1044  psf1b = _filterPsf(psf1)
1045  psf2b = _filterPsf(psf2)
1046 
1047  config = ZogyConfig()
1048  if imageSpace is True:
1049  config.inImageSpace = imageSpace
1050  config.padSize = padSize # Don't need padding if doing all in fourier space
1051  task = ZogyTask(templateExposure=subExp1, scienceExposure=subExp2,
1052  sig1=sig1, sig2=sig2, psf1=psf1b, psf2=psf2b, config=config)
1053 
1054  if not doScorr:
1055  res = task.computeDiffim(**kwargs)
1056  D = res.D
1057  else:
1058  res = task.computeScorr(**kwargs)
1059  D = res.S
1060 
1061  outExp = D.Factory(D, subExposure.getBBox())
1062  out = pipeBase.Struct(subExposure=outExp)
1063  return out
1064 
1065 

◆ setup()

def lsst.ip.diffim.zogy.ZogyTask.setup (   self,
  templateExposure = None,
  scienceExposure = None,
  sig1 = None,
  sig2 = None,
  psf1 = None,
  psf2 = None,
  correctBackground = False,
args,
**  kwargs 
)
inherited
Set up the ZOGY task.

Parameters
----------
templateExposure : `lsst.afw.image.Exposure`
    Template exposure ("Reference image" in ZOGY (2016)).
scienceExposure : `lsst.afw.image.Exposure`
    Science exposure ("New image" in ZOGY (2016)). Must have already been
    registered and photometrically matched to template.
sig1 : `float`
    (Optional) sqrt(variance) of `templateExposure`. If `None`, it is
    computed from the sqrt(mean) of the `templateExposure` variance image.
sig2 : `float`
    (Optional) sqrt(variance) of `scienceExposure`. If `None`, it is
    computed from the sqrt(mean) of the `scienceExposure` variance image.
psf1 : 2D `numpy.array`
    (Optional) 2D array containing the PSF image for the template. If
    `None`, it is extracted from the PSF taken at the center of `templateExposure`.
psf2 : 2D `numpy.array`
    (Optional) 2D array containing the PSF image for the science img. If
    `None`, it is extracted from the PSF taken at the center of `scienceExposure`.
correctBackground : `bool`
    (Optional) subtract sigma-clipped mean of exposures. Zogy doesn't correct
    nonzero backgrounds (unlike AL) so subtract them here.
*args
    additional arguments to be passed to
    `lsst.pipe.base.Task`
**kwargs
    additional keyword arguments to be passed to
    `lsst.pipe.base.Task`

Definition at line 176 of file zogy.py.

176  def setup(self, templateExposure=None, scienceExposure=None, sig1=None, sig2=None,
177  psf1=None, psf2=None, correctBackground=False, *args, **kwargs):
178  """Set up the ZOGY task.
179 
180  Parameters
181  ----------
182  templateExposure : `lsst.afw.image.Exposure`
183  Template exposure ("Reference image" in ZOGY (2016)).
184  scienceExposure : `lsst.afw.image.Exposure`
185  Science exposure ("New image" in ZOGY (2016)). Must have already been
186  registered and photometrically matched to template.
187  sig1 : `float`
188  (Optional) sqrt(variance) of `templateExposure`. If `None`, it is
189  computed from the sqrt(mean) of the `templateExposure` variance image.
190  sig2 : `float`
191  (Optional) sqrt(variance) of `scienceExposure`. If `None`, it is
192  computed from the sqrt(mean) of the `scienceExposure` variance image.
193  psf1 : 2D `numpy.array`
194  (Optional) 2D array containing the PSF image for the template. If
195  `None`, it is extracted from the PSF taken at the center of `templateExposure`.
196  psf2 : 2D `numpy.array`
197  (Optional) 2D array containing the PSF image for the science img. If
198  `None`, it is extracted from the PSF taken at the center of `scienceExposure`.
199  correctBackground : `bool`
200  (Optional) subtract sigma-clipped mean of exposures. Zogy doesn't correct
201  nonzero backgrounds (unlike AL) so subtract them here.
202  *args
203  additional arguments to be passed to
204  `lsst.pipe.base.Task`
205  **kwargs
206  additional keyword arguments to be passed to
207  `lsst.pipe.base.Task`
208  """
209  if self.template is None and templateExposure is None:
210  return
211  if self.science is None and scienceExposure is None:
212  return
213 
214  self.template = templateExposure
215  self.science = scienceExposure
216 
217  self.statsControl = afwMath.StatisticsControl()
218  self.statsControl.setNumSigmaClip(3.)
219  self.statsControl.setNumIter(3)
220  self.statsControl.setAndMask(afwImage.Mask.getPlaneBitMask(
221  self.config.ignoreMaskPlanes))
222 
223  self.im1 = self.template.getMaskedImage().getImage().getArray()
224  self.im2 = self.science.getMaskedImage().getImage().getArray()
225  self.im1_var = self.template.getMaskedImage().getVariance().getArray()
226  self.im2_var = self.science.getMaskedImage().getVariance().getArray()
227 
228  def selectPsf(psf, exposure):
229  if psf is not None:
230  return psf
231  else:
232  bbox1 = self.template.getBBox()
233  xcen = (bbox1.getBeginX() + bbox1.getEndX()) / 2.
234  ycen = (bbox1.getBeginY() + bbox1.getEndY()) / 2.
235  return exposure.getPsf().computeKernelImage(geom.Point2D(xcen, ycen)).getArray()
236 
237  self.im1_psf = selectPsf(psf1, self.template)
238  self.im2_psf = selectPsf(psf2, self.science)
239 
240  # Make sure PSFs are the same size. Messy, but should work for all cases.
241  psf1 = self.im1_psf
242  psf2 = self.im2_psf
243  pShape1 = psf1.shape
244  pShape2 = psf2.shape
245  if (pShape1[0] < pShape2[0]):
246  psf1 = np.pad(psf1, ((0, pShape2[0] - pShape1[0]), (0, 0)), mode='constant', constant_values=0.)
247  elif (pShape2[0] < pShape1[0]):
248  psf2 = np.pad(psf2, ((0, pShape1[0] - pShape2[0]), (0, 0)), mode='constant', constant_values=0.)
249  if (pShape1[1] < pShape2[1]):
250  psf1 = np.pad(psf1, ((0, 0), (0, pShape2[1] - pShape1[1])), mode='constant', constant_values=0.)
251  elif (pShape2[1] < pShape1[1]):
252  psf2 = np.pad(psf2, ((0, 0), (0, pShape1[1] - pShape2[1])), mode='constant', constant_values=0.)
253 
254  # PSFs' centers may be offset relative to each other; now fix that!
255  maxLoc1 = np.unravel_index(np.argmax(psf1), psf1.shape)
256  maxLoc2 = np.unravel_index(np.argmax(psf2), psf2.shape)
257  # *Very* rarely happens but if they're off by >1 pixel, do it more than once.
258  while (maxLoc1[0] != maxLoc2[0]) or (maxLoc1[1] != maxLoc2[1]):
259  if maxLoc1[0] > maxLoc2[0]:
260  psf2[1:, :] = psf2[:-1, :]
261  elif maxLoc1[0] < maxLoc2[0]:
262  psf1[1:, :] = psf1[:-1, :]
263  if maxLoc1[1] > maxLoc2[1]:
264  psf2[:, 1:] = psf2[:, :-1]
265  elif maxLoc1[1] < maxLoc2[1]:
266  psf1[:, 1:] = psf1[:, :-1]
267  maxLoc1 = np.unravel_index(np.argmax(psf1), psf1.shape)
268  maxLoc2 = np.unravel_index(np.argmax(psf2), psf2.shape)
269 
270  # Make sure there are no div-by-zeros
271  psf1[psf1 < MIN_KERNEL] = MIN_KERNEL
272  psf2[psf2 < MIN_KERNEL] = MIN_KERNEL
273 
274  self.im1_psf = psf1
275  self.im2_psf = psf2
276 
277  self.sig1 = np.sqrt(self._computeVarianceMean(self.template)) if sig1 is None else sig1
278  self.sig2 = np.sqrt(self._computeVarianceMean(self.science)) if sig2 is None else sig2
279  # if sig1 or sig2 are NaN, then the entire region being Zogy-ed is masked.
280  # Don't worry about it - the result will be masked but avoid warning messages.
281  if np.isnan(self.sig1) or self.sig1 == 0:
282  self.sig1 = 1.
283  if np.isnan(self.sig2) or self.sig2 == 0:
284  self.sig2 = 1.
285 
286  # Zogy doesn't correct nonzero backgrounds (unlike AL) so subtract them here.
287  if correctBackground:
288  def _subtractImageMean(exposure):
289  """Compute the sigma-clipped mean of the image of `exposure`."""
290  mi = exposure.getMaskedImage()
291  statObj = afwMath.makeStatistics(mi.getImage(), mi.getMask(),
292  afwMath.MEANCLIP, self.statsControl)
293  mean = statObj.getValue(afwMath.MEANCLIP)
294  if not np.isnan(mean):
295  mi -= mean
296 
297  _subtractImageMean(self.template)
298  _subtractImageMean(self.science)
299 
300  # Define the normalization of each image from the config
301  self.Fr = self.config.templateFluxScaling # default is 1
302  self.Fn = self.config.scienceFluxScaling # default is 1
303  # If 'scaleByCalibration' is True then these norms are overwritten
304  if self.config.scaleByCalibration:
305  calib_template = self.template.getPhotoCalib()
306  calib_science = self.science.getPhotoCalib()
307  if calib_template is None:
308  self.log.warning("No calibration information available for template image.")
309  if calib_science is None:
310  self.log.warning("No calibration information available for science image.")
311  if calib_template is None or calib_science is None:
312  self.log.warning("Due to lack of calibration information, "
313  "reverting to templateFluxScaling and scienceFluxScaling.")
314  else:
315  self.Fr = 1/calib_template.getCalibrationMean()
316  self.Fn = 1/calib_science.getCalibrationMean()
317 
318  self.log.info("Setting template image scaling to Fr=%f" % self.Fr)
319  self.log.info("Setting science image scaling to Fn=%f" % self.Fn)
320 
321  self.padSize = self.config.padSize # default is 7
322 

Member Data Documentation

◆ ConfigClass

lsst.ip.diffim.zogy.ZogyMapper.ConfigClass = ZogyConfig
static

Definition at line 931 of file zogy.py.

◆ Fn

lsst.ip.diffim.zogy.ZogyTask.Fn
inherited

Definition at line 301 of file zogy.py.

◆ Fr

lsst.ip.diffim.zogy.ZogyTask.Fr
inherited

Definition at line 300 of file zogy.py.

◆ im1

lsst.ip.diffim.zogy.ZogyTask.im1
inherited

Definition at line 222 of file zogy.py.

◆ im1_psf

lsst.ip.diffim.zogy.ZogyTask.im1_psf
inherited

Definition at line 236 of file zogy.py.

◆ im1_var

lsst.ip.diffim.zogy.ZogyTask.im1_var
inherited

Definition at line 224 of file zogy.py.

◆ im2

lsst.ip.diffim.zogy.ZogyTask.im2
inherited

Definition at line 223 of file zogy.py.

◆ im2_psf

lsst.ip.diffim.zogy.ZogyTask.im2_psf
inherited

Definition at line 237 of file zogy.py.

◆ im2_var

lsst.ip.diffim.zogy.ZogyTask.im2_var
inherited

Definition at line 225 of file zogy.py.

◆ padSize

lsst.ip.diffim.zogy.ZogyTask.padSize
inherited

Definition at line 320 of file zogy.py.

◆ science

lsst.ip.diffim.zogy.ZogyTask.science
inherited

Definition at line 171 of file zogy.py.

◆ sig1

lsst.ip.diffim.zogy.ZogyTask.sig1
inherited

Definition at line 276 of file zogy.py.

◆ sig2

lsst.ip.diffim.zogy.ZogyTask.sig2
inherited

Definition at line 277 of file zogy.py.

◆ statsControl

lsst.ip.diffim.zogy.ZogyTask.statsControl
inherited

Definition at line 216 of file zogy.py.

◆ template

lsst.ip.diffim.zogy.ZogyTask.template
inherited

Definition at line 171 of file zogy.py.


The documentation for this class was generated from the following file:
lsst::log.log.logContinued.info
def info(fmt, *args)
Definition: logContinued.py:201
lsst::afw::math::makeStatistics
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
Definition: Statistics.h:520
lsst.pipe.tasks.assembleCoadd.run
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)
Definition: assembleCoadd.py:712
lsst::log.log.logContinued.warning
def warning(fmt, *args)
Definition: logContinued.py:209
lsst::afw::math::StatisticsControl
Pass parameters to a Statistics object.
Definition: Statistics.h:93
lsst::geom::Point< double, 2 >
lsst::afw::image.slicing.clone
clone
Definition: slicing.py:257