LSST Applications g0265f82a02+d6b5cd48b5,g02d81e74bb+a41d3748ce,g1470d8bcf6+6be6c9203b,g2079a07aa2+14824f138e,g212a7c68fe+a4f2ea4efa,g2305ad1205+72971fe858,g295015adf3+ab2c85acae,g2bbee38e9b+d6b5cd48b5,g337abbeb29+d6b5cd48b5,g3ddfee87b4+31b3a28dff,g487adcacf7+082e807817,g50ff169b8f+5929b3527e,g52b1c1532d+a6fc98d2e7,g591dd9f2cf+b2918d57ae,g5a732f18d5+66d966b544,g64a986408d+a41d3748ce,g858d7b2824+a41d3748ce,g8a8a8dda67+a6fc98d2e7,g99cad8db69+7fe4acdf18,g9ddcbc5298+d4bad12328,ga1e77700b3+246acaaf9c,ga8c6da7877+84af8b3ff8,gb0e22166c9+3863383f4c,gb6a65358fc+d6b5cd48b5,gba4ed39666+9664299f35,gbb8dafda3b+d8d527deb2,gc07e1c2157+b2dbe6b631,gc120e1dc64+61440b2abb,gc28159a63d+d6b5cd48b5,gcf0d15dbbd+31b3a28dff,gdaeeff99f8+a38ce5ea23,ge6526c86ff+39927bb362,ge79ae78c31+d6b5cd48b5,gee10cc3b42+a6fc98d2e7,gf1cff7945b+a41d3748ce,v24.1.5.rc1
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Static Public Attributes | Protected Member Functions | Static Protected Member Functions | Static Protected Attributes | List of all members
lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask Class Reference
Inheritance diagram for lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask:
lsst.ip.diffim.subtractImages.AlardLuptonPreconvolveSubtractTask

Public Member Functions

 __init__ (self, **kwargs)
 
 run (self, template, science, sources, finalizedPsfApCorrCatalog=None, visitSummary=None)
 
 runConvolveTemplate (self, template, science, selectSources)
 
 runConvolveScience (self, template, science, selectSources)
 
 finalize (self, template, science, difference, kernel, templateMatched=True, preConvMode=False, preConvKernel=None, spatiallyVarying=False)
 
 updateMasks (self, template, science)
 

Public Attributes

 convolutionControl
 
 log
 

Static Public Attributes

 ConfigClass = AlardLuptonSubtractConfig
 

Protected Member Functions

 _applyExternalCalibrations (self, exposure, visitSummary)
 
 _convolveExposure (self, exposure, kernel, convolutionControl, bbox=None, psf=None, photoCalib=None, interpolateBadMaskPlanes=False)
 
 _sourceSelector (self, sources, mask)
 
 _prepareInputs (self, template, science, visitSummary=None)
 
 _clearMask (self, mask, clearMaskPlanes=None)
 

Static Protected Member Functions

 _validateExposures (template, science)
 
 _checkMask (mask, sources, excludeMaskPlanes)
 
 _renameMaskPlanes (mask, maskPlane, newMaskPlane)
 

Static Protected Attributes

str _DefaultName = "alardLuptonSubtract"
 

Detailed Description

Compute the image difference of a science and template image using
the Alard & Lupton (1998) algorithm.

Definition at line 258 of file subtractImages.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.__init__ ( self,
** kwargs )

Definition at line 265 of file subtractImages.py.

265 def __init__(self, **kwargs):
266 super().__init__(**kwargs)
267 self.makeSubtask("decorrelate")
268 self.makeSubtask("makeKernel")
269 if self.config.doScaleVariance:
270 self.makeSubtask("scaleVariance")
271
272 self.convolutionControl = lsst.afw.math.ConvolutionControl()
273 # Normalization is an extra, unnecessary, calculation and will result
274 # in mis-subtraction of the images if there are calibration errors.
275 self.convolutionControl.setDoNormalize(False)
276 self.convolutionControl.setDoCopyEdge(True)
277
Parameters to control convolution.

Member Function Documentation

◆ _applyExternalCalibrations()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._applyExternalCalibrations ( self,
exposure,
visitSummary )
protected
Replace calibrations (psf, and ApCorrMap) on this exposure with
external ones.".

Parameters
----------
exposure : `lsst.afw.image.exposure.Exposure`
    Input exposure to adjust calibrations.
visitSummary : `lsst.afw.table.ExposureCatalog`
    Exposure catalog with external calibrations to be applied. Catalog
    uses the detector id for the catalog id, sorted on id for fast
    lookup.

Returns
-------
exposure : `lsst.afw.image.exposure.Exposure`
    Exposure with adjusted calibrations.

Definition at line 278 of file subtractImages.py.

