32 from lsst.utils.timer
import timeMethod
34 from .imageMapReduce
import (ImageMapReduceConfig, ImageMapReduceTask,
37 __all__ = (
"DecorrelateALKernelTask",
"DecorrelateALKernelConfig",
38 "DecorrelateALKernelMapper",
"DecorrelateALKernelMapReduceConfig",
39 "DecorrelateALKernelSpatialConfig",
"DecorrelateALKernelSpatialTask")
43 """Configuration parameters for the DecorrelateALKernelTask
46 ignoreMaskPlanes = pexConfig.ListField(
48 doc=
"""Mask planes to ignore for sigma-clipped statistics""",
49 default=(
"INTRP",
"EDGE",
"DETECTED",
"SAT",
"CR",
"BAD",
"NO_DATA",
"DETECTED_NEGATIVE")
51 completeVarPlanePropagation = pexConfig.Field(
54 doc=
"Compute the full effect of the decorrelated matching kernel on the variance plane."
55 " Otherwise use a model weighed sum of the input variances."
60 """Decorrelate the effect of convolution by Alard-Lupton matching kernel in image difference
65 Pipe-task that removes the neighboring-pixel covariance in an
66 image difference that are added when the template image is
67 convolved with the Alard-Lupton PSF matching kernel.
69 The image differencing pipeline task @link
70 ip.diffim.psfMatch.PsfMatchTask PSFMatchTask@endlink and @link
71 ip.diffim.psfMatch.PsfMatchConfigAL PSFMatchConfigAL@endlink uses
72 the Alard and Lupton (1998) method for matching the PSFs of the
73 template and science exposures prior to subtraction. The
74 Alard-Lupton method identifies a matching kernel, which is then
75 (typically) convolved with the template image to perform PSF
76 matching. This convolution has the effect of adding covariance
77 between neighboring pixels in the template image, which is then
78 added to the image difference by subtraction.
80 The pixel covariance may be corrected by whitening the noise of
81 the image difference. This task performs such a decorrelation by
82 computing a decorrelation kernel (based upon the A&L matching
83 kernel and variances in the template and science images) and
84 convolving the image difference with it. This process is described
85 in detail in [DMTN-021](http://dmtn-021.lsst.io).
87 This task has no standalone example, however it is applied as a
88 subtask of pipe.tasks.imageDifference.ImageDifferenceTask.
90 ConfigClass = DecorrelateALKernelConfig
91 _DefaultName =
"ip_diffim_decorrelateALKernel"
94 """Create the image decorrelation Task
99 arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
101 keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
103 pipeBase.Task.__init__(self, *args, **kwargs)
108 self.
statsControlstatsControl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.ignoreMaskPlanes))
112 exposure.getMaskedImage().getMask(),
114 var = statObj.getValue(afwMath.MEANCLIP)
118 def run(self, scienceExposure, templateExposure, subtractedExposure, psfMatchingKernel,
119 preConvKernel=None, xcen=None, ycen=None, svar=None, tvar=None,
120 templateMatched=True, preConvMode=False, **kwargs):
121 """Perform decorrelation of an image difference or of a score difference exposure.
123 Corrects the difference or score image due to the convolution of the
124 templateExposure with the A&L PSF matching kernel.
125 See [DMTN-021, Equation 1](http://dmtn-021.lsst.io/#equation-1) and
126 [DMTN-179](http://dmtn-179.lsst.io/) for details.
130 scienceExposure : `lsst.afw.image.Exposure`
131 The original science exposure (before pre-convolution, if ``preConvMode==True``).
132 templateExposure : `lsst.afw.image.Exposure`
133 The original template exposure warped into the science exposure dimensions.
134 subtractedExposure : `lsst.afw.image.Exposure`
135 the subtracted exposure produced by
136 `ip_diffim.ImagePsfMatchTask.subtractExposures()`. The `subtractedExposure` must
137 inherit its PSF from `exposure`, see notes below.
138 psfMatchingKernel : `lsst.afw.detection.Psf`
139 An (optionally spatially-varying) PSF matching kernel produced
140 by `ip_diffim.ImagePsfMatchTask.subtractExposures()`.
141 preConvKernel : `lsst.afw.math.Kernel`, optional
142 If not `None`, then the `scienceExposure` was pre-convolved with (the reflection of)
143 this kernel. Must be normalized to sum to 1.
144 Allowed only if ``templateMatched==True`` and ``preConvMode==True``.
145 Defaults to the PSF of the science exposure at the image center.
146 xcen : `float`, optional
147 X-pixel coordinate to use for computing constant matching kernel to use
148 If `None` (default), then use the center of the image.
149 ycen : `float`, optional
150 Y-pixel coordinate to use for computing constant matching kernel to use
151 If `None` (default), then use the center of the image.
152 svar : `float`, optional
153 Image variance for science image
154 If `None` (default) then compute the variance over the entire input science image.
155 tvar : `float`, optional
156 Image variance for template image
157 If `None` (default) then compute the variance over the entire input template image.
158 templateMatched : `bool`, optional
159 If True, the template exposure was matched (convolved) to the science exposure.
160 See also notes below.
161 preConvMode : `bool`, optional
162 If True, ``subtractedExposure`` is assumed to be a likelihood difference image
163 and will be noise corrected as a likelihood image.
165 Additional keyword arguments propagated from DecorrelateALKernelSpatialTask.
169 result : `lsst.pipe.base.Struct`
170 - ``correctedExposure`` : the decorrelated diffim
174 If ``preConvMode==True``, ``subtractedExposure`` is assumed to be a
175 score image and the noise correction for likelihood images
176 is applied. The resulting image is an optimal detection likelihood image
177 when the templateExposure has noise. (See DMTN-179) If ``preConvKernel`` is
178 not specified, the PSF of ``scienceExposure`` is assumed as pre-convolution kernel.
180 The ``subtractedExposure`` is NOT updated. The returned ``correctedExposure``
181 has an updated but spatially fixed PSF. It is calculated as the center of
182 image PSF corrected by the center of image matching kernel.
184 If ``templateMatched==True``, the templateExposure was matched (convolved)
185 to the ``scienceExposure`` by ``psfMatchingKernel``. Otherwise the ``scienceExposure``
186 was matched (convolved) by ``psfMatchingKernel``.
188 This task discards the variance plane of ``subtractedExposure`` and re-computes
189 it from the variance planes of ``scienceExposure`` and ``templateExposure``.
190 The image plane of ``subtractedExposure`` must be at the photometric level
191 set by the AL PSF matching in `ImagePsfMatchTask.subtractExposures`.
192 The assumptions about the photometric level are controlled by the
193 `templateMatched` option in this task.
195 Here we currently convert a spatially-varying matching kernel into a constant kernel,
196 just by computing it at the center of the image (tickets DM-6243, DM-6244).
198 We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute
199 the decorrelation kernel.
201 TODO DM-23857 As part of the spatially varying correction implementation
202 consider whether returning a Struct is still necessary.
204 if preConvKernel
is not None and not (templateMatched
and preConvMode):
205 raise ValueError(
"Pre-convolution kernel is allowed only if "
206 "preConvMode==True and templateMatched==True.")
208 spatialKernel = psfMatchingKernel
209 kimg = afwImage.ImageD(spatialKernel.getDimensions())
210 bbox = subtractedExposure.getBBox()
212 xcen = (bbox.getBeginX() + bbox.getEndX()) / 2.
214 ycen = (bbox.getBeginY() + bbox.getEndY()) / 2.
215 self.log.
info(
"Using matching kernel computed at (%d, %d)", xcen, ycen)
216 spatialKernel.computeImage(kimg,
False, xcen, ycen)
220 if preConvKernel
is None:
221 preConvKernel = scienceExposure.getPsf().getLocalKernel()
222 preConvImg = afwImage.ImageD(preConvKernel.getDimensions())
223 preConvKernel.computeImage(preConvImg,
True)
229 self.log.
info(
"Original variance plane means. Science:%.5e, warped template:%.5e)",
234 self.log.
info(
"Decorrelation after template image convolution")
237 exposure = scienceExposure
238 matchedExposure = templateExposure
241 self.log.
info(
"Decorrelation after science image convolution")
244 exposure = templateExposure
245 matchedExposure = scienceExposure
250 if np.isnan(expVar)
or np.isnan(matchedVar):
252 if (np.all(np.isnan(exposure.image.array))
253 or np.all(np.isnan(matchedExposure.image.array))):
254 self.log.
warning(
'Template or science image is entirely NaNs: skipping decorrelation.')
255 outExposure = subtractedExposure.clone()
256 return pipeBase.Struct(correctedExposure=outExposure, )
260 mOverExpVar = matchedVar/expVar
261 if mOverExpVar > 1e8:
262 self.log.
warning(
"Diverging correction: matched image variance is "
263 " much larger than the unconvolved one's"
264 ", matchedVar/expVar:%.2e", mOverExpVar)
267 self.log.
info(
"Variance plane mean of uncorrected diffim: %f", oldVarMean)
270 diffExpArr = subtractedExposure.image.array
271 psfImg = subtractedExposure.getPsf().computeKernelImage(
geom.Point2D(xcen, ycen))
272 psfDim = psfImg.getDimensions()
273 psfArr = psfImg.array
278 self.log.
debug(
"Matching kernel sum: %.3e", kSum)
281 self.log.
info(
"Decorrelation of likelihood image")
283 psfArr.shape, diffExpArr.shape)
286 self.log.
info(
"Decorrelation of difference image")
293 psfcI = afwImage.ImageD(psfDim)
294 psfcI.array = corrPsfArr
296 psfNew = measAlg.KernelPsf(psfcK)
298 correctedExposure = subtractedExposure.clone()
299 correctedExposure.image.array[...] = diffExpArr
303 if self.config.completeVarPlanePropagation:
304 self.log.
debug(
"Using full variance plane calculation in decorrelation")
306 exposure.variance.array, matchedExposure.variance.array,
307 expVar, matchedVar, corr.cnft, corr.crft)
309 self.log.
debug(
"Using estimated variance plane calculation in decorrelation")
311 exposure.variance.array, matchedExposure.variance.array,
312 corr.cnft, corr.crft)
314 corrExpVarArr = correctedExposure.variance.array
315 corrExpVarArr[...] = newVarArr
317 if not templateMatched:
320 corrExpVarArr /= kSumSq
321 correctedExposure.setPsf(psfNew)
324 self.log.
info(
"Variance plane mean of corrected diffim: %.5e", newVarMean)
328 return pipeBase.Struct(correctedExposure=correctedExposure, )
331 """Calculate the common shape for FFT operations. Set `self.freqSpaceShape`
336 shapes : one or more `tuple` of `int`
337 Shapes of the arrays. All must have the same dimensionality.
338 At least one shape must be provided.
346 For each dimension, gets the smallest even number greater than or equal to
347 `N1+N2-1` where `N1` and `N2` are the two largest values.
348 In case of only one shape given, rounds up to even each dimension value.
350 S = np.array(shapes, dtype=int)
355 commonShape = np.sum(S, axis=0) - 1
358 commonShape[commonShape % 2 != 0] += 1
364 """Zero pad an image where the origin is at the center and replace the
365 origin to the corner as required by the periodic input of FFT. Implement also
366 the inverse operation, crop the padding and re-center data.
371 An array to copy from.
372 newShape : `tuple` of `int`
373 The dimensions of the resulting array. For padding, the resulting array
374 must be larger than A in each dimension. For the inverse operation this
375 must be the original, before padding size of the array.
376 useInverse : bool, optional
377 Selector of forward, add padding, operation (False)
378 or its inverse, crop padding, operation (True).
383 The padded or unpadded array with shape of `newShape` and the same dtype as A.
387 For odd dimensions, the splitting is rounded to
388 put the center pixel into the new corner origin (0,0). This is to be consistent
389 e.g. for a dirac delta kernel that is originally located at the center pixel.
396 firstHalves = [x//2
for x
in A.shape]
397 secondHalves = [x-y
for x, y
in zip(A.shape, firstHalves)]
400 secondHalves = [x//2
for x
in newShape]
401 firstHalves = [x-y
for x, y
in zip(newShape, secondHalves)]
403 R = np.zeros_like(A, shape=newShape)
404 R[-firstHalves[0]:, -firstHalves[1]:] = A[:firstHalves[0], :firstHalves[1]]
405 R[:secondHalves[0], -firstHalves[1]:] = A[-secondHalves[0]:, :firstHalves[1]]
406 R[:secondHalves[0], :secondHalves[1]] = A[-secondHalves[0]:, -secondHalves[1]:]
407 R[-firstHalves[0]:, :secondHalves[1]] = A[:firstHalves[0], -secondHalves[1]:]
411 """Compute the Lupton decorrelation post-convolution kernel for decorrelating an
412 image difference, based on the PSF-matching kernel.
416 kappa : `numpy.ndarray` of `float`
417 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
419 Average variance of science image used for PSF matching.
421 Average variance of the template (matched) image used for PSF matching.
425 corrft : `numpy.ndarray` of `float`
426 The frequency space representation of the correction. The array is real (dtype float).
427 Shape is `self.freqSpaceShape`.
429 cnft, crft : `numpy.ndarray` of `complex`
430 The overall convolution (pre-conv, PSF matching, noise correction) kernel
431 for the science and template images, respectively for the variance plane
432 calculations. These are intermediate results in frequency space.
436 The maximum correction factor converges to `sqrt(tvar/svar)` towards high frequencies.
437 This should be a plausible value.
441 kft = np.fft.fft2(kappa)
442 kftAbsSq = np.real(np.conj(kft) * kft)
444 denom = svar + tvar * kftAbsSq
445 corrft = np.sqrt((svar + tvar * kSum*kSum) / denom)
448 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
451 """Compute the correction kernel for a score image.
455 kappa : `numpy.ndarray`
456 A matching kernel 2-d numpy.array derived from Alard & Lupton PSF matching.
458 Average variance of science image used for PSF matching (before pre-convolution).
460 Average variance of the template (matched) image used for PSF matching.
461 preConvArr : `numpy.ndarray`
462 The pre-convolution kernel of the science image. It should be the PSF
463 of the science image or an approximation of it. It must be normed to sum 1.
467 corrft : `numpy.ndarray` of `float`
468 The frequency space representation of the correction. The array is real (dtype float).
469 Shape is `self.freqSpaceShape`.
470 cnft, crft : `numpy.ndarray` of `complex`
471 The overall convolution (pre-conv, PSF matching, noise correction) kernel
472 for the science and template images, respectively for the variance plane
473 calculations. These are intermediate results in frequency space.
477 To be precise, the science image should be _correlated_ by ``preConvArray`` but this
478 does not matter for this calculation.
480 ``cnft``, ``crft`` contain the scaling factor as well.
485 kft = np.fft.fft2(kappa)
487 preFt = np.fft.fft2(preConvArr)
488 preFtAbsSq = np.real(np.conj(preFt) * preFt)
489 kftAbsSq = np.real(np.conj(kft) * kft)
492 tiny = np.finfo(preFtAbsSq.dtype).tiny * 1000.
493 flt = preFtAbsSq < tiny
497 preFtAbsSq[flt] = tiny
498 denom = svar + tvar * kftAbsSq / preFtAbsSq
499 corrft = (svar + tvar * kSum*kSum) / denom
500 cnft = np.conj(preFt)*corrft
502 return pipeBase.Struct(corrft=corrft, cnft=cnft, crft=crft)
506 """Estimate the variance planes.
508 The estimation assumes that around each pixel the surrounding
509 pixels' sigmas within the convolution kernel are the same.
513 vplane1, vplane2 : `numpy.ndarray` of `float`
514 Variance planes of the original (before pre-convolution or matching)
516 c1ft, c2ft : `numpy.ndarray` of `complex`
517 The overall convolution that includes the matching and the
518 afterburner in frequency space. The result of either
519 ``computeScoreCorrection`` or ``computeDiffimCorrection``.
523 vplaneD : `numpy.ndarray` of `float`
524 The estimated variance plane of the difference/score image
525 as a weighted sum of the input variances.
529 See DMTN-179 Section 5 about the variance plane calculations.
531 w1 = np.sum(np.real(np.conj(c1ft)*c1ft)) / c1ft.size
532 w2 = np.sum(np.real(np.conj(c2ft)*c2ft)) / c2ft.size
535 return vplane1*w1 + vplane2*w2
538 """Full propagation of the variance planes of the original exposures.
540 The original variance planes of independent pixels are convolved with the
541 image space square of the overall kernels.
545 vplane1, vplane2 : `numpy.ndarray` of `float`
546 Variance planes of the original (before pre-convolution or matching)
548 varMean1, varMean2 : `float`
549 Replacement average values for non-finite ``vplane1`` and ``vplane2`` values respectively.
551 c1ft, c2ft : `numpy.ndarray` of `complex`
552 The overall convolution that includes the matching and the
553 afterburner in frequency space. The result of either
554 ``computeScoreCorrection`` or ``computeDiffimCorrection``.
558 vplaneD : `numpy.ndarray` of `float`
559 The variance plane of the difference/score images.
563 See DMTN-179 Section 5 about the variance plane calculations.
565 Infs and NaNs are allowed and kept in the returned array.
567 D = np.real(np.fft.ifft2(c1ft))
568 c1SqFt = np.fft.fft2(D*D)
570 v1shape = vplane1.shape
571 filtInf = np.isinf(vplane1)
572 filtNan = np.isnan(vplane1)
574 vplane1 = np.copy(vplane1)
575 vplane1[filtInf | filtNan] = varMean1
577 v1 = np.real(np.fft.ifft2(np.fft.fft2(D) * c1SqFt))
582 D = np.real(np.fft.ifft2(c2ft))
583 c2ft = np.fft.fft2(D*D)
585 v2shape = vplane2.shape
586 filtInf = np.isinf(vplane2)
587 filtNan = np.isnan(vplane2)
588 vplane2 = np.copy(vplane2)
589 vplane2[filtInf | filtNan] = varMean2
591 v2 = np.real(np.fft.ifft2(np.fft.fft2(D) * c2ft))
599 """Compute the (decorrelated) difference image's new PSF.
603 corrft : `numpy.ndarray`
604 The frequency space representation of the correction calculated by
605 `computeCorrection`. Shape must be `self.freqSpaceShape`.
606 psfOld : `numpy.ndarray`
607 The psf of the difference image to be corrected.
611 psfNew : `numpy.ndarray`
612 The corrected psf, same shape as `psfOld`, sum normed to 1.
616 There is no algorithmic guarantee that the corrected psf can
617 meaningfully fit to the same size as the original one.
619 psfShape = psfOld.shape
621 psfNew = np.fft.fft2(psfNew)
623 psfNew = np.fft.ifft2(psfNew)
626 psfNew = psfNew/psfNew.sum()
630 """Compute the decorrelated difference image.
634 corrft : `numpy.ndarray`
635 The frequency space representation of the correction calculated by
636 `computeCorrection`. Shape must be `self.freqSpaceShape`.
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.
691 subExposure : `lsst.afw.image.Exposure`
692 the sub-exposure of the diffim
693 expandedSubExposure : `lsst.afw.image.Exposure`
694 the expanded sub-exposure upon which to operate
695 fullBBox : `lsst.geom.Box2I`
696 the bounding box of the original exposure
697 template : `lsst.afw.image.Exposure`
698 the corresponding sub-exposure of the template exposure
699 science : `lsst.afw.image.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())
743 res = DecorrelateALKernelTask.run(self, subExp2, subExp1, expandedSubExposure,
744 psfMatchingKernel, preConvKernel, **kwargs)
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.
statsControlstatsControl.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.
855 scienceExposure : `lsst.afw.image.Exposure`
856 the science Exposure used for PSF matching
857 templateExposure : `lsst.afw.image.Exposure`
858 the template Exposure used for PSF matching
859 subtractedExposure : `lsst.afw.image.Exposure`
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)
A kernel created from an Image.
Pass parameters to a Statistics object.
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)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
def setLevel(loggername, level)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.