23 __all__ = [
"backgroundSubtract",
"writeKernelCellSet",
"sourceToFootprintList",
"NbasisEvaluator"]
25 from builtins
import range
26 from builtins
import object
31 from collections
import Counter
35 from .
import diffimLib
45 from .makeKernelBasisList
import makeKernelBasisList
59 rdmImage = img.Factory(img.getDimensions())
65 """Return a Poisson noise image based on im 67 Uses numpy.random; you may wish to call numpy.random.seed first. 69 @warning This uses an undocumented numpy API (the documented API 70 uses a single float expectation value instead of an array). 72 @param[in] im image; the output image has the same dimensions and shape 73 and its expectation value is the value of im at each pixel 75 import numpy.random
as rand
77 noiseIm = im.Factory(im.getBBox())
78 noiseArr = noiseIm.getArray()
80 with np.errstate(invalid=
'ignore'):
81 intNoiseArr = rand.poisson(imArr)
83 noiseArr[:, :] = intNoiseArr.astype(noiseArr.dtype)
94 kCoeffs = ((1.0, 0.0, 0.0),
95 (0.005, -0.000001, 0.000001),
96 (0.005, 0.000004, 0.000004),
97 (-0.001, -0.000030, 0.000030),
98 (-0.001, 0.000015, 0.000015),
99 (-0.005, -0.000050, 0.000050))
104 deltaFunctionCounts=1.e4, tGaussianWidth=1.0,
105 addNoise=True, bgValue=100., display=False):
107 from .
import imagePsfMatch
109 configFake.kernel.name =
"AL" 110 subconfigFake = configFake.kernel.active
111 subconfigFake.alardNGauss = 1
112 subconfigFake.alardSigGauss = [2.5, ]
113 subconfigFake.alardDegGauss = [2, ]
114 subconfigFake.sizeCellX = sizeCell
115 subconfigFake.sizeCellY = sizeCell
116 subconfigFake.spatialKernelOrder = 1
117 subconfigFake.spatialModelType =
"polynomial" 118 subconfigFake.singleKernelClipping =
False 119 subconfigFake.spatialKernelClipping =
False 121 subconfigFake.fitForBackground =
True 123 policyFake = pexConfig.makePolicy(subconfigFake)
126 kSize = subconfigFake.kernelSize
129 gaussKernelWidth = sizeCell//2
134 spatialKernelWidth = kSize
137 border = (gaussKernelWidth + spatialKernelWidth)//2
140 totalSize = nCell * sizeCell + 2*border
142 for x
in range(nCell):
143 for y
in range(nCell):
144 tim.set(x*sizeCell + sizeCell//2 + border - 1,
145 y*sizeCell + sizeCell//2 + border - 1,
149 gaussFunction = afwMath.GaussianFunction2D(tGaussianWidth, tGaussianWidth)
151 cim = afwImage.ImageF(tim.getDimensions())
156 bbox = gaussKernel.shrinkBBox(tim.getBBox(afwImage.LOCAL))
157 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
161 polyFunc = afwMath.PolynomialFunction2D(1)
163 nToUse = min(len(kCoeffs), len(basisList))
167 sKernel.setSpatialParameters(kCoeffs[:nToUse])
168 sim = afwImage.ImageF(tim.getDimensions())
172 bbox = sKernel.shrinkBBox(sim.getBBox(afwImage.LOCAL))
178 tim += 2 * np.abs(np.min(tim.getArray()))
186 sim = afwImage.ImageF(sim, bbox, afwImage.LOCAL)
187 svar = afwImage.ImageF(sim,
True)
190 sMi = afwImage.MaskedImageF(sim, smask, svar)
192 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
193 tvar = afwImage.ImageF(tim,
True)
196 tMi = afwImage.MaskedImageF(tim, tmask, tvar)
200 ds9.mtv(tMi, frame=1)
201 ds9.mtv(sMi, frame=2)
209 stampHalfWidth = 2 * kSize
210 for x
in range(nCell):
211 for y
in range(nCell):
212 xCoord = x * sizeCell + sizeCell // 2
213 yCoord = y * sizeCell + sizeCell // 2
215 yCoord - stampHalfWidth)
217 yCoord + stampHalfWidth)
219 tsi = afwImage.MaskedImageF(tMi, bbox, origin=afwImage.LOCAL)
220 ssi = afwImage.MaskedImageF(sMi, bbox, origin=afwImage.LOCAL)
222 kc = diffimLib.makeKernelCandidate(xCoord, yCoord, tsi, ssi, policyFake)
223 kernelCellSet.insertCandidate(kc)
227 return tMi, sMi, sKernel, kernelCellSet, configFake
237 algorithm = config.algorithm
238 binsize = config.binSize
239 undersample = config.undersampleStyle
241 bctrl.setUndersampleStyle(undersample)
242 for maskedImage
in maskedImages:
243 bctrl.setNxSample(maskedImage.getWidth()//binsize + 1)
244 bctrl.setNySample(maskedImage.getHeight()//binsize + 1)
245 image = maskedImage.getImage()
248 image -= backobj.getImageF()
249 backgrounds.append(backobj.getImageF())
253 logger = Log.getLogger(
"ip.diffim.backgroundSubtract")
254 logger.debug(
"Total time for background subtraction : %.2f s", (t1-t0))
263 if not os.path.isdir(outdir):
266 for cell
in kernelCellSet.getCellList():
267 for cand
in cell.begin(
False):
268 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
269 xCand = int(cand.getXCenter())
270 yCand = int(cand.getYCenter())
271 idCand = cand.getId()
272 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.ORIG)
273 kernel = cand.getKernelImage(diffimLib.KernelCandidateF.ORIG)
274 diffIm.writeFits(os.path.join(outdir,
'diffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
275 kernel.writeFits(os.path.join(outdir,
'kernel_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
278 ski = afwImage.ImageD(kernel.getDimensions())
279 psfMatchingKernel.computeImage(ski,
False, xCand, yCand)
281 sbg = backgroundModel(xCand, yCand)
282 sdmi = cand.getDifferenceImage(sk, sbg)
283 sdmi.writeFits(os.path.join(outdir,
'sdiffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
291 """ Takes an input list of Sources that were selected to constrain 292 the Psf-matching Kernel and turns them into a List of Footprints, 293 which are used to seed a set of KernelCandidates. The function 294 checks both the template and science image for masked pixels, 295 rejecting the Source if certain Mask bits (defined in config) are 296 set within the Footprint. 298 @param candidateInList: Input list of Sources 299 @param templateExposure: Template image, to be checked for Mask bits in Source Footprint 300 @param scienceExposure: Science image, to be checked for Mask bits in Source Footprint 301 @param config: Config that defines the Mask planes that indicate an invalid Source and Bbox grow radius 302 @param log: Log for output 304 @return a list of dicts having a "source" and "footprint" field, to be used for Psf-matching 307 candidateOutList = []
308 fsb = diffimLib.FindSetBitsU()
310 for mp
in config.badMaskPlanes:
311 badBitMask |= afwImage.Mask.getPlaneBitMask(mp)
312 bbox = scienceExposure.getBBox()
315 if config.scaleByFwhm:
316 fpGrowPix = int(config.fpGrowKernelScaling * kernelSize + 0.5)
318 fpGrowPix = config.fpGrowPix
319 log.info(
"Growing %d kernel candidate stars by %d pixels", len(candidateInList), fpGrowPix)
321 for kernelCandidate
in candidateInList:
322 if not type(kernelCandidate) == afwTable.SourceRecord:
323 raise RuntimeError(
"Candiate not of type afwTable.SourceRecord")
326 center =
afwGeom.Point2I(scienceExposure.getWcs().skyToPixel(kernelCandidate.getCoord()))
327 if center[0] < bbox.getMinX()
or center[0] > bbox.getMaxX():
329 if center[1] < bbox.getMinY()
or center[1] > bbox.getMaxY():
332 xmin = center[0] - fpGrowPix
333 xmax = center[0] + fpGrowPix
334 ymin = center[1] - fpGrowPix
335 ymax = center[1] + fpGrowPix
338 if (xmin - bbox.getMinX()) < 0:
339 xmax += (xmin - bbox.getMinX())
340 xmin -= (xmin - bbox.getMinX())
341 if (ymin - bbox.getMinY()) < 0:
342 ymax += (ymin - bbox.getMinY())
343 ymin -= (ymin - bbox.getMinY())
344 if (bbox.getMaxX() - xmax) < 0:
345 xmin -= (bbox.getMaxX() - xmax)
346 xmax += (bbox.getMaxX() - xmax)
347 if (bbox.getMaxY() - ymax) < 0:
348 ymin -= (bbox.getMaxY() - ymax)
349 ymax += (bbox.getMaxY() - ymax)
350 if xmin > xmax
or ymin > ymax:
355 fsb.apply(afwImage.MaskedImageF(templateExposure.getMaskedImage(), kbbox, deep=
False).getMask())
357 fsb.apply(afwImage.MaskedImageF(scienceExposure.getMaskedImage(), kbbox, deep=
False).getMask())
362 if not((bm1 & badBitMask)
or (bm2 & badBitMask)):
363 candidateOutList.append({
'source': kernelCandidate,
365 log.info(
"Selected %d / %d sources for KernelCandidacy", len(candidateOutList), len(candidateInList))
366 return candidateOutList
370 basisList, doBuild=False):
371 """Takes an input list of Sources, and turns them into 372 KernelCandidates for fitting of the Psf-matching kernel.""" 373 kernelSize = basisList[0].getWidth()
375 kernelSize, dConfig, log)
378 if doBuild
and not basisList:
381 policy = pexConfig.makePolicy(kConfig)
382 visitor = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
384 policy = pexConfig.makePolicy(kConfig)
385 for cand
in footprintList:
386 bbox = cand[
'footprint'].getBBox()
387 tmi = afwImage.MaskedImageF(templateExposure.getMaskedImage(), bbox)
388 smi = afwImage.MaskedImageF(scienceExposure.getMaskedImage(), bbox)
389 kCand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, policy)
391 visitor.processCandidate(kCand)
392 kCand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN)
393 candList.append(kCand)
403 """A functor to evaluate the Bayesian Information Criterion for the number of basis sets 404 going into the kernel fitting""" 406 def __init__(self, psfMatchConfig, psfFwhmPixTc, psfFwhmPixTnc):
411 raise RuntimeError(
"BIC only implemnted for AL (alard lupton) basis")
416 for d1i
in range(1, d1+1):
417 for d2i
in range(1, d2+1):
418 for d3i
in range(1, d3+1):
419 dList = [d1i, d2i, d3i]
423 visitor = diffimLib.BuildSingleKernelVisitorF(kList, pexConfig.makePolicy(bicConfig))
424 visitor.setSkipBuilt(
False)
425 kernelCellSet.visitCandidates(visitor, bicConfig.nStarPerCell)
427 for cell
in kernelCellSet.getCellList():
428 for cand
in cell.begin(
False):
429 if cand.getStatus() != afwMath.SpatialCellCandidate.GOOD:
431 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.RECENT)
432 bbox = cand.getKernel(diffimLib.KernelCandidateF.RECENT).shrinkBBox(
433 diffIm.getBBox(afwImage.LOCAL))
434 diffIm = type(diffIm)(diffIm, bbox,
True)
435 chi2 = diffIm.getImage().getArray()**2 / diffIm.getVariance().getArray()
436 n = chi2.shape[0] * chi2.shape[1]
437 bic = np.sum(chi2) + k * np.log(n)
438 if cand.getId()
not in bicArray:
439 bicArray[cand.getId()] = {}
440 bicArray[cand.getId()][(d1i, d2i, d3i)] = bic
443 for candId
in bicArray:
444 cconfig, cvals = list(bicArray[candId].keys()), list(bicArray[candId].values())
445 idx = np.argsort(cvals)
446 bestConfig = cconfig[idx[0]]
447 bestConfigs.append(bestConfig)
449 counter = Counter(bestConfigs).most_common(3)
450 log.info(
"B.I.C. prefers basis complexity %s %d times; %s %d times; %s %d times",
451 counter[0][0], counter[0][1],
452 counter[1][0], counter[1][1],
453 counter[2][0], counter[2][1])
454 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)
std::shared_ptr< Background > makeBackground(ImageT const &img, BackgroundControl const &bgCtrl)
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
void randomGaussianImage(ImageT *image, Random &rand)
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)