278 def _applyExternalCalibrations(self, exposure, visitSummary):
279 """Replace calibrations (psf, and ApCorrMap) on this exposure with
280 external ones.".
281
282 Parameters
283 ----------
284 exposure : `lsst.afw.image.exposure.Exposure`
285 Input exposure to adjust calibrations.
286 visitSummary : `lsst.afw.table.ExposureCatalog`
287 Exposure catalog with external calibrations to be applied. Catalog
288 uses the detector id for the catalog id, sorted on id for fast
289 lookup.
290
291 Returns
292 -------
293 exposure : `lsst.afw.image.exposure.Exposure`
294 Exposure with adjusted calibrations.
295 """
296 detectorId = exposure.info.getDetector().getId()
297
298 row = visitSummary.find(detectorId)
299 if row is None:
300 self.log.warning("Detector id %s not found in external calibrations catalog; "
301 "Using original calibrations.", detectorId)
302 else:
303 psf = row.getPsf()
304 apCorrMap = row.getApCorrMap()
305 if psf is None:
306 self.log.warning("Detector id %s has None for psf in "
307 "external calibrations catalog; Using original psf and aperture correction.",
308 detectorId)
309 elif apCorrMap is None:
310 self.log.warning("Detector id %s has None for apCorrMap in "
311 "external calibrations catalog; Using original psf and aperture correction.",
312 detectorId)
313 else:
314 exposure.setPsf(psf)
315 exposure.info.setApCorrMap(apCorrMap)
316
317 return exposure
318

◆ _checkMask()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._checkMask ( mask,
sources,
excludeMaskPlanes )
staticprotected
Exclude sources that are located on masked pixels.

Parameters
----------
mask : `lsst.afw.image.Mask`
    The image mask plane to use to reject sources
    based on the location of their centroid on the ccd.
sources : `lsst.afw.table.SourceCatalog`
    The source catalog to evaluate.
excludeMaskPlanes : `list` of `str`
    List of the names of the mask planes to exclude.

Returns
-------
flags : `numpy.ndarray` of `bool`
    Array indicating whether each source in the catalog should be
    kept (True) or rejected (False) based on the value of the
    mask plane at its location.

Definition at line 763 of file subtractImages.py.

763 def _checkMask(mask, sources, excludeMaskPlanes):
764 """Exclude sources that are located on masked pixels.
765
766 Parameters
767 ----------
768 mask : `lsst.afw.image.Mask`
769 The image mask plane to use to reject sources
770 based on the location of their centroid on the ccd.
771 sources : `lsst.afw.table.SourceCatalog`
772 The source catalog to evaluate.
773 excludeMaskPlanes : `list` of `str`
774 List of the names of the mask planes to exclude.
775
776 Returns
777 -------
778 flags : `numpy.ndarray` of `bool`
779 Array indicating whether each source in the catalog should be
780 kept (True) or rejected (False) based on the value of the
781 mask plane at its location.
782 """
783 setExcludeMaskPlanes = [
784 maskPlane for maskPlane in excludeMaskPlanes if maskPlane in mask.getMaskPlaneDict()
785 ]
786
787 excludePixelMask = mask.getPlaneBitMask(setExcludeMaskPlanes)
788
789 xv = np.rint(sources.getX() - mask.getX0())
790 yv = np.rint(sources.getY() - mask.getY0())
791
792 mv = mask.array[yv.astype(int), xv.astype(int)]
793 flags = np.bitwise_and(mv, excludePixelMask) == 0
794 return flags
795

◆ _clearMask()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._clearMask ( self,
mask,
clearMaskPlanes = None )
protected
Clear the mask plane of the template.

Parameters
----------
mask : `lsst.afw.image.Mask`
    The mask plane to erase, which will be modified in place.
clearMaskPlanes : `list` of `str`, optional
    Erase the specified mask planes.
    If not supplied, the entire mask will be erased.

Definition at line 901 of file subtractImages.py.

901 def _clearMask(self, mask, clearMaskPlanes=None):
902 """Clear the mask plane of the template.
903
904 Parameters
905 ----------
906 mask : `lsst.afw.image.Mask`
907 The mask plane to erase, which will be modified in place.
908 clearMaskPlanes : `list` of `str`, optional
909 Erase the specified mask planes.
910 If not supplied, the entire mask will be erased.
911 """
912 if clearMaskPlanes is None:
913 clearMaskPlanes = list(mask.getMaskPlaneDict().keys())
914
915 bitMaskToClear = mask.getPlaneBitMask(clearMaskPlanes)
916 mask &= ~bitMaskToClear
917
918

◆ _convolveExposure()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._convolveExposure ( self,
exposure,
kernel,
convolutionControl,
bbox = None,
psf = None,
photoCalib = None,
interpolateBadMaskPlanes = False )
protected
Convolve an exposure with the given kernel.

Parameters
----------
exposure : `lsst.afw.Exposure`
    exposure to convolve.
kernel : `lsst.afw.math.LinearCombinationKernel`
    PSF matching kernel computed in the ``makeKernel`` subtask.
convolutionControl : `lsst.afw.math.ConvolutionControl`
    Configuration for convolve algorithm.
bbox : `lsst.geom.Box2I`, optional
    Bounding box to trim the convolved exposure to.
psf : `lsst.afw.detection.Psf`, optional
    Point spread function (PSF) to set for the convolved exposure.
photoCalib : `lsst.afw.image.PhotoCalib`, optional
    Photometric calibration of the convolved exposure.

Returns
-------
convolvedExp : `lsst.afw.Exposure`
    The convolved image.

Definition at line 662 of file subtractImages.py.

