26 from collections
import Counter
37 import lsst.afw.math.mathLib
as afwMath
40 from .makeKernelBasisList
import makeKernelBasisList
53 rdmImage = img.Factory(img.getDimensions())
58 """Return a Poisson noise image based on im
60 Uses numpy.random; you may wish to call numpy.random.seed first.
62 @warning This uses an undocumented numpy API (the documented API
63 uses a single float expectation value instead of an array).
65 @param[in] im image; the output image has the same dimensions and shape
66 and its expectation value is the value of im at each pixel
68 import numpy.random
as rand
70 noiseIm = im.Factory(im.getBBox())
71 noiseArr = noiseIm.getArray()
73 intNoiseArr = rand.poisson(imArr)
74 noiseArr[:, :] = intNoiseArr.astype(noiseArr.dtype)
83 kCoeffs = (( 1.0, 0.0, 0.0),
84 ( 0.005, -0.000001, 0.000001),
85 ( 0.005, 0.000004, 0.000004),
86 ( -0.001, -0.000030, 0.000030),
87 ( -0.001, 0.000015, 0.000015),
88 ( -0.005, -0.000050, 0.000050))
92 deltaFunctionCounts = 1.e4, tGaussianWidth = 1.0,
93 addNoise =
True, bgValue = 100., display =
False):
95 from .
import imagePsfMatch
97 configFake.kernel.name =
"AL"
98 subconfigFake = configFake.kernel.active
99 subconfigFake.alardNGauss = 1
100 subconfigFake.alardSigGauss = [2.5,]
101 subconfigFake.alardDegGauss = [2,]
102 subconfigFake.sizeCellX = sizeCell
103 subconfigFake.sizeCellY = sizeCell
104 subconfigFake.spatialKernelOrder = 1
105 subconfigFake.spatialModelType =
"polynomial"
106 subconfigFake.singleKernelClipping =
False
107 subconfigFake.spatialKernelClipping =
False
109 subconfigFake.fitForBackground =
True
111 policyFake = pexConfig.makePolicy(subconfigFake)
114 kSize = subconfigFake.kernelSize
117 gaussKernelWidth = sizeCell // 2
122 spatialKernelWidth = kSize
125 border = (gaussKernelWidth + spatialKernelWidth)//2
128 totalSize = nCell * sizeCell + 2 * border
130 for x
in range(nCell):
131 for y
in range(nCell):
132 tim.set(x * sizeCell + sizeCell // 2 + border - 1,
133 y * sizeCell + sizeCell // 2 + border - 1,
137 gaussFunction = afwMath.GaussianFunction2D(tGaussianWidth, tGaussianWidth)
139 cim = afwImage.ImageF(tim.getDimensions())
144 bbox = gaussKernel.shrinkBBox(tim.getBBox(afwImage.LOCAL))
145 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
149 polyFunc = afwMath.PolynomialFunction2D(1)
151 nToUse = min(len(kCoeffs), len(basisList))
155 sKernel.setSpatialParameters(kCoeffs[:nToUse])
156 sim = afwImage.ImageF(tim.getDimensions())
160 bbox = sKernel.shrinkBBox(sim.getBBox(afwImage.LOCAL))
166 tim += 2 * np.abs(np.min(tim.getArray()))
174 sim = afwImage.ImageF(sim, bbox, afwImage.LOCAL)
175 svar = afwImage.ImageF(sim,
True)
176 smask = afwImage.MaskU(sim.getDimensions())
178 sMi = afwImage.MaskedImageF(sim, smask, svar)
180 tim = afwImage.ImageF(tim, bbox, afwImage.LOCAL)
181 tvar = afwImage.ImageF(tim,
True)
182 tmask = afwImage.MaskU(tim.getDimensions())
184 tMi = afwImage.MaskedImageF(tim, tmask, tvar)
188 ds9.mtv(tMi, frame=1)
189 ds9.mtv(sMi, frame=2)
197 stampHalfWidth = 2 * kSize
198 for x
in range(nCell):
199 for y
in range(nCell):
200 xCoord = x * sizeCell + sizeCell // 2
201 yCoord = y * sizeCell + sizeCell // 2
203 yCoord - stampHalfWidth)
205 yCoord + stampHalfWidth)
207 tsi = afwImage.MaskedImageF(tMi, bbox, afwImage.LOCAL)
208 ssi = afwImage.MaskedImageF(sMi, bbox, afwImage.LOCAL)
210 kc = diffimLib.makeKernelCandidate(xCoord, yCoord, tsi, ssi, policyFake)
211 kernelCellSet.insertCandidate(kc)
215 return tMi, sMi, sKernel, kernelCellSet, configFake
225 algorithm = config.algorithm
226 binsize = config.binSize
227 undersample = config.undersampleStyle
229 bctrl.setUndersampleStyle(undersample)
230 for maskedImage
in maskedImages:
231 bctrl.setNxSample(maskedImage.getWidth()//binsize + 1)
232 bctrl.setNySample(maskedImage.getHeight()//binsize + 1)
233 image = maskedImage.getImage()
236 image -= backobj.getImageF()
237 backgrounds.append(backobj.getImageF())
242 "Total time for background subtraction : %.2f s" % (t1-t0))
250 if not os.path.isdir(outdir):
253 for cell
in kernelCellSet.getCellList():
254 for cand
in cell.begin(
False):
255 cand = diffimLib.cast_KernelCandidateF(cand)
256 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
257 xCand = int(cand.getXCenter())
258 yCand = int(cand.getYCenter())
259 idCand = cand.getId()
260 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.ORIG)
261 kernel = cand.getKernelImage(diffimLib.KernelCandidateF.ORIG)
262 diffIm.writeFits(os.path.join(outdir,
'diffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
263 kernel.writeFits(os.path.join(outdir,
'kernel_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
266 ski = afwImage.ImageD(kernel.getDimensions())
267 psfMatchingKernel.computeImage(ski,
False, xCand, yCand)
269 sbg = backgroundModel(xCand, yCand)
270 sdmi = cand.getDifferenceImage(sk, sbg)
271 sdmi.writeFits(os.path.join(outdir,
'sdiffim_c%d_x%d_y%d.fits' % (idCand, xCand, yCand)))
278 """ Takes an input list of Sources that were selected to constrain
279 the Psf-matching Kernel and turns them into a List of Footprints,
280 which are used to seed a set of KernelCandidates. The function
281 checks both the template and science image for masked pixels,
282 rejecting the Source if certain Mask bits (defined in config) are
283 set within the Footprint.
285 @param candidateInList: Input list of Sources
286 @param templateExposure: Template image, to be checked for Mask bits in Source Footprint
287 @param scienceExposure: Science image, to be checked for Mask bits in Source Footprint
288 @param config: Config that defines the Mask planes that indicate an invalid Source and Bbox grow radius
289 @param log: Log for output
291 @return a list of dicts having a "source" and "footprint" field, to be used for Psf-matching
294 candidateOutList = []
295 fsb = diffimLib.FindSetBitsU()
297 for mp
in config.badMaskPlanes:
298 badBitMask |= afwImage.MaskU.getPlaneBitMask(mp)
299 bbox = scienceExposure.getBBox()
302 if config.scaleByFwhm:
303 fpGrowPix = int(config.fpGrowKernelScaling * kernelSize + 0.5)
305 fpGrowPix = config.fpGrowPix
306 log.info(
"Growing %d kernel candidate stars by %d pixels" % (len(candidateInList), fpGrowPix))
308 for kernelCandidate
in candidateInList:
310 raise RuntimeError, (
"Candiate not of type afwTable.SourceRecord")
313 center =
afwGeom.Point2I(scienceExposure.getWcs().skyToPixel(kernelCandidate.getCoord()))
314 if center[0] < bbox.getMinX()
or center[0] > bbox.getMaxX():
316 if center[1] < bbox.getMinY()
or center[1] > bbox.getMaxY():
319 xmin = center[0] - fpGrowPix
320 xmax = center[0] + fpGrowPix
321 ymin = center[1] - fpGrowPix
322 ymax = center[1] + fpGrowPix
325 if (xmin - bbox.getMinX()) < 0:
326 xmax += (xmin - bbox.getMinX())
327 xmin -= (xmin - bbox.getMinX())
328 if (ymin - bbox.getMinY()) < 0:
329 ymax += (ymin - bbox.getMinY())
330 ymin -= (ymin - bbox.getMinY())
331 if (bbox.getMaxX() - xmax) < 0:
332 xmin -= (bbox.getMaxX() - xmax)
333 xmax += (bbox.getMaxX() - xmax)
334 if (bbox.getMaxY() - ymax) < 0:
335 ymin -= (bbox.getMaxY() - ymax)
336 ymax += (bbox.getMaxY() - ymax)
337 if xmin > xmax
or ymin > ymax:
342 fsb.apply(afwImage.MaskedImageF(templateExposure.getMaskedImage(), kbbox,
False).getMask())
344 fsb.apply(afwImage.MaskedImageF(scienceExposure.getMaskedImage(), kbbox,
False).getMask())
349 if not((bm1 & badBitMask)
or (bm2 & badBitMask)):
350 candidateOutList.append({
'source':kernelCandidate,
'footprint':
afwDetect.Footprint(kbbox)})
351 log.info(
"Selected %d / %d sources for KernelCandidacy" % (len(candidateOutList), len(candidateInList)))
352 return candidateOutList
355 basisList, doBuild=
False):
356 """Takes an input list of Sources, and turns them into
357 KernelCandidates for fitting of the Psf-matching kernel."""
358 kernelSize = basisList[0].getWidth()
360 kernelSize, dConfig, log)
363 if doBuild
and not basisList:
366 policy = pexConfig.makePolicy(kConfig)
367 visitor = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
369 policy = pexConfig.makePolicy(kConfig)
370 for cand
in footprintList:
371 bbox = cand[
'footprint'].getBBox()
372 tmi = afwImage.MaskedImageF(templateExposure.getMaskedImage(), bbox)
373 smi = afwImage.MaskedImageF(scienceExposure.getMaskedImage(), bbox)
374 kCand = diffimLib.makeKernelCandidate(cand[
'source'], tmi, smi, policy)
376 visitor.processCandidate(kCand)
377 kCand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN)
378 candList.append(kCand)
388 """A functor to evaluate the Bayesian Information Criterion for the number of basis sets going into the kernel fitting"""
389 def __init__(self, psfMatchConfig, psfFwhmPixTc, psfFwhmPixTnc):
393 if not self.psfMatchConfig.kernelBasisSet ==
"alard-lupton":
394 raise RuntimeError,
"BIC only implemnted for AL (alard lupton) basis"
397 d1, d2, d3 = self.psfMatchConfig.alardDegGauss
399 for d1i
in range(1, d1+1):
400 for d2i
in range(1, d2+1):
401 for d3i
in range(1, d3+1):
402 dList = [d1i, d2i, d3i]
406 visitor = diffimLib.BuildSingleKernelVisitorF(kList, pexConfig.makePolicy(bicConfig))
407 visitor.setSkipBuilt(
False)
408 kernelCellSet.visitCandidates(visitor, bicConfig.nStarPerCell)
410 for cell
in kernelCellSet.getCellList():
411 for cand
in cell.begin(
False):
412 cand = diffimLib.cast_KernelCandidateF(cand)
413 if cand.getStatus() != afwMath.SpatialCellCandidate.GOOD:
415 diffIm = cand.getDifferenceImage(diffimLib.KernelCandidateF.RECENT)
416 bbox = cand.getKernel(diffimLib.KernelCandidateF.RECENT).shrinkBBox(diffIm.getBBox(afwImage.LOCAL))
417 diffIm = type(diffIm)(diffIm, bbox,
True)
418 chi2 = diffIm.getImage().getArray()**2 / diffIm.getVariance().getArray()
419 n = chi2.shape[0] * chi2.shape[1]
420 bic = np.sum(chi2) + k * np.log(n)
421 if not bicArray.has_key(cand.getId()):
422 bicArray[cand.getId()] = {}
423 bicArray[cand.getId()][(d1i, d2i, d3i)] = bic
426 candIds = bicArray.keys()
427 for candId
in candIds:
428 cconfig, cvals = bicArray[candId].keys(), bicArray[candId].values()
429 idx = np.argsort(cvals)
430 bestConfig = cconfig[idx[0]]
431 bestConfigs.append(bestConfig)
433 counter = Counter(bestConfigs).most_common(3)
434 log.info(
"B.I.C. prefers basis complexity %s %d times; %s %d times; %s %d times" % (counter[0][0], counter[0][1],
435 counter[1][0], counter[1][1],
436 counter[2][0], counter[2][1]))
437 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.
limited backward compatibility to the DC2 run-time trace facilities
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)
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.
std::vector< boost::shared_ptr< Kernel > > KernelList
A kernel described by a function.
A kernel created from an Image.