22 __all__ = [
"PiffPsfDeterminerConfig",
"PiffPsfDeterminerTask"]
34 from .piffPsf
import PiffPsf
38 spatialOrder = pexConfig.Field(
39 doc=
"specify spatial order for PSF kernel creation",
43 samplingSize = pexConfig.Field(
44 doc=
"Resolution of the internal PSF model relative to the pixel size; "
45 "e.g. 0.5 is equal to 2x oversampling",
49 outlierNSigma = pexConfig.Field(
50 doc=
"n sigma for chisq outlier rejection",
54 outlierMaxRemove = pexConfig.Field(
55 doc=
"Max fraction of stars to remove as outliers each iteration",
59 maxSNR = pexConfig.Field(
60 doc=
"Rescale the weight of bright stars such that their SNR is less "
73 """Derive a weight map without Poisson variance component due to signal.
77 maskedImage : `afw.image.MaskedImage`
78 PSF candidate postage stamp
80 Maximum SNR applying variance floor.
85 Array to use for weight.
87 imArr = maskedImage.image.array
88 varArr = maskedImage.variance.array
89 good = (varArr != 0) & np.isfinite(varArr) & np.isfinite(imArr)
94 fit = np.polyfit(imArr[good], varArr[good], deg=1)
96 weightArr = np.zeros_like(imArr, dtype=float)
97 weightArr[good] = 1./fit[1]
104 """Rescale weight of bright stars to cap the computed SNR.
109 Signal (image) array of stamp.
110 weightArr : `ndarray`
111 Weight map array. May be rescaled in place.
113 Index array of pixels to use when computing SNR.
115 Threshold for adjusting variance plane implementing maximum SNR.
142 F = np.sum(weightArr[good]*imArr[good]**2, dtype=float)
144 SNR = 0.0
if F < Npix
else (F-Npix)/np.sqrt(F)
147 factor = (maxSNR / SNR)**2
148 weightArr[good] *= factor
151 def _computeWeightAlternative(maskedImage, maxSNR):
152 """Alternative algorithm for creating weight map.
154 This version is equivalent to that used by Piff internally. The weight map
155 it produces tends to leave a residual when removing the Poisson component
156 due to the signal. We leave it here as a reference, but without intending
159 imArr = maskedImage.image.array
160 varArr = maskedImage.variance.array
161 good = (varArr != 0) & np.isfinite(varArr) & np.isfinite(imArr)
163 fit = np.polyfit(imArr[good], varArr[good], deg=1)
166 varArr[good] -= imArr[good] / gain
167 weightArr = np.zeros_like(imArr, dtype=float)
168 weightArr[good] = 1./varArr[good]
175 """A measurePsfTask PSF estimator using Piff as the implementation.
177 ConfigClass = PiffPsfDeterminerConfig
180 self, exposure, psfCandidateList, metadata=None, flagKey=None
182 """Determine a Piff PSF model for an exposure given a list of PSF
187 exposure : `lsst.afw.image.Exposure`
188 Exposure containing the PSF candidates.
189 psfCandidateList : `list` of `lsst.meas.algorithms.PsfCandidate`
190 A sequence of PSF candidates typically obtained by detecting sources
191 and then running them through a star selector.
192 metadata : `lsst.daf.base import PropertyList` or `None`, optional
193 A home for interesting tidbits of information.
194 flagKey : `str` or `None`, optional
195 Schema key used to mark sources actually used in PSF determination.
199 psf : `lsst.meas.extensions.piff.PiffPsf`
200 The measured PSF model.
202 Unused by this PsfDeterminer.
205 for candidate
in psfCandidateList:
206 cmi = candidate.getMaskedImage()
210 bds = galsim.BoundsI(
211 galsim.PositionI(*bbox.getMin()),
212 galsim.PositionI(*bbox.getMax())
214 gsImage = galsim.Image(bds, scale=1.0, dtype=float)
215 gsImage.array[:] = cmi.image.array
216 gsWeight = galsim.Image(bds, scale=1.0, dtype=float)
217 gsWeight.array[:] = weight
219 source = candidate.getSource()
220 image_pos = galsim.PositionD(source.getX(), source.getY())
222 data = piff.StarData(
227 stars.append(piff.Star(data,
None))
229 kernelSize = int(np.clip(
230 self.config.kernelSize,
231 self.config.kernelSizeMin,
232 self.config.kernelSizeMax
239 'scale': self.config.samplingSize,
243 'type':
'BasisPolynomial',
244 'order': self.config.spatialOrder
248 'nsigma': self.config.outlierNSigma,
249 'max_remove': self.config.outlierMaxRemove
253 piffResult = piff.PSF.process(piffConfig)
255 wcs = {0: galsim.PixelScale(1.0)}
258 logger = logging.getLogger(self.log.
getName()+
".Piff")
261 piffResult.fit(stars, wcs, pointing, logger=logger)
262 psf =
PiffPsf(kernelSize, kernelSize, piffResult)
264 used_image_pos = [s.image_pos
for s
in piffResult.stars]
266 for candidate
in psfCandidateList:
267 source = candidate.getSource()
268 posd = galsim.PositionD(source.getX(), source.getY())
269 if posd
in used_image_pos:
270 source.set(flagKey,
True)
272 if metadata
is not None:
273 metadata.set(
"spatialFitChi2", piffResult.chisq)
274 metadata.set(
"numAvailStars", len(stars))
275 metadata.set(
"numGoodStars", len(piffResult.stars))
276 metadata.set(
"avgX", np.mean([p.x
for p
in piffResult.stars]))
277 metadata.set(
"avgY", np.mean([p.y
for p
in piffResult.stars]))
282 measAlg.psfDeterminerRegistry.register(
"piff", PiffPsfDeterminerTask)
Base class for PSF determiners.
def determinePsf(self, exposure, psfCandidateList, metadata=None, flagKey=None)
std::string const & getName() const noexcept
Return a filter's name.
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.
def computeWeight(maskedImage, maxSNR)
def applyMaxSNR(imArr, weightArr, good, maxSNR)