667 ):
668 """Convolve an exposure with the given kernel.
669
670 Parameters
671 ----------
672 exposure : `lsst.afw.Exposure`
673 exposure to convolve.
674 kernel : `lsst.afw.math.LinearCombinationKernel`
675 PSF matching kernel computed in the ``makeKernel`` subtask.
676 convolutionControl : `lsst.afw.math.ConvolutionControl`
677 Configuration for convolve algorithm.
678 bbox : `lsst.geom.Box2I`, optional
679 Bounding box to trim the convolved exposure to.
680 psf : `lsst.afw.detection.Psf`, optional
681 Point spread function (PSF) to set for the convolved exposure.
682 photoCalib : `lsst.afw.image.PhotoCalib`, optional
683 Photometric calibration of the convolved exposure.
684
685 Returns
686 -------
687 convolvedExp : `lsst.afw.Exposure`
688 The convolved image.
689 """
690 convolvedExposure = exposure.clone()
691 if psf is not None:
692 convolvedExposure.setPsf(psf)
693 if photoCalib is not None:
694 convolvedExposure.setPhotoCalib(photoCalib)
695 if interpolateBadMaskPlanes and self.config.badMaskPlanes is not None:
696 nInterp = _interpolateImage(convolvedExposure.maskedImage,
697 self.config.badMaskPlanes)
698 self.metadata.add("nInterpolated", nInterp)
699 convolvedImage = lsst.afw.image.MaskedImageF(convolvedExposure.getBBox())
700 lsst.afw.math.convolve(convolvedImage, convolvedExposure.maskedImage, kernel, convolutionControl)
701 convolvedExposure.setMaskedImage(convolvedImage)
702 if bbox is None:
703 return convolvedExposure
704 else:
705 return convolvedExposure[bbox]
706
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
Convolve an Image or MaskedImage with a Kernel, setting pixels of an existing output image.

◆ _prepareInputs()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._prepareInputs ( self,
template,
science,
visitSummary = None )
protected
Perform preparatory calculations common to all Alard&Lupton Tasks.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure. The
    variance plane of the template image is modified in place.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template. The variance plane
    of the science image is modified in place.
visitSummary : `lsst.afw.table.ExposureCatalog`, optional
    Exposure catalog with external calibrations to be applied.  Catalog
    uses the detector id for the catalog id, sorted on id for fast
    lookup.

Definition at line 796 of file subtractImages.py.

796 def _prepareInputs(self, template, science, visitSummary=None):
797 """Perform preparatory calculations common to all Alard&Lupton Tasks.
798
799 Parameters
800 ----------
801 template : `lsst.afw.image.ExposureF`
802 Template exposure, warped to match the science exposure. The
803 variance plane of the template image is modified in place.
804 science : `lsst.afw.image.ExposureF`
805 Science exposure to subtract from the template. The variance plane
806 of the science image is modified in place.
807 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
808 Exposure catalog with external calibrations to be applied. Catalog
809 uses the detector id for the catalog id, sorted on id for fast
810 lookup.
811 """
812 self._validateExposures(template, science)
813 if visitSummary is not None:
814 self._applyExternalCalibrations(science, visitSummary=visitSummary)
815 templateCoverageFraction = checkTemplateIsSufficient(
816 template[science.getBBox()], self.log,
817 requiredTemplateFraction=self.config.requiredTemplateFraction,
818 exceptionMessage="Not attempting subtraction. To force subtraction,"
819 " set config requiredTemplateFraction=0"
820 )
821 self.metadata.add("templateCoveragePercent", 100*templateCoverageFraction)
822
823 if self.config.doScaleVariance:
824 # Scale the variance of the template and science images before
825 # convolution, subtraction, or decorrelation so that they have the
826 # correct ratio.
827 templateVarFactor = self.scaleVariance.run(template.maskedImage)
828 sciVarFactor = self.scaleVariance.run(science.maskedImage)
829 self.log.info("Template variance scaling factor: %.2f", templateVarFactor)
830 self.metadata.add("scaleTemplateVarianceFactor", templateVarFactor)
831 self.log.info("Science variance scaling factor: %.2f", sciVarFactor)
832 self.metadata.add("scaleScienceVarianceFactor", sciVarFactor)
833
834 # Erase existing detection mask planes.
835 # We don't want the detection mask from the science image
836 self.updateMasks(template, science)
837

◆ _renameMaskPlanes()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._renameMaskPlanes ( mask,
maskPlane,
newMaskPlane )
staticprotected
Rename a mask plane by adding the new name and copying the data.

Parameters
----------
mask : `lsst.afw.image.Mask`
    The mask image to update in place.
maskPlane : `str`
    The name of the existing mask plane to copy.
newMaskPlane : `str`
    The new name of the mask plane that will be added.
    If the mask plane already exists, it will be updated in place.

Definition at line 883 of file subtractImages.py.

883 def _renameMaskPlanes(mask, maskPlane, newMaskPlane):
884 """Rename a mask plane by adding the new name and copying the data.
885
886 Parameters
887 ----------
888 mask : `lsst.afw.image.Mask`
889 The mask image to update in place.
890 maskPlane : `str`
891 The name of the existing mask plane to copy.
892 newMaskPlane : `str`
893 The new name of the mask plane that will be added.
894 If the mask plane already exists, it will be updated in place.
895 """
896 mask.addMaskPlane(newMaskPlane)
897 originBitMask = mask.getPlaneBitMask(maskPlane)
898 destinationBitMask = mask.getPlaneBitMask(newMaskPlane)
899 mask.array |= ((mask.array & originBitMask) > 0)*destinationBitMask
900

