lsst.pipe.tasks g4544ed029c+0a480c5277
|
Classes | |
class | SubtractBrightStarsConnections |
Variables | |
logger = logging.getLogger(__name__) | |
inputExposure : `~lsst.afw.image.ExposureF` | |
inputBrightStarStamps : | |
inputExtendedPsf : `~lsst.pipe.tasks.extended_psf.ExtendedPsf` | |
dataId : `dict` or `~lsst.daf.butler.DataCoordinate` | |
skyCorr : `~lsst.afw.math.backgroundList.BackgroundList`, optional | |
refObjLoader : `~lsst.meas.algorithms.ReferenceObjectLoader`, optional | |
subtractorExp : `~lsst.afw.image.ExposureF` | |
invImages : `list` [`~lsst.afw.image.MaskedImageF`] | |
calexp : `~lsst.afw.image.Exposure` or `~lsst.afw.image.MaskedImage` | |
model : `~lsst.afw.image.MaskedImageF` | |
star : `~lsst.meas.algorithms.brightStarStamps.BrightStarStamp` | |
inPlace : `bool` | |
nb90Rots : `int` | |
psf_annular_flux : `float`, optional | |
scalingFactor : `float` | |
brightStarList : | |
annulusImage : `~lsst.afw.image.MaskedImageF` | |
brightStarStamp : | |
annulus : `~lsst.afw.image.MaskedImageF` | |
float | annularFlux (between 0 and 1) |
maskedModel : `~lsst.afw.image.MaskedImageF` | |
brightStarStamps : | |
numpy | PsfAnnularFluxes .array |
bbox : `~lsst.geom.Box2I` | |
invImage : `~lsst.afw.image.MaskedImageF` | |
subtractor : `~lsst.afw.image.MaskedImageF` | |
bool | multipleAnnuli , optional |
Retrieve extended PSF model and subtract bright stars at visit level.
float lsst.pipe.tasks.subtractBrightStars.annularFlux (between 0 and 1) |
Definition at line 587 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.annulus : `~lsst.afw.image.MaskedImageF` |
Definition at line 564 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.annulusImage : `~lsst.afw.image.MaskedImageF` |
self.setWarpTask() missedStars = self.warper.extractStamps( inputExposure, refObjLoader=refObjLoader, inputBrightStarStamps=inputBrightStarStamps ) if missedStars.starStamps: self.warpOutputs = self.warper.warpStamps(missedStars.starStamps, missedStars.pixCenters) brightStarList = [ BrightStarStamp( stamp_im=warp, archive_element=transform, position=self.warpOutputs.xy0s[j], gaiaGMag=missedStars.gMags[j], gaiaId=missedStars.gaiaIds[j], minValidAnnulusFraction=self.warper.config.minValidAnnulusFraction, ) for j, (warp, transform) in enumerate( zip(self.warpOutputs.warpedStars, self.warpOutputs.warpTransforms) ) ] else: brightStarList = [] return brightStarList def initAnnulusImage(self):
# Create SpanSet of annulus. outerCircle = SpanSet.fromShape( brightStarStamp.optimalOuterRadius, Stencil.CIRCLE, offset=self.warper.modelCenter ) innerCircle = SpanSet.fromShape( brightStarStamp.optimalInnerRadius, Stencil.CIRCLE, offset=self.warper.modelCenter ) annulus = outerCircle.intersectNot(innerCircle) return annulus def applyStatsControl(self, annulusImage):
Definition at line 538 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.bbox : `~lsst.geom.Box2I` |
Definition at line 675 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.brightStarList : |
Definition at line 505 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.brightStarStamp : |
maskPlaneDict = self.model.mask.getMaskPlaneDict() annulusImage = MaskedImageF(self.modelStampSize, planeDict=maskPlaneDict) annulusImage.mask.array[:] = 2 ** maskPlaneDict["NO_DATA"] return annulusImage def createAnnulus(self, brightStarStamp):
andMask = reduce( ior, (annulusImage.mask.getPlaneBitMask(bm) for bm in self.warper.config.badMaskPlanes) ) self.missedStatsControl.setAndMask(andMask) annulusStat = makeStatistics(annulusImage, self.missedStatsFlag, self.missedStatsControl) return annulusStat.getValue() def findPsfAnnularFlux(self, brightStarStamp, maskedModel):
outerRadii = [] annularFluxes = [] maskedModel = MaskedImageF(self.model.image) # The model has wrong bbox values. Should be fixed in extended_psf.py? maskedModel.setXY0(0, 0) for star in brightStarStamps: if star.optimalOuterRadius not in outerRadii: annularFlux = self.findPsfAnnularFlux(star, maskedModel) outerRadii.append(star.optimalOuterRadius) annularFluxes.append(annularFlux) return np.array([outerRadii, annularFluxes]).T def preparePlaneModelStamp(self, brightStarStamp):
Definition at line 558 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.brightStarStamps : |
annulusImage = self.initAnnulusImage() annulus = self.createAnnulus(brightStarStamp) annulus.copyMaskedImage(maskedModel, annulusImage) annularFlux = self.applyStatsControl(annulusImage) return annularFlux def findPsfAnnularFluxes(self, brightStarStamps):
Definition at line 630 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.calexp : `~lsst.afw.image.Exposure` or `~lsst.afw.image.MaskedImage` |
self.inputExpBBox = inputExposure.getBBox() if self.config.doApplySkyCorr and (skyCorr is not None): self.log.info( "Applying sky correction to exposure %s (exposure will be modified in-place).", dataId ) self.applySkyCorr(inputExposure, skyCorr) # Create an empty image the size of the exposure. # TODO: DM-31085 (set mask planes). subtractorExp = ExposureF(bbox=inputExposure.getBBox()) subtractor = subtractorExp.maskedImage # Make a copy of the input model. self.model = inputExtendedPsf(dataId["detector"]).clone() self.modelStampSize = self.model.getDimensions() # Number of 90 deg. rotations to reverse each stamp's rotation. self.inv90Rots = 4 - inputBrightStarStamps.nb90Rots % 4 self.model = rotateImageBy90(self.model, self.inv90Rots) brightStarList = self.makeBrightStarList(inputBrightStarStamps, inputExposure, refObjLoader) invImages = [] subtractor, invImages = self.buildSubtractor( inputBrightStarStamps, subtractor, invImages, multipleAnnuli=False ) if brightStarList: self.setMissedStarsStatsControl() # This may change when multiple star bins are used for PSF # creation. innerRadius = inputBrightStarStamps._innerRadius outerRadius = inputBrightStarStamps._outerRadius brightStarStamps, badStamps = BrightStarStamps.initAndNormalize( brightStarList, innerRadius=innerRadius, outerRadius=outerRadius, nb90Rots=self.warpOutputs.nb90Rots, imCenter=self.warper.modelCenter, use_archive=True, statsControl=self.missedStatsControl, statsFlag=self.missedStatsFlag, badMaskPlanes=self.warper.config.badMaskPlanes, discardNanFluxObjects=False, forceFindFlux=True, ) self.psf_annular_fluxes = self.findPsfAnnularFluxes(brightStarStamps) subtractor, invImages = self.buildSubtractor( brightStarStamps, subtractor, invImages, multipleAnnuli=True ) else: badStamps = [] badStamps = BrightStarStamps(badStamps) return subtractorExp, invImages, badStamps def _setUpStatistics(self, exampleMask):
if self.config.scalingType == "leastSquare": # Set the mask planes which will be ignored. andMask = reduce( ior, (exampleMask.getPlaneBitMask(bm) for bm in self.config.badMaskPlanes), ) self.statsControl = StatisticsControl( andMask=andMask, ) self.statsFlag = stringToStatisticsProperty("SUM") def applySkyCorr(self, calexp, skyCorr):
Definition at line 391 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.dataId : `dict` or `~lsst.daf.butler.DataCoordinate` |
Definition at line 293 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.inPlace : `bool` |
Definition at line 412 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.inputBrightStarStamps : |
if self.config.scalingType == "annularFlux": scalingFactor = star.annularFlux * psf_annular_flux elif self.config.scalingType == "leastSquare": if self.statsControl is None: self._setUpStatistics(star.stamp_im.mask) starIm = star.stamp_im.clone() # Rotate the star postage stamp. starIm = rotateImageBy90(starIm, nb90Rots) # Reverse the prior star flux normalization ("unnormalize"). starIm *= star.annularFlux # The estimator of the scalingFactor (f) that minimizes (Y-fX)^2 # is E[XY]/E[XX]. xy = starIm.clone() xy.image.array *= model.image.array xx = starIm.clone() xx.image.array = model.image.array**2 # Compute the least squares scaling factor. xySum = makeStatistics(xy, self.statsFlag, self.statsControl).getValue() xxSum = makeStatistics(xx, self.statsFlag, self.statsControl).getValue() scalingFactor = xySum / xxSum if xxSum else 1 if inPlace: model.image *= scalingFactor return scalingFactor def _overrideWarperConfig(self):
# TODO: Replace these copied values with a warperConfig. self.warper.config.minValidAnnulusFraction = self.config.minValidAnnulusFraction self.warper.config.numSigmaClip = self.config.numSigmaClip self.warper.config.numIter = self.config.numIter self.warper.config.annularFluxStatistic = self.config.annularFluxStatistic self.warper.config.badMaskPlanes = self.config.badMaskPlanes self.warper.config.stampSize = self.config.subtractionBox self.warper.modelStampBuffer = self.config.subtractionBoxBuffer self.warper.config.magLimit = self.config.magLimit self.warper.setModelStamp() def setMissedStarsStatsControl(self):
self.missedStatsControl = StatisticsControl( numSigmaClip=self.warper.config.numSigmaClip, numIter=self.warper.config.numIter, ) self.missedStatsFlag = stringToStatisticsProperty(self.warper.config.annularFluxStatistic) def setWarpTask(self):
self.warper = ProcessBrightStarsTask() self._overrideWarperConfig() self.warper.modelCenter = self.modelStampSize[0] // 2, self.modelStampSize[1] // 2 def makeBrightStarList(self, inputBrightStarStamps, inputExposure, refObjLoader):
Definition at line 285 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.inputExposure : `~lsst.afw.image.ExposureF` |
doWriteSubtractor = Field[bool]( doc="Should an exposure containing all bright star models be written to disk?", default=True, ) doWriteSubtractedExposure = Field[bool]( doc="Should an exposure with bright stars subtracted be written to disk?", default=True, ) magLimit = Field[float]( doc="Magnitude limit, in Gaia G; all stars brighter than this value will be subtracted", default=18, ) minValidAnnulusFraction = Field[float]( doc="Minimum number of valid pixels that must fall within the annulus for the bright star to be " "saved for subsequent generation of a PSF.", default=0.0, ) numSigmaClip = Field[float]( doc="Sigma for outlier rejection; ignored if annularFluxStatistic != 'MEANCLIP'.", default=4, ) numIter = Field[int]( doc="Number of iterations of outlier rejection; ignored if annularFluxStatistic != 'MEANCLIP'.", default=3, ) warpingKernelName = ChoiceField[str]( doc="Warping kernel", default="lanczos5", allowed={ "bilinear": "bilinear interpolation", "lanczos3": "Lanczos kernel of order 3", "lanczos4": "Lanczos kernel of order 4", "lanczos5": "Lanczos kernel of order 5", "lanczos6": "Lanczos kernel of order 6", "lanczos7": "Lanczos kernel of order 7", }, ) scalingType = ChoiceField[str]( doc="How the model should be scaled to each bright star; implemented options are " "`annularFlux` to reuse the annular flux of each stamp, or `leastSquare` to perform " "least square fitting on each pixel with no bad mask plane set.", default="leastSquare", allowed={ "annularFlux": "reuse BrightStarStamp annular flux measurement", "leastSquare": "find least square scaling factor", }, ) annularFluxStatistic = ChoiceField[str]( doc="Type of statistic to use to compute annular flux.", default="MEANCLIP", allowed={ "MEAN": "mean", "MEDIAN": "median", "MEANCLIP": "clipped mean", }, ) badMaskPlanes = ListField[str]( doc="Mask planes that, if set, lead to associated pixels not being included in the computation of " "the scaling factor (`BAD` should always be included). Ignored if scalingType is `annularFlux`, " "as the stamps are expected to already be normalized.", # Note that `BAD` should always be included, as secondary detected # sources (i.e., detected sources other than the primary source of # interest) also get set to `BAD`. default=("BAD", "CR", "CROSSTALK", "EDGE", "NO_DATA", "SAT", "SUSPECT", "UNMASKEDNAN"), ) subtractionBox = ListField[int]( doc="Size of the stamps to be extracted, in pixels.", default=(250, 250), ) subtractionBoxBuffer = Field[float]( doc=( "'Buffer' (multiplicative) factor to be applied to determine the size of the stamp the " "processed stars will be saved in. This is also the size of the extended PSF model. The buffer " "region is masked and contain no data and subtractionBox determines the region where contains " "the data." ), default=1.1, ) doApplySkyCorr = Field[bool]( doc="Apply full focal plane sky correction before extracting stars?", default=True, ) refObjLoader = ConfigField[LoadReferenceObjectsConfig]( doc="Reference object loader for astrometric calibration.", ) class SubtractBrightStarsTask(PipelineTask):
ConfigClass = SubtractBrightStarsConfig _DefaultName = "subtractBrightStars" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Placeholders to set up Statistics if scalingType is leastSquare. self.statsControl, self.statsFlag = None, None # Warping control; only contains shiftingALg provided in config. self.warpControl = WarpingControl(self.config.warpingKernelName) def runQuantum(self, butlerQC, inputRefs, outputRefs): # Docstring inherited. inputs = butlerQC.get(inputRefs) dataId = butlerQC.quantum.dataId refObjLoader = ReferenceObjectLoader( dataIds=[ref.datasetRef.dataId for ref in inputRefs.refCat], refCats=inputs.pop("refCat"), name=self.config.connections.refCat, config=self.config.refObjLoader, ) subtractor, _, badStamps = self.run(**inputs, dataId=dataId, refObjLoader=refObjLoader) if self.config.doWriteSubtractedExposure: outputExposure = inputs["inputExposure"].clone() outputExposure.image -= subtractor.image else: outputExposure = None outputBackgroundExposure = subtractor if self.config.doWriteSubtractor else None # In its current state, the code produces outputBadStamps which are the # stamps of stars that have not been subtracted from the image for any # reason. If all the stars are subtracted from the calexp, the output # is an empty fits file. output = Struct( outputExposure=outputExposure, outputBackgroundExposure=outputBackgroundExposure, outputBadStamps=badStamps, ) butlerQC.put(output, outputRefs) def run( self, inputExposure, inputBrightStarStamps, inputExtendedPsf, dataId, skyCorr=None, refObjLoader=None ):
Definition at line 283 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.inputExtendedPsf : `~lsst.pipe.tasks.extended_psf.ExtendedPsf` |
Definition at line 290 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.invImage : `~lsst.afw.image.MaskedImageF` |
Definition at line 679 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.invImages : `list` [`~lsst.afw.image.MaskedImageF`] |
Definition at line 309 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.logger = logging.getLogger(__name__) |
Definition at line 49 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.maskedModel : `~lsst.afw.image.MaskedImageF` |
Definition at line 610 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.model : `~lsst.afw.image.MaskedImageF` |
if isinstance(calexp, Exposure): calexp = calexp.getMaskedImage() calexp -= skyCorr.getImage() def scaleModel(self, model, star, inPlace=True, nb90Rots=0, psf_annular_flux=1.0):
Definition at line 407 of file subtractBrightStars.py.
bool lsst.pipe.tasks.subtractBrightStars.multipleAnnuli , optional |
Definition at line 725 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.nb90Rots : `int` |
Definition at line 414 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.psf_annular_flux : `float`, optional |
Definition at line 416 of file subtractBrightStars.py.
numpy lsst.pipe.tasks.subtractBrightStars.PsfAnnularFluxes .array |
Definition at line 636 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.refObjLoader : `~lsst.meas.algorithms.ReferenceObjectLoader`, optional |
Definition at line 300 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.scalingFactor : `float` |
Definition at line 424 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.skyCorr : `~lsst.afw.math.backgroundList.BackgroundList`, optional |
Definition at line 296 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.star : `~lsst.meas.algorithms.brightStarStamps.BrightStarStamp` |
Definition at line 410 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.subtractor : `~lsst.afw.image.MaskedImageF` |
# Set the origin. self.model.setXY0(brightStarStamp.position) # Create an empty destination image. invTransform = brightStarStamp.archive_element.inverted() invOrigin = Point2I(invTransform.applyForward(Point2D(brightStarStamp.position))) bbox = Box2I(corner=invOrigin, dimensions=self.modelStampSize) invImage = MaskedImageF(bbox) # Apply inverse transform. goodPix = warpImage(invImage, self.model, invTransform, self.warpControl) if not goodPix: # Do we want to find another way or just subtract the non-warped # scaled model? # Currently the code just leaves the failed ones un-subtracted. raise RuntimeError( f"Warping of a model failed for star {brightStarStamp.gaiaId}: no good pixel in output." ) return bbox, invImage def addScaledModel(self, subtractor, brightStarStamp, multipleAnnuli=False):
Definition at line 718 of file subtractBrightStars.py.
lsst.pipe.tasks.subtractBrightStars.subtractorExp : `~lsst.afw.image.ExposureF` |
Definition at line 305 of file subtractBrightStars.py.