1 from builtins
import range
2 from builtins
import object
28 from collections
import Counter
32 from .
import diffimLib
39 import lsst.afw.math.mathLib
as afwMath
42 from .makeKernelBasisList
import makeKernelBasisList
56 rdmImage = img.Factory(img.getDimensions())
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
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)
146 cim = afwImage.ImageF(tim.getDimensions())
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))
162 sKernel.setSpatialParameters(kCoeffs[:nToUse])
163 sim = afwImage.ImageF(tim.getDimensions())
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.MaskU(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.MaskU(tim.getDimensions())
191 tMi = afwImage.MaskedImageF(tim, tmask, tvar)
195 ds9.mtv(tMi, frame=1)
196 ds9.mtv(sMi, frame=2)
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
210 yCoord - stampHalfWidth)
212 yCoord + stampHalfWidth)
214 tsi = afwImage.MaskedImageF(tMi, bbox, afwImage.LOCAL)
215 ssi = afwImage.MaskedImageF(sMi, bbox, 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
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()
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 cand = diffimLib.KernelCandidateF.cast(cand)
264 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
265 xCand = int(cand.getXCenter())
266 yCand = int(cand.getYCenter())
267 idCand = cand.getId()
268 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.ORIG)
269 kernel = cand.getKernelImage(diffimLib.KernelCandidateF.ORIG)
270 diffIm.writeFits(os.path.join(outdir,
'diffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
271 kernel.writeFits(os.path.join(outdir,
'kernel_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
274 ski = afwImage.ImageD(kernel.getDimensions())
275 psfMatchingKernel.computeImage(ski,
False, xCand, yCand)
277 sbg = backgroundModel(xCand, yCand)
278 sdmi = cand.getDifferenceImage(sk, sbg)
279 sdmi.writeFits(os.path.join(outdir,
'sdiffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
287 """ Takes an input list of Sources that were selected to constrain
288 the Psf-matching Kernel and turns them into a List of Footprints,
289 which are used to seed a set of KernelCandidates. The function
290 checks both the template and science image for masked pixels,
291 rejecting the Source if certain Mask bits (defined in config) are
292 set within the Footprint.
294 @param candidateInList: Input list of Sources
295 @param templateExposure: Template image, to be checked for Mask bits in Source Footprint
296 @param scienceExposure: Science image, to be checked for Mask bits in Source Footprint
297 @param config: Config that defines the Mask planes that indicate an invalid Source and Bbox grow radius
298 @param log: Log for output
300 @return a list of dicts having a "source" and "footprint" field, to be used for Psf-matching
303 candidateOutList = []
304 fsb = diffimLib.FindSetBitsU()
306 for mp
in config.badMaskPlanes:
307 badBitMask |= afwImage.MaskU.getPlaneBitMask(mp)
308 bbox = scienceExposure.getBBox()
311 if config.scaleByFwhm:
312 fpGrowPix = int(config.fpGrowKernelScaling * kernelSize + 0.5)
314 fpGrowPix = config.fpGrowPix
315 log.info(
"Growing %d kernel candidate stars by %d pixels", len(candidateInList), fpGrowPix)
317 for kernelCandidate
in candidateInList:
319 raise RuntimeError(
"Candiate not of type afwTable.SourceRecord")
322 center =
afwGeom.Point2I(scienceExposure.getWcs().skyToPixel(kernelCandidate.getCoord()))
323 if center[0] < bbox.getMinX()
or center[0] > bbox.getMaxX():
325 if center[1] < bbox.getMinY()
or center[1] > bbox.getMaxY():
328 xmin = center[0] - fpGrowPix
329 xmax = center[0] + fpGrowPix
330 ymin = center[1] - fpGrowPix
331 ymax = center[1] + fpGrowPix
334 if (xmin - bbox.getMinX()) < 0:
335 xmax += (xmin - bbox.getMinX())
336 xmin -= (xmin - bbox.getMinX())
337 if (ymin - bbox.getMinY()) < 0:
338 ymax += (ymin - bbox.getMinY())
339 ymin -= (ymin - bbox.getMinY())
340 if (bbox.getMaxX() - xmax) < 0:
341 xmin -= (bbox.getMaxX() - xmax)
342 xmax += (bbox.getMaxX() - xmax)
343 if (bbox.getMaxY() - ymax) < 0:
344 ymin -= (bbox.getMaxY() - ymax)
345 ymax += (bbox.getMaxY() - ymax)
346 if xmin > xmax
or ymin > ymax:
351 fsb.apply(afwImage.MaskedImageF(templateExposure.getMaskedImage(), kbbox,
False).getMask())
353 fsb.apply(afwImage.MaskedImageF(scienceExposure.getMaskedImage(), kbbox,
False).getMask())
358 if not((bm1 & badBitMask)
or (bm2 & badBitMask)):
359 candidateOutList.append({
'source': kernelCandidate,
'footprint':
afwDetect.Footprint(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):
405 if not self.psfMatchConfig.kernelBasisSet ==
"alard-lupton":
406 raise RuntimeError(
"BIC only implemnted for AL (alard lupton) basis")
409 d1, d2, d3 = self.psfMatchConfig.alardDegGauss
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 cand = diffimLib.KernelCandidateF.cast(cand)
425 if cand.getStatus() != afwMath.SpatialCellCandidate.GOOD:
427 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.RECENT)
428 bbox = cand.getKernel(diffimLib.KernelCandidateF.RECENT).shrinkBBox(
429 diffIm.getBBox(afwImage.LOCAL))
430 diffIm = type(diffIm)(diffIm, bbox,
True)
431 chi2 = diffIm.getImage().getArray()**2 / diffIm.getVariance().getArray()
432 n = chi2.shape[0] * chi2.shape[1]
433 bic = np.sum(chi2) + k * np.log(n)
434 if cand.getId()
not in bicArray:
435 bicArray[cand.getId()] = {}
436 bicArray[cand.getId()][(d1i, d2i, d3i)] = bic
439 for candId
in bicArray:
440 cconfig, cvals = list(bicArray[candId].keys()), list(bicArray[candId].values())
441 idx = np.argsort(cvals)
442 bestConfig = cconfig[idx[0]]
443 bestConfigs.append(bestConfig)
445 counter = Counter(bestConfigs).most_common(3)
446 log.info(
"B.I.C. prefers basis complexity %s %d times; %s %d times; %s %d times",
447 counter[0][0], counter[0][1],
448 counter[1][0], counter[1][1],
449 counter[2][0], counter[2][1])
450 return counter[0][0], counter[1][0], counter[2][0]
boost::shared_ptr< Background > makeBackground(ImageT const &img, BackgroundControl const &bgCtrl)
A convenience function that uses function overloading to make the correct type of Background...
Configuration for image-to-image Psf matching.
Pass parameters to a Background object.
An integer coordinate rectangle.
A collection of SpatialCells covering an entire image.
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.
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
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.
A kernel described by a function.
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.
std::vector< boost::shared_ptr< Kernel > > KernelList