35 from .makeKernelBasisList 
import makeKernelBasisList
 
   36 from .psfMatch 
import PsfMatchTask, PsfMatchConfigDF, PsfMatchConfigAL
 
   37 from . 
import utils 
as diffimUtils
 
   38 from . 
import diffimLib
 
   39 from . 
import diffimTools
 
   41 from lsst.utils.timer 
import timeMethod
 
   43 __all__ = [
"ImagePsfMatchConfig", 
"ImagePsfMatchTask", 
"subtractAlgorithmRegistry"]
 
   45 sigma2fwhm = 2.*np.sqrt(2.*np.log(2.))
 
   49     """Configuration for image-to-image Psf matching. 
   51     kernel = pexConfig.ConfigChoiceField(
 
   59     selectDetection = pexConfig.ConfigurableField(
 
   60         target=SourceDetectionTask,
 
   61         doc=
"Initial detections used to feed stars to kernel fitting",
 
   63     selectMeasurement = pexConfig.ConfigurableField(
 
   64         target=SingleFrameMeasurementTask,
 
   65         doc=
"Initial measurements used to feed stars to kernel fitting",
 
   75         self.
selectMeasurementselectMeasurement.algorithms.names = (
'base_SdssCentroid', 
'base_PsfFlux', 
'base_PixelFlags',
 
   76                                                    'base_SdssShape', 
'base_GaussianFlux', 
'base_SkyCoord')
 
   83     """Psf-match two MaskedImages or Exposures using the sources in the images. 
   88         Arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 
   90         Keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 
   94     Upon initialization, the kernel configuration is defined by self.config.kernel.active. 
   95     The task creates an lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig. 
   96     A schema for the selection and measurement of candidate lsst.ip.diffim.KernelCandidates is 
   97     defined, and used to initize subTasks selectDetection (for candidate detection) and selectMeasurement 
   98     (for candidate measurement). 
  102     Build a Psf-matching kernel using two input images, either as MaskedImages (in which case they need 
  103     to be astrometrically aligned) or Exposures (in which case astrometric alignment will happen by 
  104     default but may be turned off).  This requires a list of input Sources which may be provided 
  105     by the calling Task; if not, the Task will perform a coarse source detection 
  106     and selection for this purpose. Sources are vetted for signal-to-noise and masked pixels 
  107     (in both the template and science image), and substamps around each acceptable 
  108     source are extracted and used to create an instance of KernelCandidate. 
  109     Each KernelCandidate is then placed within a lsst.afw.math.SpatialCellSet, which is used by an ensemble of 
  110     lsst.afw.math.CandidateVisitor instances to build the Psf-matching kernel.   These visitors include, in 
  111     the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor, 
  112     and AssessSpatialKernelVisitor. 
  114     Sigma clipping of KernelCandidates is performed as follows: 
  116     - BuildSingleKernelVisitor, using the substamp diffim residuals from the per-source kernel fit, 
  117         if PsfMatchConfig.singleKernelClipping is True 
  118     - KernelSumVisitor, using the mean and standard deviation of the kernel sum from all candidates, 
  119         if PsfMatchConfig.kernelSumClipping is True 
  120     - AssessSpatialKernelVisitor, using the substamp diffim ressiduals from the spatial kernel fit, 
  121         if PsfMatchConfig.spatialKernelClipping is True 
  123     The actual solving for the kernel (and differential background model) happens in 
  124     lsst.ip.diffim.PsfMatchTask._solve.  This involves a loop over the SpatialCellSet that first builds the 
  125     per-candidate matching kernel for the requested number of KernelCandidates per cell 
  126     (PsfMatchConfig.nStarPerCell).  The quality of this initial per-candidate difference image is examined, 
  127     using moments of the pixel residuals in the difference image normalized by the square root of the variance 
  128     (i.e. sigma); ideally this should follow a normal (0, 1) distribution, 
  129     but the rejection thresholds are set 
  130     by the config (PsfMatchConfig.candidateResidualMeanMax and PsfMatchConfig.candidateResidualStdMax). 
  131     All candidates that pass this initial build are then examined en masse to find the 
  132     mean/stdev of the kernel sums across all candidates. 
  133     Objects that are significantly above or below the mean, 
  134     typically due to variability or sources that are saturated in one image but not the other, 
  135     are also rejected.This threshold is defined by PsfMatchConfig.maxKsumSigma. 
  136     Finally, a spatial model is built using all currently-acceptable candidates, 
  137     and the spatial model used to derive a second set of (spatial) residuals 
  138     which are again used to reject bad candidates, using the same thresholds as above. 
  142     There is no run() method for this Task.  Instead there are 4 methods that 
  143     may be used to invoke the Psf-matching.  These are 
  144     `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages`, 
  145     `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages`, 
  146     `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures`, and 
  147     `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures`. 
  149     The methods that operate on lsst.afw.image.MaskedImage require that the images already be astrometrically 
  150     aligned, and are the same shape.  The methods that operate on lsst.afw.image.Exposure allow for the 
  151     input images to be misregistered and potentially be different sizes; by default a 
  152     lsst.afw.math.LanczosWarpingKernel is used to perform the astrometric alignment.  The methods 
  153     that "match" images return a Psf-matched image, while the methods that "subtract" images 
  154     return a Psf-matched and template subtracted image. 
  156     See each method's returned lsst.pipe.base.Struct for more details. 
  160     The lsst.pipe.base.cmdLineTask.CmdLineTask command line task interface supports a 
  161     flag -d/--debug to import debug.py from your PYTHONPATH.  The relevant contents of debug.py 
  162     for this Task include: 
  169             di = lsstDebug.getInfo(name) 
  170             if name == "lsst.ip.diffim.psfMatch": 
  171                 di.display = True                 # enable debug output 
  172                 di.maskTransparency = 80          # display mask transparency 
  173                 di.displayCandidates = True       # show all the candidates and residuals 
  174                 di.displayKernelBasis = False     # show kernel basis functions 
  175                 di.displayKernelMosaic = True     # show kernel realized across the image 
  176                 di.plotKernelSpatialModel = False # show coefficients of spatial model 
  177                 di.showBadCandidates = True       # show the bad candidates (red) along with good (green) 
  178             elif name == "lsst.ip.diffim.imagePsfMatch": 
  179                 di.display = True                 # enable debug output 
  180                 di.maskTransparency = 30          # display mask transparency 
  181                 di.displayTemplate = True         # show full (remapped) template 
  182                 di.displaySciIm = True            # show science image to match to 
  183                 di.displaySpatialCells = True     # show spatial cells 
  184                 di.displayDiffIm = True           # show difference image 
  185                 di.showBadCandidates = True       # show the bad candidates (red) along with good (green) 
  186             elif name == "lsst.ip.diffim.diaCatalogSourceSelector": 
  187                 di.display = False                # enable debug output 
  188                 di.maskTransparency = 30          # display mask transparency 
  189                 di.displayExposure = True         # show exposure with candidates indicated 
  190                 di.pauseAtEnd = False             # pause when done 
  192         lsstDebug.Info = DebugInfo 
  195     Note that if you want addional logging info, you may add to your scripts: 
  199         import lsst.log.utils as logUtils 
  200         logUtils.traceSetAt("lsst.ip.diffim", 4) 
  204     A complete example of using ImagePsfMatchTask 
  206     This code is imagePsfMatchTask.py in the examples directory, and can be run as e.g. 
  210         examples/imagePsfMatchTask.py --debug 
  211         examples/imagePsfMatchTask.py --debug --mode="matchExposures" 
  212         examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits 
  213         --science /path/to/scienceExp.fits 
  215     Create a subclass of ImagePsfMatchTask that allows us to either match exposures, or subtract exposures: 
  219         class MyImagePsfMatchTask(ImagePsfMatchTask): 
  221             def __init__(self, args, kwargs): 
  222                 ImagePsfMatchTask.__init__(self, args, kwargs) 
  224             def run(self, templateExp, scienceExp, mode): 
  225                 if mode == "matchExposures": 
  226                     return self.matchExposures(templateExp, scienceExp) 
  227                 elif mode == "subtractExposures": 
  228                     return self.subtractExposures(templateExp, scienceExp) 
  230     And allow the user the freedom to either run the script in default mode, 
  231     or point to their own images on disk. 
  232     Note that these images must be readable as an lsst.afw.image.Exposure. 
  234     We have enabled some minor display debugging in this script via the --debug option.  However, if you 
  235     have an lsstDebug debug.py in your PYTHONPATH you will get additional debugging displays.  The following 
  236     block checks for this script: 
  243                 # Since I am displaying 2 images here, set the starting frame number for the LSST debug LSST 
  244                 debug.lsstDebug.frame = 3 
  245             except ImportError as e: 
  246                 print(e, file=sys.stderr) 
  248     Finally, we call a run method that we define below. 
  249     First set up a Config and modify some of the parameters. 
  250     E.g. use an "Alard-Lupton" sum-of-Gaussian basis, 
  251     fit for a differential background, and use low order spatial 
  252     variation in the kernel and background: 
  258         # Create the Config and use sum of gaussian basis 
  260         config = ImagePsfMatchTask.ConfigClass() 
  261         config.kernel.name = "AL" 
  262         config.kernel.active.fitForBackground = True 
  263         config.kernel.active.spatialKernelOrder = 1 
  264         config.kernel.active.spatialBgOrder = 0 
  266     Make sure the images (if any) that were sent to the script exist on disk and are readable.  If no images 
  267     are sent, make some fake data up for the sake of this example script (have a look at the code if you want 
  268     more details on generateFakeImages): 
  272         # Run the requested method of the Task 
  273         if args.template is not None and args.science is not None: 
  274             if not os.path.isfile(args.template): 
  275                 raise FileNotFoundError("Template image %s does not exist" % (args.template)) 
  276             if not os.path.isfile(args.science): 
  277                 raise FileNotFoundError("Science image %s does not exist" % (args.science)) 
  279                 templateExp = afwImage.ExposureF(args.template) 
  280             except Exception as e: 
  281                 raise RuntimeError("Cannot read template image %s" % (args.template)) 
  283                 scienceExp = afwImage.ExposureF(args.science) 
  284             except Exception as e: 
  285                 raise RuntimeError("Cannot read science image %s" % (args.science)) 
  287             templateExp, scienceExp = generateFakeImages() 
  288             config.kernel.active.sizeCellX = 128 
  289             config.kernel.active.sizeCellY = 128 
  291     Create and run the Task: 
  296         psfMatchTask = MyImagePsfMatchTask(config=config) 
  298         result = psfMatchTask.run(templateExp, scienceExp, args.mode) 
  300     And finally provide some optional debugging displays: 
  305         # See if the LSST debug has incremented the frame number; if not start with frame 3 
  307             frame = debug.lsstDebug.frame + 1 
  310         afwDisplay.Display(frame=frame).mtv(result.matchedExposure, 
  311                                             title="Example script: Matched Template Image") 
  312         if "subtractedExposure" in result.getDict(): 
  313             afwDisplay.Display(frame=frame + 1).mtv(result.subtractedExposure, 
  314                                                     title="Example script: Subtracted Image") 
  317     ConfigClass = ImagePsfMatchConfig
 
  320         """Create the ImagePsfMatchTask. 
  322         PsfMatchTask.__init__(self, *args, **kwargs)
 
  329         self.
selectSchemaselectSchema = afwTable.SourceTable.makeMinimalSchema()
 
  331         self.makeSubtask(
"selectDetection", schema=self.
selectSchemaselectSchema)
 
  335         """Return the FWHM in pixels of a Psf. 
  337         sigPix = psf.computeShape().getDeterminantRadius()
 
  338         return sigPix*sigma2fwhm
 
  342                        templateFwhmPix=None, scienceFwhmPix=None,
 
  343                        candidateList=None, doWarping=True, convolveTemplate=True):
 
  344         """Warp and PSF-match an exposure to the reference. 
  346         Do the following, in order: 
  348         - Warp templateExposure to match scienceExposure, 
  349             if doWarping True and their WCSs do not already match 
  350         - Determine a PSF matching kernel and differential background model 
  351             that matches templateExposure to scienceExposure 
  352         - Convolve templateExposure by PSF matching kernel 
  356         templateExposure : `lsst.afw.image.Exposure` 
  357             Exposure to warp and PSF-match to the reference masked image 
  358         scienceExposure : `lsst.afw.image.Exposure` 
  359             Exposure whose WCS and PSF are to be matched to 
  360         templateFwhmPix :`float` 
  361             FWHM (in pixels) of the Psf in the template image (image to convolve) 
  362         scienceFwhmPix : `float` 
  363             FWHM (in pixels) of the Psf in the science image 
  364         candidateList : `list`, optional 
  365             a list of footprints/maskedImages for kernel candidates; 
  366             if `None` then source detection is run. 
  368             - Currently supported: list of Footprints or measAlg.PsfCandidateF 
  371             what to do if ``templateExposure`` and ``scienceExposure`` WCSs do not match: 
  373             - if `True` then warp ``templateExposure`` to match ``scienceExposure`` 
  374             - if `False` then raise an Exception 
  376         convolveTemplate : `bool` 
  377             Whether to convolve the template image or the science image: 
  379             - if `True`, ``templateExposure`` is warped if doWarping, 
  380               ``templateExposure`` is convolved 
  381             - if `False`, ``templateExposure`` is warped if doWarping, 
  382               ``scienceExposure`` is convolved 
  386         results : `lsst.pipe.base.Struct` 
  387             An `lsst.pipe.base.Struct` containing these fields: 
  389             - ``matchedImage`` : the PSF-matched exposure = 
  390                 Warped ``templateExposure`` convolved by psfMatchingKernel. This has: 
  392                 - the same parent bbox, Wcs and PhotoCalib as scienceExposure 
  393                 - the same filter as templateExposure 
  394                 - no Psf (because the PSF-matching process does not compute one) 
  396             - ``psfMatchingKernel`` : the PSF matching kernel 
  397             - ``backgroundModel`` : differential background model 
  398             - ``kernelCellSet`` : SpatialCellSet used to solve for the PSF matching kernel 
  403            Raised if doWarping is False and ``templateExposure`` and 
  404            ``scienceExposure`` WCSs do not match 
  406         if not self.
_validateWcs_validateWcs(templateExposure, scienceExposure):
 
  408                 self.log.
info(
"Astrometrically registering template to science image")
 
  409                 templatePsf = templateExposure.getPsf()
 
  412                                                            scienceExposure.getWcs())
 
  413                 psfWarped = 
WarpedPsf(templatePsf, xyTransform)
 
  416                                                              destBBox=scienceExposure.getBBox())
 
  417                 templateExposure.setPsf(psfWarped)
 
  419                 self.log.
error(
"ERROR: Input images not registered")
 
  420                 raise RuntimeError(
"Input images not registered")
 
  422         if templateFwhmPix 
is None:
 
  423             if not templateExposure.hasPsf():
 
  424                 self.log.
warning(
"No estimate of Psf FWHM for template image")
 
  426                 templateFwhmPix = self.
getFwhmPixgetFwhmPix(templateExposure.getPsf())
 
  427                 self.log.
info(
"templateFwhmPix: %s", templateFwhmPix)
 
  429         if scienceFwhmPix 
is None:
 
  430             if not scienceExposure.hasPsf():
 
  431                 self.log.
warning(
"No estimate of Psf FWHM for science image")
 
  433                 scienceFwhmPix = self.
getFwhmPixgetFwhmPix(scienceExposure.getPsf())
 
  434                 self.log.
info(
"scienceFwhmPix: %s", scienceFwhmPix)
 
  437             kernelSize = self.
makeKernelBasisListmakeKernelBasisList(templateFwhmPix, scienceFwhmPix)[0].getWidth()
 
  439                 templateExposure, scienceExposure, kernelSize, candidateList)
 
  441                 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
 
  442                 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
 
  444             kernelSize = self.
makeKernelBasisListmakeKernelBasisList(scienceFwhmPix, templateFwhmPix)[0].getWidth()
 
  446                 templateExposure, scienceExposure, kernelSize, candidateList)
 
  448                 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
 
  449                 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
 
  452         psfMatchedExposure.setFilterLabel(templateExposure.getFilterLabel())
 
  453         psfMatchedExposure.setPhotoCalib(scienceExposure.getPhotoCalib())
 
  454         results.warpedExposure = templateExposure
 
  455         results.matchedExposure = psfMatchedExposure
 
  460                           templateFwhmPix=None, scienceFwhmPix=None):
 
  461         """PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage). 
  463         Do the following, in order: 
  465         - Determine a PSF matching kernel and differential background model 
  466             that matches templateMaskedImage to scienceMaskedImage 
  467         - Convolve templateMaskedImage by the PSF matching kernel 
  471         templateMaskedImage : `lsst.afw.image.MaskedImage` 
  472             masked image to PSF-match to the reference masked image; 
  473             must be warped to match the reference masked image 
  474         scienceMaskedImage : `lsst.afw.image.MaskedImage` 
  475             maskedImage whose PSF is to be matched to 
  476         templateFwhmPix : `float` 
  477             FWHM (in pixels) of the Psf in the template image (image to convolve) 
  478         scienceFwhmPix : `float` 
  479             FWHM (in pixels) of the Psf in the science image 
  480         candidateList : `list`, optional 
  481             A list of footprints/maskedImages for kernel candidates; 
  482             if `None` then source detection is run. 
  484             - Currently supported: list of Footprints or measAlg.PsfCandidateF 
  489         An `lsst.pipe.base.Struct` containing these fields: 
  491         - psfMatchedMaskedImage: the PSF-matched masked image = 
  492             ``templateMaskedImage`` convolved with psfMatchingKernel. 
  493             This has the same xy0, dimensions and wcs as ``scienceMaskedImage``. 
  494         - psfMatchingKernel: the PSF matching kernel 
  495         - backgroundModel: differential background model 
  496         - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 
  501             Raised if input images have different dimensions 
  507         displaySpatialCells = 
