1 from __future__
import absolute_import, division, print_function
2 from future
import standard_library
3 standard_library.install_aliases()
28 import lsst.afw.image
as afwImage
29 import lsst.afw.geom
as afwGeom
30 import lsst.meas.algorithms
as measAlg
31 import lsst.afw.math
as afwMath
32 import lsst.pex.config
as pexConfig
33 import lsst.pipe.base
as pipeBase
36 from .imageMapReduce
import (ImageMapReduceConfig, ImageMapperSubtask,
37 ImageMapperSubtaskConfig)
39 __all__ = [
"ZogyTask",
"ZogyConfig",
40 "ZogyMapperSubtask",
"ZogyMapReduceConfig"]
43 """Tasks for performing the "Proper image subtraction" algorithm of 44 Zackay, et al. (2016), hereafter simply referred to as 'ZOGY (2016)'. 46 `ZogyTask` contains methods to perform the basic estimation of the 47 ZOGY diffim `D`, its updated PSF, and the variance-normalized 48 likelihood image `S_corr`. We have implemented ZOGY using the 49 proscribed methodology, computing all convolutions in Fourier space, 50 and also variants in which the convolutions are performed in real 51 (image) space. The former is faster and results in fewer artifacts 52 when the PSFs are noisy (i.e., measured, for example, via 53 `PsfEx`). The latter is presumed to be preferred as it can account for 54 masks correctly with fewer "ringing" artifacts from edge effects or 55 saturated stars, but noisy PSFs result in their own smaller 56 artifacts. Removal of these artifacts is a subject of continuing 57 research. Currently, we "pad" the PSFs when performing the 58 subtractions in real space, which reduces, but does not entirely 59 eliminate these artifacts. 61 All methods in `ZogyTask` assume template and science images are 62 already accurately photometrically and astrometrically registered. 64 `ZogyMapperSubtask` is a wrapper which runs `ZogyTask` in the 65 `ImageMapReduce` framework, computing of ZOGY diffim's on small, 66 overlapping sub-images, thereby enabling complete ZOGY diffim's which 67 account for spatially-varying noise and PSFs across the two input 68 exposures. An example of the use of this task is in the `testZogy.py` 74 """Configuration parameters for the ZogyTask 76 inImageSpace = pexConfig.Field(
79 doc=
"""Perform all convolutions in real (image) space rather than Fourier space. 80 Currently if True, this results in artifacts when using real (noisy) PSFs.""" 83 padSize = pexConfig.Field(
86 doc=
"""Number of pixels to pad PSFs to avoid artifacts (when inImageSpace is True)""" 89 templateFluxScaling = pexConfig.Field(
92 doc=
"""Template flux scaling factor (Fr in ZOGY paper)""" 95 scienceFluxScaling = pexConfig.Field(
98 doc=
"""Science flux scaling factor (Fn in ZOGY paper)""" 101 doTrimKernels = pexConfig.Field(
104 doc=
"""Trim kernels for image-space ZOGY. Speeds up convolutions and shrinks artifacts. 105 Subject of future research.""" 108 doFilterPsfs = pexConfig.Field(
111 doc=
"""Filter PSFs for image-space ZOGY. Aids in reducing artifacts. 112 Subject of future research.""" 115 ignoreMaskPlanes = pexConfig.ListField(
117 default=(
"INTRP",
"EDGE",
"DETECTED",
"SAT",
"CR",
"BAD",
"NO_DATA",
"DETECTED_NEGATIVE"),
118 doc=
"""Mask planes to ignore for statistics""" 123 """Task to perform ZOGY proper image subtraction. See module-level documentation for 126 In all methods, im1 is R (reference, or template) and im2 is N (new, or science). 128 ConfigClass = ZogyConfig
129 _DefaultName =
"ip_diffim_Zogy" 131 def __init__(self, templateExposure, scienceExposure, sig1=None, sig2=None,
132 psf1=None, psf2=None, *args, **kwargs):
133 """Create the ZOGY task. 137 templateExposure : lsst.afw.image.Exposure 138 Template exposure ("Reference image" in ZOGY (2016)). 139 scienceExposure : lsst.afw.image.Exposure 140 Science exposure ("New image" in ZOGY (2016)). Must have already been 141 registered and photmetrically matched to template. 143 (Optional) sqrt(variance) of `templateExposure`. If `None`, it is 144 computed from the sqrt(mean) of the `templateExposure` variance image. 146 (Optional) sqrt(variance) of `scienceExposure`. If `None`, it is 147 computed from the sqrt(mean) of the `scienceExposure` variance image. 148 psf1 : 2D numpy.array 149 (Optional) 2D array containing the PSF image for the template. If 150 `None`, it is extracted from the PSF taken at the center of `templateExposure`. 151 psf2 : 2D numpy.array 152 (Optional) 2D array containing the PSF image for the science img. If 153 `None`, it is extracted from the PSF taken at the center of `scienceExposure`. 155 additional arguments to be passed to 156 `lsst.pipe.base.task.Task.__init__` 158 additional keyword arguments to be passed to 159 `lsst.pipe.base.task.Task.__init__` 161 pipeBase.Task.__init__(self, *args, **kwargs)
170 .getPlaneBitMask(self.config.ignoreMaskPlanes))
173 self.
im2 = self.
science.getMaskedImage().getImage().getArray()
177 def selectPsf(psf, exposure):
182 xcen = (bbox1.getBeginX() + bbox1.getEndX()) / 2.
183 ycen = (bbox1.getBeginY() + bbox1.getEndY()) / 2.
184 return exposure.getPsf().computeKernelImage(afwGeom.Point2D(xcen, ycen)).getArray()
193 mode=
'constant', constant_values=0)
197 mode=
'constant', constant_values=0)
202 self.
Fr = self.config.templateFluxScaling
203 self.
Fn = self.config.scienceFluxScaling
207 """Compute the sigma-clipped mean of the variance image of `exposure`. 209 statObj = afwMath.makeStatistics(exposure.getMaskedImage().getVariance(),
210 exposure.getMaskedImage().getMask(),
212 var = statObj.getValue(afwMath.MEANCLIP)
217 """Zero-pad `psf` to the dimensions given by `size`. 222 Input psf to be padded 224 Two element list containing the dimensions to pad the `psf` to 229 The padded copy of the input `psf`. 234 if padSize0 > 0
or padSize1 > 0:
239 psf = np.pad(psf, ((padSize0, padSize0-1), (padSize1, padSize1-1)), mode=
'constant',
245 """Zero-pad `psf` to same dimensions as im. 250 Input psf to be padded 251 im : lsst.afw.Image, MaskedImage or Exposure 252 Dimensions of this image are used to set the PSF pad dimensions 257 The padded copy of the input `psf`. 259 return ZogyTask._padPsfToSize(psf, (im.shape[0]//2 - psf.shape[0]//2,
260 im.shape[1]//2 - psf.shape[1]//2))
263 """Compute standard ZOGY quantities used by (nearly) all methods. 265 Many of the ZOGY calculations require similar quantities, including 266 FFTs of the PSFs, and the "denominator" term (e.g. in eq. 13 of 267 ZOGY manuscript (2016). This function consolidates many of those 272 psf1 : 2D numpy.array 273 (Optional) Input psf of template, override if already padded 274 psf2 : 2D numpy.array 275 (Optional) Input psf of science image, override if already padded 279 A lsst.pipe.base.Struct containing: 280 - Pr : 2D numpy.array, the (possibly zero-padded) template PSF 281 - Pn : 2D numpy.array, the (possibly zero-padded) science PSF 282 - Pr_hat : 2D numpy.array, the FFT of `Pr` 283 - Pn_hat : 2D numpy.array, the FFT of `Pn` 284 - denom : 2D numpy.array, the denominator of equation (13) in ZOGY (2016) manuscript 285 - Fd : float, the relative flux scaling factor between science and template 287 psf1 = self.
im1_psf if psf1
is None else psf1
288 psf2 = self.
im2_psf if psf2
is None else psf2
289 padSize = self.
padSize if padSize
is None else padSize
292 Pr = ZogyTask._padPsfToSize(psf1, (padSize, padSize))
293 Pn = ZogyTask._padPsfToSize(psf2, (padSize, padSize))
296 Pr_hat = np.fft.fft2(Pr)
297 Pr_hat2 = np.conj(Pr_hat) * Pr_hat
298 Pn_hat = np.fft.fft2(Pn)
299 Pn_hat2 = np.conj(Pn_hat) * Pn_hat
300 denom = np.sqrt((sigN**2 * self.
Fr**2 * Pr_hat2) + (sigR**2 * self.
Fn**2 * Pn_hat2))
301 Fd = self.
Fr*self.
Fn / np.sqrt(sigN**2 * self.
Fr**2 + sigR**2 * self.
Fn**2)
303 res = pipeBase.Struct(
304 Pr=Pr, Pn=Pn, Pr_hat=Pr_hat, Pn_hat=Pn_hat, denom=denom, Fd=Fd
310 """Compute ZOGY diffim `D` as proscribed in ZOGY (2016) manuscript 312 Compute the ZOGY eqn. (13): 314 \widehat{D} = \frac{Fr\widehat{Pr}\widehat{N} - 315 F_n\widehat{Pn}\widehat{R}}{\sqrt{\sigma_n^2 Fr^2 316 |\widehat{Pr}|^2 + \sigma_r^2 F_n^2 |\widehat{Pn}|^2}} 318 where $D$ is the optimal difference image, $R$ and $N$ are the 319 reference and "new" image, respectively, $Pr$ and $P_n$ are their 320 PSFs, $Fr$ and $Fn$ are their flux-based zero-points (which we 321 will set to one here), $\sigma_r^2$ and $\sigma_n^2$ are their 322 variance, and $\widehat{D}$ denotes the FT of $D$. 326 A lsst.pipe.base.Struct containing: 327 - D : 2D numpy.array, the proper image difference 328 - D_var : 2D numpy.array, the variance image for `D` 331 psf1 = ZogyTask._padPsfToImageSize(self.
im1_psf, self.
im1)
332 psf2 = ZogyTask._padPsfToImageSize(self.
im2_psf, self.
im2)
336 def _filterKernel(K, trim_amount):
339 K[:ps, :] = K[-ps:, :] = 0
340 K[:, :ps] = K[:, -ps:] = 0
343 Kr_hat = self.
Fr * preqs.Pr_hat / preqs.denom
344 Kn_hat = self.
Fn * preqs.Pn_hat / preqs.denom
345 if debug
and self.config.doTrimKernels:
348 ps = (Kn_hat.shape[1] - 80)//2
349 Kn = _filterKernel(np.fft.ifft2(Kn_hat), ps)
350 Kn_hat = np.fft.fft2(Kn)
351 Kr = _filterKernel(np.fft.ifft2(Kr_hat), ps)
352 Kr_hat = np.fft.fft2(Kr)
354 def processImages(im1, im2, doAdd=False):
356 im1[np.isinf(im1)] = np.nan
357 im1[np.isnan(im1)] = np.nanmean(im1)
358 im2[np.isinf(im2)] = np.nan
359 im2[np.isnan(im2)] = np.nanmean(im2)
361 R_hat = np.fft.fft2(im1)
362 N_hat = np.fft.fft2(im2)
364 D_hat = Kr_hat * N_hat
366 D_hat -= Kn_hat * R_hat
368 D_hat += Kn_hat * R_hat
370 D = np.fft.ifft2(D_hat)
371 D = np.fft.ifftshift(D.real) / preqs.Fd
375 D = processImages(self.
im1, self.
im2, doAdd=
False)
379 return pipeBase.Struct(D=D, D_var=D_var)
382 """! Convolve an Exposure with a decorrelation convolution kernel. 386 exposure : lsst.afw.image.Exposure to be convolved. 387 kernel : 2D numpy.array to convolve the image with 391 A new lsst.afw.image.Exposure with the convolved pixels and the (possibly 396 - We optionally re-center the kernel if necessary and return the possibly 399 kernelImg = afwImage.ImageD(kernel.shape[0], kernel.shape[1])
400 kernelImg.getArray()[:, :] = kernel
401 kern = afwMath.FixedKernel(kernelImg)
403 maxloc = np.unravel_index(np.argmax(kernel), kernel.shape)
404 kern.setCtrX(maxloc[0])
405 kern.setCtrY(maxloc[1])
406 outExp = exposure.clone()
407 convCntrl = afwMath.ConvolutionControl(doNormalize=
False, doCopyEdge=
False,
408 maxInterpolationDistance=0)
410 afwMath.convolve(outExp.getMaskedImage(), exposure.getMaskedImage(), kern, convCntrl)
413 afwMath.convolve(outExp, exposure, kern, convCntrl)
418 """Compute ZOGY diffim `D` using image-space convlutions 420 This method is still being debugged as it results in artifacts 421 when the PSFs are noisy (see module-level docstring). Thus 422 there are several options still enabled by the `debug` flag, 423 which are disabled by defult. 427 padSize : int, the amount to pad the PSFs by 428 debug : bool, flag to enable debugging tests and options 432 D : lsst.afw.Exposure 433 the proper image difference, including correct variance, 441 Kr_hat = (preqs.Pr_hat + delta) / (preqs.denom + delta)
442 Kn_hat = (preqs.Pn_hat + delta) / (preqs.denom + delta)
443 Kr = np.fft.ifft2(Kr_hat).real
444 Kr = np.roll(np.roll(Kr, -1, 0), -1, 1)
445 Kn = np.fft.ifft2(Kn_hat).real
446 Kn = np.roll(np.roll(Kn, -1, 0), -1, 1)
448 def _trimKernel(self, K, trim_amount):
452 K = K[ps:-ps, ps:-ps]
455 padSize = self.
padSize if padSize
is None else padSize
457 if debug
and self.config.doTrimKernels:
460 Kn = _trimKernel(Kn, padSize)
461 Kr = _trimKernel(Kr, padSize)
467 tmp = D.getMaskedImage()
468 tmp -= exp1.getMaskedImage()
473 """Utility method to set an exposure's PSF when provided as a 2-d numpy.array 475 psfI = afwImage.ImageD(psfArr.shape[0], psfArr.shape[1])
476 psfI.getArray()[:, :] = psfArr
477 psfK = afwMath.FixedKernel(psfI)
478 psfNew = measAlg.KernelPsf(psfK)
479 exposure.setPsf(psfNew)
483 """Wrapper method to compute ZOGY proper diffim 485 This method should be used as the public interface for 486 computing the ZOGY diffim. 491 Override config `inImageSpace` parameter 493 Override config `padSize` parameter 495 additional keyword arguments to be passed to 496 `computeDiffimFourierSpace` or `computeDiffimImageSpace`. 500 D : lsst.afw.Exposure 501 the proper image difference, including correct variance, 504 inImageSpace = self.config.inImageSpace
if inImageSpace
is None else inImageSpace
506 padSize = self.
padSize if padSize
is None else padSize
511 D.getMaskedImage().getImage().getArray()[:, :] = res.D
512 D.getMaskedImage().getVariance().getArray()[:, :] = res.D_var
519 """Compute the ZOGY diffim PSF (ZOGY manuscript eq. 14) 524 Override config `padSize` parameter 526 Return the FFT of the diffim PSF (do not inverse-FFT it) 527 psf1 : 2D numpy.array 528 (Optional) Input psf of template, override if already padded 529 psf2 : 2D numpy.array 530 (Optional) Input psf of science image, override if already padded 534 Pd : 2D numpy.array, the diffim PSF (or FFT of PSF if `keepFourier=True`) 536 preqs = self.
computePrereqs(psf1=psf1, psf2=psf2, padSize=padSize)
538 Pd_hat_numerator = (self.
Fr * self.
Fn * preqs.Pr_hat * preqs.Pn_hat)
539 Pd_hat = Pd_hat_numerator / (preqs.Fd * preqs.denom)
544 Pd = np.fft.ifft2(Pd_hat)
545 Pd = np.fft.ifftshift(Pd).real
550 R_hat=None, Kr_hat=None, Kr=None,
551 N_hat=None, Kn_hat=None, Kn=None):
552 """Compute the astrometric noise correction terms 554 Compute the correction for estimated astrometric noise as 555 proscribed in ZOGY (2016), section 3.3. All convolutions 556 performed either in real (image) or Fourier space. 560 xVarAst, yVarAst : float 561 estimated astrometric noise (variance of astrometric registration errors) 563 Perform all convolutions in real (image) space rather than Fourier space 564 R_hat : 2-D numpy.array 565 (Optional) FFT of template image, only required if `inImageSpace=False` 566 Kr_hat : 2-D numpy.array 567 FFT of Kr kernel (eq. 28 of ZOGY (2016)), only required if `inImageSpace=False` 569 Kr kernel (eq. 28 of ZOGY (2016)), only required if `inImageSpace=True`. 570 Kr is associated with the template (reference). 571 N_hat : 2-D numpy.array 572 FFT of science image, only required if `inImageSpace=False` 573 Kn_hat : 2-D numpy.array 574 FFT of Kn kernel (eq. 29 of ZOGY (2016)), only required if `inImageSpace=False` 576 Kn kernel (eq. 29 of ZOGY (2016)), only required if `inImageSpace=True`. 577 Kn is associated with the science (new) image. 581 VastSR, VastSN : 2-D numpy.arrays containing the values in eqs. 30 and 32 of 585 if xVarAst + yVarAst > 0:
588 S_R = S_R.getMaskedImage().getImage().getArray()
590 S_R = np.fft.ifft2(R_hat * Kr_hat)
591 gradRx, gradRy = np.gradient(S_R)
592 VastSR = xVarAst * gradRx**2. + yVarAst * gradRy**2.
596 S_N = S_N.getMaskedImage().getImage().getArray()
598 S_N = np.fft.ifft2(N_hat * Kn_hat)
599 gradNx, gradNy = np.gradient(S_N)
600 VastSN = xVarAst * gradNx**2. + yVarAst * gradNy**2.
602 return VastSR, VastSN
605 """Compute corrected likelihood image, optimal for source detection 607 Compute ZOGY S_corr image. This image can be thresholded for 608 detection without optimal filtering, and the variance image is 609 corrected to account for astrometric noise (errors in 610 astrometric registration whether systematic or due to effects 611 such as DCR). The calculations here are all performed in 612 Fourier space, as proscribed in ZOGY (2016). 616 xVarAst, yVarAst : float 617 estimated astrometric noise (variance of astrometric registration errors) 621 A lsst.pipe.base.Struct containing: 622 - S : numpy.array, the likelihood image S (eq. 12 of ZOGY (2016)) 623 - S_var : the corrected variance image (denominator of eq. 25 of ZOGY (2016)) 624 - Dpsf : the PSF of the diffim D, likely never to be used. 627 psf1 = ZogyTask._padPsfToImageSize(self.
im1_psf, self.
im1)
628 psf2 = ZogyTask._padPsfToImageSize(self.
im2_psf, self.
im2)
633 R_hat = np.fft.fft2(self.
im1)
634 N_hat = np.fft.fft2(self.
im2)
635 D_hat = self.
Fr * preqs.Pr_hat * N_hat - self.
Fn * preqs.Pn_hat * R_hat
638 Pd_hat = self.
computeDiffimPsf(padSize=0, keepFourier=
True, psf1=psf1, psf2=psf2)
639 Pd_bar = np.conj(Pd_hat)
640 S = np.fft.ifft2(D_hat * Pd_bar)
644 Pn_hat2 = np.conj(preqs.Pn_hat) * preqs.Pn_hat
645 Kr_hat = self.
Fr * self.
Fn**2. * np.conj(preqs.Pr_hat) * Pn_hat2 / preqs.denom**2.
646 Pr_hat2 = np.conj(preqs.Pr_hat) * preqs.Pr_hat
647 Kn_hat = self.
Fn * self.
Fr**2. * np.conj(preqs.Pn_hat) * Pr_hat2 / preqs.denom**2.
649 Kr_hat2 = np.fft.fft2(np.fft.ifft2(Kr_hat)**2.)
650 Kn_hat2 = np.fft.fft2(np.fft.ifft2(Kn_hat)**2.)
651 var1c_hat = Kr_hat2 * np.fft.fft2(self.
im1_var)
652 var2c_hat = Kn_hat2 * np.fft.fft2(self.
im2_var)
656 R_hat=R_hat, Kr_hat=Kr_hat,
657 N_hat=N_hat, Kn_hat=Kn_hat)
659 S_var = np.sqrt(np.fft.ifftshift(np.fft.ifft2(var1c_hat + var2c_hat)) + fGradR + fGradN)
662 S = np.fft.ifftshift(np.fft.ifft2(Kn_hat * N_hat - Kr_hat * R_hat))
666 return pipeBase.Struct(S=S.real, S_var=S_var.real, Dpsf=Pd)
669 """Compute corrected likelihood image, optimal for source detection 671 Compute ZOGY S_corr image. This image can be thresholded for 672 detection without optimal filtering, and the variance image is 673 corrected to account for astrometric noise (errors in 674 astrometric registration whether systematic or due to effects 675 such as DCR). The calculations here are all performed in 680 xVarAst, yVarAst : float 681 estimated astrometric noise (variance of astrometric registration errors) 686 - S : lsst.afw.image.Exposure, the likelihood exposure S (eq. 12 of ZOGY (2016)), 687 including corrected variance, masks, and PSF 688 - D : lsst.afw.Exposure, the proper image difference, including correct 689 variance, masks, and PSF 694 padSize = self.
padSize if padSize
is None else padSize
698 Pd_bar = np.fliplr(np.flipud(Pd))
700 tmp = S.getMaskedImage()
705 Pn_hat2 = np.conj(preqs.Pn_hat) * preqs.Pn_hat
706 Kr_hat = self.
Fr * self.
Fn**2. * np.conj(preqs.Pr_hat) * Pn_hat2 / preqs.denom**2.
707 Pr_hat2 = np.conj(preqs.Pr_hat) * preqs.Pr_hat
708 Kn_hat = self.
Fn * self.
Fr**2. * np.conj(preqs.Pn_hat) * Pr_hat2 / preqs.denom**2.
710 Kr = np.fft.ifft2(Kr_hat).real
711 Kr = np.roll(np.roll(Kr, -1, 0), -1, 1)
712 Kn = np.fft.ifft2(Kn_hat).real
713 Kn = np.roll(np.roll(Kn, -1, 0), -1, 1)
721 Smi = S.getMaskedImage()
723 S_var = np.sqrt(var1c.getArray() + var2c.getArray() + fGradR + fGradN)
724 S.getMaskedImage().getVariance().getArray()[:, :] = S_var
729 def computeScorr(self, xVarAst=0., yVarAst=0., inImageSpace=None, padSize=0, **kwargs):
730 """Wrapper method to compute ZOGY corrected likelihood image, optimal for 733 This method should be used as the public interface for 734 computing the ZOGY S_corr. 738 xVarAst, yVarAst : float 739 estimated astrometric noise (variance of astrometric registration errors) 741 Override config `inImageSpace` parameter 743 Override config `padSize` parameter 747 S : lsst.afw.image.Exposure, the likelihood exposure S (eq. 12 of ZOGY (2016)), 748 including corrected variance, masks, and PSF 750 inImageSpace = self.config.inImageSpace
if inImageSpace
is None else inImageSpace
757 S.getMaskedImage().getImage().getArray()[:, :] = res.S
758 S.getMaskedImage().getVariance().getArray()[:, :] = res.S_var
765 """Task to be used as an ImageMapperSubtask for performing 766 ZOGY image subtraction on a grid of subimages. 768 ConfigClass = ZogyConfig
769 _DefaultName =
'ip_diffim_ZogyMapper' 772 ImageMapperSubtask.__init__(self, *args, **kwargs)
774 def run(self, subExposure, expandedSubExposure, fullBBox, template,
776 """Perform ZOGY proper image subtraction on sub-images 778 This method performs ZOGY proper image subtraction on 779 `subExposure` using local measures for image variances and 780 PSF. `subExposure` is a sub-exposure of the science image. It 781 also requires the corresponding sub-exposures of the template 782 (`template`). The operations are actually performed on 783 `expandedSubExposure` to allow for invalid edge pixels arising 784 from convolutions, which are then removed. 788 subExposure : lsst.afw.image.Exposure 789 the sub-exposure of the diffim 790 expandedSubExposure : lsst.afw.image.Exposure 791 the expanded sub-exposure upon which to operate 792 fullBBox : lsst.afw.geom.BoundingBox 793 the bounding box of the original exposure 794 template : lsst.afw.image.Exposure 795 the template exposure, from which a corresponding sub-exposure 798 additional keyword arguments propagated from 799 `ImageMapReduceTask.run`. These include: 801 Compute and return the corrected likelihood image S_corr 802 rather than the proper image difference 803 - inImageSpace : bool 804 Perform all convolutions in real (image) space rather than 805 in Fourier space. This option currently leads to artifacts 806 when using real (measured and noisy) PSFs, thus it is set 807 to `False` by default. 808 These kwargs may also include arguments to be propagated to 809 `ZogyTask.computeDiffim` and `ZogyTask.computeScorr`. 813 A `lsst.pipe.base.Struct` containing the result of the 814 `subExposure` processing, labelled 'subExposure'. In this case 815 it is either the subExposure of the proper image difference D, 816 or (if `doScorr==True`) the corrected likelihood exposure `S`. 820 This `run` method accepts parameters identical to those of 821 `ImageMapperSubtask.run`, since it is called from the 822 `ImageMapperTask`. See that class for more information. 824 bbox = subExposure.getBBox()
825 center = ((bbox.getBeginX() + bbox.getEndX()) // 2., (bbox.getBeginY() + bbox.getEndY()) // 2.)
826 center = afwGeom.Point2D(center[0], center[1])
828 imageSpace = kwargs.pop(
'inImageSpace',
False)
829 doScorr = kwargs.pop(
'doScorr',
False)
830 sigmas = kwargs.pop(
'sigmas',
None)
831 padSize = kwargs.pop(
'padSize', 7)
834 subExp2 = expandedSubExposure
837 subExp1 = template.Factory(template, expandedSubExposure.getBBox())
843 sig1, sig2 = sigmas[0], sigmas[1]
845 def _makePsfSquare(psf):
846 if psf.shape[0] < psf.shape[1]:
848 psf = np.pad(psf, ((1, 1), (0, 0)), mode=
'constant')
849 elif psf.shape[0] > psf.shape[1]:
850 psf = np.pad(psf, ((0, 0), (1, 1)), mode=
'constant')
853 psf2 = subExp2.getPsf().computeKernelImage(center).getArray()
854 psf2 = _makePsfSquare(psf2)
856 psf1 = template.getPsf().computeKernelImage(center).getArray()
857 psf1 = _makePsfSquare(psf1)
860 if subExp1.getDimensions()[0] < psf1.shape[0]
or subExp1.getDimensions()[1] < psf1.shape[1]:
861 return pipeBase.Struct(subExposure=subExposure)
864 """Filter a noisy Psf to remove artifacts. Subject of future research.""" 866 if psf.shape[0] == 41:
869 psf[0:10, :] = psf[:, 0:10] = psf[31:41, :] = psf[:, 31:41] = 0
875 if self.config.doFilterPsfs:
877 psf1b = _filterPsf(psf1)
878 psf2b = _filterPsf(psf2)
881 if imageSpace
is True:
882 config.inImageSpace = imageSpace
883 config.padSize = padSize
884 task =
ZogyTask(templateExposure=subExp1, scienceExposure=subExp2,
885 sig1=sig1, sig2=sig2, psf1=psf1b, psf2=psf2b, config=config)
888 D = task.computeDiffim(**kwargs)
890 D = task.computeScorr(**kwargs)
892 outExp = D.Factory(D, subExposure.getBBox())
893 out = pipeBase.Struct(subExposure=outExp)
898 mapperSubtask = pexConfig.ConfigurableField(
899 doc=
'Zogy subtask to run on each sub-image',
900 target=ZogyMapperSubtask
def __init__(self, args, kwargs)
def _computeVarAstGradients(self, xVarAst=0., yVarAst=0., inImageSpace=False, R_hat=None, Kr_hat=None, Kr=None, N_hat=None, Kn_hat=None, Kn=None)
def _padPsfToImageSize(psf, im)
def computeDiffimFourierSpace(self, debug=False, kwargs)
def computePrereqs(self, psf1=None, psf2=None, padSize=0)
def run(self, subExposure, expandedSubExposure, fullBBox, template, kwargs)
def _padPsfToSize(psf, size)
def computeDiffim(self, inImageSpace=None, padSize=None, kwargs)
def computeScorrFourierSpace(self, xVarAst=0., yVarAst=0., kwargs)
def __init__(self, templateExposure, scienceExposure, sig1=None, sig2=None, psf1=None, psf2=None, args, kwargs)
def _computeVarianceMean(self, exposure)
def computeDiffimPsf(self, padSize=0, keepFourier=False, psf1=None, psf2=None)
def _doConvolve(self, exposure, kernel, recenterKernel=False)
Convolve an Exposure with a decorrelation convolution kernel.
def _setNewPsf(self, exposure, psfArr)
def computeScorr(self, xVarAst=0., yVarAst=0., inImageSpace=None, padSize=0, kwargs)
def computeDiffimImageSpace(self, padSize=None, debug=False, kwargs)
def computeScorrImageSpace(self, xVarAst=0., yVarAst=0., padSize=None, kwargs)