21 from __future__
import absolute_import, division, print_function
31 import lsst.pipe.base
as pipeBase
32 from lsst.meas.algorithms
import SourceDetectionTask, SubtractBackgroundTask
34 from .makeKernelBasisList
import makeKernelBasisList
35 from .psfMatch
import PsfMatchTask, PsfMatchConfigDF, PsfMatchConfigAL
36 from .
import utils
as diUtils
37 from .
import diffimLib
38 from .
import diffimTools
41 sigma2fwhm = 2. * np.sqrt(2. * np.log(2.))
45 """!Configuration for image-to-image Psf matching""" 46 kernel = pexConfig.ConfigChoiceField(
54 selectDetection = pexConfig.ConfigurableField(
55 target=SourceDetectionTask,
56 doc=
"Initial detections used to feed stars to kernel fitting",
58 selectMeasurement = pexConfig.ConfigurableField(
59 target=SingleFrameMeasurementTask,
60 doc=
"Initial measurements used to feed stars to kernel fitting",
70 self.
selectMeasurement.algorithms.names = (
'base_SdssCentroid',
'base_PsfFlux',
'base_PixelFlags',
71 'base_SdssShape',
'base_GaussianFlux',
'base_SkyCoord')
86 \anchor ImagePsfMatchTask_ 88 \brief Psf-match two MaskedImages or Exposures using the sources in the images 90 \section ip_diffim_imagepsfmatch_Contents Contents 92 - \ref ip_diffim_imagepsfmatch_Purpose 93 - \ref ip_diffim_imagepsfmatch_Initialize 94 - \ref ip_diffim_imagepsfmatch_IO 95 - \ref ip_diffim_imagepsfmatch_Config 96 - \ref ip_diffim_imagepsfmatch_Metadata 97 - \ref ip_diffim_imagepsfmatch_Debug 98 - \ref ip_diffim_imagepsfmatch_Example 100 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 102 \section ip_diffim_imagepsfmatch_Purpose Description 104 Build a Psf-matching kernel using two input images, either as MaskedImages (in which case they need 105 to be astrometrically aligned) or Exposures (in which case astrometric alignment will happen by 106 default but may be turned off). This requires a list of input Sources which may be provided 107 by the calling Task; if not, the Task will perform a coarse source detection and selection for this purpose. 108 Sources are vetted for signal-to-noise and masked pixels (in both the template and science image), and 109 substamps around each acceptable source are extracted and used to create an instance of KernelCandidate. 110 Each KernelCandidate is then placed within a lsst.afw.math.SpatialCellSet, which is used by an ensemble of 111 lsst.afw.math.CandidateVisitor instances to build the Psf-matching kernel. These visitors include, in 112 the order that they are called: BuildSingleKernelVisitor, KernelSumVisitor, BuildSpatialKernelVisitor, 113 and AssessSpatialKernelVisitor. 115 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, 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. Objects that are significantly above or below the mean, 132 typically due to variability or sources that are saturated in one image but not the other, are also rejected. 133 This threshold is defined by PsfMatchConfig.maxKsumSigma. Finally, a spatial model is built using all 134 currently-acceptable candidates, and the spatial model used to derive a second set of (spatial) residuals 135 which are again used to reject bad candidates, using the same thresholds as above. 137 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 139 \section ip_diffim_imagepsfmatch_Initialize Task initialization 141 \copydoc \_\_init\_\_ 143 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 145 \section ip_diffim_imagepsfmatch_IO Invoking the Task 147 There is no run() method for this Task. Instead there are 4 methods that 148 may be used to invoke the Psf-matching. These are 149 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchMaskedImages matchMaskedImages\endlink, 150 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractMaskedImages subtractMaskedImages\endlink, 151 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.matchExposures matchExposures\endlink, and 152 \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask.subtractExposures subtractExposures\endlink. 154 The methods that operate on lsst.afw.image.MaskedImage require that the images already be astrometrically 155 aligned, and are the same shape. The methods that operate on lsst.afw.image.Exposure allow for the 156 input images to be misregistered and potentially be different sizes; by default a 157 lsst.afw.math.LanczosWarpingKernel is used to perform the astrometric alignment. The methods 158 that "match" images return a Psf-matched image, while the methods that "subtract" images 159 return a Psf-matched and template subtracted image. 161 See each method's returned lsst.pipe.base.Struct for more details. 163 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 165 \section ip_diffim_imagepsfmatch_Config Configuration parameters 167 See \ref ImagePsfMatchConfig 169 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 171 \section ip_diffim_imagepsfmatch_Metadata Quantities set in Metadata 173 See \ref ip_diffim_psfmatch_Metadata "PsfMatchTask" 175 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 177 \section ip_diffim_imagepsfmatch_Debug Debug variables 179 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 180 flag \c -d/--debug to import \b debug.py from your \c PYTHONPATH. The relevant contents of debug.py 181 for this Task include: 187 di = lsstDebug.getInfo(name) 188 if name == "lsst.ip.diffim.psfMatch": 189 di.display = True # enable debug output 190 di.maskTransparency = 80 # ds9 mask transparency 191 di.displayCandidates = True # show all the candidates and residuals 192 di.displayKernelBasis = False # show kernel basis functions 193 di.displayKernelMosaic = True # show kernel realized across the image 194 di.plotKernelSpatialModel = False # show coefficients of spatial model 195 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 196 elif name == "lsst.ip.diffim.imagePsfMatch": 197 di.display = True # enable debug output 198 di.maskTransparency = 30 # ds9 mask transparency 199 di.displayTemplate = True # show full (remapped) template 200 di.displaySciIm = True # show science image to match to 201 di.displaySpatialCells = True # show spatial cells 202 di.displayDiffIm = True # show difference image 203 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 204 elif name == "lsst.ip.diffim.diaCatalogSourceSelector": 205 di.display = False # enable debug output 206 di.maskTransparency = 30 # ds9 mask transparency 207 di.displayExposure = True # show exposure with candidates indicated 208 di.pauseAtEnd = False # pause when done 210 lsstDebug.Info = DebugInfo 214 Note that if you want addional logging info, you may add to your scripts: 216 import lsst.log.utils as logUtils 217 logUtils.traceSetAt("ip.diffim", 4) 220 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 222 \section ip_diffim_imagepsfmatch_Example A complete example of using ImagePsfMatchTask 224 This code is imagePsfMatchTask.py in the examples directory, and can be run as \em e.g. 226 examples/imagePsfMatchTask.py --debug 227 examples/imagePsfMatchTask.py --debug --mode="matchExposures" 228 examples/imagePsfMatchTask.py --debug --template /path/to/templateExp.fits --science /path/to/scienceExp.fits 231 \dontinclude imagePsfMatchTask.py 232 Create a subclass of ImagePsfMatchTask that allows us to either match exposures, or subtract exposures: 233 \skip MyImagePsfMatchTask 234 @until self.subtractExposures 236 And allow the user the freedom to either run the script in default mode, or point to their own images on disk. 237 Note that these images must be readable as an lsst.afw.image.Exposure: 241 We have enabled some minor display debugging in this script via the --debug option. However, if you 242 have an lsstDebug debug.py in your PYTHONPATH you will get additional debugging displays. The following 243 block checks for this script: 247 \dontinclude imagePsfMatchTask.py 248 Finally, we call a run method that we define below. First set up a Config and modify some of the parameters. 249 E.g. use an "Alard-Lupton" sum-of-Gaussian basis, fit for a differential background, and use low order spatial 250 variation in the kernel and background: 252 @until spatialBgOrder 254 Make sure the images (if any) that were sent to the script exist on disk and are readable. If no images 255 are sent, make some fake data up for the sake of this example script (have a look at the code if you want 256 more details on generateFakeImages): 260 Create and run the Task: 264 And finally provide some optional debugging displays: 266 @until result.subtractedExposure 267 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 270 ConfigClass = ImagePsfMatchConfig
273 """!Create the ImagePsfMatchTask 275 \param *args arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 276 \param **kwargs keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__ 278 Upon initialization, the kernel configuration is defined by self.config.kernel.active. 279 The task creates an lsst.afw.math.Warper from the subConfig self.config.kernel.active.warpingConfig. 280 A schema for the selection and measurement of candidate lsst.ip.diffim.KernelCandidates is 281 defined, and used to initize subTasks selectDetection (for candidate detection) and selectMeasurement 282 (for candidate measurement). 284 PsfMatchTask.__init__(self, *args, **kwargs)
286 self.
_warper = afwMath.Warper.fromConfig(self.
kConfig.warpingConfig)
289 self.
background = SubtractBackgroundTask(config=self.
kConfig.afwBackgroundConfig, name=
"background",
293 self.makeSubtask(
"selectDetection", schema=self.
selectSchema)
297 """!Return the FWHM in pixels of a Psf""" 298 sigPix = psf.computeShape().getDeterminantRadius()
299 return sigPix * sigma2fwhm
303 templateFwhmPix=None, scienceFwhmPix=None,
304 candidateList=None, doWarping=True, convolveTemplate=True):
305 """!Warp and PSF-match an exposure to the reference 307 Do the following, in order: 308 - Warp templateExposure to match scienceExposure, 309 if doWarping True and their WCSs do not already match 310 - Determine a PSF matching kernel and differential background model 311 that matches templateExposure to scienceExposure 312 - Convolve templateExposure by PSF matching kernel 314 @param templateExposure: Exposure to warp and PSF-match to the reference masked image 315 @param scienceExposure: Exposure whose WCS and PSF are to be matched to 316 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 317 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 318 @param candidateList: a list of footprints/maskedImages for kernel candidates; 319 if None then source detection is run. 320 - Currently supported: list of Footprints or measAlg.PsfCandidateF 321 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match: 322 - if True then warp templateExposure to match scienceExposure 323 - if False then raise an Exception 324 @param convolveTemplate: convolve the template image or the science image 325 - if True, templateExposure is warped if doWarping, templateExposure is convolved 326 - if False, templateExposure is warped if doWarping, scienceExposure is convolved 328 @return a pipeBase.Struct containing these fields: 329 - matchedImage: the PSF-matched exposure = 330 warped templateExposure convolved by psfMatchingKernel. This has: 331 - the same parent bbox, Wcs and Calib as scienceExposure 332 - the same filter as templateExposure 333 - no Psf (because the PSF-matching process does not compute one) 334 - psfMatchingKernel: the PSF matching kernel 335 - backgroundModel: differential background model 336 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 338 Raise a RuntimeError if doWarping is False and templateExposure's and scienceExposure's 341 if not self.
_validateWcs(templateExposure, scienceExposure):
343 self.log.info(
"Astrometrically registering template to science image")
344 templatePsf = templateExposure.getPsf()
345 templateExposure = self.
_warper.warpExposure(scienceExposure.getWcs(),
347 destBBox=scienceExposure.getBBox())
348 templateExposure.setPsf(templatePsf)
350 self.log.error(
"ERROR: Input images not registered")
351 raise RuntimeError(
"Input images not registered")
353 if templateFwhmPix
is None:
354 if not templateExposure.hasPsf():
355 self.log.warn(
"No estimate of Psf FWHM for template image")
357 templateFwhmPix = self.
getFwhmPix(templateExposure.getPsf())
358 self.log.info(
"templateFwhmPix: {}".format(templateFwhmPix))
360 if scienceFwhmPix
is None:
361 if not scienceExposure.hasPsf():
362 self.log.warn(
"No estimate of Psf FWHM for science image")
364 scienceFwhmPix = self.
getFwhmPix(scienceExposure.getPsf())
365 self.log.info(
"scienceFwhmPix: {}".format(scienceFwhmPix))
368 candidateList = self.
makeCandidateList(templateExposure, scienceExposure, kernelSize, candidateList)
372 templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), candidateList,
373 templateFwhmPix=templateFwhmPix, scienceFwhmPix=scienceFwhmPix)
376 scienceExposure.getMaskedImage(), templateExposure.getMaskedImage(), candidateList,
377 templateFwhmPix=scienceFwhmPix, scienceFwhmPix=templateFwhmPix)
380 psfMatchedExposure.setFilter(templateExposure.getFilter())
381 psfMatchedExposure.setCalib(scienceExposure.getCalib())
382 results.warpedExposure = templateExposure
383 results.matchedExposure = psfMatchedExposure
387 def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList,
388 templateFwhmPix=None, scienceFwhmPix=None):
389 """!PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage) 391 Do the following, in order: 392 - Determine a PSF matching kernel and differential background model 393 that matches templateMaskedImage to scienceMaskedImage 394 - Convolve templateMaskedImage by the PSF matching kernel 396 @param templateMaskedImage: masked image to PSF-match to the reference masked image; 397 must be warped to match the reference masked image 398 @param scienceMaskedImage: maskedImage whose PSF is to be matched to 399 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 400 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 401 @param candidateList: a list of footprints/maskedImages for kernel candidates; 402 if None then source detection is run. 403 - Currently supported: list of Footprints or measAlg.PsfCandidateF 405 @return a pipeBase.Struct containing these fields: 406 - psfMatchedMaskedImage: the PSF-matched masked image = 407 templateMaskedImage convolved with psfMatchingKernel. 408 This has the same xy0, dimensions and wcs as scienceMaskedImage. 409 - psfMatchingKernel: the PSF matching kernel 410 - backgroundModel: differential background model 411 - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel 413 Raise a RuntimeError if input images have different dimensions 420 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
422 if not maskTransparency:
425 ds9.setMaskTransparency(maskTransparency)
427 if not candidateList:
428 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
430 if not self.
_validateSize(templateMaskedImage, scienceMaskedImage):
431 self.log.error(
"ERROR: Input images different size")
432 raise RuntimeError(
"Input images different size")
434 if display
and displayTemplate:
435 ds9.mtv(templateMaskedImage, frame=lsstDebug.frame, title=
"Image to convolve")
438 if display
and displaySciIm:
439 ds9.mtv(scienceMaskedImage, frame=lsstDebug.frame, title=
"Image to not convolve")
446 if display
and displaySpatialCells:
447 diUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet,
448 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, ctypeBad=ds9.RED,
449 size=4, frame=lsstDebug.frame, title=
"Image to not convolve")
452 if templateFwhmPix
and scienceFwhmPix:
453 self.log.info(
"Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix)
455 if self.
kConfig.useBicForKernelBasis:
460 bicDegrees = nbe(tmpKernelCellSet, self.log)
462 alardDegGauss=bicDegrees[0], metadata=self.metadata)
466 metadata=self.metadata)
468 spatialSolution, psfMatchingKernel, backgroundModel = self.
_solve(kernelCellSet, basisList)
470 psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox())
472 afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, doNormalize)
473 return pipeBase.Struct(
474 matchedImage=psfMatchedMaskedImage,
475 psfMatchingKernel=psfMatchingKernel,
476 backgroundModel=backgroundModel,
477 kernelCellSet=kernelCellSet,
482 templateFwhmPix=None, scienceFwhmPix=None,
483 candidateList=None, doWarping=True, convolveTemplate=True):
484 """!Register, Psf-match and subtract two Exposures 486 Do the following, in order: 487 - Warp templateExposure to match scienceExposure, if their WCSs do not already match 488 - Determine a PSF matching kernel and differential background model 489 that matches templateExposure to scienceExposure 490 - PSF-match templateExposure to scienceExposure 491 - Compute subtracted exposure (see return values for equation). 493 @param templateExposure: exposure to PSF-match to scienceExposure 494 @param scienceExposure: reference Exposure 495 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 496 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 497 @param candidateList: a list of footprints/maskedImages for kernel candidates; 498 if None then source detection is run. 499 - Currently supported: list of Footprints or measAlg.PsfCandidateF 500 @param doWarping: what to do if templateExposure's and scienceExposure's WCSs do not match: 501 - if True then warp templateExposure to match scienceExposure 502 - if False then raise an Exception 503 @param convolveTemplate: convolve the template image or the science image 504 - if True, templateExposure is warped if doWarping, templateExposure is convolved 505 - if False, templateExposure is warped if doWarping, scienceExposure is convolved 507 @return a pipeBase.Struct containing these fields: 508 - subtractedExposure: subtracted Exposure = scienceExposure - (matchedImage + backgroundModel) 509 - matchedImage: templateExposure after warping to match templateExposure (if doWarping true), 510 and convolving with psfMatchingKernel 511 - psfMatchingKernel: PSF matching kernel 512 - backgroundModel: differential background model 513 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel 516 templateExposure=templateExposure,
517 scienceExposure=scienceExposure,
518 templateFwhmPix=templateFwhmPix,
519 scienceFwhmPix=scienceFwhmPix,
520 candidateList=candidateList,
522 convolveTemplate=convolveTemplate
525 subtractedExposure = afwImage.ExposureF(scienceExposure,
True)
527 subtractedMaskedImage = subtractedExposure.getMaskedImage()
528 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
529 subtractedMaskedImage -= results.backgroundModel
531 subtractedExposure.setMaskedImage(results.warpedExposure.getMaskedImage())
532 subtractedMaskedImage = subtractedExposure.getMaskedImage()
533 subtractedMaskedImage -= results.matchedExposure.getMaskedImage()
534 subtractedMaskedImage -= results.backgroundModel
537 subtractedMaskedImage *= -1
540 subtractedMaskedImage /= results.psfMatchingKernel.computeImage(
541 afwImage.ImageD(results.psfMatchingKernel.getDimensions()),
False)
547 if not maskTransparency:
550 ds9.setMaskTransparency(maskTransparency)
551 if display
and displayDiffIm:
552 ds9.mtv(templateExposure, frame=lsstDebug.frame, title=
"Template")
554 ds9.mtv(results.matchedExposure, frame=lsstDebug.frame, title=
"Matched template")
556 ds9.mtv(scienceExposure, frame=lsstDebug.frame, title=
"Science Image")
558 ds9.mtv(subtractedExposure, frame=lsstDebug.frame, title=
"Difference Image")
561 results.subtractedExposure = subtractedExposure
566 templateFwhmPix=None, scienceFwhmPix=None):
567 """!Psf-match and subtract two MaskedImages 569 Do the following, in order: 570 - PSF-match templateMaskedImage to scienceMaskedImage 571 - Determine the differential background 572 - Return the difference: scienceMaskedImage - 573 ((warped templateMaskedImage convolved with psfMatchingKernel) + backgroundModel) 575 @param templateMaskedImage: MaskedImage to PSF-match to scienceMaskedImage 576 @param scienceMaskedImage: reference MaskedImage 577 @param templateFwhmPix: FWHM (in pixels) of the Psf in the template image (image to convolve) 578 @param scienceFwhmPix: FWHM (in pixels) of the Psf in the science image 579 @param candidateList: a list of footprints/maskedImages for kernel candidates; 580 if None then source detection is run. 581 - Currently supported: list of Footprints or measAlg.PsfCandidateF 583 @return a pipeBase.Struct containing these fields: 584 - subtractedMaskedImage = scienceMaskedImage - (matchedImage + backgroundModel) 585 - matchedImage: templateMaskedImage convolved with psfMatchingKernel 586 - psfMatchingKernel: PSF matching kernel 587 - backgroundModel: differential background model 588 - kernelCellSet: SpatialCellSet used to determine PSF matching kernel 590 if not candidateList:
591 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
594 templateMaskedImage=templateMaskedImage,
595 scienceMaskedImage=scienceMaskedImage,
596 candidateList=candidateList,
597 templateFwhmPix=templateFwhmPix,
598 scienceFwhmPix=scienceFwhmPix,
601 subtractedMaskedImage = afwImage.MaskedImageF(scienceMaskedImage,
True)
602 subtractedMaskedImage -= results.matchedImage
603 subtractedMaskedImage -= results.backgroundModel
604 results.subtractedMaskedImage = subtractedMaskedImage
610 if not maskTransparency:
613 ds9.setMaskTransparency(maskTransparency)
614 if display
and displayDiffIm:
615 ds9.mtv(subtractedMaskedImage, frame=lsstDebug.frame)
621 """!Get sources to use for Psf-matching 623 This method runs detection and measurement on an exposure. 624 The returned set of sources will be used as candidates for 627 @param exposure: Exposure on which to run detection/measurement 628 @param sigma: Detection threshold 629 @param doSmooth: Whether or not to smooth the Exposure with Psf before detection 630 @param idFactory: Factory for the generation of Source ids 632 @return source catalog containing candidates for the Psf-matching 636 table = afwTable.SourceTable.make(self.
selectSchema, idFactory)
639 mi = exposure.getMaskedImage()
641 imArr = mi.getImage().getArray()
642 maskArr = mi.getMask().getArray()
643 miArr = np.ma.masked_array(imArr, mask=maskArr)
645 bkgd = self.
background.fitBackground(mi).getImageF()
647 self.log.warn(
"Failed to get background model. Falling back to median background estimation")
648 bkgd = np.ma.extras.median(miArr)
654 detRet = self.selectDetection.makeSourceCatalog(
660 selectSources = detRet.sources
661 self.selectMeasurement.run(measCat=selectSources, exposure=exposure)
669 """!Make a list of acceptable KernelCandidates 671 Accept or generate a list of candidate sources for 672 Psf-matching, and examine the Mask planes in both of the 673 images for indications of bad pixels 675 @param templateExposure: Exposure that will be convolved 676 @param scienceExposure: Exposure that will be matched-to 677 @param kernelSize: Dimensions of the Psf-matching Kernel, used to grow detection footprints 678 @param candidateList: List of Sources to examine. Elements must be of type afw.table.Source 679 or a type that wraps a Source and has a getSource() method, such as 680 meas.algorithms.PsfCandidateF. 682 @return a list of dicts having a "source" and "footprint" 683 field for the Sources deemed to be appropriate for Psf 686 if candidateList
is None:
689 if len(candidateList) < 1:
690 raise RuntimeError(
"No candidates in candidateList")
692 listTypes = set(type(x)
for x
in candidateList)
693 if len(listTypes) > 1:
694 raise RuntimeError(
"Candidate list contains mixed types: %s" % [l
for l
in listTypes])
696 if not isinstance(candidateList[0], afwTable.SourceRecord):
698 candidateList[0].getSource()
699 except Exception
as e:
700 raise RuntimeError(
"Candidate List is of type: %s. " % (type(candidateList[0])) +
701 "Can only make candidate list from list of afwTable.SourceRecords, " +
702 "measAlg.PsfCandidateF or other type with a getSource() method: %s" % (e))
703 candidateList = [c.getSource()
for c
in candidateList]
705 candidateList = diffimTools.sourceToFootprintList(candidateList,
706 templateExposure, scienceExposure,
710 if len(candidateList) == 0:
711 raise RuntimeError(
"Cannot find any objects suitable for KernelCandidacy")
715 def _adaptCellSize(self, candidateList):
716 """! NOT IMPLEMENTED YET""" 719 def _buildCellSet(self, templateMaskedImage, scienceMaskedImage, candidateList):
720 """!Build a SpatialCellSet for use with the solve method 722 @param templateMaskedImage: MaskedImage to PSF-matched to scienceMaskedImage 723 @param scienceMaskedImage: reference MaskedImage 724 @param candidateList: a list of footprints/maskedImages for kernel candidates; 725 if None then source detection is run. 726 - Currently supported: list of Footprints or measAlg.PsfCandidateF 728 @return kernelCellSet: a SpatialCellSet for use with self._solve 730 if not candidateList:
731 raise RuntimeError(
"Candidate list must be populated by makeCandidateList")
737 sizeCellX, sizeCellY)
739 policy = pexConfig.makePolicy(self.
kConfig)
741 for cand
in candidateList:
742 bbox = cand[
'footprint'].getBBox()
744 tmi = afwImage.MaskedImageF(templateMaskedImage, bbox)
745 smi = afwImage.MaskedImageF(scienceMaskedImage, bbox)
746 cand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, policy)
748 self.log.debug(
"Candidate %d at %f, %f", cand.getId(), cand.getXCenter(), cand.getYCenter())
749 kernelCellSet.insertCandidate(cand)
753 def _validateSize(self, templateMaskedImage, scienceMaskedImage):
754 """!Return True if two image-like objects are the same size 756 return templateMaskedImage.getDimensions() == scienceMaskedImage.getDimensions()
758 def _validateWcs(self, templateExposure, scienceExposure):
759 """!Return True if the WCS of the two Exposures have the same origin and extent 761 templateWcs = templateExposure.getWcs()
762 scienceWcs = scienceExposure.getWcs()
763 templateBBox = templateExposure.getBBox()
764 scienceBBox = scienceExposure.getBBox()
767 templateOrigin = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getBegin()))
768 scienceOrigin = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getBegin()))
771 templateLimit = templateWcs.pixelToSky(
afwGeom.Point2D(templateBBox.getEnd()))
772 scienceLimit = scienceWcs.pixelToSky(
afwGeom.Point2D(scienceBBox.getEnd()))
774 self.log.info(
"Template Wcs : %f,%f -> %f,%f",
775 templateOrigin[0], templateOrigin[1],
776 templateLimit[0], templateLimit[1])
777 self.log.info(
"Science Wcs : %f,%f -> %f,%f",
778 scienceOrigin[0], scienceOrigin[1],
779 scienceLimit[0], scienceLimit[1])
781 templateBBox =
afwGeom.Box2D(templateOrigin.getPosition(), templateLimit.getPosition())
782 scienceBBox =
afwGeom.Box2D(scienceOrigin.getPosition(), scienceLimit.getPosition())
783 if not (templateBBox.overlaps(scienceBBox)):
784 raise RuntimeError(
"Input images do not overlap at all")
786 if ((templateOrigin.getPosition() != scienceOrigin.getPosition())
or 787 (templateLimit.getPosition() != scienceLimit.getPosition())
or 788 (templateExposure.getDimensions() != scienceExposure.getDimensions())):
793 subtractAlgorithmRegistry = pexConfig.makeRegistry(
794 doc=
"A registry of subtraction algorithms for use as a subtask in imageDifference",
797 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.