lsstDebug.Info(__name__).displaySpatialCells
 
  509         if not maskTransparency:
 
  512             afwDisplay.setDefaultMaskTransparency(maskTransparency)
 
  514         if not candidateList:
 
  515             raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
 
  517         if not self.
_validateSize_validateSize(templateMaskedImage, scienceMaskedImage):
 
  518             self.log.
error(
"ERROR: Input images different size")
 
  519             raise RuntimeError(
"Input images different size")
 
  521         if display 
and displayTemplate:
 
  522             disp = afwDisplay.Display(frame=lsstDebug.frame)
 
  523             disp.mtv(templateMaskedImage, title=
"Image to convolve")
 
  526         if display 
and displaySciIm:
 
  527             disp = afwDisplay.Display(frame=lsstDebug.frame)
 
  528             disp.mtv(scienceMaskedImage, title=
"Image to not convolve")
 
  535         if display 
and displaySpatialCells:
 
  536             diffimUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
 
  537                                                symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
 
  538                                                ctypeBad=afwDisplay.RED, size=4, frame=lsstDebug.frame,
 
  539                                                title=
"Image to not convolve")
 
  542         if templateFwhmPix 
and scienceFwhmPix:
 
  543             self.log.
info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
 
  550             bicDegrees = nbe(tmpKernelCellSet, self.log)
 
  552                                                  basisDegGauss=bicDegrees[0], metadata=self.metadata)
 
  556                                                  metadata=self.metadata)
 
  558         spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve_solve(kernelCellSet, basisList)
 
  560         psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
 
  562         convolutionControl.setDoNormalize(
False)
 
  563         afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, convolutionControl)
 
  564         return pipeBase.Struct(
 
  565             matchedImage=psfMatchedMaskedImage,
 
  566             psfMatchingKernel=psfMatchingKernel,
 
  567             backgroundModel=backgroundModel,
 
  568             kernelCellSet=kernelCellSet,
 
  573                           templateFwhmPix=None, scienceFwhmPix=None,
 
  574                           candidateList=None, doWarping=True, convolveTemplate=True):
 
  575         """Register, Psf-match and subtract two Exposures. 
  577         Do the following, in order: 
  579         - Warp templateExposure to match scienceExposure, if their WCSs do not already match 
  580         - Determine a PSF matching kernel and differential background model 
  581             that matches templateExposure to scienceExposure 
  582         - PSF-match templateExposure to scienceExposure 
  583         - Compute subtracted exposure (see return values for equation). 
  587         templateExposure : `lsst.afw.image.ExposureF` 
  588             Exposure to PSF-match to scienceExposure 
  589         scienceExposure : `lsst.afw.image.ExposureF` 
  591         templateFwhmPix : `float` 
  592             FWHM (in pixels) of the Psf in the template image (image to convolve) 
  593         scienceFwhmPix : `float` 
  594             FWHM (in pixels) of the Psf in the science image 
  595         candidateList : `list`, optional 
  596             A list of footprints/maskedImages for kernel candidates; 
  597             if `None` then source detection is run. 
  599             - Currently supported: list of Footprints or measAlg.PsfCandidateF 
  602             What to do if ``templateExposure``` and ``scienceExposure`` WCSs do 
  605             - if `True` then warp ``templateExposure`` to match ``scienceExposure`` 
  606             - if `False` then raise an Exception 
  608         convolveTemplate : `bool` 
  609             Convolve the template image or the science image 
  611             - if `True`, ``templateExposure`` is warped if doWarping, 
  612               ``templateExposure`` is convolved 
  613             - if `False`, ``templateExposure`` is warped if doWarping, 
  614               ``scienceExposure is`` convolved 
  618         result : `lsst.pipe.base.Struct` 
  619             An `lsst.pipe.base.Struct` containing these fields: 
  621             - ``subtractedExposure`` : subtracted Exposure 
  622                 scienceExposure - (matchedImage + backgroundModel) 
  623             - ``matchedImage`` : ``templateExposure`` after warping to match 
  624                                  ``templateExposure`` (if doWarping true), 
  625                                  and convolving with psfMatchingKernel 
  626             - ``psfMatchingKernel`` : PSF matching kernel 
  627             - ``backgroundModel`` : differential background model 
  628             - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel 
  631             templateExposure=templateExposure,
 
  632             scienceExposure=scienceExposure,
 
  633             templateFwhmPix=templateFwhmPix,
 
  634             scienceFwhmPix=scienceFwhmPix,
 
  635             candidateList=candidateList,
 
  637             convolveTemplate=convolveTemplate
 
  640         subtractedExposure = afwImage.ExposureF(scienceExposure, deep=
True)
 
  646             subtractedMaskedImage = subtractedExposure.maskedImage
 
  647             subtractedMaskedImage -= results.matchedExposure.maskedImage
 
  648             subtractedMaskedImage -= results.backgroundModel
 
  650             subtractedMaskedImage = subtractedExposure.maskedImage
 
  651             subtractedMaskedImage[:, :] = results.warpedExposure.maskedImage
 
  652             subtractedMaskedImage -= results.matchedExposure.maskedImage
 
  653             subtractedMaskedImage -= results.backgroundModel
 
  656             subtractedMaskedImage *= -1
 
  659             subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
 
  660                 afwImage.ImageD(results.psfMatchingKernel.getDimensions()), 
False)
 
  662             subtractedExposure.setPsf(results.warpedExposure.getPsf())
 
  668         if not maskTransparency:
 
  671             afwDisplay.setDefaultMaskTransparency(maskTransparency)
 
  672         if display 
and displayDiffIm:
 
  673             disp = afwDisplay.Display(frame=lsstDebug.frame)
 
  674             disp.mtv(templateExposure, title=
"Template")
 
  676             disp = afwDisplay.Display(frame=lsstDebug.frame)
 
  677             disp.mtv(results.matchedExposure, title=
"Matched template")
 
  679             disp = afwDisplay.Display(frame=lsstDebug.frame)
 
  680             disp.mtv(scienceExposure, title=
"Science Image")
 
  682             disp = afwDisplay.Display(frame=lsstDebug.frame)
 
  683             disp.mtv(subtractedExposure, title=
"Difference Image")
 
  686         results.subtractedExposure = subtractedExposure
 
  691                              templateFwhmPix=None, scienceFwhmPix=None):
 
  692         """Psf-match and subtract two MaskedImages. 
  694         Do the following, in order: 
  696         - PSF-match templateMaskedImage to scienceMaskedImage 
  697         - Determine the differential background 
  698         - Return the difference: scienceMaskedImage 
  699             ((warped templateMaskedImage convolved with psfMatchingKernel) + backgroundModel) 
  703         templateMaskedImage : `lsst.afw.image.MaskedImage` 
  704             MaskedImage to PSF-match to ``scienceMaskedImage`` 
  705         scienceMaskedImage : `lsst.afw.image.MaskedImage` 
  706             Reference MaskedImage 
  707         templateFwhmPix : `float` 
  708             FWHM (in pixels) of the Psf in the template image (image to convolve) 
  709         scienceFwhmPix : `float` 
  710             FWHM (in pixels) of the Psf in the science image 
  711         candidateList : `list`, optional 
  712             A list of footprints/maskedImages for kernel candidates; 
  713             if `None` then source detection is run. 
  715             - Currently supported: list of Footprints or measAlg.PsfCandidateF 
  719         results : `lsst.pipe.base.Struct` 
  720             An `lsst.pipe.base.Struct` containing these fields: 
  722             - ``subtractedMaskedImage`` : ``scienceMaskedImage`` - (matchedImage + backgroundModel) 
  723             - ``matchedImage`` : templateMaskedImage convolved with psfMatchingKernel 
  724             - `psfMatchingKernel`` : PSF matching kernel 
  725             - ``backgroundModel`` : differential background model 
  726             - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel 
  729         if not candidateList:
 
  730             raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
 
  733             templateMaskedImage=templateMaskedImage,
 
  734             scienceMaskedImage=scienceMaskedImage,
 
  735             candidateList=candidateList,
 
  736             templateFwhmPix=templateFwhmPix,
 
  737             scienceFwhmPix=scienceFwhmPix,
 
  740         subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage, 
True)
 
  741         subtractedMaskedImage -= results.matchedImage
 
  742         subtractedMaskedImage -= results.backgroundModel
 
  743         results.subtractedMaskedImage = subtractedMaskedImage
 
  749         if not maskTransparency:
 
  752             afwDisplay.setDefaultMaskTransparency(maskTransparency)
 
  753         if display 
and displayDiffIm:
 
  754             disp = afwDisplay.Display(frame=lsstDebug.frame)
 
  755             disp.mtv(subtractedMaskedImage, title=
"Subtracted masked image")
 
  761         """Get sources to use for Psf-matching. 
  763         This method runs detection and measurement on an exposure. 
  764         The returned set of sources will be used as candidates for 
  769         exposure : `lsst.afw.image.Exposure` 
  770             Exposure on which to run detection/measurement 
  774             Whether or not to smooth the Exposure with Psf before detection 
  776             Factory for the generation of Source ids 
  781             source catalog containing candidates for the Psf-matching 
  784             table = afwTable.SourceTable.make(self.
selectSchemaselectSchema, idFactory)
 
  786             table = afwTable.SourceTable.make(self.
selectSchemaselectSchema)
 
  787         mi = exposure.getMaskedImage()
 
  789         imArr = mi.getImage().getArray()
 
  790         maskArr = mi.getMask().getArray()
 
  791         miArr = np.ma.masked_array(imArr, mask=maskArr)
 
  793             fitBg = self.
backgroundbackground.fitBackground(mi)
 
  794             bkgd = fitBg.getImageF(self.
backgroundbackground.config.algorithm,
 
  795                                    self.
backgroundbackground.config.undersampleStyle)
 
  797             self.log.
warning(
"Failed to get background model.  Falling back to median background estimation")
 
  798             bkgd = np.ma.median(miArr)
 
  804             detRet = self.selectDetection.
run(
 
  810             selectSources = detRet.sources
 
  811             self.selectMeasurement.
run(measCat=selectSources, exposure=exposure)
 
  819         """Make a list of acceptable KernelCandidates. 
  821         Accept or generate a list of candidate sources for 
  822         Psf-matching, and examine the Mask planes in both of the 
  823         images for indications of bad pixels 
  827         templateExposure : `lsst.afw.image.Exposure` 
  828             Exposure that will be convolved 
  829         scienceExposure : `lsst.afw.image.Exposure` 
  830             Exposure that will be matched-to 
  832             Dimensions of the Psf-matching Kernel, used to grow detection footprints 
  833         candidateList : `list`, optional 
  834             List of Sources to examine. Elements must be of type afw.table.Source 
  835             or a type that wraps a Source and has a getSource() method, such as 
  836             meas.algorithms.PsfCandidateF. 
  840         candidateList : `list` of `dict` 
  841             A list of dicts having a "source" and "footprint" 
  842             field for the Sources deemed to be appropriate for Psf 
  845         if candidateList 
is None:
 
  848         if len(candidateList) < 1:
 
  849             raise RuntimeError(
"No candidates in candidateList")
 
  851         listTypes = 
set(
type(x) 
for x 
in candidateList)
 
  852         if len(listTypes) > 1:
 
  853             raise RuntimeError(
"Candidate list contains mixed types: %s" % [t 
for t 
in listTypes])
 
  857                 candidateList[0].getSource()
 
  858             except Exception 
as e:
 
  859                 raise RuntimeError(f
"Candidate List is of type: {type(candidateList[0])} " 
  860                                    "Can only make candidate list from list of afwTable.SourceRecords, " 
  861                                    f
"measAlg.PsfCandidateF or other type with a getSource() method: {e}")
 
  862             candidateList = [c.getSource() 
for c 
in candidateList]
 
  864         candidateList = diffimTools.sourceToFootprintList(candidateList,
 
  865                                                           templateExposure, scienceExposure,
 
  869         if len(candidateList) == 0:
 
  870             raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
 
  875                             basisDegGauss=None, basisSigmaGauss=None, metadata=None):
 
  876         """Wrapper to set log messages for 
  877         `lsst.ip.diffim.makeKernelBasisList`. 
  881         targetFwhmPix : `float`, optional 
  882             Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`. 
  883             Not used for delta function basis sets. 
  884         referenceFwhmPix : `float`, optional 
  885             Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`. 
  886             Not used for delta function basis sets. 
  887         basisDegGauss : `list` of `int`, optional 
  888             Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`. 
  889             Not used for delta function basis sets. 
  890         basisSigmaGauss : `list` of `int`, optional 
  891             Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`. 
  892             Not used for delta function basis sets. 
  893         metadata : `lsst.daf.base.PropertySet`, optional 
  894             Passed on to `lsst.ip.diffim.generateAlardLuptonBasisList`. 
  895             Not used for delta function basis sets. 
  899         basisList: `list` of `lsst.afw.math.kernel.FixedKernel` 
  900             List of basis kernels. 
  903                                         targetFwhmPix=targetFwhmPix,
 
  904                                         referenceFwhmPix=referenceFwhmPix,
 
  905                                         basisDegGauss=basisDegGauss,
 
  906                                         basisSigmaGauss=basisSigmaGauss,
 
  908         if targetFwhmPix == referenceFwhmPix:
 
  909             self.log.
info(
"Target and reference psf fwhms are equal, falling back to config values")
 
  910         elif referenceFwhmPix > targetFwhmPix:
 
  911             self.log.
info(
"Reference psf fwhm is the greater, normal convolution mode")
 
  913             self.log.
info(
"Target psf fwhm is the greater, deconvolution mode")
 
  917     def _adaptCellSize(self, candidateList):
 
  918         """NOT IMPLEMENTED YET. 
  922     def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
 
  923         """Build a SpatialCellSet for use with the solve method. 
  927         templateMaskedImage : `lsst.afw.image.MaskedImage` 
  928             MaskedImage to PSF-matched to scienceMaskedImage 
  929         scienceMaskedImage : `lsst.afw.image.MaskedImage` 
  930             Reference MaskedImage 
  931         candidateList : `list` 
  932             A list of footprints/maskedImages for kernel candidates; 
  934             - Currently supported: list of Footprints or measAlg.PsfCandidateF 
  938         kernelCellSet : `lsst.afw.math.SpatialCellSet` 
  939             a SpatialCellSet for use with self._solve 
  941         if not candidateList:
 
  942             raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
 
  944         sizeCellX, sizeCellY = self.
_adaptCellSize_adaptCellSize(candidateList)
 
  948                                                sizeCellX, sizeCellY)
 
  952         for cand 
in candidateList:
 
  954                 bbox = cand.getBBox()
 
  956                 bbox = cand[
'footprint'].getBBox()
 
  957             tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
 
  958             smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
 
  962                     cand = cand[
'source']
 
  963             xPos = cand.getCentroid()[0]
 
  964             yPos = cand.getCentroid()[1]
 
  965             cand = diffimLib.makeKernelCandidate(xPos, yPos, tmi, smi, ps)
 
  967             self.log.
debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
 
  968             kernelCellSet.insertCandidate(cand)
 
  972     def _validateSize(self, templateMaskedImage, scienceMaskedImage):
 
  973         """Return True if two image-like objects are the same size. 
  975         return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
 
  977     def _validateWcs(self, templateExposure, scienceExposure):
 
  978         """Return True if the WCS of the two Exposures have the same origin and extent. 
  980         templateWcs = templateExposure.getWcs()
 
  981         scienceWcs = scienceExposure.getWcs()
 
  982         templateBBox = templateExposure.getBBox()
 
  983         scienceBBox = scienceExposure.getBBox()
 
  986         templateOrigin = templateWcs.pixelToSky(
geom.Point2D(templateBBox.getBegin()))
 
  987         scienceOrigin = scienceWcs.pixelToSky(
geom.Point2D(scienceBBox.getBegin()))
 
  990         templateLimit = templateWcs.pixelToSky(
geom.Point2D(templateBBox.getEnd()))
 
  991         scienceLimit = scienceWcs.pixelToSky(
geom.Point2D(scienceBBox.getEnd()))
 
  993         self.log.
info(
"Template Wcs : %f,%f -> %f,%f",
 
  994                       templateOrigin[0], templateOrigin[1],
 
  995                       templateLimit[0], templateLimit[1])
 
  996         self.log.
info(
"Science Wcs : %f,%f -> %f,%f",
 
  997                       scienceOrigin[0], scienceOrigin[1],
 
  998                       scienceLimit[0], scienceLimit[1])
 
 1000         templateBBox = 
geom.Box2D(templateOrigin.getPosition(geom.degrees),
 
 1001                                   templateLimit.getPosition(geom.degrees))
 
 1002         scienceBBox = 
geom.Box2D(scienceOrigin.getPosition(geom.degrees),
 
 1003                                  scienceLimit.getPosition(geom.degrees))
 
 1004         if not (templateBBox.overlaps(scienceBBox)):
 
 1005             raise RuntimeError(
"Input images do not overlap at all")
 
 1007         if ((templateOrigin != scienceOrigin)
 
 1008             or (templateLimit != scienceLimit)
 
 1009                 or (templateExposure.getDimensions() != scienceExposure.getDimensions())):
 
 1014 subtractAlgorithmRegistry = pexConfig.makeRegistry(
 
 1015     doc=
"A registry of subtraction algorithms for use as a subtask in imageDifference",
 
 1018 subtractAlgorithmRegistry.register(
'al', ImagePsfMatchTask)
 
Parameters to control convolution.
 
A collection of SpatialCells covering an entire image.
 
Record class that contains measurements made on a single exposure.
 
Class for storing ordered metadata with comments.
 
A floating-point coordinate rectangle geometry.
 
def _validateWcs(self, templateExposure, scienceExposure)
 
def _adaptCellSize(self, candidateList)
 
def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList)
 
def _validateSize(self, templateMaskedImage, scienceMaskedImage)
 
def subtractMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
 
def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
 
def __init__(self, *args, **kwargs)
 
def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
 
def getFwhmPix(self, psf)
 
def subtractExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
 
def matchExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
 
def makeKernelBasisList(self, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, basisSigmaGauss=None, metadata=None)
 
def makeCandidateList(self, templateExposure, scienceExposure, kernelSize, candidateList=None)
 
def _buildCellSet(self, *args)
 
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
 
A Psf class that maps an arbitrary Psf through a coordinate transformation.
 
daf::base::PropertySet * set
 
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
A Transform obtained by putting two SkyWcs objects "back to back".
 
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
 
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
 
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.
 
int warpExposure(DestExposureT &destExposure, SrcExposureT const &srcExposure, WarpingControl const &control, typename DestExposureT::MaskedImageT::SinglePixel padValue=lsst::afw::math::edgePixel< typename DestExposureT::MaskedImageT >(typename lsst::afw::image::detail::image_traits< typename DestExposureT::MaskedImageT >::image_category()))
Warp (remap) one exposure to another.
 
def run(self, coaddExposures, bbox, wcs)
 
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.