22from astropy
import units
as u
23from astropy.stats
import gaussian_fwhm_to_sigma
31 computeDifferenceImageMetrics,
32 checkMask, setSourceFootprints)
33from lsst.meas.algorithms
import ScaleVarianceTask, ScienceSourceSelectorTask
38from .
import MakeKernelTask, DecorrelateALKernelTask
39from lsst.utils.timer
import timeMethod
41__all__ = [
"AlardLuptonSubtractConfig",
"AlardLuptonSubtractTask",
42 "AlardLuptonPreconvolveSubtractConfig",
"AlardLuptonPreconvolveSubtractTask",
43 "SimplifiedSubtractConfig",
"SimplifiedSubtractTask",
44 "InsufficientKernelSourcesError"]
46_dimensions = (
"instrument",
"visit",
"detector")
47_defaultTemplates = {
"coaddName":
"deep",
"fakesType":
""}
51 """Raised when there are too few sources to calculate the PSF matching
55 msg = (f
"Only {nSources} sources were selected for PSF matching,"
56 f
" but {nRequired} are required.")
69 dimensions=_dimensions,
70 defaultTemplates=_defaultTemplates):
71 template = connectionTypes.Input(
72 doc=
"Input warped template to subtract.",
73 dimensions=(
"instrument",
"visit",
"detector"),
74 storageClass=
"ExposureF",
75 name=
"{fakesType}{coaddName}Diff_templateExp"
77 science = connectionTypes.Input(
78 doc=
"Input science exposure to subtract from.",
79 dimensions=(
"instrument",
"visit",
"detector"),
80 storageClass=
"ExposureF",
81 name=
"{fakesType}calexp"
83 sources = connectionTypes.Input(
84 doc=
"Sources measured on the science exposure; "
85 "used to select sources for making the matching kernel.",
86 dimensions=(
"instrument",
"visit",
"detector"),
87 storageClass=
"SourceCatalog",
90 visitSummary = connectionTypes.Input(
91 doc=(
"Per-visit catalog with final calibration objects. "
92 "These catalogs use the detector id for the catalog id, "
93 "sorted on id for fast lookup."),
94 dimensions=(
"instrument",
"visit"),
95 storageClass=
"ExposureCatalog",
96 name=
"finalVisitSummary",
101 if not config.doApplyExternalCalibrations:
106 dimensions=_dimensions,
107 defaultTemplates=_defaultTemplates):
108 difference = connectionTypes.Output(
109 doc=
"Result of subtracting convolved template from science image.",
110 dimensions=(
"instrument",
"visit",
"detector"),
111 storageClass=
"ExposureF",
112 name=
"{fakesType}{coaddName}Diff_differenceTempExp",
114 matchedTemplate = connectionTypes.Output(
115 doc=
"Warped and PSF-matched template used to create `subtractedExposure`.",
116 dimensions=(
"instrument",
"visit",
"detector"),
117 storageClass=
"ExposureF",
118 name=
"{fakesType}{coaddName}Diff_matchedExp",
120 psfMatchingKernel = connectionTypes.Output(
121 doc=
"Kernel used to PSF match the science and template images.",
122 dimensions=(
"instrument",
"visit",
"detector"),
123 storageClass=
"MatchingKernel",
124 name=
"{fakesType}{coaddName}Diff_psfMatchKernel",
126 kernelSources = connectionTypes.Output(
127 doc=
"Final selection of sources used for psf matching.",
128 dimensions=(
"instrument",
"visit",
"detector"),
129 storageClass=
"SourceCatalog",
130 name=
"{fakesType}{coaddName}Diff_psfMatchSources"
135 dimensions=_dimensions,
136 defaultTemplates=_defaultTemplates):
137 scoreExposure = connectionTypes.Output(
138 doc=
"The maximum likelihood image, used for the detection of diaSources.",
139 dimensions=(
"instrument",
"visit",
"detector"),
140 storageClass=
"ExposureF",
141 name=
"{fakesType}{coaddName}Diff_scoreTempExp",
143 psfMatchingKernel = connectionTypes.Output(
144 doc=
"Kernel used to PSF match the science and template images.",
145 dimensions=(
"instrument",
"visit",
"detector"),
146 storageClass=
"MatchingKernel",
147 name=
"{fakesType}{coaddName}Diff_psfScoreMatchKernel",
149 kernelSources = connectionTypes.Output(
150 doc=
"Final selection of sources used for psf matching.",
151 dimensions=(
"instrument",
"visit",
"detector"),
152 storageClass=
"SourceCatalog",
153 name=
"{fakesType}{coaddName}Diff_psfScoreMatchSources"
161class SimplifiedSubtractConnections(SubtractInputConnections, SubtractImageOutputConnections):
162 inputPsfMatchingKernel = connectionTypes.Input(
163 doc=
"Kernel used to PSF match the science and template images.",
164 dimensions=(
"instrument",
"visit",
"detector"),
165 storageClass=
"MatchingKernel",
166 name=
"{fakesType}{coaddName}Diff_psfMatchKernel",
172 if config.useExistingKernel:
180 makeKernel = lsst.pex.config.ConfigurableField(
181 target=MakeKernelTask,
182 doc=
"Task to construct a matching kernel for convolution.",
184 doDecorrelation = lsst.pex.config.Field(
187 doc=
"Perform diffim decorrelation to undo pixel correlation due to A&L "
188 "kernel convolution? If True, also update the diffim PSF."
190 decorrelate = lsst.pex.config.ConfigurableField(
191 target=DecorrelateALKernelTask,
192 doc=
"Task to decorrelate the image difference.",
194 requiredTemplateFraction = lsst.pex.config.Field(
197 doc=
"Raise NoWorkFound and do not attempt image subtraction if template covers less than this "
198 " fraction of pixels. Setting to 0 will always attempt image subtraction."
200 minTemplateFractionForExpectedSuccess = lsst.pex.config.Field(
203 doc=
"Raise NoWorkFound if PSF-matching fails and template covers less than this fraction of pixels."
204 " If the fraction of pixels covered by the template is less than this value (and greater than"
205 " requiredTemplateFraction) this task is attempted but failure is anticipated and tolerated."
207 doScaleVariance = lsst.pex.config.Field(
210 doc=
"Scale variance of the image difference?"
212 scaleVariance = lsst.pex.config.ConfigurableField(
213 target=ScaleVarianceTask,
214 doc=
"Subtask to rescale the variance of the template to the statistically expected level."
216 doSubtractBackground = lsst.pex.config.Field(
217 doc=
"Subtract the background fit when solving the kernel? "
218 "It is generally better to instead subtract the background in detectAndMeasure.",
222 doApplyExternalCalibrations = lsst.pex.config.Field(
224 "Replace science Exposure's calibration objects with those"
225 " in visitSummary. Ignored if `doApplyFinalizedPsf is True."
230 sourceSelector = lsst.pex.config.ConfigurableField(
231 target=ScienceSourceSelectorTask,
232 doc=
"Task to select sources to be used for PSF matching.",
234 fallbackSourceSelector = lsst.pex.config.ConfigurableField(
235 target=ScienceSourceSelectorTask,
236 doc=
"Task to select sources to be used for PSF matching."
237 "Used only if the kernel calculation fails and"
238 "`allowKernelSourceDetection` is set. The fallback source detection"
239 " will not include all of the same plugins as the original source "
240 " detection, so not all of the same flags can be used.",
242 detectionThreshold = lsst.pex.config.Field(
245 doc=
"Minimum signal to noise ratio of detected sources "
246 "to use for calculating the PSF matching kernel.",
247 deprecated=
"No longer used. Will be removed after v30"
249 detectionThresholdMax = lsst.pex.config.Field(
252 doc=
"Maximum signal to noise ratio of detected sources "
253 "to use for calculating the PSF matching kernel.",
254 deprecated=
"No longer used. Will be removed after v30"
256 restrictKernelEdgeSources = lsst.pex.config.Field(
259 doc=
"Exclude sources close to the edge from the kernel calculation?"
261 maxKernelSources = lsst.pex.config.Field(
264 doc=
"Maximum number of sources to use for calculating the PSF matching kernel."
265 "Set to -1 to disable."
267 minKernelSources = lsst.pex.config.Field(
270 doc=
"Minimum number of sources needed for calculating the PSF matching kernel."
272 excludeMaskPlanes = lsst.pex.config.ListField(
274 default=(
"NO_DATA",
"BAD",
"SAT",
"EDGE",
"FAKE",
"HIGH_VARIANCE"),
275 doc=
"Template mask planes to exclude when selecting sources for PSF matching.",
277 badMaskPlanes = lsst.pex.config.ListField(
279 default=(
"NO_DATA",
"BAD",
"SAT",
"EDGE"),
280 doc=
"Mask planes to interpolate over."
282 preserveTemplateMask = lsst.pex.config.ListField(
284 default=(
"NO_DATA",
"BAD",
"HIGH_VARIANCE"),
285 doc=
"Mask planes from the template to propagate to the image difference."
287 renameTemplateMask = lsst.pex.config.ListField(
289 default=(
"SAT",
"INJECTED",
"INJECTED_CORE",),
290 doc=
"Mask planes from the template to propagate to the image difference"
291 "with '_TEMPLATE' appended to the name."
293 allowKernelSourceDetection = lsst.pex.config.Field(
296 doc=
"Re-run source detection for kernel candidates if an error is"
297 " encountered while calculating the matching kernel."
304 self.
makeKernel.kernel.active.fitForBackground =
True
305 self.
makeKernel.kernel.active.spatialKernelOrder = 1
306 self.
makeKernel.kernel.active.spatialBgOrder = 2
309 doSignalToNoise =
True
311 signalToNoiseMinimum = 10
312 signalToNoiseMaximum = 500
333 pipelineConnections=AlardLuptonSubtractConnections):
334 mode = lsst.pex.config.ChoiceField(
336 default=
"convolveTemplate",
337 allowed={
"auto":
"Choose which image to convolve at runtime.",
338 "convolveScience":
"Only convolve the science image.",
339 "convolveTemplate":
"Only convolve the template image."},
340 doc=
"Choose which image to convolve at runtime, or require that a specific image is convolved."
345 """Compute the image difference of a science and template image using
346 the Alard & Lupton (1998) algorithm.
348 ConfigClass = AlardLuptonSubtractConfig
349 _DefaultName =
"alardLuptonSubtract"
350 usePreconvolution =
False
351 """Whether this task preconvolves the science image with its own PSF
352 before kernel-matching. Subclasses that preconvolve override this to
357 self.makeSubtask(
"decorrelate")
358 self.makeSubtask(
"makeKernel")
359 self.makeSubtask(
"sourceSelector")
360 self.makeSubtask(
"fallbackSourceSelector")
361 if self.config.doScaleVariance:
362 self.makeSubtask(
"scaleVariance")
371 """Replace calibrations (psf, and ApCorrMap) on this exposure with
376 exposure : `lsst.afw.image.exposure.Exposure`
377 Input exposure to adjust calibrations.
378 visitSummary : `lsst.afw.table.ExposureCatalog`
379 Exposure catalog with external calibrations to be applied. Catalog
380 uses the detector id for the catalog id, sorted on id for fast
385 exposure : `lsst.afw.image.exposure.Exposure`
386 Exposure with adjusted calibrations.
388 detectorId = exposure.info.getDetector().getId()
390 row = visitSummary.find(detectorId)
392 self.
log.warning(
"Detector id %s not found in external calibrations catalog; "
393 "Using original calibrations.", detectorId)
396 apCorrMap = row.getApCorrMap()
398 self.
log.warning(
"Detector id %s has None for psf in "
399 "external calibrations catalog; Using original psf and aperture correction.",
401 elif apCorrMap
is None:
402 self.
log.warning(
"Detector id %s has None for apCorrMap in "
403 "external calibrations catalog; Using original psf and aperture correction.",
407 exposure.info.setApCorrMap(apCorrMap)
412 inputs = butlerQC.get(inputRefs)
415 results = self.
run(**inputs)
416 except lsst.pipe.base.AlgorithmError
as e:
417 error = lsst.pipe.base.AnnotatedPartialOutputsError.annotate(e, self, log=self.
log)
421 butlerQC.put(results, outputRefs)
424 def run(self, template, science, sources, visitSummary=None):
425 """PSF match, subtract, and decorrelate two images.
429 template : `lsst.afw.image.ExposureF`
430 Template exposure, warped to match the science exposure.
431 science : `lsst.afw.image.ExposureF`
432 Science exposure to subtract from the template.
433 sources : `lsst.afw.table.SourceCatalog`
434 Identified sources on the science exposure. This catalog is used to
435 select sources in order to perform the AL PSF matching on stamp
437 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
438 Exposure catalog with external calibrations to be applied. Catalog
439 uses the detector id for the catalog id, sorted on id for fast
444 results : `lsst.pipe.base.Struct`
445 ``difference`` : `lsst.afw.image.ExposureF`
446 Result of subtracting template and science.
447 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
448 Warped and PSF-matched template exposure.
449 ``backgroundModel`` : `lsst.afw.math.Function2D`
450 Background model that was fit while solving for the
452 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
453 Kernel used to PSF-match the convolved image.
454 ``kernelSources` : `lsst.afw.table.SourceCatalog`
455 Sources from the input catalog that were used to construct the
463 kernelResult = self.
runMakeKernel(template, science, sources=sources,
464 convolveTemplate=convolveTemplate,
465 runSourceDetection=
False)
467 if self.config.doSubtractBackground:
468 backgroundModel = kernelResult.backgroundModel
470 backgroundModel =
None
472 subtractResults = self.
runConvolveTemplate(template, science, kernelResult.psfMatchingKernel,
473 backgroundModel=backgroundModel)
475 subtractResults = self.
runConvolveScience(template, science, kernelResult.psfMatchingKernel,
476 backgroundModel=backgroundModel)
477 subtractResults.kernelSources = kernelResult.kernelSources
479 metrics = computeDifferenceImageMetrics(science, subtractResults.difference, sources)
481 self.metadata[
"differenceFootprintRatioMean"] = metrics.differenceFootprintRatioMean
482 self.metadata[
"differenceFootprintRatioStdev"] = metrics.differenceFootprintRatioStdev
483 self.metadata[
"differenceFootprintSkyRatioMean"] = metrics.differenceFootprintSkyRatioMean
484 self.metadata[
"differenceFootprintSkyRatioStdev"] = metrics.differenceFootprintSkyRatioStdev
485 self.
log.info(
"Mean, stdev of ratio of difference to science "
486 "pixels in star footprints: %5.4f, %5.4f",
487 self.metadata[
"differenceFootprintRatioMean"],
488 self.metadata[
"differenceFootprintRatioStdev"])
490 return subtractResults
493 """Determine whether the template should be convolved with the PSF
498 template : `lsst.afw.image.ExposureF`
499 Template exposure, warped to match the science exposure.
500 science : `lsst.afw.image.ExposureF`
501 Science exposure to subtract from the template.
505 convolveTemplate : `bool`
506 Convolve the template to match the two images?
511 If an unsupported convolution mode is supplied.
514 raise RuntimeError(
"Choosing a convolution method is incompatible with preconvolution!")
515 if self.config.mode ==
"auto":
518 fwhmExposureBuffer=self.config.makeKernel.fwhmExposureBuffer,
519 fwhmExposureGrid=self.config.makeKernel.fwhmExposureGrid)
522 self.
log.info(
"Average template PSF size is greater, "
523 "but science PSF greater in one dimension: convolving template image.")
525 self.
log.info(
"Science PSF size is greater: convolving template image.")
527 self.
log.info(
"Template PSF size is greater: convolving science image.")
528 elif self.config.mode ==
"convolveTemplate":
529 self.
log.info(
"`convolveTemplate` is set: convolving template image.")
530 convolveTemplate =
True
531 elif self.config.mode ==
"convolveScience":
532 self.
log.info(
"`convolveScience` is set: convolving science image.")
533 convolveTemplate =
False
535 raise RuntimeError(f
"Cannot handle AlardLuptonSubtract mode: {self.config.mode}")
536 return convolveTemplate
538 def runMakeKernel(self, template, science, sources=None, convolveTemplate=True, runSourceDetection=False):
539 """Construct the PSF-matching kernel. Not used for preconvolution.
543 template : `lsst.afw.image.ExposureF`
544 Template exposure, warped to match the science exposure.
545 science : `lsst.afw.image.ExposureF`
546 Science exposure to subtract from the template.
547 sources : `lsst.afw.table.SourceCatalog`
548 Identified sources on the science exposure. This catalog is used to
549 select sources in order to perform the AL PSF matching on stamp
551 Not used if ``runSourceDetection`` is set.
552 convolveTemplate : `bool`, optional
553 Construct the matching kernel to convolve the template?
554 runSourceDetection : `bool`, optional
555 Run a minimal version of source detection to determine kernel
556 candidates? If False, a source list to select kernel candidates
557 from must be supplied.
561 results : `lsst.pipe.base.Struct`
562 ``backgroundModel`` : `lsst.afw.math.Function2D`
563 Background model that was fit while solving for the
565 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
566 Kernel used to PSF-match the convolved image.
567 ``kernelSources` : `lsst.afw.table.SourceCatalog`
568 Sources from the input catalog that were used to construct the
572 raise RuntimeError(
"Incorrect matching kernel calculation configured. "
573 "`runMakeKernel` can't be called if `usePreconvolution` is set.")
585 if runSourceDetection:
589 kernelResult = self.makeKernel.
run(reference, target, kernelSources,
591 templateFwhmPix=referenceFwhmPix,
592 scienceFwhmPix=targetFwhmPix)
594 self.
log.warning(
"Failed to match template. Checking coverage")
597 self.config.minTemplateFractionForExpectedSuccess,
598 exceptionMessage=
"Template coverage lower than expected to succeed."
599 f
" Failure is tolerable: {e}")
603 return lsst.pipe.base.Struct(backgroundModel=kernelResult.backgroundModel,
604 psfMatchingKernel=kernelResult.psfMatchingKernel,
605 kernelSources=kernelSources)
608 """Run detection on the science image and use the template mask plane
609 to reject candidate sources.
613 template : `lsst.afw.image.ExposureF`
614 Template exposure, warped to match the science exposure.
615 science : `lsst.afw.image.ExposureF`
616 Science exposure to subtract from the template.
620 kernelSources : `lsst.afw.table.SourceCatalog`
621 Sources from the input catalog to use to construct the
624 kernelSize = self.makeKernel.makeKernelBasisList(
626 sources = self.makeKernel.makeCandidateList(template, science, kernelSize,
632 """Convolve the template image with a PSF-matching kernel and subtract
633 from the science image.
637 template : `lsst.afw.image.ExposureF`
638 Template exposure, warped to match the science exposure.
639 science : `lsst.afw.image.ExposureF`
640 Science exposure to subtract from the template.
641 psfMatchingKernel : `lsst.afw.math.Kernel`
642 Kernel to be used to PSF-match the science image to the template.
643 backgroundModel : `lsst.afw.math.Function2D`, optional
644 Background model that was fit while solving for the PSF-matching
649 results : `lsst.pipe.base.Struct`
651 ``difference`` : `lsst.afw.image.ExposureF`
652 Result of subtracting template and science.
653 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
654 Warped and PSF-matched template exposure.
655 ``backgroundModel`` : `lsst.afw.math.Function2D`
656 Background model that was fit while solving for the PSF-matching kernel
657 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
658 Kernel used to PSF-match the template to the science image.
660 self.metadata[
"convolvedExposure"] =
"Template"
664 bbox=science.getBBox(),
666 photoCalib=science.photoCalib)
668 difference =
_subtractImages(science, matchedTemplate, backgroundModel=backgroundModel)
669 correctedExposure = self.
finalize(template, science, difference,
671 templateMatched=
True)
673 return lsst.pipe.base.Struct(difference=correctedExposure,
674 matchedTemplate=matchedTemplate,
675 matchedScience=science,
676 backgroundModel=backgroundModel,
677 psfMatchingKernel=psfMatchingKernel)
680 """Convolve the science image with a PSF-matching kernel and subtract
685 template : `lsst.afw.image.ExposureF`
686 Template exposure, warped to match the science exposure.
687 science : `lsst.afw.image.ExposureF`
688 Science exposure to subtract from the template.
689 psfMatchingKernel : `lsst.afw.math.Kernel`
690 Kernel to be used to PSF-match the science image to the template.
691 backgroundModel : `lsst.afw.math.Function2D`, optional
692 Background model that was fit while solving for the PSF-matching
697 results : `lsst.pipe.base.Struct`
699 ``difference`` : `lsst.afw.image.ExposureF`
700 Result of subtracting template and science.
701 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
702 Warped template exposure. Note that in this case, the template
703 is not PSF-matched to the science image.
704 ``backgroundModel`` : `lsst.afw.math.Function2D`
705 Background model that was fit while solving for the PSF-matching kernel
706 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
707 Kernel used to PSF-match the science image to the template.
709 self.metadata[
"convolvedExposure"] =
"Science"
710 bbox = science.getBBox()
712 kernelImage = lsst.afw.image.ImageD(psfMatchingKernel.getDimensions())
713 xcen, ycen = bbox.getCenter()
714 norm = psfMatchingKernel.computeImage(kernelImage, doNormalize=
False, x=xcen, y=ycen)
721 matchedScience.maskedImage /= norm
722 matchedTemplate = template.clone()[bbox]
723 matchedTemplate.setPhotoCalib(science.photoCalib)
725 if backgroundModel
is not None:
727 invertedBackground = backgroundModel.clone()
728 invertedBackground.setParameters([-p
for p
in backgroundModel.getParameters()])
729 backgroundModel = invertedBackground
731 difference =
_subtractImages(matchedScience, matchedTemplate, backgroundModel=backgroundModel)
733 correctedExposure = self.
finalize(template, science, difference,
735 templateMatched=
False)
737 return lsst.pipe.base.Struct(difference=correctedExposure,
738 matchedTemplate=matchedTemplate,
739 matchedScience=matchedScience,
740 backgroundModel=backgroundModel,
741 psfMatchingKernel=psfMatchingKernel)
743 def finalize(self, template, science, difference, kernel,
744 templateMatched=True,
747 spatiallyVarying=False):
748 """Decorrelate the difference image to undo the noise correlations
749 caused by convolution.
753 template : `lsst.afw.image.ExposureF`
754 Template exposure, warped to match the science exposure.
755 science : `lsst.afw.image.ExposureF`
756 Science exposure to subtract from the template.
757 difference : `lsst.afw.image.ExposureF`
758 Result of subtracting template and science.
759 kernel : `lsst.afw.math.Kernel`
760 An (optionally spatially-varying) PSF matching kernel
761 templateMatched : `bool`, optional
762 Was the template PSF-matched to the science image?
763 preConvMode : `bool`, optional
764 Was the science image preconvolved with its own PSF
765 before PSF matching the template?
766 preConvKernel : `lsst.afw.detection.Psf`, optional
767 If not `None`, then the science image was pre-convolved with
768 (the reflection of) this kernel. Must be normalized to sum to 1.
769 spatiallyVarying : `bool`, optional
770 Compute the decorrelation kernel spatially varying across the image?
774 correctedExposure : `lsst.afw.image.ExposureF`
775 The decorrelated image difference.
777 if self.config.doDecorrelation:
778 self.
log.info(
"Decorrelating image difference.")
782 correctedExposure = self.decorrelate.
run(science, template[science.getBBox()], difference, kernel,
783 templateMatched=templateMatched,
784 preConvMode=preConvMode,
785 preConvKernel=preConvKernel,
786 spatiallyVarying=spatiallyVarying).correctedExposure
788 self.
log.info(
"NOT decorrelating image difference.")
789 correctedExposure = difference
790 return correctedExposure
793 """Calculate an exposure's limiting magnitude.
795 This method uses the photometric zeropoint together with the
796 PSF size from the average position of the exposure.
800 exposure : `lsst.afw.image.Exposure`
801 The target exposure to calculate the limiting magnitude for.
802 nsigma : `float`, optional
803 The detection threshold in sigma.
804 fallbackPsfSize : `float`, optional
805 PSF FWHM to use in the event the exposure PSF cannot be retrieved.
809 maglim : `astropy.units.Quantity`
810 The limiting magnitude of the exposure, or np.nan.
812 if exposure.photoCalib
is None:
815 psf = exposure.getPsf()
816 psf_shape = psf.computeShape(psf.getAveragePosition())
818 afwDetection.InvalidPsfError,
820 if fallbackPsfSize
is None:
821 self.
log.info(
"Unable to evaluate PSF, setting maglim to nan")
823 self.
log.info(
"Unable to evaluate PSF, using fallback FWHM %f", fallbackPsfSize)
824 psf_area = np.pi*(fallbackPsfSize/2)**2
827 psf_area = np.pi*np.sqrt(psf_shape.getIxx()*psf_shape.getIyy())
829 zeropoint = exposure.photoCalib.instFluxToMagnitude(1)
830 return zeropoint - 2.5*np.log10(nsigma*np.sqrt(psf_area))
834 """Check that the WCS of the two Exposures match, the template bbox
835 contains the science bbox, and that the bands match.
839 template : `lsst.afw.image.ExposureF`
840 Template exposure, warped to match the science exposure.
841 science : `lsst.afw.image.ExposureF`
842 Science exposure to subtract from the template.
847 Raised if the WCS of the template is not equal to the science WCS,
848 if the science image is not fully contained in the template
849 bounding box, or if the bands do not match.
851 assert template.wcs == science.wcs, \
852 "Template and science exposure WCS are not identical."
853 templateBBox = template.getBBox()
854 scienceBBox = science.getBBox()
855 assert science.filter.bandLabel == template.filter.bandLabel, \
856 "Science and template exposures have different bands: %s, %s" % \
857 (science.filter, template.filter)
859 assert templateBBox.contains(scienceBBox), \
860 "Template bbox does not contain all of the science image."
866 interpolateBadMaskPlanes=False,
868 """Convolve an exposure with the given kernel.
872 exposure : `lsst.afw.Exposure`
873 exposure to convolve.
874 kernel : `lsst.afw.math.LinearCombinationKernel`
875 PSF matching kernel computed in the ``makeKernel`` subtask.
876 convolutionControl : `lsst.afw.math.ConvolutionControl`
877 Configuration for convolve algorithm.
878 bbox : `lsst.geom.Box2I`, optional
879 Bounding box to trim the convolved exposure to.
880 psf : `lsst.afw.detection.Psf`, optional
881 Point spread function (PSF) to set for the convolved exposure.
882 photoCalib : `lsst.afw.image.PhotoCalib`, optional
883 Photometric calibration of the convolved exposure.
884 interpolateBadMaskPlanes : `bool`, optional
885 If set, interpolate over mask planes specified in
886 ``config.badMaskPlanes`` before convolving the image.
890 convolvedExp : `lsst.afw.Exposure`
893 convolvedExposure = exposure.clone()
895 convolvedExposure.setPsf(psf)
896 if photoCalib
is not None:
897 convolvedExposure.setPhotoCalib(photoCalib)
898 if interpolateBadMaskPlanes
and self.config.badMaskPlanes
is not None:
900 self.config.badMaskPlanes)
901 self.metadata[
"nInterpolated"] = nInterp
902 convolvedImage = lsst.afw.image.MaskedImageF(convolvedExposure.getBBox())
904 convolvedExposure.setMaskedImage(convolvedImage)
906 return convolvedExposure
908 return convolvedExposure[bbox]
911 """Select sources from a catalog that meet the selection criteria.
912 The selection criteria include any configured parameters of the
913 `sourceSelector` subtask, as well as checking the science and template
918 template : `lsst.afw.image.ExposureF`
919 Template exposure, warped to match the science exposure.
920 science : `lsst.afw.image.ExposureF`
921 Science exposure to subtract from the template.
922 sources : `lsst.afw.table.SourceCatalog`
923 Input source catalog to select sources from.
924 fallback : `bool`, optional
925 Switch indicating the source selector is being called after
926 running the fallback source detection subtask, which does not run a
927 full set of measurement plugins and can't use the same settings for
932 kernelSources : `lsst.afw.table.SourceCatalog`
933 The input source catalog, with flagged and low signal-to-noise
934 sources removed and footprints added.
938 InsufficientKernelSourcesError
939 An AlgorithmError that is raised if there are not enough PSF
940 candidates to construct the PSF matching kernel.
943 selected = self.fallbackSourceSelector.selectSources(sources).selected
945 selected = self.sourceSelector.selectSources(sources).selected
950 selectSources = sources[selected].copy(deep=
True)
952 kernelSources = setSourceFootprints(selectSources, kernelSize=kSize)
953 bbox = science.getBBox()
958 if self.config.restrictKernelEdgeSources:
961 scienceSelected = checkMask(science.mask[bbox], kernelSources, self.config.excludeMaskPlanes)
962 templateSelected = checkMask(template.mask[bbox], kernelSources, self.config.excludeMaskPlanes)
963 maskSelected = scienceSelected & templateSelected
964 kernelSources = kernelSources[maskSelected].copy(deep=
True)
967 if (len(kernelSources) > self.config.maxKernelSources) & (self.config.maxKernelSources > 0):
968 signalToNoise = kernelSources.getPsfInstFlux()/kernelSources.getPsfInstFluxErr()
969 indices = np.argsort(signalToNoise)
970 indices = indices[-self.config.maxKernelSources:]
971 selected = np.zeros(len(kernelSources), dtype=bool)
972 selected[indices] =
True
973 kernelSources = kernelSources[selected].copy(deep=
True)
975 self.
log.info(
"%i/%i=%.1f%% of sources selected for PSF matching from the input catalog",
976 len(kernelSources), len(sources), 100*len(kernelSources)/len(sources))
977 if len(kernelSources) < self.config.minKernelSources:
978 self.
log.error(
"Too few sources to calculate the PSF matching kernel: "
979 "%i selected but %i needed for the calculation.",
980 len(kernelSources), self.config.minKernelSources)
981 if self.config.allowKernelSourceDetection
and not fallback:
988 nRequired=self.config.minKernelSources)
990 self.metadata[
"nPsfSources"] = len(kernelSources)
995 """Perform preparatory calculations common to all Alard&Lupton Tasks.
999 template : `lsst.afw.image.ExposureF`
1000 Template exposure, warped to match the science exposure. The
1001 variance plane of the template image is modified in place.
1002 science : `lsst.afw.image.ExposureF`
1003 Science exposure to subtract from the template. The variance plane
1004 of the science image is modified in place.
1005 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
1006 Exposure catalog with external calibrations to be applied. Catalog
1007 uses the detector id for the catalog id, sorted on id for fast
1011 if visitSummary
is not None:
1014 template[science.getBBox()], science, self.
log,
1015 requiredTemplateFraction=self.config.requiredTemplateFraction,
1016 exceptionMessage=
"Not attempting subtraction. To force subtraction,"
1017 " set config requiredTemplateFraction=0"
1019 self.metadata[
"templateCoveragePercent"] = 100*templateCoverageFraction
1021 if self.config.doScaleVariance:
1025 templateVarFactor = self.scaleVariance.
run(template.maskedImage)
1026 sciVarFactor = self.scaleVariance.
run(science.maskedImage)
1027 self.
log.info(
"Template variance scaling factor: %.2f", templateVarFactor)
1028 self.metadata[
"scaleTemplateVarianceFactor"] = templateVarFactor
1029 self.
log.info(
"Science variance scaling factor: %.2f", sciVarFactor)
1030 self.metadata[
"scaleScienceVarianceFactor"] = sciVarFactor
1056 self.
log.info(
"Unable to evaluate PSF at the average position. "
1057 "Evaluting PSF on a grid of points."
1061 fwhmExposureBuffer=self.config.makeKernel.fwhmExposureBuffer,
1062 fwhmExposureGrid=self.config.makeKernel.fwhmExposureGrid
1066 fwhmExposureBuffer=self.config.makeKernel.fwhmExposureBuffer,
1067 fwhmExposureGrid=self.config.makeKernel.fwhmExposureGrid
1076 if np.isnan(maglim_science):
1077 self.
log.warning(
"Limiting magnitude of the science image is NaN!")
1078 fluxlim_science = (maglim_science*u.ABmag).to_value(u.nJy)
1080 if np.isnan(maglim_template):
1081 self.
log.info(
"Cannot evaluate template limiting mag; adopting science limiting mag for diffim")
1082 maglim_diffim = maglim_science
1084 fluxlim_template = (maglim_template*u.ABmag).to_value(u.nJy)
1085 maglim_diffim = (np.sqrt(fluxlim_science**2 + fluxlim_template**2)*u.nJy).to(u.ABmag).value
1086 self.metadata[
"scienceLimitingMagnitude"] = maglim_science
1087 self.metadata[
"templateLimitingMagnitude"] = maglim_template
1088 self.metadata[
"diffimLimitingMagnitude"] = maglim_diffim
1091 """Update the science and template mask planes before differencing.
1095 template : `lsst.afw.image.Exposure`
1096 Template exposure, warped to match the science exposure.
1097 The template mask planes will be erased, except for a few specified
1099 science : `lsst.afw.image.Exposure`
1100 Science exposure to subtract from the template.
1101 The DETECTED and DETECTED_NEGATIVE mask planes of the science image
1104 self.
_clearMask(science.mask, clearMaskPlanes=[
"DETECTED",
"DETECTED_NEGATIVE"])
1111 clearMaskPlanes = [mp
for mp
in template.mask.getMaskPlaneDict().keys()
1112 if mp
not in self.config.preserveTemplateMask]
1113 renameMaskPlanes = [mp
for mp
in self.config.renameTemplateMask
1114 if mp
in template.mask.getMaskPlaneDict().keys()]
1119 if "FAKE" in science.mask.getMaskPlaneDict().keys():
1120 self.
log.info(
"Adding injected mask plane to science image")
1122 if "FAKE" in template.mask.getMaskPlaneDict().keys():
1123 self.
log.info(
"Adding injected mask plane to template image")
1125 if "INJECTED" in renameMaskPlanes:
1126 renameMaskPlanes.remove(
"INJECTED")
1127 if "INJECTED_TEMPLATE" in clearMaskPlanes:
1128 clearMaskPlanes.remove(
"INJECTED_TEMPLATE")
1130 for maskPlane
in renameMaskPlanes:
1132 self.
_clearMask(template.mask, clearMaskPlanes=clearMaskPlanes)
1136 """Rename a mask plane by adding the new name and copying the data.
1140 mask : `lsst.afw.image.Mask`
1141 The mask image to update in place.
1143 The name of the existing mask plane to copy.
1144 newMaskPlane : `str`
1145 The new name of the mask plane that will be added.
1146 If the mask plane already exists, it will be updated in place.
1148 mask.addMaskPlane(newMaskPlane)
1149 originBitMask = mask.getPlaneBitMask(maskPlane)
1150 destinationBitMask = mask.getPlaneBitMask(newMaskPlane)
1151 mask.array |= ((mask.array & originBitMask) > 0)*destinationBitMask
1154 """Clear the mask plane of an exposure.
1158 mask : `lsst.afw.image.Mask`
1159 The mask plane to erase, which will be modified in place.
1160 clearMaskPlanes : `list` of `str`, optional
1161 Erase the specified mask planes.
1162 If not supplied, the entire mask will be erased.
1164 if clearMaskPlanes
is None:
1165 clearMaskPlanes = list(mask.getMaskPlaneDict().keys())
1167 bitMaskToClear = mask.getPlaneBitMask(clearMaskPlanes)
1168 mask &= ~bitMaskToClear
1172 SubtractScoreOutputConnections):
1177 pipelineConnections=AlardLuptonPreconvolveSubtractConnections):
1182 """Subtract a template from a science image, convolving the science image
1183 before computing the kernel, and also convolving the template before
1186 ConfigClass = AlardLuptonPreconvolveSubtractConfig
1187 _DefaultName =
"alardLuptonPreconvolveSubtract"
1188 usePreconvolution =
True
1190 def run(self, template, science, sources, visitSummary=None):
1191 """Preconvolve the science image with its own PSF,
1192 convolve the template image with a PSF-matching kernel and subtract
1193 from the preconvolved science image.
1197 template : `lsst.afw.image.ExposureF`
1198 The template image, which has previously been warped to the science
1199 image. The template bbox will be padded by a few pixels compared to
1201 science : `lsst.afw.image.ExposureF`
1202 The science exposure.
1203 sources : `lsst.afw.table.SourceCatalog`
1204 Identified sources on the science exposure. This catalog is used to
1205 select sources in order to perform the AL PSF matching on stamp
1207 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
1208 Exposure catalog with complete external calibrations. Catalog uses
1209 the detector id for the catalog id, sorted on id for fast lookup.
1213 results : `lsst.pipe.base.Struct`
1214 ``scoreExposure`` : `lsst.afw.image.ExposureF`
1215 Result of subtracting the convolved template and science
1216 images. Attached PSF is that of the original science image.
1217 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
1218 Warped and PSF-matched template exposure. Attached PSF is that
1219 of the original science image.
1220 ``matchedScience`` : `lsst.afw.image.ExposureF`
1221 The science exposure after convolving with its own PSF.
1222 Attached PSF is that of the original science image.
1223 ``backgroundModel`` : `lsst.afw.math.Function2D`
1224 Background model that was fit while solving for the
1226 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
1227 Final kernel used to PSF-match the template to the science
1230 self.
_prepareInputs(template, science, visitSummary=visitSummary)
1234 interpolateBadMaskPlanes=
True)
1235 self.metadata[
"convolvedExposure"] =
"Preconvolution"
1239 self.metadata[
"preconvolvedSciencePsfSize"] = self.
matchedPsfSize
1241 kernelSources = self.
_sourceSelector(template, matchedScience, sources)
1242 subtractResults = self.
runPreconvolve(template, science, matchedScience,
1243 kernelSources, convolutionKernel)
1246 self.
log.warning(
"Failed to match template. Checking coverage")
1249 self.config.minTemplateFractionForExpectedSuccess,
1250 exceptionMessage=
"Template coverage lower than expected to succeed."
1251 f
" Failure is tolerable: {e}")
1255 return subtractResults
1259 """Set the EDGE mask bit on pixels outside a known-valid region.
1263 mask : `~lsst.afw.image.Mask`
1264 Exposure mask that will be modified in place. Must have
1265 an ``EDGE`` mask plane.
1266 innerBBox : `~lsst.geom.Box2I`
1267 The valid inner region. Pixels
1268 outside this bbox will have their ``EDGE`` bit set.
1270 bbox = mask.getBBox()
1271 edgeBit = mask.getPlaneBitMask(
"EDGE")
1272 dx0 = innerBBox.getMinX() - bbox.getMinX()
1273 dx1 = bbox.getMaxX() - innerBBox.getMaxX()
1274 dy0 = innerBBox.getMinY() - bbox.getMinY()
1275 dy1 = bbox.getMaxY() - innerBBox.getMaxY()
1277 mask.array[:dy0, :] |= edgeBit
1279 mask.array[-dy1:, :] |= edgeBit
1281 mask.array[:, :dx0] |= edgeBit
1283 mask.array[:, -dx1:] |= edgeBit
1287 """Build a normalized, reflected matched-filter kernel from a PSF.
1289 Convolving an image with this kernel is equivalent to correlating
1290 the image with the PSF, so peaks in the output align with the PSF's
1291 centroid — even for asymmetric PSFs. The kernel is evaluated at the
1292 PSF's average position and returned as a constant
1293 `~lsst.afw.math.Kernel`.
1297 psf : `~lsst.afw.detection.Psf`
1298 The PSF to derive the preconvolution kernel from.
1302 kernel : `~lsst.afw.math.Kernel`
1303 The PSF reflected about both axes, normalized to sum to one.
1308 Raised if the PSF kernel has an even size along either axis.
1309 It's not possible to center an even-sized kernel.
1311 avgPos = psf.getAveragePosition()
1312 localKernel = psf.getLocalKernel(avgPos)
1313 dims = localKernel.getDimensions()
1314 if dims.x % 2 == 0
or dims.y % 2 == 0:
1316 f
"Preconvolution requires an odd-sized PSF kernel, got {dims.x}x{dims.y}. "
1318 kimg = lsst.afw.image.ImageD(dims)
1319 localKernel.computeImage(kimg, doNormalize=
True)
1322 kimg.array[...] = kimg.array[::-1, ::-1]
1325 def runPreconvolve(self, template, science, matchedScience, kernelSources, preConvKernel):
1326 """Convolve the science image with its own PSF, then convolve the
1327 template with a matching kernel and subtract to form the Score
1332 template : `lsst.afw.image.ExposureF`
1333 Template exposure, warped to match the science exposure.
1334 science : `lsst.afw.image.ExposureF`
1335 Science exposure to subtract from the template.
1336 matchedScience : `lsst.afw.image.ExposureF`
1337 The science exposure, convolved with the reflection of its own PSF.
1338 kernelSources : `lsst.afw.table.SourceCatalog`
1339 Identified sources on the science exposure. This catalog is used to
1340 select sources in order to perform the AL PSF matching on stamp
1342 preConvKernel : `lsst.afw.math.Kernel`
1343 The kernel that was used to preconvolve the ``science``
1344 exposure. Must be normalized to sum to 1.
1348 results : `lsst.pipe.base.Struct`
1350 ``scoreExposure`` : `lsst.afw.image.ExposureF`
1351 Result of subtracting the convolved template and science
1352 images. Attached PSF is that of the original science image.
1353 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
1354 Warped and PSF-matched template exposure. Attached PSF is that
1355 of the original science image.
1356 ``matchedScience`` : `lsst.afw.image.ExposureF`
1357 The science exposure after convolving with its own PSF.
1358 Attached PSF is that of the original science image.
1359 ``backgroundModel`` : `lsst.afw.math.Function2D`
1360 Background model that was fit while solving for the
1362 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
1363 Final kernel used to PSF-match the template to the science
1366 bbox = science.getBBox()
1367 innerBBox = preConvKernel.shrinkBBox(bbox)
1369 kernelResult = self.makeKernel.
run(template[innerBBox], matchedScience[innerBBox], kernelSources,
1374 matchedTemplate = self.
_convolveExposure(template, kernelResult.psfMatchingKernel,
1378 interpolateBadMaskPlanes=
True,
1379 photoCalib=science.photoCalib)
1381 backgroundModel=(kernelResult.backgroundModel
1382 if self.config.doSubtractBackground
else None))
1383 correctedScore = self.
finalize(template[bbox], science, score,
1384 kernelResult.psfMatchingKernel,
1385 templateMatched=
True, preConvMode=
True,
1386 preConvKernel=preConvKernel)
1391 return lsst.pipe.base.Struct(scoreExposure=correctedScore,
1392 matchedTemplate=matchedTemplate,
1393 matchedScience=matchedScience,
1394 backgroundModel=kernelResult.backgroundModel,
1395 psfMatchingKernel=kernelResult.psfMatchingKernel,
1396 kernelSources=kernelSources)
1400 exceptionMessage=""):
1401 """Raise NoWorkFound if template coverage < requiredTemplateFraction
1405 templateExposure : `lsst.afw.image.ExposureF`
1406 The template exposure to check
1407 logger : `logging.Logger`
1408 Logger for printing output.
1409 requiredTemplateFraction : `float`, optional
1410 Fraction of pixels of the science image required to have coverage
1412 exceptionMessage : `str`, optional
1413 Message to include in the exception raised if the template coverage
1418 templateCoverageFraction: `float`
1419 Fraction of pixels in the template with data.
1423 lsst.pipe.base.NoWorkFound
1424 Raised if fraction of good pixels, defined as not having NO_DATA
1425 set, is less than the requiredTemplateFraction
1429 noTemplate = templateExposure.mask.array & templateExposure.mask.getPlaneBitMask(
'NO_DATA')
1432 noScience = scienceExposure.mask.array & scienceExposure.mask.getPlaneBitMask(
'NO_DATA')
1433 pixNoData = np.count_nonzero(noTemplate | noScience)
1434 pixGood = templateExposure.getBBox().getArea() - pixNoData
1435 templateCoverageFraction = pixGood/templateExposure.getBBox().getArea()
1436 logger.info(
"template has %d good pixels (%.1f%%)", pixGood, 100*templateCoverageFraction)
1438 if templateCoverageFraction < requiredTemplateFraction:
1439 message = (
"Insufficient Template Coverage. (%.1f%% < %.1f%%)" % (
1440 100*templateCoverageFraction,
1441 100*requiredTemplateFraction))
1442 raise lsst.pipe.base.NoWorkFound(message +
" " + exceptionMessage)
1443 return templateCoverageFraction
1447 """Subtract template from science, propagating relevant metadata.
1451 science : `lsst.afw.Exposure`
1452 The input science image.
1453 template : `lsst.afw.Exposure`
1454 The template to subtract from the science image.
1455 backgroundModel : `lsst.afw.MaskedImage`, optional
1456 Differential background model
1460 difference : `lsst.afw.Exposure`
1461 The subtracted image.
1463 difference = science.clone()
1464 if backgroundModel
is not None:
1465 difference.maskedImage -= backgroundModel
1466 difference.maskedImage -= template.maskedImage
1471 """Determine that the PSF of ``exp1`` is not wider than that of ``exp2``.
1475 exp1 : `~lsst.afw.image.Exposure`
1476 Exposure with the reference point spread function (PSF) to evaluate.
1477 exp2 : `~lsst.afw.image.Exposure`
1478 Exposure with a candidate point spread function (PSF) to evaluate.
1479 fwhmExposureBuffer : `float`
1480 Fractional buffer margin to be left out of all sides of the image
1481 during the construction of the grid to compute mean PSF FWHM in an
1482 exposure, if the PSF is not available at its average position.
1483 fwhmExposureGrid : `int`
1484 Grid size to compute the mean FWHM in an exposure, if the PSF is not
1485 available at its average position.
1489 True if ``exp1`` has a PSF that is not wider than that of ``exp2`` in
1493 shape1 = getPsfFwhm(exp1.psf, average=
False)
1494 shape2 = getPsfFwhm(exp2.psf, average=
False)
1496 shape1 = evaluateMeanPsfFwhm(exp1,
1497 fwhmExposureBuffer=fwhmExposureBuffer,
1498 fwhmExposureGrid=fwhmExposureGrid
1500 shape2 = evaluateMeanPsfFwhm(exp2,
1501 fwhmExposureBuffer=fwhmExposureBuffer,
1502 fwhmExposureGrid=fwhmExposureGrid
1504 return shape1 <= shape2
1507 xTest = shape1[0] <= shape2[0]
1508 yTest = shape1[1] <= shape2[1]
1509 return xTest | yTest
1513 pipelineConnections=SimplifiedSubtractConnections):
1514 mode = lsst.pex.config.ChoiceField(
1516 default=
"convolveTemplate",
1517 allowed={
"auto":
"Choose which image to convolve at runtime.",
1518 "convolveScience":
"Only convolve the science image.",
1519 "convolveTemplate":
"Only convolve the template image."},
1520 doc=
"Choose which image to convolve at runtime, or require that a specific image is convolved."
1522 useExistingKernel = lsst.pex.config.Field(
1525 doc=
"Use a pre-existing PSF matching kernel?"
1526 "If False, source detection and measurement will be run."
1531 """Compute the image difference of a science and template image using
1532 the Alard & Lupton (1998) algorithm.
1534 ConfigClass = SimplifiedSubtractConfig
1535 _DefaultName =
"simplifiedSubtract"
1538 def run(self, template, science, visitSummary=None, inputPsfMatchingKernel=None):
1539 """PSF match, subtract, and decorrelate two images.
1543 template : `lsst.afw.image.ExposureF`
1544 Template exposure, warped to match the science exposure.
1545 science : `lsst.afw.image.ExposureF`
1546 Science exposure to subtract from the template.
1547 visitSummary : `lsst.afw.table.ExposureCatalog`, optional
1548 Exposure catalog with external calibrations to be applied. Catalog
1549 uses the detector id for the catalog id, sorted on id for fast
1551 inputPsfMatchingKernel : `lsst.afw.math.Kernel`, optional
1552 Pre-existing PSF matching kernel to use for convolution.
1553 Required, and only used, if ``config.useExistingKernel`` is set.
1557 results : `lsst.pipe.base.Struct`
1558 ``difference`` : `lsst.afw.image.ExposureF`
1559 Result of subtracting template and science.
1560 ``matchedTemplate`` : `lsst.afw.image.ExposureF`
1561 Warped and PSF-matched template exposure.
1562 ``backgroundModel`` : `lsst.afw.math.Function2D`
1563 Background model that was fit while solving for the
1565 ``psfMatchingKernel`` : `lsst.afw.math.Kernel`
1566 Kernel used to PSF-match the convolved image.
1567 ``kernelSources` : `lsst.afw.table.SourceCatalog`
1568 Sources detected on the science image that were used to
1569 construct the PSF-matching kernel.
1573 lsst.pipe.base.NoWorkFound
1574 Raised if fraction of good pixels, defined as not having NO_DATA
1575 set, is less then the configured requiredTemplateFraction
1577 self.
_prepareInputs(template, science, visitSummary=visitSummary)
1582 if self.config.useExistingKernel:
1583 psfMatchingKernel = inputPsfMatchingKernel
1584 backgroundModel =
None
1585 kernelSources =
None
1587 kernelResult = self.
runMakeKernel(template, science, convolveTemplate=convolveTemplate,
1588 runSourceDetection=
True)
1589 psfMatchingKernel = kernelResult.psfMatchingKernel
1590 kernelSources = kernelResult.kernelSources
1591 if self.config.doSubtractBackground:
1592 backgroundModel = kernelResult.backgroundModel
1594 backgroundModel =
None
1595 if convolveTemplate:
1597 backgroundModel=backgroundModel)
1600 backgroundModel=backgroundModel)
1601 if kernelSources
is not None:
1602 subtractResults.kernelSources = kernelSources
1603 return subtractResults
1607 """Replace masked image pixels with interpolated values.
1611 maskedImage : `lsst.afw.image.MaskedImage`
1612 Image on which to perform interpolation.
1613 badMaskPlanes : `list` of `str`
1614 List of mask planes to interpolate over.
1615 fallbackValue : `float`, optional
1616 Value to set when interpolation fails.
1621 The number of masked pixels that were replaced.
1623 imgBadMaskPlanes = [
1624 maskPlane
for maskPlane
in badMaskPlanes
if maskPlane
in maskedImage.mask.getMaskPlaneDict()
1627 image = maskedImage.image.array
1628 badPixels = (maskedImage.mask.array & maskedImage.mask.getPlaneBitMask(imgBadMaskPlanes)) > 0
1629 image[badPixels] = np.nan
1630 if fallbackValue
is None:
1631 fallbackValue = np.nanmedian(image)
1634 image[badPixels] = fallbackValue
1635 return np.sum(badPixels)
_flagScoreEdge(mask, innerBBox)
run(self, template, science, sources, visitSummary=None)
runPreconvolve(self, template, science, matchedScience, kernelSources, preConvKernel)
_makePreconvolutionKernel(psf)
_clearMask(self, mask, clearMaskPlanes=None)
_prepareInputs(self, template, science, visitSummary=None)
chooseConvolutionMethod(self, template, science)
runConvolveTemplate(self, template, science, psfMatchingKernel, backgroundModel=None)
runQuantum(self, butlerQC, inputRefs, outputRefs)
run(self, template, science, sources, visitSummary=None)
runConvolveScience(self, template, science, psfMatchingKernel, backgroundModel=None)
_calculateMagLim(self, exposure, nsigma=5.0, fallbackPsfSize=None)
_applyExternalCalibrations(self, exposure, visitSummary)
updateMasks(self, template, science)
runMakeKernel(self, template, science, sources=None, convolveTemplate=True, runSourceDetection=False)
_convolveExposure(self, exposure, kernel, convolutionControl, bbox=None, psf=None, photoCalib=None, interpolateBadMaskPlanes=False)
_sourceSelector(self, template, science, sources, fallback=False)
runKernelSourceDetection(self, template, science)
finalize(self, template, science, difference, kernel, templateMatched=True, preConvMode=False, preConvKernel=None, spatiallyVarying=False)
_validateExposures(template, science)
_renameMaskPlanes(mask, maskPlane, newMaskPlane)
__init__(self, *, nSources, nRequired)
__init__(self, *, config=None)
run(self, template, science, visitSummary=None, inputPsfMatchingKernel=None)
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
_subtractImages(science, template, backgroundModel=None)
checkTemplateIsSufficient(templateExposure, scienceExposure, logger, requiredTemplateFraction=0., exceptionMessage="")
_interpolateImage(maskedImage, badMaskPlanes, fallbackValue=None)
_shapeTest(exp1, exp2, fwhmExposureBuffer, fwhmExposureGrid)