◆ _sourceSelector()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._sourceSelector ( self,
sources,
mask )
protected
Select sources from a catalog that meet the selection criteria.

Parameters
----------
sources : `lsst.afw.table.SourceCatalog`
    Input source catalog to select sources from.
mask : `lsst.afw.image.Mask`
    The image mask plane to use to reject sources
    based on their location on the ccd.

Returns
-------
selectSources : `lsst.afw.table.SourceCatalog`
    The input source catalog, with flagged and low signal-to-noise
    sources removed.

Raises
------
RuntimeError
    If there are too few sources to compute the PSF matching kernel
    remaining after source selection.

Definition at line 707 of file subtractImages.py.

707 def _sourceSelector(self, sources, mask):
708 """Select sources from a catalog that meet the selection criteria.
709
710 Parameters
711 ----------
712 sources : `lsst.afw.table.SourceCatalog`
713 Input source catalog to select sources from.
714 mask : `lsst.afw.image.Mask`
715 The image mask plane to use to reject sources
716 based on their location on the ccd.
717
718 Returns
719 -------
720 selectSources : `lsst.afw.table.SourceCatalog`
721 The input source catalog, with flagged and low signal-to-noise
722 sources removed.
723
724 Raises
725 ------
726 RuntimeError
727 If there are too few sources to compute the PSF matching kernel
728 remaining after source selection.
729 """
730 flags = np.ones(len(sources), dtype=bool)
731 for flag in self.config.badSourceFlags:
732 try:
733 flags *= ~sources[flag]
734 except Exception as e:
735 self.log.warning("Could not apply source flag: %s", e)
736 signalToNoise = sources.getPsfInstFlux()/sources.getPsfInstFluxErr()
737 sToNFlag = signalToNoise > self.config.detectionThreshold
738 flags *= sToNFlag
739 sToNFlagMax = signalToNoise < self.config.detectionThresholdMax
740 flags *= sToNFlagMax
741 flags *= self._checkMask(mask, sources, self.config.excludeMaskPlanes)
742 selectSources = sources[flags].copy(deep=True)
743 if (len(selectSources) > self.config.maxKernelSources) & (self.config.maxKernelSources > 0):
744 signalToNoise = selectSources.getPsfInstFlux()/selectSources.getPsfInstFluxErr()
745 indices = np.argsort(signalToNoise)
746 indices = indices[-self.config.maxKernelSources:]
747 flags = np.zeros(len(selectSources), dtype=bool)
748 flags[indices] = True
749 selectSources = selectSources[flags].copy(deep=True)
750
751 self.log.info("%i/%i=%.1f%% of sources selected for PSF matching from the input catalog",
752 len(selectSources), len(sources), 100*len(selectSources)/len(sources))
753 if len(selectSources) < self.config.minKernelSources:
754 self.log.error("Too few sources to calculate the PSF matching kernel: "
755 "%i selected but %i needed for the calculation.",
756 len(selectSources), self.config.minKernelSources)
757 raise RuntimeError("Cannot compute PSF matching kernel: too few sources selected.")
758 self.metadata.add("nPsfSources", len(selectSources))
759
760 return selectSources
761

◆ _validateExposures()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._validateExposures ( template,
science )
staticprotected
Check that the WCS of the two Exposures match, and the template bbox
contains the science bbox.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.

Raises
------
AssertionError
    Raised if the WCS of the template is not equal to the science WCS,
    or if the science image is not fully contained in the template
    bounding box.

Definition at line 636 of file subtractImages.py.

636 def _validateExposures(template, science):
637 """Check that the WCS of the two Exposures match, and the template bbox
638 contains the science bbox.
639
640 Parameters
641 ----------
642 template : `lsst.afw.image.ExposureF`
643 Template exposure, warped to match the science exposure.
644 science : `lsst.afw.image.ExposureF`
645 Science exposure to subtract from the template.
646
647 Raises
648 ------
649 AssertionError
650 Raised if the WCS of the template is not equal to the science WCS,
651 or if the science image is not fully contained in the template
652 bounding box.
653 """
654 assert template.wcs == science.wcs,\
655 "Template and science exposure WCS are not identical."
656 templateBBox = template.getBBox()
657 scienceBBox = science.getBBox()
658
659 assert templateBBox.contains(scienceBBox),\
660 "Template bbox does not contain all of the science image."
661

◆ finalize()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.finalize ( self,
template,
science,
difference,
kernel,
templateMatched = True,
preConvMode = False,
preConvKernel = None,
spatiallyVarying = False )
Decorrelate the difference image to undo the noise correlations
caused by convolution.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
difference : `lsst.afw.image.ExposureF`
    Result of subtracting template and science.
kernel : `lsst.afw.math.Kernel`
    An (optionally spatially-varying) PSF matching kernel
templateMatched : `bool`, optional
    Was the template PSF-matched to the science image?
preConvMode : `bool`, optional
    Was the science image preconvolved with its own PSF
    before PSF matching the template?
preConvKernel : `lsst.afw.detection.Psf`, optional
    If not `None`, then the science image was pre-convolved with
    (the reflection of) this kernel. Must be normalized to sum to 1.
