22 __all__ = [
"ImagePsfMatchConfig",
"ImagePsfMatchTask",
"subtractAlgorithmRegistry"]
32 import lsst.pipe.base
as pipeBase
33 from lsst.meas.algorithms
import SourceDetectionTask, SubtractBackgroundTask
35 from .makeKernelBasisList
import makeKernelBasisList
36 from .psfMatch
import PsfMatchTask, PsfMatchConfigDF, PsfMatchConfigAL
37 from .
import utils
as dituils
38 from .
import diffimLib
39 from .
import diffimTools
42 sigma2fwhm = 2. * np.sqrt(2. * np.log(2.))
46 """!Configuration for image-to-image Psf matching""" 47 kernel = pexConfig.ConfigChoiceField(
55 selectDetection = pexConfig.ConfigurableField(
56 target=SourceDetectionTask,
57 doc=
"Initial detections used to feed stars to kernel fitting",
59 selectMeasurement = pexConfig.ConfigurableField(
60 target=SingleFrameMeasurementTask,
61 doc=
"Initial measurements used to feed stars to kernel fitting",
71 self.
selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
72 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord')
87 @anchor ImagePsfMatchTask_ 89 @brief Psf-match two MaskedImages or Exposures using the sources in the images 91 @section ip_diffim_imagepsfmatch_Contents Contents 93 - @ref ip_diffim_imagepsfmatch_Purpose 94 - @ref ip_diffim_imagepsfmatch_Initialize 95 - @ref ip_diffim_imagepsfmatch_IO 96 - @ref ip_diffim_imagepsfmatch_Config 97 - @ref ip_diffim_imagepsfmatch_Metadata 98 - @ref ip_diffim_imagepsfmatch_Debug 99 - @ref ip_diffim_imagepsfmatch_Example 101 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 103 @section ip_diffim_imagepsfmatch_Purpose Description 105 Build a Psf-matching kernel using two input images, either as MaskedImages (in which case they need 106 to be astrometrically aligned) or Exposures (in which case astrometric alignment will happen by 107 default but may be turned off). This requires a list of input Sources which may be provided 108 by the calling Task; if not, the Task will perform a coarse source detection and selection for this purpose. 109 Sources are vetted for signal-to-noise and masked pixels (in both the template and science image), and 110 substamps around each acceptable source are extracted and used to create an instance of KernelCandidate. 111 Each KernelCandidate is then placed within a lsst.afw.math.SpatialCellSet, which is used by an ensemble of 112 lsst.afw.math.CandidateVisitor instances to build the Psf-matching kernel. These visitors include, in 113 the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor, 114 and AssessSpatialKernelVisitor. 116 Sigma clipping of KernelCandidates is performed as follows: 117 - BuildSingleKernelVisitor, using the substamp diffim residuals from the per-source kernel fit, 118 if PsfMatchConfig.singleKernelClipping is True 119 - KernelSumVisitor, using the mean and standard deviation of the kernel sum from all candidates, 120 if PsfMatchConfig.kernelSumClipping is True 121 - AssessSpatialKernelVisitor, using the substamp diffim ressiduals from the spatial kernel fit, 122 if PsfMatchConfig.spatialKernelClipping is True 124 The actual solving for the kernel (and differential background model) happens in 125 lsst.ip.diffim.PsfMatchTask._solve. This involves a loop over the SpatialCellSet that first builds the 126 per-candidate matching kernel for the requested number of KernelCandidates per cell 127 (PsfMatchConfig.nStarPerCell). The quality of this initial per-candidate difference image is examined, 128 using moments of the pixel residuals in the difference image normalized by the square root of the variance 129 (i.e. sigma); ideally this should follow a normal (0, 1) distribution, 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. 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, are also rejected. 134 This threshold is defined by PsfMatchConfig.maxKsumSigma. Finally, a spatial model is built using all 135 currently-acceptable candidates, and the spatial model used to derive a second set of (spatial) residuals 136 which are again used to reject bad candidates, using the same thresholds as above. 138 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 140 @section ip_diffim_imagepsfmatch_Initialize Task initialization 142 @copydoc \_\_init\_\_ 144 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 146 @section ip_diffim_imagepsfmatch_IO Invoking the Task 148 There is no run() method for this Task. Instead there are 4 methods that 149 may be used to invoke the Psf-matching. These are 150 @link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages matchMaskedImages@endlink, 151 @link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages subtractMaskedImages@endlink, 152 @link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures matchExposures@endlink, and 153 @link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures subtractExposures@endlink. 155 The methods that operate on lsst.afw.image.MaskedImage require that the images already be astrometrically 156 aligned, and are the same shape. The methods that operate on lsst.afw.image.Exposure allow for the 157 input images to be misregistered and potentially be different sizes; by default a 158 lsst.afw.math.LanczosWarpingKernel is used to perform the astrometric alignment. The methods 159 that "match" images return a Psf-matched image, while the methods that "subtract" images 160 return a Psf-matched and template subtracted image. 162 See each method's returned lsst.pipe.base.Struct for more details. 164 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 166 @section ip_diffim_imagepsfmatch_Config Configuration parameters 168 See @ref ImagePsfMatchConfig 170 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 172 @section ip_diffim_imagepsfmatch_Metadata Quantities set in Metadata 174 See @ref ip_diffim_psfmatch_Metadata "PsfMatchTask" 176 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 178 @section ip_diffim_imagepsfmatch_Debug Debug variables 180 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 181 flag @c -d/--debug to import @b debug.py from your @c PYTHONPATH. The relevant contents of debug.py 182 for this Task include: 188 di = lsstDebug.getInfo(name) 189 if name == "lsst.ip.diffim.psfMatch": 190 di.display = True # enable debug output 191 di.maskTransparency = 80 # ds9 mask transparency 192 di.displayCandidates = True # show all the candidates and residuals 193 di.displayKernelBasis = False # show kernel basis functions 194 di.displayKernelMosaic = True # show kernel realized across the image 195 di.plotKernelSpatialModel = False # show coefficients of spatial model 196 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 197 elif name == "lsst.ip.diffim.imagePsfMatch": 198 di.display = True # enable debug output 199 di.maskTransparency = 30 # ds9 mask transparency 200 di.displayTemplate = True # show full (remapped) template 201 di.displaySciIm = True # show science image to match to 202 di.displaySpatialCells = True # show spatial cells 203 di.displayDiffIm = True # show difference image 204 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 205 elif name == "lsst.ip.diffim.diaCatalogSourceSelector": 206 di.display = False # enable debug output 207 di.maskTransparency = 30 # ds9 mask transparency 208 di.displayExposure = True # show exposure with candidates indicated 209 di.pauseAtEnd = False # pause when done 211 lsstDebug.Info = DebugInfo 215 Note that if you want addional logging info, you may add to your scripts: 217 import lsst.log.utils as logUtils 218 logUtils.traceSetAt("ip.diffim", 4) 221 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 223 @section ip_diffim_imagepsfmatch_Example A complete example of using ImagePsfMatchTask 225 This code is imagePsfMatchTask.py in the examples directory, and can be run as @em e.g. 227 examples/imagePsfMatchTask.py --debug 228 examples/imagePsfMatchTask.py --debug --mode="matchExposures" 229 examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits --science /path/to/scienceExp.fits 232 @dontinclude imagePsfMatchTask.py 233 Create a subclass of ImagePsfMatchTask that allows us to either match exposures, or subtract exposures: 234 @skip MyImagePsfMatchTask 235 @until self.subtractExposures 237 And allow the user the freedom to either run the script in default mode, or point to their own images on disk. 238 Note that these images must be readable as an lsst.afw.image.Exposure: 242 We have enabled some minor display debugging in this script via the --debug option. However, if you 243 have an lsstDebug debug.py in your PYTHONPATH you will get additional debugging displays. The following 244 block checks for this script: 248 @dontinclude imagePsfMatchTask.py 249 Finally, we call a run method that we define below. First set up a Config and modify some of the parameters. 250 E.g. use an "Alard-Lupton" sum-of-Gaussian basis, fit for a differential background, and use low order spatial 251 variation in the kernel and background: 253 @until spatialBgOrder 255 Make sure the images (if any) that were sent to the script exist on disk and are readable. If no images 256 are sent, make some fake data up for the sake of this example script (have a look at the code if you want 257 more details on generateFakeImages): 261 Create and run the Task: 265 And finally provide some optional debugging displays: 267 @until result.subtractedExposure 268 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 271 ConfigClass = ImagePsfMatchConfig
274 """!Create the ImagePsfMatchTask 276 @param *args arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 277 @param **kwargs keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 279 Upon initialization, the kernel configuration is defined by self.config.kernel.active. 280 The task creates an lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig. 281 A schema for the selection and measurement of candidate lsst.ip.diffim.KernelCandidates is 282 defined, and used to initize subTasks selectDetection (for candidate detection) and selectMeasurement 283 (for candidate measurement). 285 PsfMatchTask.__init__(self, *args, **kwargs)
287 self.
_warper = afwMath.Warper.fromConfig(self.
kConfig.warpingConfig)
290 self.
background = SubtractBackgroundTask(config=self.
kConfig.afwBackgroundConfig, name=
"background",
294 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
298 """!Return the FWHM in pixels of a Psf""" 299 sigPix = psf.computeShape().getDeterminantRadius()
300 return sigPix * sigma2fwhm
304 templateFwhmPix=None, scienceFwhmPix=None,
305 candidateList=None, doWarping=True, convolveTemplate=True):
306 """!Warp and PSF-match an exposure to the reference 308 Do the following, in order: 309 - Warp templateExposure to match scienceExposure, 310 if doWarping True and their WCSs do not already match 311 - Determine a PSF matching kernel and differential background model 312 that matches templateExposure to scienceExposure 313 - Convolve templateExposure by PSF matching kernel 315 @param templateExposure: Exposure to warp and PSF-match to the reference masked image 316 @param scienceExposure: Exposure whose WCS and PSF are to be matched to 317 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 318 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 319 @param candidateList: a list of footprints/maskedImages for kernel candidates; 320 if None then source detection is run. 321 - Currently supported: list of Footprints or measAlg.PsfCandidateF 322 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match: 323 - if True then warp templateExposure to match scienceExposure 324 - if False then raise an Exception 325 @param convolveTemplate: convolve the template image or the science image 326 - if True, templateExposure is warped if doWarping, templateExposure is convolved 327 - if False, templateExposure is warped if doWarping, scienceExposure is convolved 329 @return a pipeBase.Struct containing these fields: 330 - matchedImage: the PSF-matched exposure = 331 warped templateExposure convolved by psfMatchingKernel. This has: 332 - the same parent bbox, Wcs and Calib as scienceExposure 333 - the same filter as templateExposure 334 - no Psf (because the PSF-matching process does not compute one) 335 - psfMatchingKernel: the PSF matching kernel 336 - backgroundModel: differential background model 337 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 339 Raise a RuntimeError if doWarping is False and templateExposure's and scienceExposure's 342 if not self.
_validateWcs(templateExposure, scienceExposure):
344 self.log.info(
"Astrometrically registering template to science image")
345 templatePsf = templateExposure.getPsf()
346 templateExposure = self.
_warper.warpExposure(scienceExposure.getWcs(),
348 destBBox=scienceExposure.getBBox())
349 templateExposure.setPsf(templatePsf)
351 self.log.error(
"ERROR: Input images not registered")
352 raise RuntimeError(
"Input images not registered")
354 if templateFwhmPix
is None:
355 if not templateExposure.hasPsf():
356 self.log.warn(
"No estimate of Psf FWHM for template image")
358 templateFwhmPix = self.
getFwhmPix(templateExposure.getPsf())
359 self.log.info(
"templateFwhmPix: {}".format(templateFwhmPix))
361 if scienceFwhmPix
is None:
362 if not scienceExposure.hasPsf():
363 self.log.warn(
"No estimate of Psf FWHM for science image")
365 scienceFwhmPix = self.
getFwhmPix(scienceExposure.getPsf())
366 self.log.info(
"scienceFwhmPix: {}".format(scienceFwhmPix))
369 candidateList = self.
makeCandidateList(templateExposure, scienceExposure, kernelSize, candidateList)
373 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
374 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
377 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
378 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
381 psfMatchedExposure.setFilter(templateExposure.getFilter())
382 psfMatchedExposure.setCalib(scienceExposure.getCalib())
383 results.warpedExposure = templateExposure
384 results.matchedExposure = psfMatchedExposure
388 def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList,
389 templateFwhmPix=None, scienceFwhmPix=None):
390 """!PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage) 392 Do the following, in order: 393 - Determine a PSF matching kernel and differential background model 394 that matches templateMaskedImage to scienceMaskedImage 395 - Convolve templateMaskedImage by the PSF matching kernel 397 @param templateMaskedImage: masked image to PSF-match to the reference masked image; 398 must be warped to match the reference masked image 399 @param scienceMaskedImage: maskedImage whose PSF is to be matched to 400 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 401 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 402 @param candidateList: a list of footprints/maskedImages for kernel candidates; 403 if None then source detection is run. 404 - Currently supported: list of Footprints or measAlg.PsfCandidateF 406 @return a pipeBase.Struct containing these fields: 407 - psfMatchedMaskedImage: the PSF-matched masked image = 408 templateMaskedImage convolved with psfMatchingKernel. 409 This has the same xy0, dimensions and wcs as scienceMaskedImage. 410 - psfMatchingKernel: the PSF matching kernel 411 - backgroundModel: differential background model 412 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 414 Raise a RuntimeError if input images have different dimensions 421 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
423 if not maskTransparency:
426 ds9.setMaskTransparency(maskTransparency)
428 if not candidateList:
429 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
431 if not self.
_validateSize(templateMaskedImage, scienceMaskedImage):
432 self.log.error(
"ERROR: Input images different size")
433 raise RuntimeError(
"Input images different size")
435 if display
and displayTemplate:
436 ds9.mtv(templateMaskedImage, frame=lsstDebug.frame, title=
"Image to convolve")
439 if display
and displaySciIm:
440 ds9.mtv(scienceMaskedImage, frame=lsstDebug.frame, title=
"Image to not convolve")
447 if display
and displaySpatialCells:
448 dituils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
449 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, ctypeBad=ds9.RED,
450 size=4, frame=lsstDebug.frame, title=
"Image to not convolve")
453 if templateFwhmPix
and scienceFwhmPix:
454 self.log.info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
456 if self.
kConfig.useBicForKernelBasis:
461 bicDegrees = nbe(tmpKernelCellSet, self.log)
463 alardDegGauss=bicDegrees[0], metadata=self.metadata)
467 metadata=self.metadata)
469 spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve(kernelCellSet, basisList)
471 psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
473 afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, doNormalize)
474 return pipeBase.Struct(
475 matchedImage=psfMatchedMaskedImage,
476 psfMatchingKernel=psfMatchingKernel,
477 backgroundModel=backgroundModel,
478 kernelCellSet=kernelCellSet,
483 templateFwhmPix=None, scienceFwhmPix=None,
484 candidateList=None, doWarping=True, convolveTemplate=True):
485 """!Register, Psf-match and subtract two Exposures 487 Do the following, in order: 488 - Warp templateExposure to match scienceExposure, if their WCSs do not already match 489 - Determine a PSF matching kernel and differential background model 490 that matches templateExposure to scienceExposure 491 - PSF-match templateExposure to scienceExposure 492 - Compute subtracted exposure (see return values for equation). 494 @param templateExposure: exposure to PSF-match to scienceExposure 495 @param scienceExposure: reference Exposure 496 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 497 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 498 @param candidateList: a list of footprints/maskedImages for kernel candidates; 499 if None then source detection is run. 500 - Currently supported: list of Footprints or measAlg.PsfCandidateF 501 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match: 502 - if True then warp templateExposure to match scienceExposure 503 - if False then raise an Exception 504 @param convolveTemplate: convolve the template image or the science image 505 - if True, templateExposure is warped if doWarping, templateExposure is convolved 506 - if False, templateExposure is warped if doWarping, scienceExposure is convolved 508 @return a pipeBase.Struct containing these fields: 509 - subtractedExposure: subtracted Exposure = scienceExposure - (matchedImage + backgroundModel) 510 - matchedImage: templateExposure after warping to match templateExposure (if doWarping true), 511 and convolving with psfMatchingKernel 512 - psfMatchingKernel: PSF matching kernel 513 - backgroundModel: differential background model 514 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel 517 templateExposure=templateExposure,
518 scienceExposure=scienceExposure,
519 templateFwhmPix=templateFwhmPix,
520 scienceFwhmPix=scienceFwhmPix,
521 candidateList=candidateList,
523 convolveTemplate=convolveTemplate
526 subtractedExposure = afwImage.ExposureF(scienceExposure,
True)
528 subtractedMaskedImage = subtractedExposure.getMaskedImage()
529 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
530 subtractedMaskedImage -= results.backgroundModel
532 subtractedExposure.setMaskedImage(results.warpedExposure.getMaskedImage())
533 subtractedMaskedImage = subtractedExposure.getMaskedImage()
534 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
535 subtractedMaskedImage -= results.backgroundModel
538 subtractedMaskedImage *= -1
541 subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
542 afwImage.ImageD(results.psfMatchingKernel.getDimensions()),
False)
548 if not maskTransparency:
551 ds9.setMaskTransparency(maskTransparency)
552 if display
and displayDiffIm:
553 ds9.mtv(templateExposure, frame=lsstDebug.frame, title=
"Template")
555 ds9.mtv(results.matchedExposure, frame=lsstDebug.frame, title=
"Matched template")
557 ds9.mtv(scienceExposure, frame=lsstDebug.frame, title=
"Science Image")
559 ds9.mtv(subtractedExposure, frame=lsstDebug.frame, title=
"Difference Image")
562 results.subtractedExposure = subtractedExposure
567 templateFwhmPix=None, scienceFwhmPix=None):
568 """!Psf-match and subtract two MaskedImages 570 Do the following, in order: 571 - PSF-match templateMaskedImage to scienceMaskedImage 572 - Determine the differential background 573 - Return the difference: scienceMaskedImage - 574 ((warped templateMaskedImage convolved with psfMatchingKernel) + backgroundModel) 576 @param templateMaskedImage: MaskedImage to PSF-match to scienceMaskedImage 577 @param scienceMaskedImage: reference MaskedImage 578 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 579 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 580 @param candidateList: a list of footprints/maskedImages for kernel candidates; 581 if None then source detection is run. 582 - Currently supported: list of Footprints or measAlg.PsfCandidateF 584 @return a pipeBase.Struct containing these fields: 585 - subtractedMaskedImage = scienceMaskedImage - (matchedImage + backgroundModel) 586 - matchedImage: templateMaskedImage convolved with psfMatchingKernel 587 - psfMatchingKernel: PSF matching kernel 588 - backgroundModel: differential background model 589 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel 591 if not candidateList:
592 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
595 templateMaskedImage=templateMaskedImage,
596 scienceMaskedImage=scienceMaskedImage,
597 candidateList=candidateList,
598 templateFwhmPix=templateFwhmPix,
599 scienceFwhmPix=scienceFwhmPix,
602 subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage,
True)
603 subtractedMaskedImage -= results.matchedImage
604 subtractedMaskedImage -= results.backgroundModel
605 results.subtractedMaskedImage = subtractedMaskedImage
611 if not maskTransparency:
614 ds9.setMaskTransparency(maskTransparency)
615 if display
and displayDiffIm:
616 ds9.mtv(subtractedMaskedImage, frame=lsstDebug.frame)
622 """!Get sources to use for Psf-matching 624 This method runs detection and measurement on an exposure. 625 The returned set of sources will be used as candidates for 628 @param exposure: Exposure on which to run detection/measurement 629 @param sigma: Detection threshold 630 @param doSmooth: Whether or not to smooth the Exposure with Psf before detection 631 @param idFactory: Factory for the generation of Source ids 633 @return source catalog containing candidates for the Psf-matching 637 table = afwTable.SourceTable.make(self.
selectSchema, idFactory)
640 mi = exposure.getMaskedImage()
642 imArr = mi.getImage().getArray()
643 maskArr = mi.getMask().getArray()
644 miArr = np.ma.masked_array(imArr, mask=maskArr)
646 bkgd = self.
background.fitBackground(mi).getImageF()
648 self.log.warn(
"Failed to get background model. Falling back to median background estimation")
649 bkgd = np.ma.extras.median(miArr)
655 detRet = self.selectDetection.makeSourceCatalog(
661 selectSources = detRet.sources
662 self.selectMeasurement.run(measCat=selectSources, exposure=exposure)
670 """!Make a list of acceptable KernelCandidates 672 Accept or generate a list of candidate sources for 673 Psf-matching, and examine the Mask planes in both of the 674 images for indications of bad pixels 676 @param templateExposure: Exposure that will be convolved 677 @param scienceExposure: Exposure that will be matched-to 678 @param kernelSize: Dimensions of the Psf-matching Kernel, used to grow detection footprints 679 @param candidateList: List of Sources to examine. Elements must be of type afw.table.Source 680 or a type that wraps a Source and has a getSource() method, such as 681 meas.algorithms.PsfCandidateF. 683 @return a list of dicts having a "source" and "footprint" 684 field for the Sources deemed to be appropriate for Psf 687 if candidateList
is None:
690 if len(candidateList) < 1:
691 raise RuntimeError(
"No candidates in candidateList")
693 listTypes = set(type(x)
for x
in candidateList)
694 if len(listTypes) > 1:
695 raise RuntimeError(
"Candidate list contains mixed types: %s" % [l
for l
in listTypes])
697 if not isinstance(candidateList[0], afwTable.SourceRecord):
699 candidateList[0].getSource()
700 except Exception
as e:
701 raise RuntimeError(
"Candidate List is of type: %s. " % (type(candidateList[0])) +
702 "Can only make candidate list from list of afwTable.SourceRecords, " +
703 "measAlg.PsfCandidateF or other type with a getSource() method: %s" % (e))
704 candidateList = [c.getSource()
for c
in candidateList]
706 candidateList = diffimTools.sourceToFootprintList(candidateList,
707 templateExposure, scienceExposure,
711 if len(candidateList) == 0:
712 raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
716 def _adaptCellSize(self, candidateList):
717 """! NOT IMPLEMENTED YET""" 720 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
721 """!Build a SpatialCellSet for use with the solve method 723 @param templateMaskedImage: MaskedImage to PSF-matched to scienceMaskedImage 724 @param scienceMaskedImage: reference MaskedImage 725 @param candidateList: a list of footprints/maskedImages for kernel candidates; 726 if None then source detection is run. 727 - Currently supported: list of Footprints or measAlg.PsfCandidateF 729 @return kernelCellSet: a SpatialCellSet for use with self._solve 731 if not candidateList:
732 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
738 sizeCellX, sizeCellY)
740 policy = pexConfig.makePolicy(self.
kConfig)
742 for cand
in candidateList:
743 bbox = cand[
'footprint'].getBBox()
745 tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
746 smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
747 cand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, policy)
749 self.log.debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
750 kernelCellSet.insertCandidate(cand)
754 def _validateSize(self, templateMaskedImage, scienceMaskedImage):
755 """!Return True if two image-like objects are the same size 757 return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
759 def _validateWcs(self, templateExposure, scienceExposure):
760 """!Return True if the WCS of the two Exposures have the same origin and extent 762 templateWcs = templateExposure.getWcs()
763 scienceWcs = scienceExposure.getWcs()
764 templateBBox = templateExposure.getBBox()
765 scienceBBox = scienceExposure.getBBox()
768 templateOrigin = templateWcs.pixelToSky(afwGeom.Point2D(templateBBox.getBegin()))
769 scienceOrigin = scienceWcs.pixelToSky(afwGeom.Point2D(scienceBBox.getBegin()))
772 templateLimit = templateWcs.pixelToSky(afwGeom.Point2D(templateBBox.getEnd()))
773 scienceLimit = scienceWcs.pixelToSky(afwGeom.Point2D(scienceBBox.getEnd()))
775 self.log.info(
"Template Wcs : %f,%f -> %f,%f",
776 templateOrigin[0], templateOrigin[1],
777 templateLimit[0], templateLimit[1])
778 self.log.info(
"Science Wcs : %f,%f -> %f,%f",
779 scienceOrigin[0], scienceOrigin[1],
780 scienceLimit[0], scienceLimit[1])
782 templateBBox = afwGeom.Box2D(templateOrigin.getPosition(afwGeom.degrees),
783 templateLimit.getPosition(afwGeom.degrees))
784 scienceBBox = afwGeom.Box2D(scienceOrigin.getPosition(afwGeom.degrees),
785 scienceLimit.getPosition(afwGeom.degrees))
786 if not (templateBBox.overlaps(scienceBBox)):
787 raise RuntimeError(
"Input images do not overlap at all")
789 if ((templateOrigin != scienceOrigin)
or 790 (templateLimit != scienceLimit)
or 791 (templateExposure.getDimensions() != scienceExposure.getDimensions())):
796 subtractAlgorithmRegistry = pexConfig.makeRegistry(
797 doc=
"A registry of subtraction algorithms for use as a subtask in imageDifference",
800 subtractAlgorithmRegistry.register(
'al', ImagePsfMatchTask)
Base class for Psf Matching; should not be called directly.
Configuration for image-to-image Psf matching.
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)
def getSelectSources(self, exposure, sigma=None, doSmooth=True, idFactory=None)
Get sources to use for Psf-matching.
def _validateSize(self, templateMaskedImage, scienceMaskedImage)
Return True if two image-like objects are the same size.
Psf-match two MaskedImages or Exposures using the sources in the images.
def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList)
Build a SpatialCellSet for use with the solve method.
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 >())
def _adaptCellSize(self, candidateList)
NOT IMPLEMENTED YET.
def __init__(self, args, kwargs)
Create the ImagePsfMatchTask.
def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage) ...
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
Solve for the PSF matching kernel.
def matchExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
Warp and PSF-match an exposure to the reference.
def makeCandidateList(self, templateExposure, scienceExposure, kernelSize, candidateList=None)
Make a list of acceptable KernelCandidates.
def subtractMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None)
Psf-match and subtract two MaskedImages.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
def getFwhmPix(self, psf)
Return the FWHM in pixels of a Psf.
def subtractExposures(self, templateExposure, scienceExposure, templateFwhmPix=None, scienceFwhmPix=None, candidateList=None, doWarping=True, convolveTemplate=True)
Register, Psf-match and subtract two Exposures.
def _validateWcs(self, templateExposure, scienceExposure)
Return True if the WCS of the two Exposures have the same origin and extent.