25 import lsst.pex.config
as pexConfig
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
42 __all__ = [
"ImagePsfMatchConfig",
"ImagePsfMatchTask",
"subtractAlgorithmRegistry"]
44 sigma2fwhm = 2.*np.sqrt(2.*np.log(2.))
48 """Configuration for image-to-image Psf matching. 50 kernel = pexConfig.ConfigChoiceField(
58 selectDetection = pexConfig.ConfigurableField(
59 target=SourceDetectionTask,
60 doc=
"Initial detections used to feed stars to kernel fitting",
62 selectMeasurement = pexConfig.ConfigurableField(
63 target=SingleFrameMeasurementTask,
64 doc=
"Initial measurements used to feed stars to kernel fitting",
74 self.
selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
75 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord')
82 """Psf-match two MaskedImages or Exposures using the sources in the images. 87 Arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 89 Keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 93 Upon initialization, the kernel configuration is defined by self.config.kernel.active. 94 The task creates an lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig. 95 A schema for the selection and measurement of candidate lsst.ip.diffim.KernelCandidates is 96 defined, and used to initize subTasks selectDetection (for candidate detection) and selectMeasurement 97 (for candidate measurement). 101 Build a Psf-matching kernel using two input images, either as MaskedImages (in which case they need 102 to be astrometrically aligned) or Exposures (in which case astrometric alignment will happen by 103 default but may be turned off). This requires a list of input Sources which may be provided 104 by the calling Task; if not, the Task will perform a coarse source detection 105 and selection for this purpose. Sources are vetted for signal-to-noise and masked pixels 106 (in both the template and science image), and substamps around each acceptable 107 source are extracted and used to create an instance of KernelCandidate. 108 Each KernelCandidate is then placed within a lsst.afw.math.SpatialCellSet, which is used by an ensemble of 109 lsst.afw.math.CandidateVisitor instances to build the Psf-matching kernel. These visitors include, in 110 the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor, 111 and AssessSpatialKernelVisitor. 113 Sigma clipping of KernelCandidates is performed as follows: 115 - BuildSingleKernelVisitor, using the substamp diffim residuals from the per-source kernel fit, 116 if PsfMatchConfig.singleKernelClipping is True 117 - KernelSumVisitor, using the mean and standard deviation of the kernel sum from all candidates, 118 if PsfMatchConfig.kernelSumClipping is True 119 - AssessSpatialKernelVisitor, using the substamp diffim ressiduals from the spatial kernel fit, 120 if PsfMatchConfig.spatialKernelClipping is True 122 The actual solving for the kernel (and differential background model) happens in 123 lsst.ip.diffim.PsfMatchTask._solve. This involves a loop over the SpatialCellSet that first builds the 124 per-candidate matching kernel for the requested number of KernelCandidates per cell 125 (PsfMatchConfig.nStarPerCell). The quality of this initial per-candidate difference image is examined, 126 using moments of the pixel residuals in the difference image normalized by the square root of the variance 127 (i.e. sigma); ideally this should follow a normal (0, 1) distribution, 128 but the rejection thresholds are set 129 by the config (PsfMatchConfig.candidateResidualMeanMax and PsfMatchConfig.candidateResidualStdMax). 130 All candidates that pass this initial build are then examined en masse to find the 131 mean/stdev of the kernel sums across all candidates. 132 Objects that are significantly above or below the mean, 133 typically due to variability or sources that are saturated in one image but not the other, 134 are also rejected.This threshold is defined by PsfMatchConfig.maxKsumSigma. 135 Finally, a spatial model is built using all currently-acceptable candidates, 136 and the spatial model used to derive a second set of (spatial) residuals 137 which are again used to reject bad candidates, using the same thresholds as above. 141 There is no run() method for this Task. Instead there are 4 methods that 142 may be used to invoke the Psf-matching. These are 143 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages`, 144 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages`, 145 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures`, and 146 `~lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures`. 148 The methods that operate on lsst.afw.image.MaskedImage require that the images already be astrometrically 149 aligned, and are the same shape. The methods that operate on lsst.afw.image.Exposure allow for the 150 input images to be misregistered and potentially be different sizes; by default a 151 lsst.afw.math.LanczosWarpingKernel is used to perform the astrometric alignment. The methods 152 that "match" images return a Psf-matched image, while the methods that "subtract" images 153 return a Psf-matched and template subtracted image. 155 See each method's returned lsst.pipe.base.Struct for more details. 159 The lsst.pipe.base.cmdLineTask.CmdLineTask command line task interface supports a 160 flag -d/--debug to import debug.py from your PYTHONPATH. The relevant contents of debug.py 161 for this Task include: 168 di = lsstDebug.getInfo(name) 169 if name == "lsst.ip.diffim.psfMatch": 170 di.display = True # enable debug output 171 di.maskTransparency = 80 # display mask transparency 172 di.displayCandidates = True # show all the candidates and residuals 173 di.displayKernelBasis = False # show kernel basis functions 174 di.displayKernelMosaic = True # show kernel realized across the image 175 di.plotKernelSpatialModel = False # show coefficients of spatial model 176 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 177 elif name == "lsst.ip.diffim.imagePsfMatch": 178 di.display = True # enable debug output 179 di.maskTransparency = 30 # display mask transparency 180 di.displayTemplate = True # show full (remapped) template 181 di.displaySciIm = True # show science image to match to 182 di.displaySpatialCells = True # show spatial cells 183 di.displayDiffIm = True # show difference image 184 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 185 elif name == "lsst.ip.diffim.diaCatalogSourceSelector": 186 di.display = False # enable debug output 187 di.maskTransparency = 30 # display mask transparency 188 di.displayExposure = True # show exposure with candidates indicated 189 di.pauseAtEnd = False # pause when done 191 lsstDebug.Info = DebugInfo 194 Note that if you want addional logging info, you may add to your scripts: 198 import lsst.log.utils as logUtils 199 logUtils.traceSetAt("ip.diffim", 4) 203 A complete example of using ImagePsfMatchTask 205 This code is imagePsfMatchTask.py in the examples directory, and can be run as e.g. 209 examples/imagePsfMatchTask.py --debug 210 examples/imagePsfMatchTask.py --debug --mode="matchExposures" 211 examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits 212 --science /path/to/scienceExp.fits 214 Create a subclass of ImagePsfMatchTask that allows us to either match exposures, or subtract exposures: 218 class MyImagePsfMatchTask(ImagePsfMatchTask): 220 def __init__(self, args, kwargs): 221 ImagePsfMatchTask.__init__(self, args, kwargs) 223 def run(self, templateExp, scienceExp, mode): 224 if mode == "matchExposures": 225 return self.matchExposures(templateExp, scienceExp) 226 elif mode == "subtractExposures": 227 return self.subtractExposures(templateExp, scienceExp) 229 And allow the user the freedom to either run the script in default mode, 230 or point to their own images on disk. 231 Note that these images must be readable as an lsst.afw.image.Exposure. 233 We have enabled some minor display debugging in this script via the --debug option. However, if you 234 have an lsstDebug debug.py in your PYTHONPATH you will get additional debugging displays. The following 235 block checks for this script: 242 # Since I am displaying 2 images here, set the starting frame number for the LSST debug LSST 243 debug.lsstDebug.frame = 3 244 except ImportError as e: 245 print(e, file=sys.stderr) 247 Finally, we call a run method that we define below. 248 First set up a Config and modify some of the parameters. 249 E.g. use an "Alard-Lupton" sum-of-Gaussian basis, 250 fit for a differential background, and use low order spatial 251 variation in the kernel and background: 257 # Create the Config and use sum of gaussian basis 259 config = ImagePsfMatchTask.ConfigClass() 260 config.kernel.name = "AL" 261 config.kernel.active.fitForBackground = True 262 config.kernel.active.spatialKernelOrder = 1 263 config.kernel.active.spatialBgOrder = 0 265 Make sure the images (if any) that were sent to the script exist on disk and are readable. If no images 266 are sent, make some fake data up for the sake of this example script (have a look at the code if you want 267 more details on generateFakeImages): 271 # Run the requested method of the Task 272 if args.template is not None and args.science is not None: 273 if not os.path.isfile(args.template): 274 raise Exception("Template image %s does not exist" % (args.template)) 275 if not os.path.isfile(args.science): 276 raise Exception("Science image %s does not exist" % (args.science)) 278 templateExp = afwImage.ExposureF(args.template) 279 except Exception as e: 280 raise Exception("Cannot read template image %s" % (args.template)) 282 scienceExp = afwImage.ExposureF(args.science) 283 except Exception as e: 284 raise Exception("Cannot read science image %s" % (args.science)) 286 templateExp, scienceExp = generateFakeImages() 287 config.kernel.active.sizeCellX = 128 288 config.kernel.active.sizeCellY = 128 290 Create and run the Task: 295 psfMatchTask = MyImagePsfMatchTask(config=config) 297 result = psfMatchTask.run(templateExp, scienceExp, args.mode) 299 And finally provide some optional debugging displays: 304 # See if the LSST debug has incremented the frame number; if not start with frame 3 306 frame = debug.lsstDebug.frame + 1 309 afwDisplay.Display(frame=frame).mtv(result.matchedExposure, 310 title="Example script: Matched Template Image") 311 if "subtractedExposure" in result.getDict(): 312 afwDisplay.Display(frame=frame + 1).mtv(result.subtractedExposure, 313 title="Example script: Subtracted Image") 316 ConfigClass = ImagePsfMatchConfig
319 """Create the ImagePsfMatchTask. 321 PsfMatchTask.__init__(self, *args, **kwargs)
323 self.
_warper = afwMath.Warper.fromConfig(self.
kConfig.warpingConfig)
330 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
334 """Return the FWHM in pixels of a Psf. 336 sigPix = psf.computeShape().getDeterminantRadius()
337 return sigPix*sigma2fwhm
341 templateFwhmPix=None, scienceFwhmPix=None,
342 candidateList=None, doWarping=True, convolveTemplate=True):
343 """Warp and PSF-match an exposure to the reference. 345 Do the following, in order: 347 - Warp templateExposure to match scienceExposure, 348 if doWarping True and their WCSs do not already match 349 - Determine a PSF matching kernel and differential background model 350 that matches templateExposure to scienceExposure 351 - Convolve templateExposure by PSF matching kernel 355 templateExposure : `lsst.afw.image.Exposure` 356 Exposure to warp and PSF-match to the reference masked image 357 scienceExposure : `lsst.afw.image.Exposure` 358 Exposure whose WCS and PSF are to be matched to 359 templateFwhmPix :`float` 360 FWHM (in pixels) of the Psf in the template image (image to convolve) 361 scienceFwhmPix : `float` 362 FWHM (in pixels) of the Psf in the science image 363 candidateList : `list`, optional 364 a list of footprints/maskedImages for kernel candidates; 365 if `None` then source detection is run. 367 - Currently supported: list of Footprints or measAlg.PsfCandidateF 370 what to do if ``templateExposure`` and ``scienceExposure`` WCSs do not match: 372 - if `True` then warp ``templateExposure`` to match ``scienceExposure`` 373 - if `False` then raise an Exception 375 convolveTemplate : `bool` 376 Whether to convolve the template image or the science image: 378 - if `True`, ``templateExposure`` is warped if doWarping, 379 ``templateExposure`` is convolved 380 - if `False`, ``templateExposure`` is warped if doWarping, 381 ``scienceExposure`` is convolved 385 results : `lsst.pipe.base.Struct` 386 An `lsst.pipe.base.Struct` containing these fields: 388 - ``matchedImage`` : the PSF-matched exposure = 389 Warped ``templateExposure`` convolved by psfMatchingKernel. This has: 391 - the same parent bbox, Wcs and PhotoCalib as scienceExposure 392 - the same filter as templateExposure 393 - no Psf (because the PSF-matching process does not compute one) 395 - ``psfMatchingKernel`` : the PSF matching kernel 396 - ``backgroundModel`` : differential background model 397 - ``kernelCellSet`` : SpatialCellSet used to solve for the PSF matching kernel 402 Raised if doWarping is False and ``templateExposure`` and 403 ``scienceExposure`` WCSs do not match 405 if not self.
_validateWcs(templateExposure, scienceExposure):
407 self.log.
info(
"Astrometrically registering template to science image")
408 templatePsf = templateExposure.getPsf()
411 scienceExposure.getWcs())
412 psfWarped =
WarpedPsf(templatePsf, xyTransform)
415 destBBox=scienceExposure.getBBox())
416 templateExposure.setPsf(psfWarped)
418 self.log.
error(
"ERROR: Input images not registered")
419 raise RuntimeError(
"Input images not registered")
421 if templateFwhmPix
is None:
422 if not templateExposure.hasPsf():
423 self.log.
warn(
"No estimate of Psf FWHM for template image")
425 templateFwhmPix = self.
getFwhmPix(templateExposure.getPsf())
426 self.log.
info(
"templateFwhmPix: {}".
format(templateFwhmPix))
428 if scienceFwhmPix
is None:
429 if not scienceExposure.hasPsf():
430 self.log.
warn(
"No estimate of Psf FWHM for science image")
432 scienceFwhmPix = self.
getFwhmPix(scienceExposure.getPsf())
433 self.log.
info(
"scienceFwhmPix: {}".
format(scienceFwhmPix))
438 templateExposure, scienceExposure, kernelSize, candidateList)
440 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
441 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
445 templateExposure, scienceExposure, kernelSize, candidateList)
447 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
448 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
451 psfMatchedExposure.setFilter(templateExposure.getFilter())
452 psfMatchedExposure.setPhotoCalib(scienceExposure.getPhotoCalib())
453 results.warpedExposure = templateExposure
454 results.matchedExposure = psfMatchedExposure
458 def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList,
459 templateFwhmPix=None, scienceFwhmPix=None):
460 """PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage). 462 Do the following, in order: 464 - Determine a PSF matching kernel and differential background model 465 that matches templateMaskedImage to scienceMaskedImage 466 - Convolve templateMaskedImage by the PSF matching kernel 470 templateMaskedImage : `lsst.afw.image.MaskedImage` 471 masked image to PSF-match to the reference masked image; 472 must be warped to match the reference masked image 473 scienceMaskedImage : `lsst.afw.image.MaskedImage` 474 maskedImage whose PSF is to be matched to 475 templateFwhmPix : `float` 476 FWHM (in pixels) of the Psf in the template image (image to convolve) 477 scienceFwhmPix : `float` 478 FWHM (in pixels) of the Psf in the science image 479 candidateList : `list`, optional 480 A list of footprints/maskedImages for kernel candidates; 481 if `None` then source detection is run. 483 - Currently supported: list of Footprints or measAlg.PsfCandidateF 488 An `lsst.pipe.base.Struct` containing these fields: 490 - psfMatchedMaskedImage: the PSF-matched masked image = 491 ``templateMaskedImage`` convolved with psfMatchingKernel. 492 This has the same xy0, dimensions and wcs as ``scienceMaskedImage``. 493 - psfMatchingKernel: the PSF matching kernel 494 - backgroundModel: differential background model 495 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 500 Raised if input images have different dimensions 506 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
508 if not maskTransparency:
511 afwDisplay.setDefaultMaskTransparency(maskTransparency)
513 if not candidateList:
514 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
516 if not self.
_validateSize(templateMaskedImage, scienceMaskedImage):
517 self.log.
error(
"ERROR: Input images different size")
518 raise RuntimeError(
"Input images different size")
520 if display
and displayTemplate:
521 disp = afwDisplay.Display(frame=lsstDebug.frame)
522 disp.mtv(templateMaskedImage, title=
"Image to convolve")
525 if display
and displaySciIm:
526 disp = afwDisplay.Display(frame=lsstDebug.frame)
527 disp.mtv(scienceMaskedImage, title=
"Image to not convolve")
534 if display
and displaySpatialCells:
535 diffimUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
536 symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
537 ctypeBad=afwDisplay.RED, size=4, frame=lsstDebug.frame,
538 title=
"Image to not convolve")
541 if templateFwhmPix
and scienceFwhmPix:
542 self.log.
info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
544 if self.
kConfig.useBicForKernelBasis:
549 bicDegrees = nbe(tmpKernelCellSet, self.log)
551 alardDegGauss=bicDegrees[0], metadata=self.metadata)
555 metadata=self.metadata)
557 spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve(kernelCellSet, basisList)
559 psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
561 afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, doNormalize)
562 return pipeBase.Struct(
563 matchedImage=psfMatchedMaskedImage,
564 psfMatchingKernel=psfMatchingKernel,
565 backgroundModel=backgroundModel,
566 kernelCellSet=kernelCellSet,
571 templateFwhmPix=None, scienceFwhmPix=None,
572 candidateList=None, doWarping=True, convolveTemplate=True):
573 """Register, Psf-match and subtract two Exposures. 575 Do the following, in order: 577 - Warp templateExposure to match scienceExposure, if their WCSs do not already match 578 - Determine a PSF matching kernel and differential background model 579 that matches templateExposure to scienceExposure 580 - PSF-match templateExposure to scienceExposure 581 - Compute subtracted exposure (see return values for equation). 585 templateExposure : `lsst.afw.image.Exposure` 586 Exposure to PSF-match to scienceExposure 587 scienceExposure : `lsst.afw.image.Exposure` 589 templateFwhmPix : `float` 590 FWHM (in pixels) of the Psf in the template image (image to convolve) 591 scienceFwhmPix : `float` 592 FWHM (in pixels) of the Psf in the science image 593 candidateList : `list`, optional 594 A list of footprints/maskedImages for kernel candidates; 595 if `None` then source detection is run. 597 - Currently supported: list of Footprints or measAlg.PsfCandidateF 600 What to do if ``templateExposure``` and ``scienceExposure`` WCSs do 603 - if `True` then warp ``templateExposure`` to match ``scienceExposure`` 604 - if `False` then raise an Exception 606 convolveTemplate : `bool` 607 Convolve the template image or the science image 609 - if `True`, ``templateExposure`` is warped if doWarping, 610 ``templateExposure`` is convolved 611 - if `False`, ``templateExposure`` is warped if doWarping, 612 ``scienceExposure is`` convolved 616 result : `lsst.pipe.base.Struct` 617 An `lsst.pipe.base.Struct` containing these fields: 619 - ``subtractedExposure`` : subtracted Exposure 620 scienceExposure - (matchedImage + backgroundModel) 621 - ``matchedImage`` : ``templateExposure`` after warping to match 622 ``templateExposure`` (if doWarping true), 623 and convolving with psfMatchingKernel 624 - ``psfMatchingKernel`` : PSF matching kernel 625 - ``backgroundModel`` : differential background model 626 - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel 629 templateExposure=templateExposure,
630 scienceExposure=scienceExposure,
631 templateFwhmPix=templateFwhmPix,
632 scienceFwhmPix=scienceFwhmPix,
633 candidateList=candidateList,
635 convolveTemplate=convolveTemplate
638 subtractedExposure = afwImage.ExposureF(scienceExposure,
True)
640 subtractedMaskedImage = subtractedExposure.getMaskedImage()
641 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
642 subtractedMaskedImage -= results.backgroundModel
644 subtractedExposure.setMaskedImage(results.warpedExposure.getMaskedImage())
645 subtractedMaskedImage = subtractedExposure.getMaskedImage()
646 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
647 subtractedMaskedImage -= results.backgroundModel
650 subtractedMaskedImage *= -1
653 subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
654 afwImage.ImageD(results.psfMatchingKernel.getDimensions()),
False)
660 if not maskTransparency:
663 afwDisplay.setDefaultMaskTransparency(maskTransparency)
664 if display
and displayDiffIm:
665 disp = afwDisplay.Display(frame=lsstDebug.frame)
666 disp.mtv(templateExposure, title=
"Template")
668 disp = afwDisplay.Display(frame=lsstDebug.frame)
669 disp.mtv(results.matchedExposure, title=
"Matched template")
671 disp = afwDisplay.Display(frame=lsstDebug.frame)
672 disp.mtv(scienceExposure, title=
"Science Image")
674 disp = afwDisplay.Display(frame=lsstDebug.frame)
675 disp.mtv(subtractedExposure, title=
"Difference Image")
678 results.subtractedExposure = subtractedExposure
683 templateFwhmPix=None, scienceFwhmPix=None):
684 """Psf-match and subtract two MaskedImages. 686 Do the following, in order: 688 - PSF-match templateMaskedImage to scienceMaskedImage 689 - Determine the differential background 690 - Return the difference: scienceMaskedImage 691 ((warped templateMaskedImage convolved with psfMatchingKernel) + backgroundModel) 695 templateMaskedImage : `lsst.afw.image.MaskedImage` 696 MaskedImage to PSF-match to ``scienceMaskedImage`` 697 scienceMaskedImage : `lsst.afw.image.MaskedImage` 698 Reference MaskedImage 699 templateFwhmPix : `float` 700 FWHM (in pixels) of the Psf in the template image (image to convolve) 701 scienceFwhmPix : `float` 702 FWHM (in pixels) of the Psf in the science image 703 candidateList : `list`, optional 704 A list of footprints/maskedImages for kernel candidates; 705 if `None` then source detection is run. 707 - Currently supported: list of Footprints or measAlg.PsfCandidateF 711 results : `lsst.pipe.base.Struct` 712 An `lsst.pipe.base.Struct` containing these fields: 714 - ``subtractedMaskedImage`` : ``scienceMaskedImage`` - (matchedImage + backgroundModel) 715 - ``matchedImage`` : templateMaskedImage convolved with psfMatchingKernel 716 - `psfMatchingKernel`` : PSF matching kernel 717 - ``backgroundModel`` : differential background model 718 - ``kernelCellSet`` : SpatialCellSet used to determine PSF matching kernel 721 if not candidateList:
722 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
725 templateMaskedImage=templateMaskedImage,
726 scienceMaskedImage=scienceMaskedImage,
727 candidateList=candidateList,
728 templateFwhmPix=templateFwhmPix,
729 scienceFwhmPix=scienceFwhmPix,
732 subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage,
True)
733 subtractedMaskedImage -= results.matchedImage
734 subtractedMaskedImage -= results.backgroundModel
735 results.subtractedMaskedImage = subtractedMaskedImage
741 if not maskTransparency:
744 afwDisplay.setDefaultMaskTransparency(maskTransparency)
745 if display
and displayDiffIm:
746 disp = afwDisplay.Display(frame=lsstDebug.frame)
747 disp.mtv(subtractedMaskedImage, title=
"Subtracted masked image")
753 """Get sources to use for Psf-matching. 755 This method runs detection and measurement on an exposure. 756 The returned set of sources will be used as candidates for 761 exposure : `lsst.afw.image.Exposure` 762 Exposure on which to run detection/measurement 766 Whether or not to smooth the Exposure with Psf before detection 768 Factory for the generation of Source ids 773 source catalog containing candidates for the Psf-matching 776 table = afwTable.SourceTable.make(self.
selectSchema, idFactory)
779 mi = exposure.getMaskedImage()
781 imArr = mi.getImage().getArray()
782 maskArr = mi.getMask().getArray()
783 miArr = np.ma.masked_array(imArr, mask=maskArr)
785 bkgd = self.
background.fitBackground(mi).getImageF()
787 self.log.
warn(
"Failed to get background model. Falling back to median background estimation")
788 bkgd = np.ma.extras.median(miArr)
794 detRet = self.selectDetection.makeSourceCatalog(
800 selectSources = detRet.sources
801 self.selectMeasurement.
run(measCat=selectSources, exposure=exposure)
809 """Make a list of acceptable KernelCandidates. 811 Accept or generate a list of candidate sources for 812 Psf-matching, and examine the Mask planes in both of the 813 images for indications of bad pixels 817 templateExposure : `lsst.afw.image.Exposure` 818 Exposure that will be convolved 819 scienceExposure : `lsst.afw.image.Exposure` 820 Exposure that will be matched-to 822 Dimensions of the Psf-matching Kernel, used to grow detection footprints 823 candidateList : `list`, optional 824 List of Sources to examine. Elements must be of type afw.table.Source 825 or a type that wraps a Source and has a getSource() method, such as 826 meas.algorithms.PsfCandidateF. 830 candidateList : `list` of `dict` 831 A list of dicts having a "source" and "footprint" 832 field for the Sources deemed to be appropriate for Psf 835 if candidateList
is None:
838 if len(candidateList) < 1:
839 raise RuntimeError(
"No candidates in candidateList")
841 listTypes =
set(
type(x)
for x
in candidateList)
842 if len(listTypes) > 1:
843 raise RuntimeError(
"Candidate list contains mixed types: %s" % [l
for l
in listTypes])
847 candidateList[0].getSource()
848 except Exception
as e:
849 raise RuntimeError(
"Candidate List is of type: %s. " % (
type(candidateList[0])) +
850 "Can only make candidate list from list of afwTable.SourceRecords, " +
851 "measAlg.PsfCandidateF or other type with a getSource() method: %s" % (e))
852 candidateList = [c.getSource()
for c
in candidateList]
854 candidateList = diffimTools.sourceToFootprintList(candidateList,
855 templateExposure, scienceExposure,
859 if len(candidateList) == 0:
860 raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
864 def _adaptCellSize(self, candidateList):
865 """NOT IMPLEMENTED YET. 869 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
870 """Build a SpatialCellSet for use with the solve method. 874 templateMaskedImage : `lsst.afw.image.MaskedImage` 875 MaskedImage to PSF-matched to scienceMaskedImage 876 scienceMaskedImage : `lsst.afw.image.MaskedImage` 877 Reference MaskedImage 878 candidateList : `list` 879 A list of footprints/maskedImages for kernel candidates; 881 - Currently supported: list of Footprints or measAlg.PsfCandidateF 885 kernelCellSet : `lsst.afw.math.SpatialCellSet` 886 a SpatialCellSet for use with self._solve 888 if not candidateList:
889 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
895 sizeCellX, sizeCellY)
897 ps = pexConfig.makePropertySet(self.
kConfig)
899 for cand
in candidateList:
901 bbox = cand.getBBox()
903 bbox = cand[
'footprint'].getBBox()
904 tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
905 smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
909 cand = cand[
'source']
910 xPos = cand.getCentroid()[0]
911 yPos = cand.getCentroid()[1]
912 cand = diffimLib.makeKernelCandidate(xPos, yPos, tmi, smi, ps)
914 self.log.
debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
915 kernelCellSet.insertCandidate(cand)
919 def _validateSize(self, templateMaskedImage, scienceMaskedImage):
920 """Return True if two image-like objects are the same size. 922 return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
924 def _validateWcs(self, templateExposure, scienceExposure):
925 """Return True if the WCS of the two Exposures have the same origin and extent. 927 templateWcs = templateExposure.getWcs()
928 scienceWcs = scienceExposure.getWcs()
929 templateBBox = templateExposure.getBBox()
930 scienceBBox = scienceExposure.getBBox()
933 templateOrigin = templateWcs.pixelToSky(
geom.Point2D(templateBBox.getBegin()))
934 scienceOrigin = scienceWcs.pixelToSky(
geom.Point2D(scienceBBox.getBegin()))
937 templateLimit = templateWcs.pixelToSky(
geom.Point2D(templateBBox.getEnd()))
938 scienceLimit = scienceWcs.pixelToSky(
geom.Point2D(scienceBBox.getEnd()))
940 self.log.
info(
"Template Wcs : %f,%f -> %f,%f",
941 templateOrigin[0], templateOrigin[1],
942 templateLimit[0], templateLimit[1])
943 self.log.
info(
"Science Wcs : %f,%f -> %f,%f",
944 scienceOrigin[0], scienceOrigin[1],
945 scienceLimit[0], scienceLimit[1])
947 templateBBox =
geom.Box2D(templateOrigin.getPosition(geom.degrees),
948 templateLimit.getPosition(geom.degrees))
949 scienceBBox =
geom.Box2D(scienceOrigin.getPosition(geom.degrees),
950 scienceLimit.getPosition(geom.degrees))
951 if not (templateBBox.overlaps(scienceBBox)):
952 raise RuntimeError(
"Input images do not overlap at all")
954 if ((templateOrigin != scienceOrigin)
or 955 (templateLimit != scienceLimit)
or 956 (templateExposure.getDimensions() != scienceExposure.getDimensions())):
961 subtractAlgorithmRegistry = pexConfig.makeRegistry(
962 doc=
"A registry of subtraction algorithms for use as a subtask in imageDifference",
965 subtractAlgorithmRegistry.register(
'al', ImagePsfMatchTask)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
A floating-point coordinate rectangle geometry.
Class for storing ordered metadata with comments.
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)
def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
def _validateSize(self, templateMaskedImage, scienceMaskedImage)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
daf::base::PropertySet * set
def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList)
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.
def _adaptCellSize(self, candidateList)
A collection of SpatialCells covering an entire image.
def __init__(self, args, kwargs)
def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
def matchExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
def makeCandidateList(self, templateExposure, scienceExposure, kernelSize, candidateList=None)
Subtract the background from an exposure.
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
A Transform obtained by putting two SkyWcs objects "back to back".
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)
def subtractMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
Record class that contains measurements made on a single exposure.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
def getFwhmPix(self, psf)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
def subtractExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
def _validateWcs(self, templateExposure, scienceExposure)
A Psf class that maps an arbitrary Psf through a coordinate transformation.