spatiallyVarying : `bool`, optional
    Compute the decorrelation kernel spatially varying across the image?

Returns
-------
correctedExposure : `lsst.afw.image.ExposureF`
    The decorrelated image difference.

Definition at line 586 of file subtractImages.py.

590 spatiallyVarying=False):
591 """Decorrelate the difference image to undo the noise correlations
592 caused by convolution.
593
594 Parameters
595 ----------
596 template : `lsst.afw.image.ExposureF`
597 Template exposure, warped to match the science exposure.
598 science : `lsst.afw.image.ExposureF`
599 Science exposure to subtract from the template.
600 difference : `lsst.afw.image.ExposureF`
601 Result of subtracting template and science.
602 kernel : `lsst.afw.math.Kernel`
603 An (optionally spatially-varying) PSF matching kernel
604 templateMatched : `bool`, optional
605 Was the template PSF-matched to the science image?
606 preConvMode : `bool`, optional
607 Was the science image preconvolved with its own PSF
608 before PSF matching the template?
609 preConvKernel : `lsst.afw.detection.Psf`, optional
610 If not `None`, then the science image was pre-convolved with
611 (the reflection of) this kernel. Must be normalized to sum to 1.
612 spatiallyVarying : `bool`, optional
613 Compute the decorrelation kernel spatially varying across the image?
614
615 Returns
616 -------
617 correctedExposure : `lsst.afw.image.ExposureF`
618 The decorrelated image difference.
619 """
620 if self.config.doDecorrelation:
621 self.log.info("Decorrelating image difference.")
622 # We have cleared the template mask plane, so copy the mask plane of
623 # the image difference so that we can calculate correct statistics
624 # during decorrelation
625 correctedExposure = self.decorrelate.run(science, template[science.getBBox()], difference, kernel,
626 templateMatched=templateMatched,
627 preConvMode=preConvMode,
628 preConvKernel=preConvKernel,
629 spatiallyVarying=spatiallyVarying).correctedExposure
630 else:
631 self.log.info("NOT decorrelating image difference.")
632 correctedExposure = difference
633 return correctedExposure
634

◆ run()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.run ( self,
template,
science,
sources,
finalizedPsfApCorrCatalog = None,
visitSummary = None )
PSF match, subtract, and decorrelate two images.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
sources : `lsst.afw.table.SourceCatalog`
    Identified sources on the science exposure. This catalog is used to
    select sources in order to perform the AL PSF matching on stamp
    images around them.
finalizedPsfApCorrCatalog : `lsst.afw.table.ExposureCatalog`, optional
    Exposure catalog with finalized psf models and aperture correction
    maps to be applied.  Catalog uses the detector id for the catalog
    id, sorted on id for fast lookup. Deprecated in favor of
    ``visitSummary``, and will be removed after v26.
visitSummary : `lsst.afw.table.ExposureCatalog`, optional
    Exposure catalog with external calibrations to be applied. Catalog
    uses the detector id for the catalog id, sorted on id for fast
    lookup. Ignored (for temporary backwards compatibility) if
    ``finalizedPsfApCorrCatalog`` is provided.

Returns
-------
results : `lsst.pipe.base.Struct`
    ``difference`` : `lsst.afw.image.ExposureF`
        Result of subtracting template and science.
    ``matchedTemplate`` : `lsst.afw.image.ExposureF`
        Warped and PSF-matched template exposure.
    ``backgroundModel`` : `lsst.afw.math.Function2D`
        Background model that was fit while solving for the
        PSF-matching kernel
    ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
        Kernel used to PSF-match the convolved image.

Raises
------
RuntimeError
    If an unsupported convolution mode is supplied.
RuntimeError
    If there are too few sources to calculate the PSF matching kernel.
lsst.pipe.base.NoWorkFound
    Raised if fraction of good pixels, defined as not having NO_DATA
    set, is less then the configured requiredTemplateFraction

Reimplemented in lsst.ip.diffim.subtractImages.AlardLuptonPreconvolveSubtractTask.

Definition at line 320 of file subtractImages.py.

