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 preConvKernel = scienceExposure.getPsf().getLocalKernel()
223 preConvImg = afwImage.ImageD(preConvKernel.getDimensions())
224 preConvKernel.computeImage(preConvImg,
True)
230 self.log.info(
"Original variance plane means. Science:%.5e, warped template:%.5e)",
236 if np.isnan(svar)
or np.isnan(tvar):
238 if (np.all(np.isnan(scienceExposure.image.array))
239 or np.all(np.isnan(templateExposure.image.array))):
240 self.log.warning(
'Template or science image is entirely NaNs: skipping decorrelation.')
241 outExposure = subtractedExposure.clone()
242 return pipeBase.Struct(correctedExposure=outExposure, )
246 self.log.info(
"Decorrelation after template image convolution")
248 targetVarianceMean = tvar
250 variance = scienceExposure.variance.array
252 targetVariance = templateExposure.variance.array
255 self.log.info(
"Decorrelation after science image convolution")
257 targetVarianceMean = svar
259 variance = templateExposure.variance.array
261 targetVariance = scienceExposure.variance.array
265 mOverExpVar = targetVarianceMean/varianceMean
266 if mOverExpVar > 1e8:
267 self.log.warning(
"Diverging correction: matched image variance is "
268 " much larger than the unconvolved one's"
269 ", targetVarianceMean/varianceMean:%.2e", mOverExpVar)
272 self.log.info(
"Variance plane mean of uncorrected diffim: %f", oldVarMean)
275 diffimShape = subtractedExposure.image.array.shape
276 psfImg = subtractedExposure.getPsf().computeKernelImage(
geom.Point2D(xcen, ycen))
277 psfShape = psfImg.array.shape
280 self.log.info(
"Decorrelation of likelihood image")
282 psfShape, diffimShape)
285 self.log.info(
"Decorrelation of difference image")
295 if self.config.completeVarPlanePropagation:
296 self.log.debug(
"Using full variance plane calculation in decorrelation")
298 variance, targetVariance,
299 varianceMean, targetVarianceMean, corr.cnft, corr.crft)
301 self.log.debug(
"Using estimated variance plane calculation in decorrelation")
303 variance, targetVariance,
304 corr.cnft, corr.crft)
309 self.log.debug(
"Matching kernel sum: %.3e", kSum)
310 if not templateMatched:
313 correctedVariance /= kSumSq
314 subtractedExposure.image.array[...] = correctedImage
315 subtractedExposure.variance.array[...] = correctedVariance
316 subtractedExposure.setPsf(correctedPsf)
319 self.log.info(
"Variance plane mean of corrected diffim: %.5e", newVarMean)
323 return pipeBase.Struct(correctedExposure=subtractedExposure, )
326 """Calculate the common shape for FFT operations. Set `self.freqSpaceShape`
331 shapes : one or more `tuple` of `int`
332 Shapes of the arrays. All must have the same dimensionality.
333 At least one shape must be provided.
341 For each dimension, gets the smallest even number greater than
or equal to
342 `N1+N2-1` where `N1`
and `N2` are the two largest values.
343 In case of only one shape given, rounds up to even each dimension value.
345 S = np.array(shapes, dtype=int)
350 commonShape = np.sum(S, axis=0) - 1
353 commonShape[commonShape % 2 != 0] += 1
355 self.log.info(
"Common frequency space shape %s", self.
freqSpaceShape)
359 """Zero pad an image where the origin is at the center and replace the
360 origin to the corner as required by the periodic input of FFT. Implement also
361 the inverse operation, crop the padding
and re-center data.
366 An array to copy
from.
367 newShape : `tuple` of `int`
368 The dimensions of the resulting array. For padding, the resulting array
369 must be larger than A
in each dimension. For the inverse operation this
370 must be the original, before padding size of the array.
371 useInverse : bool, optional
372 Selector of forward, add padding, operation (
False)
373 or its inverse, crop padding, operation (
True).
378 The padded
or unpadded array
with shape of `newShape`
and the same dtype
as A.
382 For odd dimensions, the splitting
is rounded to
383 put the center pixel into the new corner origin (0,0). This
is to be consistent
384 e.g.
for a dirac delta kernel that
is originally located at the center pixel.
391 firstHalves = [x//2
for x
in A.shape]
392 secondHalves = [x-y
for x, y
in zip(A.shape, firstHalves)]
395 secondHalves = [x//2
for x
in newShape]
396 firstHalves = [x-y
for x, y
in zip(newShape, secondHalves)]
398 R = np.zeros_like(A, shape=newShape)
399 R[-firstHalves[0]:, -firstHalves[1]:] = A[:firstHalves[0], :firstHalves[1]]
400 R[:secondHalves[0], -firstHalves[1]:] = A[-secondHalves[0]:, :firstHalves[1]]
401 R[:secondHalves[0], :secondHalves[1]] = A[-secondHalves[0]:, -secondHalves[1]:]
402 R[-firstHalves[0]:, :secondHalves[1]] = A[:firstHalves[0], -secondHalves[1]:]
406 """Compute the Lupton decorrelation post-convolution kernel for decorrelating an
407 image difference, based on the PSF-matching kernel.
411 kappa : `numpy.ndarray` of `float`
412 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
414 Average variance of science image used
for PSF matching.
416 Average variance of the template (matched) image used
for PSF matching.
420 corrft : `numpy.ndarray` of `float`
421 The frequency space representation of the correction. The array
is real (dtype float).
424 cnft, crft : `numpy.ndarray` of `complex`
425 The overall convolution (pre-conv, PSF matching, noise correction) kernel
426 for the science
and template images, respectively
for the variance plane
427 calculations. These are intermediate results
in frequency space.
431 The maximum correction factor converges to `sqrt(tvar/svar)` towards high frequencies.
432 This should be a plausible value.
436 kft = np.fft.fft2(kappa)
437 kftAbsSq = np.real(np.conj(kft) * kft)
439 denom = svar + tvar * kftAbsSq
440 corrft = np.sqrt((svar + tvar * kSum*kSum) / denom)
443 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
446 """Compute the correction kernel for a score image.
450 kappa : `numpy.ndarray`
451 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
453 Average variance of science image used
for PSF matching (before pre-convolution).
455 Average variance of the template (matched) image used
for PSF matching.
456 preConvArr : `numpy.ndarray`
457 The pre-convolution kernel of the science image. It should be the PSF
458 of the science image
or an approximation of it. It must be normed to sum 1.
462 corrft : `numpy.ndarray` of `float`
463 The frequency space representation of the correction. The array
is real (dtype float).
465 cnft, crft : `numpy.ndarray` of `complex`
466 The overall convolution (pre-conv, PSF matching, noise correction) kernel
467 for the science
and template images, respectively
for the variance plane
468 calculations. These are intermediate results
in frequency space.
472 To be precise, the science image should be _correlated_ by ``preConvArray`` but this
473 does
not matter
for this calculation.
475 ``cnft``, ``crft`` contain the scaling factor
as well.
480 kft = np.fft.fft2(kappa)
482 preFt = np.fft.fft2(preConvArr)
483 preFtAbsSq = np.real(np.conj(preFt) * preFt)
484 kftAbsSq = np.real(np.conj(kft) * kft)
487 tiny = np.finfo(preFtAbsSq.dtype).tiny * 1000.
488 flt = preFtAbsSq < tiny
492 preFtAbsSq[flt] = tiny
493 denom = svar + tvar * kftAbsSq / preFtAbsSq
494 corrft = (svar + tvar * kSum*kSum) / denom
495 cnft = np.conj(preFt)*corrft
497 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
501 """Estimate the variance planes.
503 The estimation assumes that around each pixel the surrounding
504 pixels' sigmas within the convolution kernel are the same.
508 vplane1, vplane2 : `numpy.ndarray` of `float`
509 Variance planes of the original (before pre-convolution or matching)
511 c1ft, c2ft : `numpy.ndarray` of `complex`
512 The overall convolution that includes the matching
and the
513 afterburner
in frequency space. The result of either
514 ``computeScoreCorrection``
or ``computeDiffimCorrection``.
518 vplaneD : `numpy.ndarray` of `float`
519 The estimated variance plane of the difference/score image
520 as a weighted sum of the input variances.
524 See DMTN-179 Section 5 about the variance plane calculations.
526 w1 = np.sum(np.real(np.conj(c1ft)*c1ft)) / c1ft.size
527 w2 = np.sum(np.real(np.conj(c2ft)*c2ft)) / c2ft.size
530 return vplane1*w1 + vplane2*w2
533 """Full propagation of the variance planes of the original exposures.
535 The original variance planes of independent pixels are convolved with the
536 image space square of the overall kernels.
540 vplane1, vplane2 : `numpy.ndarray` of `float`
541 Variance planes of the original (before pre-convolution
or matching)
543 varMean1, varMean2 : `float`
544 Replacement average values
for non-finite ``vplane1``
and ``vplane2`` values respectively.
546 c1ft, c2ft : `numpy.ndarray` of `complex`
547 The overall convolution that includes the matching
and the
548 afterburner
in frequency space. The result of either
549 ``computeScoreCorrection``
or ``computeDiffimCorrection``.
553 vplaneD : `numpy.ndarray` of `float`
554 The variance plane of the difference/score images.
558 See DMTN-179 Section 5 about the variance plane calculations.
560 Infs
and NaNs are allowed
and kept
in the returned array.
562 D = np.real(np.fft.ifft2(c1ft))
563 c1SqFt = np.fft.fft2(D*D)
565 v1shape = vplane1.shape
566 filtInf = np.isinf(vplane1)
567 filtNan = np.isnan(vplane1)
569 vplane1 = np.copy(vplane1)
570 vplane1[filtInf | filtNan] = varMean1
572 v1 = np.real(np.fft.ifft2(np.fft.fft2(D) * c1SqFt))
577 D = np.real(np.fft.ifft2(c2ft))
578 c2SqFt = np.fft.fft2(D*D)
580 v2shape = vplane2.shape
581 filtInf = np.isinf(vplane2)
582 filtNan = np.isnan(vplane2)
583 vplane2 = np.copy(vplane2)
584 vplane2[filtInf | filtNan] = varMean2
586 v2 = np.real(np.fft.ifft2(np.fft.fft2(D) * c2SqFt))
594 """Compute the (decorrelated) difference image's new PSF.
598 corrft : `numpy.ndarray`
599 The frequency space representation of the correction calculated by
601 psfOld : `numpy.ndarray`
602 The psf of the difference image to be corrected.
606 correctedPsf : `lsst.meas.algorithms.KernelPsf`
607 The corrected psf, same shape as `psfOld`, sum normed to 1.
611 There
is no algorithmic guarantee that the corrected psf can
612 meaningfully fit to the same size
as the original one.
614 psfShape = psfOld.shape
616 psfNew = np.fft.fft2(psfNew)
618 psfNew = np.fft.ifft2(psfNew)
621 psfNew = psfNew/psfNew.sum()
623 psfcI = afwImage.ImageD(
geom.Extent2I(psfShape[1], psfShape[0]))
626 correctedPsf = measAlg.KernelPsf(psfcK)
630 """Compute the decorrelated difference image.
634 corrft : `numpy.ndarray`
635 The frequency space representation of the correction calculated by
637 imgOld : `numpy.ndarray`
638 The difference image to be corrected.
642 imgNew : `numpy.ndarray`
643 The corrected image, same size as the input.
645 expShape = imgOld.shape
646 imgNew = np.copy(imgOld)
647 filtInf = np.isinf(imgNew)
648 filtNan = np.isnan(imgNew)
649 imgNew[filtInf] = np.nan
650 imgNew[filtInf | filtNan] = np.nanmean(imgNew)
652 imgNew = np.fft.fft2(imgNew)
654 imgNew = np.fft.ifft2(imgNew)
657 imgNew[filtNan] = np.nan
658 imgNew[filtInf] = np.inf
663 """Task to be used as an ImageMapper for performing
664 A&L decorrelation on subimages on a grid across a A&L difference image.
666 This task subclasses DecorrelateALKernelTask in order to implement
667 all of that task
's configuration parameters, as well as its `run` method.
670 ConfigClass = DecorrelateALKernelConfig
671 _DefaultName = 'ip_diffim_decorrelateALKernelMapper'
674 DecorrelateALKernelTask.__init__(self, *args, **kwargs)
676 def run(self, subExposure, expandedSubExposure, fullBBox,
677 template, science, alTaskResult=None, psfMatchingKernel=None,
678 preConvKernel=None, **kwargs):
679 """Perform decorrelation operation on `subExposure`, using
680 `expandedSubExposure` to allow for invalid edge pixels arising
from
683 This method performs A&L decorrelation on `subExposure` using
684 local measures
for image variances
and PSF. `subExposure`
is a
685 sub-exposure of the non-decorrelated A&L diffim. It also
686 requires the corresponding sub-exposures of the template
687 (`template`)
and science (`science`) exposures.
692 the sub-exposure of the diffim
694 the expanded sub-exposure upon which to operate
696 the bounding box of the original exposure
698 the corresponding sub-exposure of the template exposure
700 the corresponding sub-exposure of the science exposure
701 alTaskResult : `lsst.pipe.base.Struct`
702 the result of A&L image differencing on `science`
and
703 `template`, importantly containing the resulting
704 `psfMatchingKernel`. Can be `
None`, only
if
705 `psfMatchingKernel`
is not `
None`.
706 psfMatchingKernel : Alternative parameter
for passing the
707 A&L `psfMatchingKernel` directly.
708 preConvKernel : If
not None, then pre-filtering was applied
709 to science exposure,
and this
is the pre-convolution
712 additional keyword arguments propagated
from
713 `ImageMapReduceTask.run`.
717 A `pipeBase.Struct` containing:
719 - ``subExposure`` : the result of the `subExposure` processing.
720 - ``decorrelationKernel`` : the decorrelation kernel, currently
725 This `run` method accepts parameters identical to those of
726 `ImageMapper.run`, since it
is called
from the
727 `ImageMapperTask`. See that
class for more information.
729 templateExposure = template
730 scienceExposure = science
731 if alTaskResult
is None and psfMatchingKernel
is None:
732 raise RuntimeError(
'Both alTaskResult and psfMatchingKernel cannot be None')
733 psfMatchingKernel = alTaskResult.psfMatchingKernel
if alTaskResult
is not None else psfMatchingKernel
737 subExp2 = scienceExposure.Factory(scienceExposure, expandedSubExposure.getBBox())
738 subExp1 = templateExposure.Factory(templateExposure, expandedSubExposure.getBBox())
741 logLevel = self.log.level
742 self.log.setLevel(self.log.WARNING)
743 res = DecorrelateALKernelTask.run(self, subExp2, subExp1, expandedSubExposure,
744 psfMatchingKernel, preConvKernel, **kwargs)
745 self.log.setLevel(logLevel)
747 diffim = res.correctedExposure.Factory(res.correctedExposure, subExposure.getBBox())
748 out = pipeBase.Struct(subExposure=diffim, )
753 """Configuration parameters for the ImageMapReduceTask to direct it to use
754 DecorrelateALKernelMapper as its mapper
for A&L decorrelation.
756 mapper = pexConfig.ConfigurableField(
757 doc='A&L decorrelation task to run on each sub-image',
758 target=DecorrelateALKernelMapper
763 """Configuration parameters for the DecorrelateALKernelSpatialTask.
765 decorrelateConfig = pexConfig.ConfigField(
766 dtype=DecorrelateALKernelConfig,
767 doc='DecorrelateALKernel config to use when running on complete exposure (non spatially-varying)',
770 decorrelateMapReduceConfig = pexConfig.ConfigField(
771 dtype=DecorrelateALKernelMapReduceConfig,
772 doc=
'DecorrelateALKernelMapReduce config to use when running on each sub-image (spatially-varying)',
775 ignoreMaskPlanes = pexConfig.ListField(
777 doc=
"""Mask planes to ignore for sigma-clipped statistics""",
778 default=(
"INTRP",
"EDGE",
"DETECTED",
"SAT",
"CR",
"BAD",
"NO_DATA",
"DETECTED_NEGATIVE")
789 """Decorrelate the effect of convolution by Alard-Lupton matching kernel in image difference
794 Pipe-task that removes the neighboring-pixel covariance in an
795 image difference that are added when the template image
is
796 convolved
with the Alard-Lupton PSF matching kernel.
798 This task
is a simple wrapper around
@ref DecorrelateALKernelTask,
799 which takes a `spatiallyVarying` parameter
in its `run` method. If
800 it
is `
False`, then it simply calls the `run` method of
@ref
801 DecorrelateALKernelTask. If it
is True, then it uses the
@ref
802 ImageMapReduceTask framework to
break the exposures into
803 subExposures on a grid,
and performs the `run` method of
@ref
804 DecorrelateALKernelTask on each subExposure. This enables it to
805 account
for spatially-varying PSFs
and noise
in the exposures when
806 performing the decorrelation.
808 This task has no standalone example, however it
is applied
as a
809 subtask of pipe.tasks.imageDifference.ImageDifferenceTask.
810 There
is also an example of its use
in `tests/testImageDecorrelation.py`.
812 ConfigClass = DecorrelateALKernelSpatialConfig
813 _DefaultName = "ip_diffim_decorrelateALKernelSpatial"
816 """Create the image decorrelation Task
821 arguments to be passed to
822 `lsst.pipe.base.task.Task.__init__`
824 additional keyword arguments to be passed to
825 `lsst.pipe.base.task.Task.__init__`
827 pipeBase.Task.__init__(self, *args, **kwargs)
832 self.statsControl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.ignoreMaskPlanes))
835 """Compute the mean of the variance plane of `exposure`.
838 exposure.getMaskedImage().getMask(),
840 var = statObj.getValue(afwMath.MEANCLIP)
843 def run(self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel,
844 spatiallyVarying=True, preConvKernel=None, templateMatched=True, preConvMode=False):
845 """Perform decorrelation of an image difference exposure.
847 Decorrelates the diffim due to the convolution of the
848 templateExposure with the A&L psfMatchingKernel. If
849 `spatiallyVarying`
is True, it utilizes the spatially varying
850 matching kernel via the `imageMapReduce` framework to perform
851 spatially-varying decorrelation on a grid of subExposures.
856 the science Exposure used
for PSF matching
858 the template Exposure used
for PSF matching
860 the subtracted Exposure produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()`
861 psfMatchingKernel : an (optionally spatially-varying) PSF matching kernel produced
862 by `ip_diffim.ImagePsfMatchTask.subtractExposures()`
863 spatiallyVarying : `bool`
864 if True, perform the spatially-varying operation
865 preConvKernel : `lsst.meas.algorithms.Psf`
866 if not none, the scienceExposure has been pre-filtered
with this kernel. (Currently
867 this option
is experimental.)
868 templateMatched : `bool`, optional
869 If
True, the template exposure was matched (convolved) to the science exposure.
870 preConvMode : `bool`, optional
871 If
True, ``subtractedExposure``
is assumed to be a likelihood difference image
872 and will be noise corrected
as a likelihood image.
876 results : `lsst.pipe.base.Struct`
877 a structure containing:
878 - ``correctedExposure`` : the decorrelated diffim
880 self.log.info('Running A&L decorrelation: spatiallyVarying=%r', spatiallyVarying)
884 if np.isnan(svar)
or np.isnan(tvar):
886 if (np.all(np.isnan(scienceExposure.image.array))
887 or np.all(np.isnan(templateExposure.image.array))):
888 self.log.warning(
'Template or science image is entirely NaNs: skipping decorrelation.')
897 self.log.info(
"Variance (science, template): (%f, %f)", svar, tvar)
898 self.log.info(
"Variance (uncorrected diffim): %f", var)
899 config = self.config.decorrelateMapReduceConfig
901 results = task.run(subtractedExposure, science=scienceExposure,
902 template=templateExposure, psfMatchingKernel=psfMatchingKernel,
903 preConvKernel=preConvKernel, forceEvenSized=
True,
904 templateMatched=templateMatched, preConvMode=preConvMode)
905 results.correctedExposure = results.exposure
909 return exp.getMaskedImage().getMask()
910 gm(results.correctedExposure)[:, :] = gm(subtractedExposure)
913 self.log.info(
"Variance (corrected diffim): %f", var)
916 config = self.config.decorrelateConfig
918 results = task.run(scienceExposure, templateExposure,
919 subtractedExposure, psfMatchingKernel, preConvKernel=preConvKernel,
920 templateMatched=templateMatched, preConvMode=preConvMode)
def __init__(self, *args, **kwargs)
def run(self, subExposure, expandedSubExposure, fullBBox, template, science, alTaskResult=None, psfMatchingKernel=None, preConvKernel=None, **kwargs)
pexConfig 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())