28import lsst.meas.algorithms
as measAlg
31from lsst.utils.timer
import timeMethod
33from .imageMapReduce
import (ImageMapReduceConfig, ImageMapReduceTask,
36__all__ = (
"DecorrelateALKernelTask",
"DecorrelateALKernelConfig",
37 "DecorrelateALKernelMapper",
"DecorrelateALKernelMapReduceConfig",
38 "DecorrelateALKernelSpatialConfig",
"DecorrelateALKernelSpatialTask")
42 """Configuration parameters for the DecorrelateALKernelTask
45 ignoreMaskPlanes = pexConfig.ListField(
47 doc="""Mask planes to ignore for sigma-clipped statistics""",
48 default=(
"INTRP",
"EDGE",
"DETECTED",
"SAT",
"CR",
"BAD",
"NO_DATA",
"DETECTED_NEGATIVE")
50 completeVarPlanePropagation = pexConfig.Field(
53 doc=
"Compute the full effect of the decorrelated matching kernel on the variance plane."
54 " Otherwise use a model weighed sum of the input variances."
59 """Decorrelate the effect of convolution by Alard-Lupton matching kernel in image difference
64 Pipe-task that removes the neighboring-pixel covariance in an
65 image difference that are added when the template image
is
66 convolved
with the Alard-Lupton PSF matching kernel.
68 The image differencing pipeline task
@link
71 the Alard
and Lupton (1998) method
for matching the PSFs of the
72 template
and science exposures prior to subtraction. The
73 Alard-Lupton method identifies a matching kernel, which
is then
74 (typically) convolved
with the template image to perform PSF
75 matching. This convolution has the effect of adding covariance
76 between neighboring pixels
in the template image, which
is then
77 added to the image difference by subtraction.
79 The pixel covariance may be corrected by whitening the noise of
80 the image difference. This task performs such a decorrelation by
81 computing a decorrelation kernel (based upon the A&L matching
82 kernel
and variances
in the template
and science images)
and
83 convolving the image difference
with it. This process
is described
84 in detail
in [DMTN-021](http://dmtn-021.lsst.io).
86 This task has no standalone example, however it
is applied
as a
87 subtask of pipe.tasks.imageDifference.ImageDifferenceTask.
89 ConfigClass = DecorrelateALKernelConfig
90 _DefaultName = "ip_diffim_decorrelateALKernel"
93 """Create the image decorrelation Task
98 arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
100 keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
102 pipeBase.Task.__init__(self, *args, **kwargs)
107 self.statsControl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.ignoreMaskPlanes))
111 exposure.getMaskedImage().getMask(),
113 var = statObj.getValue(afwMath.MEANCLIP)
117 def run(self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel,
118 preConvKernel=None, xcen=None, ycen=None, svar=None, tvar=None,
119 templateMatched=True, preConvMode=False, **kwargs):
120 """Perform decorrelation of an image difference or of a score difference exposure.
122 Corrects the difference or score image due to the convolution of the
123 templateExposure
with the A&L PSF matching kernel.
124 See [DMTN-021, Equation 1](http://dmtn-021.lsst.io/
125 [DMTN-179](http://dmtn-179.lsst.io/)
for details.
130 The original science exposure (before pre-convolution,
if ``preConvMode==
True``).
132 The original template exposure warped, but
not psf-matched, to the science exposure.
134 the subtracted exposure produced by
135 `ip_diffim.ImagePsfMatchTask.subtractExposures()`. The `subtractedExposure` must
136 inherit its PSF
from `exposure`, see notes below.
138 An (optionally spatially-varying) PSF matching kernel produced
139 by `ip_diffim.ImagePsfMatchTask.subtractExposures()`.
141 If
not `
None`, then the `scienceExposure` was pre-convolved
with (the reflection of)
142 this kernel. Must be normalized to sum to 1.
143 Allowed only
if ``templateMatched==
True``
and ``preConvMode==
True``.
144 Defaults to the PSF of the science exposure at the image center.
145 xcen : `float`, optional
146 X-pixel coordinate to use
for computing constant matching kernel to use
147 If `
None` (default), then use the center of the image.
148 ycen : `float`, optional
149 Y-pixel coordinate to use
for computing constant matching kernel to use
150 If `
None` (default), then use the center of the image.
151 svar : `float`, optional
152 Image variance
for science image
153 If `
None` (default) then compute the variance over the entire input science image.
154 tvar : `float`, optional
155 Image variance
for template image
156 If `
None` (default) then compute the variance over the entire input template image.
157 templateMatched : `bool`, optional
158 If
True, the template exposure was matched (convolved) to the science exposure.
159 See also notes below.
160 preConvMode : `bool`, optional
161 If
True, ``subtractedExposure``
is assumed to be a likelihood difference image
162 and will be noise corrected
as a likelihood image.
164 Additional keyword arguments propagated
from DecorrelateALKernelSpatialTask.
168 result : `lsst.pipe.base.Struct`
169 - ``correctedExposure`` : the decorrelated diffim
173 If ``preConvMode==
True``, ``subtractedExposure``
is assumed to be a
174 score image
and the noise correction
for likelihood images
175 is applied. The resulting image
is an optimal detection likelihood image
176 when the templateExposure has noise. (See DMTN-179) If ``preConvKernel``
is
177 not specified, the PSF of ``scienceExposure``
is assumed
as pre-convolution kernel.
179 The ``subtractedExposure``
is NOT updated. The returned ``correctedExposure``
180 has an updated but spatially fixed PSF. It
is calculated
as the center of
181 image PSF corrected by the center of image matching kernel.
183 If ``templateMatched==
True``, the templateExposure was matched (convolved)
184 to the ``scienceExposure`` by ``psfMatchingKernel`` during image differencing.
185 Otherwise the ``scienceExposure`` was matched (convolved) by ``psfMatchingKernel``.
186 In either case, note that the original template
and science images are required,
187 not the psf-matched version.
189 This task discards the variance plane of ``subtractedExposure``
and re-computes
190 it
from the variance planes of ``scienceExposure``
and ``templateExposure``.
191 The image plane of ``subtractedExposure`` must be at the photometric level
192 set by the AL PSF matching
in `ImagePsfMatchTask.subtractExposures`.
193 The assumptions about the photometric level are controlled by the
194 `templateMatched` option
in this task.
196 Here we currently convert a spatially-varying matching kernel into a constant kernel,
197 just by computing it at the center of the image (tickets DM-6243, DM-6244).
199 We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute
200 the decorrelation kernel.
202 TODO DM-23857 As part of the spatially varying correction implementation
203 consider whether returning a Struct
is still necessary.
205 if preConvKernel
is not None and not (templateMatched
and preConvMode):
206 raise ValueError(
"Pre-convolution kernel is allowed only if "
207 "preConvMode==True and templateMatched==True.")
209 spatialKernel = psfMatchingKernel
210 kimg = afwImage.ImageD(spatialKernel.getDimensions())
211 bbox = subtractedExposure.getBBox()
213 xcen = (bbox.getBeginX() + bbox.getEndX()) / 2.
215 ycen = (bbox.getBeginY() + bbox.getEndY()) / 2.
216 self.log.info(
"Using matching kernel computed at (%d, %d)", xcen, ycen)
217 spatialKernel.computeImage(kimg,
False, xcen, ycen)
221 if preConvKernel
is None:
222 pos = scienceExposure.getPsf().getAveragePosition()
223 preConvKernel = scienceExposure.getPsf().getLocalKernel(pos)
224 preConvImg = afwImage.ImageD(preConvKernel.getDimensions())
225 preConvKernel.computeImage(preConvImg,
True)
231 self.log.info(
"Original variance plane means. Science:%.5e, warped template:%.5e)",
237 if np.isnan(svar)
or np.isnan(tvar):
239 if (np.all(np.isnan(scienceExposure.image.array))
240 or np.all(np.isnan(templateExposure.image.array))):
241 self.log.warning(
'Template or science image is entirely NaNs: skipping decorrelation.')
242 outExposure = subtractedExposure.clone()
243 return pipeBase.Struct(correctedExposure=outExposure, )
247 self.log.info(
"Decorrelation after template image convolution")
249 targetVarianceMean = tvar
251 variance = scienceExposure.variance.array
253 targetVariance = templateExposure.variance.array
256 self.log.info(
"Decorrelation after science image convolution")
258 targetVarianceMean = svar
260 variance = templateExposure.variance.array
262 targetVariance = scienceExposure.variance.array
266 mOverExpVar = targetVarianceMean/varianceMean
267 if mOverExpVar > 1e8:
268 self.log.warning(
"Diverging correction: matched image variance is "
269 " much larger than the unconvolved one's"
270 ", targetVarianceMean/varianceMean:%.2e", mOverExpVar)
273 self.log.info(
"Variance plane mean of uncorrected diffim: %f", oldVarMean)
276 diffimShape = subtractedExposure.image.array.shape
277 psfImg = subtractedExposure.getPsf().computeKernelImage(
geom.Point2D(xcen, ycen))
278 psfShape = psfImg.array.shape
281 self.log.info(
"Decorrelation of likelihood image")
283 psfShape, diffimShape)
286 self.log.info(
"Decorrelation of difference image")
296 if self.config.completeVarPlanePropagation:
297 self.log.debug(
"Using full variance plane calculation in decorrelation")
299 variance, targetVariance,
300 varianceMean, targetVarianceMean, corr.cnft, corr.crft)
302 self.log.debug(
"Using estimated variance plane calculation in decorrelation")
304 variance, targetVariance,
305 corr.cnft, corr.crft)
310 self.log.debug(
"Matching kernel sum: %.3e", kSum)
311 if not templateMatched:
314 correctedVariance /= kSumSq
315 subtractedExposure.image.array[...] = correctedImage
316 subtractedExposure.variance.array[...] = correctedVariance
317 subtractedExposure.setPsf(correctedPsf)
320 self.log.info(
"Variance plane mean of corrected diffim: %.5e", newVarMean)
324 return pipeBase.Struct(correctedExposure=subtractedExposure, )
327 """Calculate the common shape for FFT operations. Set `self.freqSpaceShape`
332 shapes : one or more `tuple` of `int`
333 Shapes of the arrays. All must have the same dimensionality.
334 At least one shape must be provided.
342 For each dimension, gets the smallest even number greater than
or equal to
343 `N1+N2-1` where `N1`
and `N2` are the two largest values.
344 In case of only one shape given, rounds up to even each dimension value.
346 S = np.array(shapes, dtype=int)
351 commonShape = np.sum(S, axis=0) - 1
354 commonShape[commonShape % 2 != 0] += 1
356 self.log.info(
"Common frequency space shape %s", self.
freqSpaceShape)
360 """Zero pad an image where the origin is at the center and replace the
361 origin to the corner as required by the periodic input of FFT. Implement also
362 the inverse operation, crop the padding
and re-center data.
367 An array to copy
from.
368 newShape : `tuple` of `int`
369 The dimensions of the resulting array. For padding, the resulting array
370 must be larger than A
in each dimension. For the inverse operation this
371 must be the original, before padding size of the array.
372 useInverse : bool, optional
373 Selector of forward, add padding, operation (
False)
374 or its inverse, crop padding, operation (
True).
379 The padded
or unpadded array
with shape of `newShape`
and the same dtype
as A.
383 For odd dimensions, the splitting
is rounded to
384 put the center pixel into the new corner origin (0,0). This
is to be consistent
385 e.g.
for a dirac delta kernel that
is originally located at the center pixel.
392 firstHalves = [x//2
for x
in A.shape]
393 secondHalves = [x-y
for x, y
in zip(A.shape, firstHalves)]
396 secondHalves = [x//2
for x
in newShape]
397 firstHalves = [x-y
for x, y
in zip(newShape, secondHalves)]
399 R = np.zeros_like(A, shape=newShape)
400 R[-firstHalves[0]:, -firstHalves[1]:] = A[:firstHalves[0], :firstHalves[1]]
401 R[:secondHalves[0], -firstHalves[1]:] = A[-secondHalves[0]:, :firstHalves[1]]
402 R[:secondHalves[0], :secondHalves[1]] = A[-secondHalves[0]:, -secondHalves[1]:]
403 R[-firstHalves[0]:, :secondHalves[1]] = A[:firstHalves[0], -secondHalves[1]:]
407 """Compute the Lupton decorrelation post-convolution kernel for decorrelating an
408 image difference, based on the PSF-matching kernel.
412 kappa : `numpy.ndarray` of `float`
413 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
415 Average variance of science image used
for PSF matching.
417 Average variance of the template (matched) image used
for PSF matching.
421 corrft : `numpy.ndarray` of `float`
422 The frequency space representation of the correction. The array
is real (dtype float).
425 cnft, crft : `numpy.ndarray` of `complex`
426 The overall convolution (pre-conv, PSF matching, noise correction) kernel
427 for the science
and template images, respectively
for the variance plane
428 calculations. These are intermediate results
in frequency space.
432 The maximum correction factor converges to `sqrt(tvar/svar)` towards high frequencies.
433 This should be a plausible value.
437 kft = np.fft.fft2(kappa)
438 kftAbsSq = np.real(np.conj(kft) * kft)
440 denom = svar + tvar * kftAbsSq
441 corrft = np.sqrt((svar + tvar * kSum*kSum) / denom)
444 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
447 """Compute the correction kernel for a score image.
451 kappa : `numpy.ndarray`
452 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
454 Average variance of science image used
for PSF matching (before pre-convolution).
456 Average variance of the template (matched) image used
for PSF matching.
457 preConvArr : `numpy.ndarray`
458 The pre-convolution kernel of the science image. It should be the PSF
459 of the science image
or an approximation of it. It must be normed to sum 1.
463 corrft : `numpy.ndarray` of `float`
464 The frequency space representation of the correction. The array
is real (dtype float).
466 cnft, crft : `numpy.ndarray` of `complex`
467 The overall convolution (pre-conv, PSF matching, noise correction) kernel
468 for the science
and template images, respectively
for the variance plane
469 calculations. These are intermediate results
in frequency space.
473 To be precise, the science image should be _correlated_ by ``preConvArray`` but this
474 does
not matter
for this calculation.
476 ``cnft``, ``crft`` contain the scaling factor
as well.
481 kft = np.fft.fft2(kappa)
483 preFt = np.fft.fft2(preConvArr)
484 preFtAbsSq = np.real(np.conj(preFt) * preFt)
485 kftAbsSq = np.real(np.conj(kft) * kft)
488 tiny = np.finfo(preFtAbsSq.dtype).tiny * 1000.
489 flt = preFtAbsSq < tiny
493 preFtAbsSq[flt] = tiny
494 denom = svar + tvar * kftAbsSq / preFtAbsSq
495 corrft = (svar + tvar * kSum*kSum) / denom
496 cnft = np.conj(preFt)*corrft
498 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
502 """Estimate the variance planes.
504 The estimation assumes that around each pixel the surrounding
505 pixels' sigmas within the convolution kernel are the same.
509 vplane1, vplane2 : `numpy.ndarray` of `float`
510 Variance planes of the original (before pre-convolution or matching)
512 c1ft, c2ft : `numpy.ndarray` of `complex`
513 The overall convolution that includes the matching
and the
514 afterburner
in frequency space. The result of either
515 ``computeScoreCorrection``
or ``computeDiffimCorrection``.
519 vplaneD : `numpy.ndarray` of `float`
520 The estimated variance plane of the difference/score image
521 as a weighted sum of the input variances.
525 See DMTN-179 Section 5 about the variance plane calculations.
527 w1 = np.sum(np.real(np.conj(c1ft)*c1ft)) / c1ft.size
528 w2 = np.sum(np.real(np.conj(c2ft)*c2ft)) / c2ft.size
531 return vplane1*w1 + vplane2*w2
534 """Full propagation of the variance planes of the original exposures.
536 The original variance planes of independent pixels are convolved with the
537 image space square of the overall kernels.
541 vplane1, vplane2 : `numpy.ndarray` of `float`
542 Variance planes of the original (before pre-convolution
or matching)
544 varMean1, varMean2 : `float`
545 Replacement average values
for non-finite ``vplane1``
and ``vplane2`` values respectively.
547 c1ft, c2ft : `numpy.ndarray` of `complex`
548 The overall convolution that includes the matching
and the
549 afterburner
in frequency space. The result of either
550 ``computeScoreCorrection``
or ``computeDiffimCorrection``.
554 vplaneD : `numpy.ndarray` of `float`
555 The variance plane of the difference/score images.
559 See DMTN-179 Section 5 about the variance plane calculations.
561 Infs
and NaNs are allowed
and kept
in the returned array.
563 D = np.real(np.fft.ifft2(c1ft))
564 c1SqFt = np.fft.fft2(D*D)
566 v1shape = vplane1.shape
567 filtInf = np.isinf(vplane1)
568 filtNan = np.isnan(vplane1)
570 vplane1 = np.copy(vplane1)
571 vplane1[filtInf | filtNan] = varMean1
573 v1 = np.real(np.fft.ifft2(np.fft.fft2(D) * c1SqFt))
578 D = np.real(np.fft.ifft2(c2ft))
579 c2SqFt = np.fft.fft2(D*D)
581 v2shape = vplane2.shape
582 filtInf = np.isinf(vplane2)
583 filtNan = np.isnan(vplane2)
584 vplane2 = np.copy(vplane2)
585 vplane2[filtInf | filtNan] = varMean2
587 v2 = np.real(np.fft.ifft2(np.fft.fft2(D) * c2SqFt))
595 """Compute the (decorrelated) difference image's new PSF.
599 corrft : `numpy.ndarray`
600 The frequency space representation of the correction calculated by
602 psfOld : `numpy.ndarray`
603 The psf of the difference image to be corrected.
607 correctedPsf : `lsst.meas.algorithms.KernelPsf`
608 The corrected psf, same shape as `psfOld`, sum normed to 1.
612 There
is no algorithmic guarantee that the corrected psf can
613 meaningfully fit to the same size
as the original one.
615 psfShape = psfOld.shape
617 psfNew = np.fft.fft2(psfNew)
619 psfNew = np.fft.ifft2(psfNew)
622 psfNew = psfNew/psfNew.sum()
624 psfcI = afwImage.ImageD(
geom.Extent2I(psfShape[1], psfShape[0]))
627 correctedPsf = measAlg.KernelPsf(psfcK)
631 """Compute the decorrelated difference image.
635 corrft : `numpy.ndarray`
636 The frequency space representation of the correction calculated by
638 imgOld : `numpy.ndarray`
639 The difference image to be corrected.
643 imgNew : `numpy.ndarray`
644 The corrected image, same size as the input.
646 expShape = imgOld.shape
647 imgNew = np.copy(imgOld)
648 filtInf = np.isinf(imgNew)
649 filtNan = np.isnan(imgNew)
650 imgNew[filtInf] = np.nan
651 imgNew[filtInf | filtNan] = np.nanmean(imgNew)
653 imgNew = np.fft.fft2(imgNew)
655 imgNew = np.fft.ifft2(imgNew)
658 imgNew[filtNan] = np.nan
659 imgNew[filtInf] = np.inf
664 """Task to be used as an ImageMapper for performing
665 A&L decorrelation on subimages on a grid across a A&L difference image.
667 This task subclasses DecorrelateALKernelTask in order to implement
668 all of that task
's configuration parameters, as well as its `run` method.
671 ConfigClass = DecorrelateALKernelConfig
672 _DefaultName = 'ip_diffim_decorrelateALKernelMapper'
675 DecorrelateALKernelTask.__init__(self, *args, **kwargs)
677 def run(self, subExposure, expandedSubExposure, fullBBox,
678 template, science, alTaskResult=None, psfMatchingKernel=None,
679 preConvKernel=None, **kwargs):
680 """Perform decorrelation operation on `subExposure`, using
681 `expandedSubExposure` to allow for invalid edge pixels arising
from
684 This method performs A&L decorrelation on `subExposure` using
685 local measures
for image variances
and PSF. `subExposure`
is a
686 sub-exposure of the non-decorrelated A&L diffim. It also
687 requires the corresponding sub-exposures of the template
688 (`template`)
and science (`science`) exposures.
693 the sub-exposure of the diffim
695 the expanded sub-exposure upon which to operate
697 the bounding box of the original exposure
699 the corresponding sub-exposure of the template exposure
701 the corresponding sub-exposure of the science exposure
702 alTaskResult : `lsst.pipe.base.Struct`
703 the result of A&L image differencing on `science`
and
704 `template`, importantly containing the resulting
705 `psfMatchingKernel`. Can be `
None`, only
if
706 `psfMatchingKernel`
is not `
None`.
707 psfMatchingKernel : Alternative parameter
for passing the
708 A&L `psfMatchingKernel` directly.
709 preConvKernel : If
not None, then pre-filtering was applied
710 to science exposure,
and this
is the pre-convolution
713 additional keyword arguments propagated
from
714 `ImageMapReduceTask.run`.
718 A `pipeBase.Struct` containing:
720 - ``subExposure`` : the result of the `subExposure` processing.
721 - ``decorrelationKernel`` : the decorrelation kernel, currently
726 This `run` method accepts parameters identical to those of
727 `ImageMapper.run`, since it
is called
from the
728 `ImageMapperTask`. See that
class for more information.
730 templateExposure = template
731 scienceExposure = science
732 if alTaskResult
is None and psfMatchingKernel
is None:
733 raise RuntimeError(
'Both alTaskResult and psfMatchingKernel cannot be None')
734 psfMatchingKernel = alTaskResult.psfMatchingKernel
if alTaskResult
is not None else psfMatchingKernel
738 subExp2 = scienceExposure.Factory(scienceExposure, expandedSubExposure.getBBox())
739 subExp1 = templateExposure.Factory(templateExposure, expandedSubExposure.getBBox())
742 logLevel = self.log.level
743 self.log.setLevel(self.log.WARNING)
744 res = DecorrelateALKernelTask.run(self, subExp2, subExp1, expandedSubExposure,
745 psfMatchingKernel, preConvKernel, **kwargs)
746 self.log.setLevel(logLevel)
748 diffim = res.correctedExposure.Factory(res.correctedExposure, subExposure.getBBox())
749 out = pipeBase.Struct(subExposure=diffim, )
754 """Configuration parameters for the ImageMapReduceTask to direct it to use
755 DecorrelateALKernelMapper as its mapper
for A&L decorrelation.
757 mapper = pexConfig.ConfigurableField(
758 doc='A&L decorrelation task to run on each sub-image',
759 target=DecorrelateALKernelMapper
764 """Configuration parameters for the DecorrelateALKernelSpatialTask.
766 decorrelateConfig = pexConfig.ConfigField(
767 dtype=DecorrelateALKernelConfig,
768 doc='DecorrelateALKernel config to use when running on complete exposure (non spatially-varying)',
771 decorrelateMapReduceConfig = pexConfig.ConfigField(
772 dtype=DecorrelateALKernelMapReduceConfig,
773 doc=
'DecorrelateALKernelMapReduce config to use when running on each sub-image (spatially-varying)',
776 ignoreMaskPlanes = pexConfig.ListField(
778 doc=
"""Mask planes to ignore for sigma-clipped statistics""",
779 default=(
"INTRP",
"EDGE",
"DETECTED",
"SAT",
"CR",
"BAD",
"NO_DATA",
"DETECTED_NEGATIVE")
790 """Decorrelate the effect of convolution by Alard-Lupton matching kernel in image difference
795 Pipe-task that removes the neighboring-pixel covariance in an
796 image difference that are added when the template image
is
797 convolved
with the Alard-Lupton PSF matching kernel.
799 This task
is a simple wrapper around
@ref DecorrelateALKernelTask,
800 which takes a `spatiallyVarying` parameter
in its `run` method. If
801 it
is `
False`, then it simply calls the `run` method of
@ref
802 DecorrelateALKernelTask. If it
is True, then it uses the
@ref
803 ImageMapReduceTask framework to
break the exposures into
804 subExposures on a grid,
and performs the `run` method of
@ref
805 DecorrelateALKernelTask on each subExposure. This enables it to
806 account
for spatially-varying PSFs
and noise
in the exposures when
807 performing the decorrelation.
809 This task has no standalone example, however it
is applied
as a
810 subtask of pipe.tasks.imageDifference.ImageDifferenceTask.
811 There
is also an example of its use
in `tests/testImageDecorrelation.py`.
813 ConfigClass = DecorrelateALKernelSpatialConfig
814 _DefaultName = "ip_diffim_decorrelateALKernelSpatial"
817 """Create the image decorrelation Task
822 arguments to be passed to
823 `lsst.pipe.base.task.Task.__init__`
825 additional keyword arguments to be passed to
826 `lsst.pipe.base.task.Task.__init__`
828 pipeBase.Task.__init__(self, *args, **kwargs)
833 self.statsControl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.ignoreMaskPlanes))
836 """Compute the mean of the variance plane of `exposure`.
839 exposure.getMaskedImage().getMask(),
841 var = statObj.getValue(afwMath.MEANCLIP)
844 def run(self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel,
845 spatiallyVarying=True, preConvKernel=None, templateMatched=True, preConvMode=False):
846 """Perform decorrelation of an image difference exposure.
848 Decorrelates the diffim due to the convolution of the
849 templateExposure with the A&L psfMatchingKernel. If
850 `spatiallyVarying`
is True, it utilizes the spatially varying
851 matching kernel via the `imageMapReduce` framework to perform
852 spatially-varying decorrelation on a grid of subExposures.
857 the science Exposure used
for PSF matching
859 the template Exposure used
for PSF matching
861 the subtracted Exposure produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()`
862 psfMatchingKernel : an (optionally spatially-varying) PSF matching kernel produced
863 by `ip_diffim.ImagePsfMatchTask.subtractExposures()`
864 spatiallyVarying : `bool`
865 if True, perform the spatially-varying operation
866 preConvKernel : `lsst.meas.algorithms.Psf`
867 if not none, the scienceExposure has been pre-filtered
with this kernel. (Currently
868 this option
is experimental.)
869 templateMatched : `bool`, optional
870 If
True, the template exposure was matched (convolved) to the science exposure.
871 preConvMode : `bool`, optional
872 If
True, ``subtractedExposure``
is assumed to be a likelihood difference image
873 and will be noise corrected
as a likelihood image.
877 results : `lsst.pipe.base.Struct`
878 a structure containing:
879 - ``correctedExposure`` : the decorrelated diffim
881 self.log.info('Running A&L decorrelation: spatiallyVarying=%r', spatiallyVarying)
885 if np.isnan(svar)
or np.isnan(tvar):
887 if (np.all(np.isnan(scienceExposure.image.array))
888 or np.all(np.isnan(templateExposure.image.array))):
889 self.log.warning(
'Template or science image is entirely NaNs: skipping decorrelation.')
898 self.log.info(
"Variance (science, template): (%f, %f)", svar, tvar)
899 self.log.info(
"Variance (uncorrected diffim): %f", var)
900 config = self.config.decorrelateMapReduceConfig
902 results = task.run(subtractedExposure, science=scienceExposure,
903 template=templateExposure, psfMatchingKernel=psfMatchingKernel,
904 preConvKernel=preConvKernel, forceEvenSized=
True,
905 templateMatched=templateMatched, preConvMode=preConvMode)
906 results.correctedExposure = results.exposure
910 return exp.getMaskedImage().getMask()
911 gm(results.correctedExposure)[:, :] = gm(subtractedExposure)
914 self.log.info(
"Variance (corrected diffim): %f", var)
917 config = self.config.decorrelateConfig
919 results = task.run(scienceExposure, templateExposure,
920 subtractedExposure, psfMatchingKernel, preConvKernel=preConvKernel,
921 templateMatched=templateMatched, preConvMode=preConvMode)
def __init__(self, *args, **kwargs)
def run(self, subExposure, expandedSubExposure, fullBBox, template, science, alTaskResult=None, psfMatchingKernel=None, preConvKernel=None, **kwargs)
decorrelateMapReduceConfig
def run(self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel, spatiallyVarying=True, preConvKernel=None, templateMatched=True, preConvMode=False)
def computeVarianceMean(self, exposure)
def __init__(self, *args, **kwargs)
def computeCorrectedImage(self, corrft, imgOld)
def computeScoreCorrection(self, kappa, svar, tvar, preConvArr)
def __init__(self, *args, **kwargs)
def computeCorrectedDiffimPsf(self, corrft, psfOld)
def estimateVariancePlane(vplane1, vplane2, c1ft, c2ft)
def computeDiffimCorrection(self, kappa, svar, tvar)
def computeCommonShape(self, *shapes)
def calculateVariancePlane(self, vplane1, vplane2, varMean1, varMean2, c1ft, c2ft)
def computeVarianceMean(self, exposure)
def padCenterOriginArray(A, tuple newShape, useInverse=False)
def run(self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel, preConvKernel=None, xcen=None, ycen=None, svar=None, tvar=None, templateMatched=True, preConvMode=False, **kwargs)
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())