28import lsst.meas.algorithms
as measAlg
30import lsst.pipe.base
as pipeBase
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.statsControlstatsControl.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 into the science exposure dimensions.
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``. Otherwise the ``scienceExposure``
185 was matched (convolved) by ``psfMatchingKernel``.
187 This task discards the variance plane of ``subtractedExposure``
and re-computes
188 it
from the variance planes of ``scienceExposure``
and ``templateExposure``.
189 The image plane of ``subtractedExposure`` must be at the photometric level
190 set by the AL PSF matching
in `ImagePsfMatchTask.subtractExposures`.
191 The assumptions about the photometric level are controlled by the
192 `templateMatched` option
in this task.
194 Here we currently convert a spatially-varying matching kernel into a constant kernel,
195 just by computing it at the center of the image (tickets DM-6243, DM-6244).
197 We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute
198 the decorrelation kernel.
200 TODO DM-23857 As part of the spatially varying correction implementation
201 consider whether returning a Struct
is still necessary.
203 if preConvKernel
is not None and not (templateMatched
and preConvMode):
204 raise ValueError(
"Pre-convolution kernel is allowed only if "
205 "preConvMode==True and templateMatched==True.")
207 spatialKernel = psfMatchingKernel
208 kimg = afwImage.ImageD(spatialKernel.getDimensions())
209 bbox = subtractedExposure.getBBox()
211 xcen = (bbox.getBeginX() + bbox.getEndX()) / 2.
213 ycen = (bbox.getBeginY() + bbox.getEndY()) / 2.
214 self.log.info(
"Using matching kernel computed at (%d, %d)", xcen, ycen)
215 spatialKernel.computeImage(kimg,
False, xcen, ycen)
219 if preConvKernel
is None:
220 preConvKernel = scienceExposure.getPsf().getLocalKernel()
221 preConvImg = afwImage.ImageD(preConvKernel.getDimensions())
222 preConvKernel.computeImage(preConvImg,
True)
228 self.log.info(
"Original variance plane means. Science:%.5e, warped template:%.5e)",
233 self.log.info(
"Decorrelation after template image convolution")
236 exposure = scienceExposure
237 matchedExposure = templateExposure
240 self.log.info(
"Decorrelation after science image convolution")
243 exposure = templateExposure
244 matchedExposure = scienceExposure
249 if np.isnan(expVar)
or np.isnan(matchedVar):
251 if (np.all(np.isnan(exposure.image.array))
252 or np.all(np.isnan(matchedExposure.image.array))):
253 self.log.warning(
'Template or science image is entirely NaNs: skipping decorrelation.')
254 outExposure = subtractedExposure.clone()
255 return pipeBase.Struct(correctedExposure=outExposure, )
259 mOverExpVar = matchedVar/expVar
260 if mOverExpVar > 1e8:
261 self.log.warning(
"Diverging correction: matched image variance is "
262 " much larger than the unconvolved one's"
263 ", matchedVar/expVar:%.2e", mOverExpVar)
266 self.log.info(
"Variance plane mean of uncorrected diffim: %f", oldVarMean)
269 diffExpArr = subtractedExposure.image.array
270 psfImg = subtractedExposure.getPsf().computeKernelImage(
geom.Point2D(xcen, ycen))
271 psfDim = psfImg.getDimensions()
272 psfArr = psfImg.array
277 self.log.debug(
"Matching kernel sum: %.3e", kSum)
280 self.log.info(
"Decorrelation of likelihood image")
282 psfArr.shape, diffExpArr.shape)
285 self.log.info(
"Decorrelation of difference image")
292 psfcI = afwImage.ImageD(psfDim)
293 psfcI.array = corrPsfArr
295 psfNew = measAlg.KernelPsf(psfcK)
297 correctedExposure = subtractedExposure.clone()
298 correctedExposure.image.array[...] = diffExpArr
302 if self.config.completeVarPlanePropagation:
303 self.log.debug(
"Using full variance plane calculation in decorrelation")
305 exposure.variance.array, matchedExposure.variance.array,
306 expVar, matchedVar, corr.cnft, corr.crft)
308 self.log.debug(
"Using estimated variance plane calculation in decorrelation")
310 exposure.variance.array, matchedExposure.variance.array,
311 corr.cnft, corr.crft)
313 corrExpVarArr = correctedExposure.variance.array
314 corrExpVarArr[...] = newVarArr
316 if not templateMatched:
319 corrExpVarArr /= kSumSq
320 correctedExposure.setPsf(psfNew)
323 self.log.info(
"Variance plane mean of corrected diffim: %.5e", newVarMean)
327 return pipeBase.Struct(correctedExposure=correctedExposure, )
330 """Calculate the common shape for FFT operations. Set `self.freqSpaceShape`
335 shapes : one or more `tuple` of `int`
336 Shapes of the arrays. All must have the same dimensionality.
337 At least one shape must be provided.
345 For each dimension, gets the smallest even number greater than
or equal to
346 `N1+N2-1` where `N1`
and `N2` are the two largest values.
347 In case of only one shape given, rounds up to even each dimension value.
349 S = np.array(shapes, dtype=int)
354 commonShape = np.sum(S, axis=0) - 1
357 commonShape[commonShape % 2 != 0] += 1
359 self.log.info(
"Common frequency space shape %s", self.
freqSpaceShapefreqSpaceShape)
363 """Zero pad an image where the origin is at the center and replace the
364 origin to the corner as required by the periodic input of FFT. Implement also
365 the inverse operation, crop the padding
and re-center data.
370 An array to copy
from.
371 newShape : `tuple` of `int`
372 The dimensions of the resulting array. For padding, the resulting array
373 must be larger than A
in each dimension. For the inverse operation this
374 must be the original, before padding size of the array.
375 useInverse : bool, optional
376 Selector of forward, add padding, operation (
False)
377 or its inverse, crop padding, operation (
True).
382 The padded
or unpadded array
with shape of `newShape`
and the same dtype
as A.
386 For odd dimensions, the splitting
is rounded to
387 put the center pixel into the new corner origin (0,0). This
is to be consistent
388 e.g.
for a dirac delta kernel that
is originally located at the center pixel.
395 firstHalves = [x//2
for x
in A.shape]
396 secondHalves = [x-y
for x, y
in zip(A.shape, firstHalves)]
399 secondHalves = [x//2
for x
in newShape]
400 firstHalves = [x-y
for x, y
in zip(newShape, secondHalves)]
402 R = np.zeros_like(A, shape=newShape)
403 R[-firstHalves[0]:, -firstHalves[1]:] = A[:firstHalves[0], :firstHalves[1]]
404 R[:secondHalves[0], -firstHalves[1]:] = A[-secondHalves[0]:, :firstHalves[1]]
405 R[:secondHalves[0], :secondHalves[1]] = A[-secondHalves[0]:, -secondHalves[1]:]
406 R[-firstHalves[0]:, :secondHalves[1]] = A[:firstHalves[0], -secondHalves[1]:]
410 """Compute the Lupton decorrelation post-convolution kernel for decorrelating an
411 image difference, based on the PSF-matching kernel.
415 kappa : `numpy.ndarray` of `float`
416 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
418 Average variance of science image used
for PSF matching.
420 Average variance of the template (matched) image used
for PSF matching.
424 corrft : `numpy.ndarray` of `float`
425 The frequency space representation of the correction. The array
is real (dtype float).
428 cnft, crft : `numpy.ndarray` of `complex`
429 The overall convolution (pre-conv, PSF matching, noise correction) kernel
430 for the science
and template images, respectively
for the variance plane
431 calculations. These are intermediate results
in frequency space.
435 The maximum correction factor converges to `sqrt(tvar/svar)` towards high frequencies.
436 This should be a plausible value.
440 kft = np.fft.fft2(kappa)
441 kftAbsSq = np.real(np.conj(kft) * kft)
443 denom = svar + tvar * kftAbsSq
444 corrft = np.sqrt((svar + tvar * kSum*kSum) / denom)
447 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
450 """Compute the correction kernel for a score image.
454 kappa : `numpy.ndarray`
455 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
457 Average variance of science image used
for PSF matching (before pre-convolution).
459 Average variance of the template (matched) image used
for PSF matching.
460 preConvArr : `numpy.ndarray`
461 The pre-convolution kernel of the science image. It should be the PSF
462 of the science image
or an approximation of it. It must be normed to sum 1.
466 corrft : `numpy.ndarray` of `float`
467 The frequency space representation of the correction. The array
is real (dtype float).
469 cnft, crft : `numpy.ndarray` of `complex`
470 The overall convolution (pre-conv, PSF matching, noise correction) kernel
471 for the science
and template images, respectively
for the variance plane
472 calculations. These are intermediate results
in frequency space.
476 To be precise, the science image should be _correlated_ by ``preConvArray`` but this
477 does
not matter
for this calculation.
479 ``cnft``, ``crft`` contain the scaling factor
as well.
484 kft = np.fft.fft2(kappa)
486 preFt = np.fft.fft2(preConvArr)
487 preFtAbsSq = np.real(np.conj(preFt) * preFt)
488 kftAbsSq = np.real(np.conj(kft) * kft)
491 tiny = np.finfo(preFtAbsSq.dtype).tiny * 1000.
492 flt = preFtAbsSq < tiny
496 preFtAbsSq[flt] = tiny
497 denom = svar + tvar * kftAbsSq / preFtAbsSq
498 corrft = (svar + tvar * kSum*kSum) / denom
499 cnft = np.conj(preFt)*corrft
501 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
505 """Estimate the variance planes.
507 The estimation assumes that around each pixel the surrounding
508 pixels' sigmas within the convolution kernel are the same.
512 vplane1, vplane2 : `numpy.ndarray` of `float`
513 Variance planes of the original (before pre-convolution or matching)
515 c1ft, c2ft : `numpy.ndarray` of `complex`
516 The overall convolution that includes the matching
and the
517 afterburner
in frequency space. The result of either
518 ``computeScoreCorrection``
or ``computeDiffimCorrection``.
522 vplaneD : `numpy.ndarray` of `float`
523 The estimated variance plane of the difference/score image
524 as a weighted sum of the input variances.
528 See DMTN-179 Section 5 about the variance plane calculations.
530 w1 = np.sum(np.real(np.conj(c1ft)*c1ft)) / c1ft.size
531 w2 = np.sum(np.real(np.conj(c2ft)*c2ft)) / c2ft.size
534 return vplane1*w1 + vplane2*w2
537 """Full propagation of the variance planes of the original exposures.
539 The original variance planes of independent pixels are convolved with the
540 image space square of the overall kernels.
544 vplane1, vplane2 : `numpy.ndarray` of `float`
545 Variance planes of the original (before pre-convolution
or matching)
547 varMean1, varMean2 : `float`
548 Replacement average values
for non-finite ``vplane1``
and ``vplane2`` values respectively.
550 c1ft, c2ft : `numpy.ndarray` of `complex`
551 The overall convolution that includes the matching
and the
552 afterburner
in frequency space. The result of either
553 ``computeScoreCorrection``
or ``computeDiffimCorrection``.
557 vplaneD : `numpy.ndarray` of `float`
558 The variance plane of the difference/score images.
562 See DMTN-179 Section 5 about the variance plane calculations.
564 Infs
and NaNs are allowed
and kept
in the returned array.
566 D = np.real(np.fft.ifft2(c1ft))
567 c1SqFt = np.fft.fft2(D*D)
569 v1shape = vplane1.shape
570 filtInf = np.isinf(vplane1)
571 filtNan = np.isnan(vplane1)
573 vplane1 = np.copy(vplane1)
574 vplane1[filtInf | filtNan] = varMean1
576 v1 = np.real(np.fft.ifft2(np.fft.fft2(D) * c1SqFt))
581 D = np.real(np.fft.ifft2(c2ft))
582 c2ft = np.fft.fft2(D*D)
584 v2shape = vplane2.shape
585 filtInf = np.isinf(vplane2)
586 filtNan = np.isnan(vplane2)
587 vplane2 = np.copy(vplane2)
588 vplane2[filtInf | filtNan] = varMean2
590 v2 = np.real(np.fft.ifft2(np.fft.fft2(D) * c2ft))
598 """Compute the (decorrelated) difference image's new PSF.
602 corrft : `numpy.ndarray`
603 The frequency space representation of the correction calculated by
604 `computeCorrection`. Shape must be `self.freqSpaceShapefreqSpaceShape`.
605 psfOld : `numpy.ndarray`
606 The psf of the difference image to be corrected.
610 psfNew : `numpy.ndarray`
611 The corrected psf, same shape as `psfOld`, sum normed to 1.
615 There
is no algorithmic guarantee that the corrected psf can
616 meaningfully fit to the same size
as the original one.
618 psfShape = psfOld.shape
620 psfNew = np.fft.fft2(psfNew)
622 psfNew = np.fft.ifft2(psfNew)
625 psfNew = psfNew/psfNew.sum()
629 """Compute the decorrelated difference image.
633 corrft : `numpy.ndarray`
634 The frequency space representation of the correction calculated by
635 `computeCorrection`. Shape must be `self.freqSpaceShapefreqSpaceShape`.
636 imgOld : `numpy.ndarray`
637 The difference image to be corrected.
641 imgNew : `numpy.ndarray`
642 The corrected image, same size as the input.
644 expShape = imgOld.shape
645 imgNew = np.copy(imgOld)
646 filtInf = np.isinf(imgNew)
647 filtNan = np.isnan(imgNew)
648 imgNew[filtInf] = np.nan
649 imgNew[filtInf | filtNan] = np.nanmean(imgNew)
651 imgNew = np.fft.fft2(imgNew)
653 imgNew = np.fft.ifft2(imgNew)
656 imgNew[filtNan] = np.nan
657 imgNew[filtInf] = np.inf
662 """Task to be used as an ImageMapper for performing
663 A&L decorrelation on subimages on a grid across a A&L difference image.
665 This task subclasses DecorrelateALKernelTask in order to implement
666 all of that task
's configuration parameters, as well as its `run` method.
669 ConfigClass = DecorrelateALKernelConfig
670 _DefaultName = 'ip_diffim_decorrelateALKernelMapper'
673 DecorrelateALKernelTask.__init__(self, *args, **kwargs)
675 def run(self, subExposure, expandedSubExposure, fullBBox,
676 template, science, alTaskResult=None, psfMatchingKernel=None,
677 preConvKernel=None, **kwargs):
678 """Perform decorrelation operation on `subExposure`, using
679 `expandedSubExposure` to allow for invalid edge pixels arising
from
682 This method performs A&L decorrelation on `subExposure` using
683 local measures
for image variances
and PSF. `subExposure`
is a
684 sub-exposure of the non-decorrelated A&L diffim. It also
685 requires the corresponding sub-exposures of the template
686 (`template`)
and science (`science`) exposures.
691 the sub-exposure of the diffim
693 the expanded sub-exposure upon which to operate
695 the bounding box of the original exposure
697 the corresponding sub-exposure of the template exposure
699 the corresponding sub-exposure of the science exposure
700 alTaskResult : `lsst.pipe.base.Struct`
701 the result of A&L image differencing on `science`
and
702 `template`, importantly containing the resulting
703 `psfMatchingKernel`. Can be `
None`, only
if
704 `psfMatchingKernel`
is not `
None`.
705 psfMatchingKernel : Alternative parameter
for passing the
706 A&L `psfMatchingKernel` directly.
707 preConvKernel : If
not None, then pre-filtering was applied
708 to science exposure,
and this
is the pre-convolution
711 additional keyword arguments propagated
from
712 `ImageMapReduceTask.run`.
716 A `pipeBase.Struct` containing:
718 - ``subExposure`` : the result of the `subExposure` processing.
719 - ``decorrelationKernel`` : the decorrelation kernel, currently
724 This `run` method accepts parameters identical to those of
725 `ImageMapper.run`, since it
is called
from the
726 `ImageMapperTask`. See that
class for more information.
728 templateExposure = template
729 scienceExposure = science
730 if alTaskResult
is None and psfMatchingKernel
is None:
731 raise RuntimeError(
'Both alTaskResult and psfMatchingKernel cannot be None')
732 psfMatchingKernel = alTaskResult.psfMatchingKernel
if alTaskResult
is not None else psfMatchingKernel
736 subExp2 = scienceExposure.Factory(scienceExposure, expandedSubExposure.getBBox())
737 subExp1 = templateExposure.Factory(templateExposure, expandedSubExposure.getBBox())
740 logLevel = self.log.getLevel()
741 self.log.setLevel(self.log.WARNING)
742 res = DecorrelateALKernelTask.run(self, subExp2, subExp1, expandedSubExposure,
743 psfMatchingKernel, preConvKernel, **kwargs)
744 self.log.setLevel(logLevel)
746 diffim = res.correctedExposure.Factory(res.correctedExposure, subExposure.getBBox())
747 out = pipeBase.Struct(subExposure=diffim, )
752 """Configuration parameters for the ImageMapReduceTask to direct it to use
753 DecorrelateALKernelMapper as its mapper
for A&L decorrelation.
755 mapper = pexConfig.ConfigurableField(
756 doc='A&L decorrelation task to run on each sub-image',
757 target=DecorrelateALKernelMapper
762 """Configuration parameters for the DecorrelateALKernelSpatialTask.
764 decorrelateConfig = pexConfig.ConfigField(
765 dtype=DecorrelateALKernelConfig,
766 doc='DecorrelateALKernel config to use when running on complete exposure (non spatially-varying)',
769 decorrelateMapReduceConfig = pexConfig.ConfigField(
770 dtype=DecorrelateALKernelMapReduceConfig,
771 doc=
'DecorrelateALKernelMapReduce config to use when running on each sub-image (spatially-varying)',
774 ignoreMaskPlanes = pexConfig.ListField(
776 doc=
"""Mask planes to ignore for sigma-clipped statistics""",
777 default=(
"INTRP",
"EDGE",
"DETECTED",
"SAT",
"CR",
"BAD",
"NO_DATA",
"DETECTED_NEGATIVE")
788 """Decorrelate the effect of convolution by Alard-Lupton matching kernel in image difference
793 Pipe-task that removes the neighboring-pixel covariance in an
794 image difference that are added when the template image
is
795 convolved
with the Alard-Lupton PSF matching kernel.
797 This task
is a simple wrapper around
@ref DecorrelateALKernelTask,
798 which takes a `spatiallyVarying` parameter
in its `run` method. If
799 it
is `
False`, then it simply calls the `run` method of
@ref
800 DecorrelateALKernelTask. If it
is True, then it uses the
@ref
801 ImageMapReduceTask framework to
break the exposures into
802 subExposures on a grid,
and performs the `run` method of
@ref
803 DecorrelateALKernelTask on each subExposure. This enables it to
804 account
for spatially-varying PSFs
and noise
in the exposures when
805 performing the decorrelation.
807 This task has no standalone example, however it
is applied
as a
808 subtask of pipe.tasks.imageDifference.ImageDifferenceTask.
809 There
is also an example of its use
in `tests/testImageDecorrelation.py`.
811 ConfigClass = DecorrelateALKernelSpatialConfig
812 _DefaultName = "ip_diffim_decorrelateALKernelSpatial"
815 """Create the image decorrelation Task
820 arguments to be passed to
821 `lsst.pipe.base.task.Task.__init__`
823 additional keyword arguments to be passed to
824 `lsst.pipe.base.task.Task.__init__`
826 pipeBase.Task.__init__(self, *args, **kwargs)
831 self.statsControlstatsControl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.ignoreMaskPlanes))
834 """Compute the mean of the variance plane of `exposure`.
837 exposure.getMaskedImage().getMask(),
839 var = statObj.getValue(afwMath.MEANCLIP)
842 def run(self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel,
843 spatiallyVarying=True, preConvKernel=None, templateMatched=True, preConvMode=False):
844 """Perform decorrelation of an image difference exposure.
846 Decorrelates the diffim due to the convolution of the
847 templateExposure with the A&L psfMatchingKernel. If
848 `spatiallyVarying`
is True, it utilizes the spatially varying
849 matching kernel via the `imageMapReduce` framework to perform
850 spatially-varying decorrelation on a grid of subExposures.
855 the science Exposure used
for PSF matching
857 the template Exposure used
for PSF matching
859 the subtracted Exposure produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()`
860 psfMatchingKernel : an (optionally spatially-varying) PSF matching kernel produced
861 by `ip_diffim.ImagePsfMatchTask.subtractExposures()`
862 spatiallyVarying : `bool`
863 if True, perform the spatially-varying operation
864 preConvKernel : `lsst.meas.algorithms.Psf`
865 if not none, the scienceExposure has been pre-filtered
with this kernel. (Currently
866 this option
is experimental.)
867 templateMatched : `bool`, optional
868 If
True, the template exposure was matched (convolved) to the science exposure.
869 preConvMode : `bool`, optional
870 If
True, ``subtractedExposure``
is assumed to be a likelihood difference image
871 and will be noise corrected
as a likelihood image.
875 results : `lsst.pipe.base.Struct`
876 a structure containing:
877 - ``correctedExposure`` : the decorrelated diffim
879 self.log.info('Running A&L decorrelation: spatiallyVarying=%r', spatiallyVarying)
883 if np.isnan(svar)
or np.isnan(tvar):
885 if (np.all(np.isnan(scienceExposure.image.array))
886 or np.all(np.isnan(templateExposure.image.array))):
887 self.log.warning(
'Template or science image is entirely NaNs: skipping decorrelation.')
896 self.log.info(
"Variance (science, template): (%f, %f)", svar, tvar)
897 self.log.info(
"Variance (uncorrected diffim): %f", var)
898 config = self.config.decorrelateMapReduceConfig
900 results = task.run(subtractedExposure, science=scienceExposure,
901 template=templateExposure, psfMatchingKernel=psfMatchingKernel,
902 preConvKernel=preConvKernel, forceEvenSized=
True,
903 templateMatched=templateMatched, preConvMode=preConvMode)
904 results.correctedExposure = results.exposure
908 return exp.getMaskedImage().getMask()
909 gm(results.correctedExposure)[:, :] = gm(subtractedExposure)
912 self.log.info(
"Variance (corrected diffim): %f", var)
915 config = self.config.decorrelateConfig
917 results = task.run(scienceExposure, templateExposure,
918 subtractedExposure, psfMatchingKernel, preConvKernel=preConvKernel,
919 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())