22 __all__ = [
"backgroundSubtract",
"writeKernelCellSet",
"sourceToFootprintList",
"NbasisEvaluator"]
27 from collections
import Counter
31 from .
import diffimLib
41 import lsst.pex.config
as pexConfig
42 from .makeKernelBasisList
import makeKernelBasisList
56 rdmImage = img.Factory(img.getDimensions())
62 """Return a Poisson noise image based on im 66 im : `lsst.afw.image.Image` 67 image; the output image has the same dtype, dimensions, and shape 68 and its expectation value is the value of ``im`` at each pixel 72 noiseIm : `lsst.afw.image.Image` 73 Newly constructed image instance, same type as ``im``. 77 - Warning: This uses an undocumented numpy API (the documented API 78 uses a single float expectation value instead of an array). 80 - Uses numpy.random; you may wish to call numpy.random.seed first. 82 import numpy.random
as rand
84 noiseIm = im.Factory(im.getBBox())
85 noiseArr = noiseIm.getArray()
87 intNoiseArr = rand.poisson(np.where(np.isfinite(imArr), imArr, 0.0))
89 noiseArr[:, :] = intNoiseArr.astype(noiseArr.dtype)
100 kCoeffs = ((1.0, 0.0, 0.0),
101 (0.005, -0.000001, 0.000001),
102 (0.005, 0.000004, 0.000004),
103 (-0.001, -0.000030, 0.000030),
104 (-0.001, 0.000015, 0.000015),
105 (-0.005, -0.000050, 0.000050))
110 deltaFunctionCounts=1.e4, tGaussianWidth=1.0,
111 addNoise=True, bgValue=100., display=False):
112 """Generate test template and science images with sources. 116 sizeCell : `int`, optional 117 Size of the square spatial cells in pixels. 118 nCell : `int`, optional 119 Number of adjacent spatial cells in both direction in both images. 120 deltaFunctionCounts : `float`, optional 121 Flux value for the template image sources. 122 tGaussianWidth : `float`, optional 123 Sigma of the generated Gaussian PSF sources in the template image. 124 addNoise : `bool`, optional 125 If `True`, Poisson noise is added to both the generated template 127 bgValue : `float`, optional 128 Background level to be added to the generated science image. 129 display : `bool`, optional 130 If `True` displays the generated template and science images by 131 `lsst.afw.display.Display`. 135 - The generated images consist of adjacent ``nCell x nCell`` cells, each 136 of pixel size ``sizeCell x sizeCell``. 137 - The sources in the science image are generated by convolving the 138 template by ``sKernel``. ``sKernel`` is a spatial `LinearCombinationKernel` 139 of hard wired kernel bases functions. The linear combination has first 140 order polynomial spatial dependence with polynomial parameters from ``fakeCoeffs()``. 141 - The template image sources are generated in the center of each spatial 142 cell from one pixel, set to `deltaFunctionCounts` counts, then convolved 143 by a 2D Gaussian with sigma of `tGaussianWidth` along each axis. 144 - The sources are also returned in ``kernelCellSet`` each source is "detected" 145 exactly at the center of a cell. 149 tMi : `lsst.afw.image.MaskedImage` 150 Generated template image. 151 sMi : `lsst.afw.image.MaskedImage` 152 Generated science image. 153 sKernel : `lsst.afw.math.LinearCombinationKernel` 154 The spatial kernel used to generate the sources in the science image. 155 kernelCellSet : `lsst.afw.math.SpatialCellSet` 156 Cell grid of `lsst.afw.math.SpatialCell` instances, containing 157 `lsst.ip.diffim.KernelCandidate` instances around all the generated sources 158 in the science image. 159 configFake : `lsst.ip.diffim.ImagePsfMatchConfig` 160 Config instance used in the image generation. 162 from .
import imagePsfMatch
164 configFake.kernel.name =
"AL" 165 subconfigFake = configFake.kernel.active
166 subconfigFake.alardNGauss = 1
167 subconfigFake.alardSigGauss = [2.5, ]
168 subconfigFake.alardDegGauss = [2, ]
169 subconfigFake.sizeCellX = sizeCell
170 subconfigFake.sizeCellY = sizeCell
171 subconfigFake.spatialKernelOrder = 1
172 subconfigFake.spatialModelType =
"polynomial" 173 subconfigFake.singleKernelClipping =
False 174 subconfigFake.spatialKernelClipping =
False 176 subconfigFake.fitForBackground =
True 178 psFake = pexConfig.makePropertySet(subconfigFake)
181 kSize = subconfigFake.kernelSize
184 gaussKernelWidth = sizeCell//2
189 spatialKernelWidth = kSize
192 border = (gaussKernelWidth + spatialKernelWidth)//2
195 totalSize = nCell*sizeCell + 2*border
197 for x
in range(nCell):
198 for y
in range(nCell):
199 tim[x*sizeCell + sizeCell//2 + border - 1,
200 y*sizeCell + sizeCell//2 + border - 1,
201 afwImage.LOCAL] = deltaFunctionCounts
204 gaussFunction = afwMath.GaussianFunction2D(tGaussianWidth, tGaussianWidth)
206 cim = afwImage.ImageF(tim.getDimensions())
211 bbox = gaussKernel.shrinkBBox(tim.getBBox(afwImage.LOCAL))
212 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
216 polyFunc = afwMath.PolynomialFunction2D(1)
218 nToUse =
min(len(kCoeffs), len(basisList))
222 sKernel.setSpatialParameters(kCoeffs[:nToUse])
223 sim = afwImage.ImageF(tim.getDimensions())
227 bbox = sKernel.shrinkBBox(sim.getBBox(afwImage.LOCAL))
233 tim += 2*np.abs(np.min(tim.getArray()))
241 sim = afwImage.ImageF(sim, bbox, afwImage.LOCAL)
242 svar = afwImage.ImageF(sim,
True)
245 sMi = afwImage.MaskedImageF(sim, smask, svar)
247 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
248 tvar = afwImage.ImageF(tim,
True)
251 tMi = afwImage.MaskedImageF(tim, tmask, tvar)
255 afwDisplay.Display(frame=1).
mtv(tMi)
256 afwDisplay.Display(frame=2).
mtv(sMi)
264 stampHalfWidth = 2*kSize
265 for x
in range(nCell):
266 for y
in range(nCell):
267 xCoord = x*sizeCell + sizeCell//2
268 yCoord = y*sizeCell + sizeCell//2
270 yCoord - stampHalfWidth)
272 yCoord + stampHalfWidth)
274 tsi = afwImage.MaskedImageF(tMi, bbox, origin=afwImage.LOCAL)
275 ssi = afwImage.MaskedImageF(sMi, bbox, origin=afwImage.LOCAL)
277 kc = diffimLib.makeKernelCandidate(xCoord, yCoord, tsi, ssi, psFake)
278 kernelCellSet.insertCandidate(kc)
282 return tMi, sMi, sKernel, kernelCellSet, configFake
290 """Subtract the background from masked images. 294 config : TODO: DM-17458 296 maskedImages : `list` of `lsst.afw.image.MaskedImage` 306 algorithm = config.algorithm
307 binsize = config.binSize
308 undersample = config.undersampleStyle
310 bctrl.setUndersampleStyle(undersample)
311 for maskedImage
in maskedImages:
312 bctrl.setNxSample(maskedImage.getWidth()//binsize + 1)
313 bctrl.setNySample(maskedImage.getHeight()//binsize + 1)
314 image = maskedImage.getImage()
317 image -= backobj.getImageF()
318 backgrounds.append(backobj.getImageF())
322 logger = Log.getLogger(
"ip.diffim.backgroundSubtract")
323 logger.debug(
"Total time for background subtraction : %.2f s", (t1 - t0))
336 kernelCellSet : TODO: DM-17458 338 psfMatchingKernel : TODO: DM-17458 340 backgroundModel : TODO: DM-17458 342 outdir : TODO: DM-17458 345 if not os.path.isdir(outdir):
348 for cell
in kernelCellSet.getCellList():
349 for cand
in cell.begin(
False):
350 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
351 xCand = int(cand.getXCenter())
352 yCand = int(cand.getYCenter())
353 idCand = cand.getId()
354 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.ORIG)
355 kernel = cand.getKernelImage(diffimLib.KernelCandidateF.ORIG)
356 diffIm.writeFits(os.path.join(outdir,
'diffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
357 kernel.writeFits(os.path.join(outdir,
'kernel_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
360 ski = afwImage.ImageD(kernel.getDimensions())
361 psfMatchingKernel.computeImage(ski,
False, xCand, yCand)
363 sbg = backgroundModel(xCand, yCand)
364 sdmi = cand.getDifferenceImage(sk, sbg)
365 sdmi.writeFits(os.path.join(outdir,
'sdiffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
373 """Convert a list of sources for the PSF-matching Kernel to Footprints. 377 candidateInList : TODO: DM-17458 378 Input list of Sources 379 templateExposure : TODO: DM-17458 380 Template image, to be checked for Mask bits in Source Footprint 381 scienceExposure : TODO: DM-17458 382 Science image, to be checked for Mask bits in Source Footprint 383 kernelSize : TODO: DM-17458 385 config : TODO: DM-17458 386 Config that defines the Mask planes that indicate an invalid Source and Bbox grow radius 392 candidateOutList : `list` 393 a list of dicts having a "source" and "footprint" field, to be used for Psf-matching 402 Takes an input list of Sources that were selected to constrain 403 the Psf-matching Kernel and turns them into a List of Footprints, 404 which are used to seed a set of KernelCandidates. The function 405 checks both the template and science image for masked pixels, 406 rejecting the Source if certain Mask bits (defined in config) are 407 set within the Footprint. 410 candidateOutList = []
411 fsb = diffimLib.FindSetBitsU()
413 for mp
in config.badMaskPlanes:
414 badBitMask |= afwImage.Mask.getPlaneBitMask(mp)
415 bbox = scienceExposure.getBBox()
418 if config.scaleByFwhm:
419 fpGrowPix = int(config.fpGrowKernelScaling*kernelSize + 0.5)
421 fpGrowPix = config.fpGrowPix
422 log.info(
"Growing %d kernel candidate stars by %d pixels", len(candidateInList), fpGrowPix)
424 for kernelCandidate
in candidateInList:
426 raise RuntimeError(
"Candiate not of type afwTable.SourceRecord")
429 center =
geom.Point2I(scienceExposure.getWcs().skyToPixel(kernelCandidate.getCoord()))
430 if center[0] < bbox.getMinX()
or center[0] > bbox.getMaxX():
432 if center[1] < bbox.getMinY()
or center[1] > bbox.getMaxY():
435 xmin = center[0] - fpGrowPix
436 xmax = center[0] + fpGrowPix
437 ymin = center[1] - fpGrowPix
438 ymax = center[1] + fpGrowPix
441 if (xmin - bbox.getMinX()) < 0:
442 xmax += (xmin - bbox.getMinX())
443 xmin -= (xmin - bbox.getMinX())
444 if (ymin - bbox.getMinY()) < 0:
445 ymax += (ymin - bbox.getMinY())
446 ymin -= (ymin - bbox.getMinY())
447 if (bbox.getMaxX() - xmax) < 0:
448 xmin -= (bbox.getMaxX() - xmax)
449 xmax += (bbox.getMaxX() - xmax)
450 if (bbox.getMaxY() - ymax) < 0:
451 ymin -= (bbox.getMaxY() - ymax)
452 ymax += (bbox.getMaxY() - ymax)
453 if xmin > xmax
or ymin > ymax:
458 fsb.apply(afwImage.MaskedImageF(templateExposure.getMaskedImage(), kbbox, deep=
False).getMask())
460 fsb.apply(afwImage.MaskedImageF(scienceExposure.getMaskedImage(), kbbox, deep=
False).getMask())
465 if not((bm1 & badBitMask)
or (bm2 & badBitMask)):
466 candidateOutList.append({
'source': kernelCandidate,
468 log.info(
"Selected %d / %d sources for KernelCandidacy", len(candidateOutList), len(candidateInList))
469 return candidateOutList
473 basisList, doBuild=False):
474 """Convert a list of Sources into KernelCandidates. 476 The KernelCandidates are used for fitting the Psf-matching kernel. 480 sourceTable : TODO: DM-17458 482 templateExposure : TODO: DM-17458 484 scienceExposure : TODO: DM-17458 486 kConfig : TODO: DM-17458 488 dConfig : TODO: DM-17458 492 basisList : TODO: DM-17458 494 doBuild : `bool`, optional 502 kernelSize = basisList[0].getWidth()
504 kernelSize, dConfig, log)
507 if doBuild
and not basisList:
510 ps = pexConfig.makePropertySet(kConfig)
511 visitor = diffimLib.BuildSingleKernelVisitorF(basisList, ps)
513 ps = pexConfig.makePropertySet(kConfig)
514 for cand
in footprintList:
515 bbox = cand[
'footprint'].getBBox()
516 tmi = afwImage.MaskedImageF(templateExposure.getMaskedImage(), bbox)
517 smi = afwImage.MaskedImageF(scienceExposure.getMaskedImage(), bbox)
518 kCand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, ps)
520 visitor.processCandidate(kCand)
521 kCand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN)
522 candList.append(kCand)
532 """A functor to evaluate the Bayesian Information Criterion for the number of basis sets 533 going into the kernel fitting""" 535 def __init__(self, psfMatchConfig, psfFwhmPixTc, psfFwhmPixTnc):
540 raise RuntimeError(
"BIC only implemnted for AL (alard lupton) basis")
545 for d1i
in range(1, d1 + 1):
546 for d2i
in range(1, d2 + 1):
547 for d3i
in range(1, d3 + 1):
548 dList = [d1i, d2i, d3i]
552 visitor = diffimLib.BuildSingleKernelVisitorF(kList,
553 pexConfig.makePropertySet(bicConfig))
554 visitor.setSkipBuilt(
False)
555 kernelCellSet.visitCandidates(visitor, bicConfig.nStarPerCell)
557 for cell
in kernelCellSet.getCellList():
558 for cand
in cell.begin(
False):
559 if cand.getStatus() != afwMath.SpatialCellCandidate.GOOD:
561 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.RECENT)
562 bbox = cand.getKernel(diffimLib.KernelCandidateF.RECENT).shrinkBBox(
563 diffIm.getBBox(afwImage.LOCAL))
564 diffIm =
type(diffIm)(diffIm, bbox,
True)
565 chi2 = diffIm.getImage().getArray()**2/diffIm.getVariance().getArray()
566 n = chi2.shape[0]*chi2.shape[1]
567 bic = np.sum(chi2) + k*np.log(n)
568 if cand.getId()
not in bicArray:
569 bicArray[cand.getId()] = {}
570 bicArray[cand.getId()][(d1i, d2i, d3i)] = bic
573 for candId
in bicArray:
574 cconfig, cvals =
list(bicArray[candId].
keys()),
list(bicArray[candId].values())
575 idx = np.argsort(cvals)
576 bestConfig = cconfig[idx[0]]
577 bestConfigs.append(bestConfig)
579 counter = Counter(bestConfigs).most_common(3)
580 log.info(
"B.I.C. prefers basis complexity %s %d times; %s %d times; %s %d times",
581 counter[0][0], counter[0][1],
582 counter[1][0], counter[1][1],
583 counter[2][0], counter[2][1])
584 return counter[0][0], counter[1][0], counter[2][0]
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)
A compact representation of a collection of pixels.
std::shared_ptr< Background > makeBackground(ImageT const &img, BackgroundControl const &bgCtrl)
A convenience function that uses function overloading to make the correct type of Background...
Pass parameters to a Background object.
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
A collection of SpatialCells covering an entire image.
Represent a 2-dimensional array of bitmask pixels.
A kernel that is a linear combination of fixed basis kernels.
void randomGaussianImage(ImageT *image, Random &rand)
Set image to random numbers with a gaussian N(0, 1) distribution.
def mtv(data, frame=None, title="", wcs=None, args, kwargs)
Record class that contains measurements made on a single exposure.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
A kernel described by a function.
An integer coordinate rectangle.
daf::base::PropertyList * list
A class that can be used to generate sequences of random numbers according to a number of different a...
A kernel created from an Image.