321 visitSummary=None):
322 """PSF match, subtract, and decorrelate two images.
323
324 Parameters
325 ----------
326 template : `lsst.afw.image.ExposureF`
327 Template exposure, warped to match the science exposure.
328 science : `lsst.afw.image.ExposureF`
329 Science exposure to subtract from the template.
330 sources : `lsst.afw.table.SourceCatalog`
331 Identified sources on the science exposure. This catalog is used to
332 select sources in order to perform the AL PSF matching on stamp
333 images around them.
334 finalizedPsfApCorrCatalog : `lsst.afw.table.ExposureCatalog`, optional
335 Exposure catalog with finalized psf models and aperture correction
336 maps to be applied. Catalog uses the detector id for the catalog
337 id, sorted on id for fast lookup. Deprecated in favor of
338 ``visitSummary``, and will be removed after v26.
339 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
340 Exposure catalog with external calibrations to be applied. Catalog
341 uses the detector id for the catalog id, sorted on id for fast
342 lookup. Ignored (for temporary backwards compatibility) if
343 ``finalizedPsfApCorrCatalog`` is provided.
344
345 Returns
346 -------
347 results : `lsst.pipe.base.Struct`
348 ``difference`` : `lsst.afw.image.ExposureF`
349 Result of subtracting template and science.
350 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
351 Warped and PSF-matched template exposure.
352 ``backgroundModel`` : `lsst.afw.math.Function2D`
353 Background model that was fit while solving for the
354 PSF-matching kernel
355 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
356 Kernel used to PSF-match the convolved image.
357
358 Raises
359 ------
360 RuntimeError
361 If an unsupported convolution mode is supplied.
362 RuntimeError
363 If there are too few sources to calculate the PSF matching kernel.
364 lsst.pipe.base.NoWorkFound
365 Raised if fraction of good pixels, defined as not having NO_DATA
366 set, is less then the configured requiredTemplateFraction
367 """
368
369 if finalizedPsfApCorrCatalog is not None:
370 warnings.warn(
371 "The finalizedPsfApCorrCatalog argument is deprecated in favor of the visitSummary "
372 "argument, and will be removed after v26.",
373 FutureWarning,
374 stacklevel=find_outside_stacklevel("lsst.ip.diffim"),
375 )
376 visitSummary = finalizedPsfApCorrCatalog
377
378 self._prepareInputs(template, science, visitSummary=visitSummary)
379
380 # In the event that getPsfFwhm fails, evaluate the PSF on a grid.
381 fwhmExposureBuffer = self.config.makeKernel.fwhmExposureBuffer
382 fwhmExposureGrid = self.config.makeKernel.fwhmExposureGrid
383
384 # Calling getPsfFwhm on template.psf fails on some rare occasions when
385 # the template has no input exposures at the average position of the
386 # stars. So we try getPsfFwhm first on template, and if that fails we
387 # evaluate the PSF on a grid specified by fwhmExposure* fields.
388 # To keep consistent definitions for PSF size on the template and
389 # science images, we use the same method for both.
390 try:
391 templatePsfSize = getPsfFwhm(template.psf)
392 sciencePsfSize = getPsfFwhm(science.psf)
394 self.log.info("Unable to evaluate PSF at the average position. "
395 "Evaluting PSF on a grid of points."
396 )
397 templatePsfSize = evaluateMeanPsfFwhm(template,
398 fwhmExposureBuffer=fwhmExposureBuffer,
399 fwhmExposureGrid=fwhmExposureGrid
400 )
401 sciencePsfSize = evaluateMeanPsfFwhm(science,
402 fwhmExposureBuffer=fwhmExposureBuffer,
403 fwhmExposureGrid=fwhmExposureGrid
404 )
405 self.log.info("Science PSF FWHM: %f pixels", sciencePsfSize)
406 self.log.info("Template PSF FWHM: %f pixels", templatePsfSize)
407 self.metadata.add("sciencePsfSize", sciencePsfSize)
408 self.metadata.add("templatePsfSize", templatePsfSize)
409
410 if self.config.mode == "auto":
411 convolveTemplate = _shapeTest(template,
412 science,
413 fwhmExposureBuffer=fwhmExposureBuffer,
414 fwhmExposureGrid=fwhmExposureGrid)
415 if convolveTemplate:
416 if sciencePsfSize < templatePsfSize:
417 self.log.info("Average template PSF size is greater, "
418 "but science PSF greater in one dimension: convolving template image.")
419 else:
420 self.log.info("Science PSF size is greater: convolving template image.")
421 else:
422 self.log.info("Template PSF size is greater: convolving science image.")
423 elif self.config.mode == "convolveTemplate":
424 self.log.info("`convolveTemplate` is set: convolving template image.")
425 convolveTemplate = True
426 elif self.config.mode == "convolveScience":
427 self.log.info("`convolveScience` is set: convolving science image.")
428 convolveTemplate = False
429 else:
430 raise RuntimeError("Cannot handle AlardLuptonSubtract mode: %s", self.config.mode)
431
432 try:
433 sourceMask = science.mask.clone()
434 sourceMask.array |= template[science.getBBox()].mask.array
435 selectSources = self._sourceSelector(sources, sourceMask)
436 if convolveTemplate:
437 self.metadata.add("convolvedExposure", "Template")
438 subtractResults = self.runConvolveTemplate(template, science, selectSources)
439 else:
440 self.metadata.add("convolvedExposure", "Science")
441 subtractResults = self.runConvolveScience(template, science, selectSources)
442
443 except (RuntimeError, lsst.pex.exceptions.Exception) as e:
444 self.log.warning("Failed to match template. Checking coverage")
445 # Raise NoWorkFound if template fraction is insufficient
446 checkTemplateIsSufficient(template[science.getBBox()], self.log,
447 self.config.minTemplateFractionForExpectedSuccess,
448 exceptionMessage="Template coverage lower than expected to succeed."
449 f" Failure is tolerable: {e}")
450 # checkTemplateIsSufficient did not raise NoWorkFound, so raise original exception
451 raise e
452
453 return subtractResults
454
Provides consistent interface for LSST exceptions.
Definition Exception.h:107
Reports invalid arguments.
Definition Runtime.h:66

◆ runConvolveScience()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.runConvolveScience ( self,
template,
science,
selectSources )
Convolve the science image with a PSF-matching kernel and subtract
the template image.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
selectSources : `lsst.afw.table.SourceCatalog`
    Identified sources on the science exposure. This catalog is used to
    select sources in order to perform the AL PSF matching on stamp
    images around them.

