1 from builtins
import range
2 from builtins
import object
28 from collections
import Counter
32 from .
import diffimLib
35 import lsst.afw.geom
as afwGeom
36 import lsst.afw.image
as afwImage
37 import lsst.afw.table
as afwTable
38 import lsst.afw.detection
as afwDetect
39 import lsst.afw.math.mathLib
as afwMath
40 from lsst.log
import Log
41 import lsst.pex.config
as pexConfig
42 from .makeKernelBasisList
import makeKernelBasisList
54 seed = int(10. * afwMath.makeStatistics(mi.getImage(), seedStat).getValue() + 1)
55 rdm = afwMath.Random(afwMath.Random.MT19937, seed)
56 rdmImage = img.Factory(img.getDimensions())
57 afwMath.randomGaussianImage(rdmImage, rdm)
62 """Return a Poisson noise image based on im 64 Uses numpy.random; you may wish to call numpy.random.seed first. 66 @warning This uses an undocumented numpy API (the documented API 67 uses a single float expectation value instead of an array). 69 @param[in] im image; the output image has the same dimensions and shape 70 and its expectation value is the value of im at each pixel 72 import numpy.random
as rand
74 noiseIm = im.Factory(im.getBBox())
75 noiseArr = noiseIm.getArray()
77 intNoiseArr = rand.poisson(imArr)
78 noiseArr[:, :] = intNoiseArr.astype(noiseArr.dtype)
89 kCoeffs = ((1.0, 0.0, 0.0),
90 (0.005, -0.000001, 0.000001),
91 (0.005, 0.000004, 0.000004),
92 (-0.001, -0.000030, 0.000030),
93 (-0.001, 0.000015, 0.000015),
94 (-0.005, -0.000050, 0.000050))
99 deltaFunctionCounts=1.e4, tGaussianWidth=1.0,
100 addNoise=True, bgValue=100., display=False):
102 from .
import imagePsfMatch
104 configFake.kernel.name =
"AL" 105 subconfigFake = configFake.kernel.active
106 subconfigFake.alardNGauss = 1
107 subconfigFake.alardSigGauss = [2.5, ]
108 subconfigFake.alardDegGauss = [2, ]
109 subconfigFake.sizeCellX = sizeCell
110 subconfigFake.sizeCellY = sizeCell
111 subconfigFake.spatialKernelOrder = 1
112 subconfigFake.spatialModelType =
"polynomial" 113 subconfigFake.singleKernelClipping =
False 114 subconfigFake.spatialKernelClipping =
False 116 subconfigFake.fitForBackground =
True 118 policyFake = pexConfig.makePolicy(subconfigFake)
121 kSize = subconfigFake.kernelSize
124 gaussKernelWidth = sizeCell//2
129 spatialKernelWidth = kSize
132 border = (gaussKernelWidth + spatialKernelWidth)//2
135 totalSize = nCell * sizeCell + 2*border
136 tim = afwImage.ImageF(afwGeom.Extent2I(totalSize, totalSize))
137 for x
in range(nCell):
138 for y
in range(nCell):
139 tim.set(x*sizeCell + sizeCell//2 + border - 1,
140 y*sizeCell + sizeCell//2 + border - 1,
144 gaussFunction = afwMath.GaussianFunction2D(tGaussianWidth, tGaussianWidth)
145 gaussKernel = afwMath.AnalyticKernel(gaussKernelWidth, gaussKernelWidth, gaussFunction)
146 cim = afwImage.ImageF(tim.getDimensions())
147 afwMath.convolve(cim, tim, gaussKernel,
True)
151 bbox = gaussKernel.shrinkBBox(tim.getBBox(afwImage.LOCAL))
152 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
156 polyFunc = afwMath.PolynomialFunction2D(1)
158 nToUse = min(len(kCoeffs), len(basisList))
161 sKernel = afwMath.LinearCombinationKernel(basisList[:nToUse], polyFunc)
162 sKernel.setSpatialParameters(kCoeffs[:nToUse])
163 sim = afwImage.ImageF(tim.getDimensions())
164 afwMath.convolve(sim, tim, sKernel,
True)
167 bbox = sKernel.shrinkBBox(sim.getBBox(afwImage.LOCAL))
173 tim += 2 * np.abs(np.min(tim.getArray()))
181 sim = afwImage.ImageF(sim, bbox, afwImage.LOCAL)
182 svar = afwImage.ImageF(sim,
True)
183 smask = afwImage.Mask(sim.getDimensions())
185 sMi = afwImage.MaskedImageF(sim, smask, svar)
187 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
188 tvar = afwImage.ImageF(tim,
True)
189 tmask = afwImage.Mask(tim.getDimensions())
191 tMi = afwImage.MaskedImageF(tim, tmask, tvar)
194 import lsst.afw.display.ds9
as ds9
195 ds9.mtv(tMi, frame=1)
196 ds9.mtv(sMi, frame=2)
199 kernelCellSet = afwMath.SpatialCellSet(afwGeom.Box2I(afwGeom.Point2I(0, 0),
200 afwGeom.Extent2I(sizeCell * nCell,
204 stampHalfWidth = 2 * kSize
205 for x
in range(nCell):
206 for y
in range(nCell):
207 xCoord = x * sizeCell + sizeCell // 2
208 yCoord = y * sizeCell + sizeCell // 2
209 p0 = afwGeom.Point2I(xCoord - stampHalfWidth,
210 yCoord - stampHalfWidth)
211 p1 = afwGeom.Point2I(xCoord + stampHalfWidth,
212 yCoord + stampHalfWidth)
213 bbox = afwGeom.Box2I(p0, p1)
214 tsi = afwImage.MaskedImageF(tMi, bbox, origin=afwImage.LOCAL)
215 ssi = afwImage.MaskedImageF(sMi, bbox, origin=afwImage.LOCAL)
217 kc = diffimLib.makeKernelCandidate(xCoord, yCoord, tsi, ssi, policyFake)
218 kernelCellSet.insertCandidate(kc)
222 return tMi, sMi, sKernel, kernelCellSet, configFake
232 algorithm = config.algorithm
233 binsize = config.binSize
234 undersample = config.undersampleStyle
235 bctrl = afwMath.BackgroundControl(algorithm)
236 bctrl.setUndersampleStyle(undersample)
237 for maskedImage
in maskedImages:
238 bctrl.setNxSample(maskedImage.getWidth()//binsize + 1)
239 bctrl.setNySample(maskedImage.getHeight()//binsize + 1)
240 image = maskedImage.getImage()
241 backobj = afwMath.makeBackground(image, bctrl)
243 image -= backobj.getImageF()
244 backgrounds.append(backobj.getImageF())
248 logger = Log.getLogger(
"ip.diffim.backgroundSubtract")
249 logger.debug(
"Total time for background subtraction : %.2f s", (t1-t0))
258 if not os.path.isdir(outdir):
261 for cell
in kernelCellSet.getCellList():
262 for cand
in cell.begin(
False):
263 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
264 xCand = int(cand.getXCenter())
265 yCand = int(cand.getYCenter())
266 idCand = cand.getId()
267 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.ORIG)
268 kernel = cand.getKernelImage(diffimLib.KernelCandidateF.ORIG)
269 diffIm.writeFits(os.path.join(outdir,
'diffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
270 kernel.writeFits(os.path.join(outdir,
'kernel_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
273 ski = afwImage.ImageD(kernel.getDimensions())
274 psfMatchingKernel.computeImage(ski,
False, xCand, yCand)
275 sk = afwMath.FixedKernel(ski)
276 sbg = backgroundModel(xCand, yCand)
277 sdmi = cand.getDifferenceImage(sk, sbg)
278 sdmi.writeFits(os.path.join(outdir,
'sdiffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
286 """ Takes an input list of Sources that were selected to constrain 287 the Psf-matching Kernel and turns them into a List of Footprints, 288 which are used to seed a set of KernelCandidates. The function 289 checks both the template and science image for masked pixels, 290 rejecting the Source if certain Mask bits (defined in config) are 291 set within the Footprint. 293 @param candidateInList: Input list of Sources 294 @param templateExposure: Template image, to be checked for Mask bits in Source Footprint 295 @param scienceExposure: Science image, to be checked for Mask bits in Source Footprint 296 @param config: Config that defines the Mask planes that indicate an invalid Source and Bbox grow radius 297 @param log: Log for output 299 @return a list of dicts having a "source" and "footprint" field, to be used for Psf-matching 302 candidateOutList = []
303 fsb = diffimLib.FindSetBitsU()
305 for mp
in config.badMaskPlanes:
306 badBitMask |= afwImage.Mask.getPlaneBitMask(mp)
307 bbox = scienceExposure.getBBox()
310 if config.scaleByFwhm:
311 fpGrowPix = int(config.fpGrowKernelScaling * kernelSize + 0.5)
313 fpGrowPix = config.fpGrowPix
314 log.info(
"Growing %d kernel candidate stars by %d pixels", len(candidateInList), fpGrowPix)
316 for kernelCandidate
in candidateInList:
317 if not type(kernelCandidate) == afwTable.SourceRecord:
318 raise RuntimeError(
"Candiate not of type afwTable.SourceRecord")
321 center = afwGeom.Point2I(scienceExposure.getWcs().skyToPixel(kernelCandidate.getCoord()))
322 if center[0] < bbox.getMinX()
or center[0] > bbox.getMaxX():
324 if center[1] < bbox.getMinY()
or center[1] > bbox.getMaxY():
327 xmin = center[0] - fpGrowPix
328 xmax = center[0] + fpGrowPix
329 ymin = center[1] - fpGrowPix
330 ymax = center[1] + fpGrowPix
333 if (xmin - bbox.getMinX()) < 0:
334 xmax += (xmin - bbox.getMinX())
335 xmin -= (xmin - bbox.getMinX())
336 if (ymin - bbox.getMinY()) < 0:
337 ymax += (ymin - bbox.getMinY())
338 ymin -= (ymin - bbox.getMinY())
339 if (bbox.getMaxX() - xmax) < 0:
340 xmin -= (bbox.getMaxX() - xmax)
341 xmax += (bbox.getMaxX() - xmax)
342 if (bbox.getMaxY() - ymax) < 0:
343 ymin -= (bbox.getMaxY() - ymax)
344 ymax += (bbox.getMaxY() - ymax)
345 if xmin > xmax
or ymin > ymax:
348 kbbox = afwGeom.Box2I(afwGeom.Point2I(xmin, ymin), afwGeom.Point2I(xmax, ymax))
350 fsb.apply(afwImage.MaskedImageF(templateExposure.getMaskedImage(), kbbox, deep=
False).getMask())
352 fsb.apply(afwImage.MaskedImageF(scienceExposure.getMaskedImage(), kbbox, deep=
False).getMask())
357 if not((bm1 & badBitMask)
or (bm2 & badBitMask)):
358 candidateOutList.append({
'source': kernelCandidate,
359 'footprint': afwDetect.Footprint(afwGeom.SpanSet(kbbox))})
360 log.info(
"Selected %d / %d sources for KernelCandidacy", len(candidateOutList), len(candidateInList))
361 return candidateOutList
365 basisList, doBuild=False):
366 """Takes an input list of Sources, and turns them into 367 KernelCandidates for fitting of the Psf-matching kernel.""" 368 kernelSize = basisList[0].getWidth()
370 kernelSize, dConfig, log)
373 if doBuild
and not basisList:
376 policy = pexConfig.makePolicy(kConfig)
377 visitor = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
379 policy = pexConfig.makePolicy(kConfig)
380 for cand
in footprintList:
381 bbox = cand[
'footprint'].getBBox()
382 tmi = afwImage.MaskedImageF(templateExposure.getMaskedImage(), bbox)
383 smi = afwImage.MaskedImageF(scienceExposure.getMaskedImage(), bbox)
384 kCand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, policy)
386 visitor.processCandidate(kCand)
387 kCand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN)
388 candList.append(kCand)
398 """A functor to evaluate the Bayesian Information Criterion for the number of basis sets 399 going into the kernel fitting""" 401 def __init__(self, psfMatchConfig, psfFwhmPixTc, psfFwhmPixTnc):
406 raise RuntimeError(
"BIC only implemnted for AL (alard lupton) basis")
411 for d1i
in range(1, d1+1):
412 for d2i
in range(1, d2+1):
413 for d3i
in range(1, d3+1):
414 dList = [d1i, d2i, d3i]
418 visitor = diffimLib.BuildSingleKernelVisitorF(kList, pexConfig.makePolicy(bicConfig))
419 visitor.setSkipBuilt(
False)
420 kernelCellSet.visitCandidates(visitor, bicConfig.nStarPerCell)
422 for cell
in kernelCellSet.getCellList():
423 for cand
in cell.begin(
False):
424 if cand.getStatus() != afwMath.SpatialCellCandidate.GOOD:
426 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.RECENT)
427 bbox = cand.getKernel(diffimLib.KernelCandidateF.RECENT).shrinkBBox(
428 diffIm.getBBox(afwImage.LOCAL))
429 diffIm = type(diffIm)(diffIm, bbox,
True)
430 chi2 = diffIm.getImage().getArray()**2 / diffIm.getVariance().getArray()
431 n = chi2.shape[0] * chi2.shape[1]
432 bic = np.sum(chi2) + k * np.log(n)
433 if cand.getId()
not in bicArray:
434 bicArray[cand.getId()] = {}
435 bicArray[cand.getId()][(d1i, d2i, d3i)] = bic
438 for candId
in bicArray:
439 cconfig, cvals = list(bicArray[candId].keys()), list(bicArray[candId].values())
440 idx = np.argsort(cvals)
441 bestConfig = cconfig[idx[0]]
442 bestConfigs.append(bestConfig)
444 counter = Counter(bestConfigs).most_common(3)
445 log.info(
"B.I.C. prefers basis complexity %s %d times; %s %d times; %s %d times",
446 counter[0][0], counter[0][1],
447 counter[1][0], counter[1][1],
448 counter[2][0], counter[2][1])
449 return counter[0][0], counter[1][0], counter[2][0]
Configuration for image-to-image Psf matching.
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)