Returns
-------
results : `lsst.pipe.base.Struct`

    ``difference`` : `lsst.afw.image.ExposureF`
        Result of subtracting template and science.
    ``matchedTemplate`` : `lsst.afw.image.ExposureF`
        Warped template exposure. Note that in this case, the template
        is not PSF-matched to the science image.
    ``backgroundModel`` : `lsst.afw.math.Function2D`
        Background model that was fit while solving for the PSF-matching kernel
    ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
       Kernel used to PSF-match the science image to the template.

Definition at line 520 of file subtractImages.py.

520 def runConvolveScience(self, template, science, selectSources):
521 """Convolve the science image with a PSF-matching kernel and subtract
522 the template image.
523
524 Parameters
525 ----------
526 template : `lsst.afw.image.ExposureF`
527 Template exposure, warped to match the science exposure.
528 science : `lsst.afw.image.ExposureF`
529 Science exposure to subtract from the template.
530 selectSources : `lsst.afw.table.SourceCatalog`
531 Identified sources on the science exposure. This catalog is used to
532 select sources in order to perform the AL PSF matching on stamp
533 images around them.
534
535 Returns
536 -------
537 results : `lsst.pipe.base.Struct`
538
539 ``difference`` : `lsst.afw.image.ExposureF`
540 Result of subtracting template and science.
541 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
542 Warped template exposure. Note that in this case, the template
543 is not PSF-matched to the science image.
544 ``backgroundModel`` : `lsst.afw.math.Function2D`
545 Background model that was fit while solving for the PSF-matching kernel
546 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
547 Kernel used to PSF-match the science image to the template.
548 """
549 bbox = science.getBBox()
550 kernelSources = self.makeKernel.selectKernelSources(science, template,
551 candidateList=selectSources,
552 preconvolved=False)
553 kernelResult = self.makeKernel.run(science, template, kernelSources,
554 preconvolved=False)
555 modelParams = kernelResult.backgroundModel.getParameters()
556 # We must invert the background model if the matching kernel is solved for the science image.
557 kernelResult.backgroundModel.setParameters([-p for p in modelParams])
558
559 kernelImage = lsst.afw.image.ImageD(kernelResult.psfMatchingKernel.getDimensions())
560 norm = kernelResult.psfMatchingKernel.computeImage(kernelImage, doNormalize=False)
561
562 matchedScience = self._convolveExposure(science, kernelResult.psfMatchingKernel,
563 self.convolutionControl,
564 psf=template.psf)
565
566 # Place back on native photometric scale
567 matchedScience.maskedImage /= norm
568 matchedTemplate = template.clone()[bbox]
569 matchedTemplate.maskedImage /= norm
570 matchedTemplate.setPhotoCalib(science.photoCalib)
571
572 difference = _subtractImages(matchedScience, matchedTemplate,
573 backgroundModel=(kernelResult.backgroundModel
574 if self.config.doSubtractBackground else None))
575
576 correctedExposure = self.finalize(template, science, difference,
577 kernelResult.psfMatchingKernel,
578 templateMatched=False)
579
580 return lsst.pipe.base.Struct(difference=correctedExposure,
581 matchedTemplate=matchedTemplate,
582 matchedScience=matchedScience,
583 backgroundModel=kernelResult.backgroundModel,
584 psfMatchingKernel=kernelResult.psfMatchingKernel,)
585

◆ runConvolveTemplate()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.runConvolveTemplate ( self,
template,
science,
selectSources )
Convolve the template image with a PSF-matching kernel and subtract
from the science image.

Parameters
----------
template : `lsst.afw.image.ExposureF`
    Template exposure, warped to match the science exposure.
science : `lsst.afw.image.ExposureF`
    Science exposure to subtract from the template.
selectSources : `lsst.afw.table.SourceCatalog`
    Identified sources on the science exposure. This catalog is used to
    select sources in order to perform the AL PSF matching on stamp
    images around them.

Returns
-------
results : `lsst.pipe.base.Struct`

    ``difference`` : `lsst.afw.image.ExposureF`
        Result of subtracting template and science.
    ``matchedTemplate`` : `lsst.afw.image.ExposureF`
        Warped and PSF-matched template exposure.
    ``backgroundModel`` : `lsst.afw.math.Function2D`
        Background model that was fit while solving for the PSF-matching kernel
    ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
        Kernel used to PSF-match the template to the science image.

Definition at line 455 of file subtractImages.py.

455 def runConvolveTemplate(self, template, science, selectSources):
456 """Convolve the template image with a PSF-matching kernel and subtract
457 from the science image.
458
459 Parameters
460 ----------
461 template : `lsst.afw.image.ExposureF`
462 Template exposure, warped to match the science exposure.
463 science : `lsst.afw.image.ExposureF`
464 Science exposure to subtract from the template.
465 selectSources : `lsst.afw.table.SourceCatalog`
466 Identified sources on the science exposure. This catalog is used to
467 select sources in order to perform the AL PSF matching on stamp
468 images around them.
469
470 Returns
471 -------
472 results : `lsst.pipe.base.Struct`
473
474 ``difference`` : `lsst.afw.image.ExposureF`
475 Result of subtracting template and science.
476 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
477 Warped and PSF-matched template exposure.
478 ``backgroundModel`` : `lsst.afw.math.Function2D`
479 Background model that was fit while solving for the PSF-matching kernel
480 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
481 Kernel used to PSF-match the template to the science image.
482 """
483 try:
484 kernelSources = self.makeKernel.selectKernelSources(template, science,
485 candidateList=selectSources,
486 preconvolved=False)
487 kernelResult = self.makeKernel.run(template, science, kernelSources,
488 preconvolved=False)
489 except Exception as e:
490 if self.config.allowKernelSourceDetection:
491 self.log.warning("Error encountered trying to construct the matching kernel"
492 f" Running source detection and retrying. {e}")
493 kernelSources = self.makeKernel.selectKernelSources(template, science,
494 candidateList=None,
495 preconvolved=False)
496 kernelResult = self.makeKernel.run(template, science, kernelSources,
497 preconvolved=False)
498 else:
499 raise e
500
501 matchedTemplate = self._convolveExposure(template, kernelResult.psfMatchingKernel,
502 self.convolutionControl,
503 bbox=science.getBBox(),
504 psf=science.psf,
505 photoCalib=science.photoCalib)
506
507 difference = _subtractImages(science, matchedTemplate,
508 backgroundModel=(kernelResult.backgroundModel
509 if self.config.doSubtractBackground else None))
510 correctedExposure = self.finalize(template, science, difference,
511 kernelResult.psfMatchingKernel,
512 templateMatched=True)
513
514 return lsst.pipe.base.Struct(difference=correctedExposure,
515 matchedTemplate=matchedTemplate,
516 matchedScience=science,
517 backgroundModel=kernelResult.backgroundModel,
518 psfMatchingKernel=kernelResult.psfMatchingKernel)
519

◆ updateMasks()

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.updateMasks ( self,
template,
science )
Update the science and template mask planes before differencing.

Parameters
----------
template : `lsst.afw.image.Exposure`
    Template exposure, warped to match the science exposure.
    The template mask planes will be erased, except for a few specified
    in the task config.
science : `lsst.afw.image.Exposure`
    Science exposure to subtract from the template.
    The DETECTED and DETECTED_NEGATIVE mask planes of the science image
    will be erased.

Definition at line 838 of file subtractImages.py.

838 def updateMasks(self, template, science):
839 """Update the science and template mask planes before differencing.
840
841 Parameters
842 ----------
843 template : `lsst.afw.image.Exposure`
844 Template exposure, warped to match the science exposure.
845 The template mask planes will be erased, except for a few specified
846 in the task config.
847 science : `lsst.afw.image.Exposure`
848 Science exposure to subtract from the template.
849 The DETECTED and DETECTED_NEGATIVE mask planes of the science image
850 will be erased.
851 """
852 self._clearMask(science.mask, clearMaskPlanes=["DETECTED", "DETECTED_NEGATIVE"])
853
854 # We will clear ALL template mask planes, except for those specified
855 # via the `preserveTemplateMask` config. Mask planes specified via
856 # the `renameTemplateMask` config will be copied to new planes with
857 # "_TEMPLATE" appended to their names, and the original mask plane will
858 # be cleared.
859 clearMaskPlanes = [mp for mp in template.mask.getMaskPlaneDict().keys()
860 if mp not in self.config.preserveTemplateMask]
861 renameMaskPlanes = [mp for mp in self.config.renameTemplateMask
862 if mp in template.mask.getMaskPlaneDict().keys()]
863
864 # propagate the mask plane related to Fake source injection
865 # NOTE: the fake source injection sets FAKE plane, but it should be INJECTED
866 # NOTE: This can be removed in DM-40796
867 if "FAKE" in science.mask.getMaskPlaneDict().keys():
868 self.log.info("Adding injected mask plane to science image")
869 self._renameMaskPlanes(science.mask, "FAKE", "INJECTED")
870 if "FAKE" in template.mask.getMaskPlaneDict().keys():
871 self.log.info("Adding injected mask plane to template image")
872 self._renameMaskPlanes(template.mask, "FAKE", "INJECTED_TEMPLATE")
873 if "INJECTED" in renameMaskPlanes:
874 renameMaskPlanes.remove("INJECTED")
875 if "INJECTED_TEMPLATE" in clearMaskPlanes:
876 clearMaskPlanes.remove("INJECTED_TEMPLATE")
877
878 for maskPlane in renameMaskPlanes:
879 self._renameMaskPlanes(template.mask, maskPlane, maskPlane + "_TEMPLATE")
880 self._clearMask(template.mask, clearMaskPlanes=clearMaskPlanes)
881

Member Data Documentation

◆ _DefaultName

str lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask._DefaultName = "alardLuptonSubtract"
staticprotected

Definition at line 263 of file subtractImages.py.

◆ ConfigClass

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.ConfigClass = AlardLuptonSubtractConfig
static

Definition at line 262 of file subtractImages.py.

◆ convolutionControl

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.convolutionControl

Definition at line 272 of file subtractImages.py.

◆ log

lsst.ip.diffim.subtractImages.AlardLuptonSubtractTask.log

Definition at line 446 of file subtractImages.py.


The documentation for this class was generated from the following file: