def showDiaSources(sources, exposure, isFlagged, isDipole, frame=None): """Display Dia Sources """ # # Show us the ccandidates # # Too many mask planes in diffims for plane in ("BAD", "CR", "EDGE", "INTERPOlATED", "INTRP", "SAT", "SATURATED"): ds9.setMaskPlaneVisibility(plane, False) mos = displayUtils.Mosaic() for i in range(len(sources)): source = sources[i] badFlag = isFlagged[i] dipoleFlag = isDipole[i] bbox = source.getFootprint().getBBox() stamp = exposure.Factory(exposure, bbox, True) im = displayUtils.Mosaic(gutter=1, background=0, mode="x") im.append(stamp.getMaskedImage()) lab = "%.1f,%.1f:" % (source.getX(), source.getY()) if badFlag: ctype = ds9.RED lab += "BAD" if dipoleFlag: ctype = ds9.YELLOW lab += "DIPOLE" if not badFlag and not dipoleFlag: ctype = ds9.GREEN lab += "OK" mos.append(im.makeMosaic(), lab, ctype) title = "Dia Sources" mosaicImage = mos.makeMosaic(frame=frame, title=title) return mosaicImage
def testPca(self): """Test calculating PCA""" random.seed(0) width, height = 200, 100 numBases = 3 numInputs = 3 bases = [] for i in range(numBases): im = afwImage.ImageF(width, height) array = im.getArray() x, y = np.indices(array.shape) period = 5 * (i + 1) fx = np.sin(2 * math.pi / period * x + 2 * math.pi / numBases * i) fy = np.sin(2 * math.pi / period * y + 2 * math.pi / numBases * i) array[x, y] = fx + fy bases.append(im) if display: mos = displayUtils.Mosaic(background=-10) ds9.mtv(mos.makeMosaic(bases), title="Basis functions", frame=1) inputs = [] for i in range(numInputs): im = afwImage.ImageF(afwGeom.Extent2I(width, height)) im.set(0) for b in bases: im.scaledPlus(random.random(), b) inputs.append(im) self.ImageSet.addImage(im, 1.0) if display: mos = displayUtils.Mosaic(background=-10) ds9.mtv(mos.makeMosaic(inputs), title="Inputs", frame=2) self.ImageSet.analyze() eImages = [] for img in self.ImageSet.getEigenImages(): eImages.append(img) if display: mos = displayUtils.Mosaic(background=-10) ds9.mtv(mos.makeMosaic(eImages), title="Eigenimages", frame=3) self.assertEqual(len(eImages), numInputs) # Test for orthogonality for i1, i2 in itertools.combinations(range(len(eImages)), 2): inner = afwImage.innerProduct(eImages[i1], eImages[i2]) norm1 = eImages[i1].getArray().sum() norm2 = eImages[i2].getArray().sum() inner /= norm1 * norm2 self.assertAlmostEqual(inner, 0)
def testCandidateList(self): self.assertFalse(self.cellSet.getCellList()[0].empty()) self.assertTrue(self.cellSet.getCellList()[1].empty()) self.assertFalse(self.cellSet.getCellList()[2].empty()) self.assertTrue(self.cellSet.getCellList()[3].empty()) stamps = [] stampInfo = [] for cell in self.cellSet.getCellList(): for cand in cell: # # Swig doesn't know that we inherited from SpatialCellMaskedImageCandidate; all # it knows is that we have a SpatialCellCandidate, and SpatialCellCandidates # don't know about getMaskedImage; so cast the pointer to SpatialCellMaskedImageCandidate<float> # and all will be well # cand = afwMath.cast_SpatialCellMaskedImageCandidateF(cell[0]) width, height = 29, 25 cand.setWidth(width); cand.setHeight(height); im = cand.getMaskedImage() stamps.append(im) self.assertEqual(im.getWidth(), width) self.assertEqual(im.getHeight(), height) if False and display: mos = displayUtils.Mosaic() mos.makeMosaic(stamps, frame=2)
def plotPsfCandidates(cellSet, showBadCandidates=False, frame=1): import lsst.afw.display.utils as displayUtils stamps = [] for cell in cellSet.getCellList(): for cand in cell.begin(not showBadCandidates): # maybe include bad candidates try: im = cand.getMaskedImage() chi2 = cand.getChi2() if chi2 < 1e100: chi2 = "%.1f" % chi2 else: chi2 = float("nan") stamps.append((im, "%d%s" % (maUtils.splitId(cand.getSource().getId(), True)["objId"], chi2), cand.getStatus())) except Exception: continue mos = displayUtils.Mosaic() for im, label, status in stamps: im = type(im)(im, True) try: im /= afwMath.makeStatistics(im, afwMath.MAX).getValue() except NotImplementedError: pass mos.append(im, label, ds9.GREEN if status == afwMath.SpatialCellCandidate.GOOD else ds9.YELLOW if status == afwMath.SpatialCellCandidate.UNKNOWN else ds9.RED) if mos.images: mos.makeMosaic(frame=frame, title="Psf Candidates")
def showPsf(psf, eigenValues=None, XY=None, normalize=True, display=None): """Display a PSF's eigen images If normalize is True, set the largest absolute value of each eigenimage to 1.0 (n.b. sum == 0.0 for i > 0) """ if eigenValues: coeffs = eigenValues elif XY is not None: coeffs = psf.getLocalKernel(lsst.geom.PointD(XY[0], XY[1])).getKernelParameters() else: coeffs = None mos = displayUtils.Mosaic(gutter=2, background=-0.1) for i, k in enumerate(psf.getKernel().getKernelList()): im = afwImage.ImageD(k.getDimensions()) k.computeImage(im, False) if normalize: im /= numpy.max(numpy.abs(im.getArray())) if coeffs: mos.append(im, "%g" % (coeffs[i]/coeffs[0])) else: mos.append(im) if not display: display = afwDisplay.Display() mos.makeMosaic(display=display, title="Kernel Basis Functions") return mos
def testGetImage(self): """Test returning a realisation of the dgPsf""" xcen = self.psf.getKernel().getWidth()//2 ycen = self.psf.getKernel().getHeight()//2 stamps = [] trueCenters = [] for x, y in ([10, 10], [9.4999, 10.4999], [10.5001, 10.5001]): fx, fy = x - int(x), y - int(y) if fx >= 0.5: fx -= 1.0 if fy >= 0.5: fy -= 1.0 im = self.psf.computeImage(afwGeom.Point2D(x, y)).convertF() stamps.append(im.Factory(im, True)) trueCenters.append([xcen + fx, ycen + fy]) if display: mos = displayUtils.Mosaic() # control mosaics ds9.mtv(mos.makeMosaic(stamps)) for i in range(len(trueCenters)): bbox = mos.getBBox(i) ds9.dot("+", bbox.getMinX() + xcen, bbox.getMinY() + ycen, ctype = ds9.RED, size = 1) ds9.dot("+", bbox.getMinX() + trueCenters[i][0], bbox.getMinY() + trueCenters[i][1]) ds9.dot("%.2f, %.2f" % (trueCenters[i][0], trueCenters[i][1]), bbox.getMinX() + xcen, bbox.getMinY() + 2)
def testKernelPsf(self): """Test creating a Psf from a Kernel""" x,y = 10.4999, 10.4999 ksize = 15 sigma1 = 1 # # Make a PSF from that kernel # kPsf = measAlg.KernelPsf(afwMath.AnalyticKernel(ksize, ksize, afwMath.GaussianFunction2D(sigma1, sigma1))) kIm = kPsf.computeImage(afwGeom.Point2D(x, y)) # # And now via the dgPsf model # dgPsf = measAlg.DoubleGaussianPsf(ksize, ksize, sigma1) dgIm = dgPsf.computeImage(afwGeom.Point2D(x, y)) # # Check that they're the same # diff = type(kIm)(kIm, True); diff -= dgIm stats = afwMath.makeStatistics(diff, afwMath.MAX | afwMath.MIN) self.assertAlmostEqual(stats.getValue(afwMath.MAX), 0.0, places=16) self.assertAlmostEqual(stats.getValue(afwMath.MIN), 0.0, places=16) if display: mos = displayUtils.Mosaic() mos.setBackground(-0.1) ds9.mtv(mos.makeMosaic([kIm, dgIm, diff], mode="x"), frame=1)
def showPsf(psf, eigenValues=None, XY=None, normalize=True, frame=None): """Display a PSF's eigen images If normalize is True, set the largest absolute value of each eigenimage to 1.0 (n.b. sum == 0.0 for i > 0) """ if eigenValues: coeffs = eigenValues elif XY is not None: coeffs = psf.getLocalKernel(afwGeom.PointD(XY[0], XY[1])).getKernelParameters() else: coeffs = None mos = displayUtils.Mosaic() i = 0 for k in afwMath.cast_LinearCombinationKernel(psf.getKernel()).getKernelList(): im = afwImage.ImageD(k.getDimensions()) k.computeImage(im, False) if normalize: im /= numpy.max(numpy.abs(im.getArray())) if coeffs: mos.append(im, "%g" % (coeffs[i]/coeffs[0])) i += 1 else: mos.append(im) mos.makeMosaic(frame=frame, title="Eigen Images") return mos
def showKernelBasis(kernel, frame=None): """Display a Kernel's basis images """ mos = displayUtils.Mosaic() for k in kernel.getKernelList(): im = afwImage.ImageD(k.getDimensions()) k.computeImage(im, False) mos.append(im) mos.makeMosaic(frame=frame, title="Kernel Basis Images") return mos
def runBasicTest(self, kernel, convControl, refKernel=None, kernelDescr="", rtol=1.0e-05, atol=1e-08): """Assert that afwMath::convolve gives the same result as reference convolution for a given kernel. Inputs: - kernel: convolution kernel - convControl: convolution control parameters (afwMath.ConvolutionControl) - refKernel: kernel to use for refConvolve (if None then kernel is used) - kernelDescr: description of kernel - rtol: relative tolerance (see below) - atol: absolute tolerance (see below) rtol and atol are positive, typically very small numbers. The relative difference (rtol * abs(b)) and the absolute difference "atol" are added together to compare against the absolute difference between "a" and "b". """ if refKernel is None: refKernel = kernel # strip garbage characters (whitespace and punctuation) to make a short description for saving files shortKernelDescr = self._removeGarbageChars(kernelDescr) doNormalize = convControl.getDoNormalize() doCopyEdge = convControl.getDoCopyEdge() maxInterpDist = convControl.getMaxInterpolationDistance() imMaskVar = self.maskedImage.getArrays() xy0 = self.maskedImage.getXY0() refCnvImMaskVarArr = refConvolve(imMaskVar, xy0, refKernel, doNormalize, doCopyEdge) refMaskedImage = afwImage.makeMaskedImageFromArrays(*refCnvImMaskVarArr) afwMath.convolve(self.cnvImage, self.maskedImage.getImage(), kernel, convControl) self.assertEqual(self.cnvImage.getXY0(), self.xy0) afwMath.convolve(self.cnvMaskedImage, self.maskedImage, kernel, convControl) if display and False: ds9.mtv(displayUtils.Mosaic().makeMosaic([ self.maskedImage, refMaskedImage, self.cnvMaskedImage]), frame=0) if False: for (x, y) in ((0, 0), (1, 0), (0, 1), (50, 50)): print("Mask(%d,%d) 0x%x 0x%x" % (x, y, refMaskedImage.getMask().get(x, y), self.cnvMaskedImage.getMask().get(x, y))) self.assertImagesNearlyEqual(self.cnvImage, refMaskedImage.getImage(), atol=atol, rtol=rtol) self.assertMaskedImagesNearlyEqual(self.cnvMaskedImage, refMaskedImage, atol=atol, rtol=rtol) if not sameMaskPlaneDicts(self.cnvMaskedImage, self.maskedImage): self.cnvMaskedImage.writeFits("act%s" % (shortKernelDescr,)) refMaskedImage.writeFits("des%s" % (shortKernelDescr,)) self.fail("convolve(MaskedImage, kernel=%s, doNormalize=%s, " "doCopyEdge=%s, maxInterpDist=%s) failed:\n%s" % (kernelDescr, doNormalize, doCopyEdge, maxInterpDist, "convolved mask dictionary does not match input"))
def main(rootDir, visit, ccd, n=4): # make a butler and specify your dataId butler = dafPersist.Butler(rootDir) dataId = {'visit': visit, 'ccd':ccd} # get the souces and the exposure from the butler sources = butler.get('src', dataId) exposure = butler.get("calexp", dataId) # make a list of the ones with the calib.psf.used flag set psfSources = [s for s in sources if s.get("calib.psf.used")] wx, wy = 25, 25 # the width to use for the bounding box subImages = [] labels = [] for psfSrc in psfSources[0:n]: # make a bounding box for this source x, y = int(psfSrc.getX()), int(psfSrc.getY()) bbox = afwGeom.Box2I(afwGeom.Point2I(x-wx/2, y-wy/2), afwGeom.Extent2I(wx, wy)) # create a new image, and add it to our list subimg = afwImage.ImageF(exposure.getMaskedImage().getImage(), bbox) subImages.append(subimg) # use the ID for a label label = "ID="+str(psfSrc.getId()) labels.append(label) # create a Mosaic object and set the specifications for your mosaic m = dispUtils.Mosaic() m.setGutter(2) # width of the space between each subimage m.setBackground(0) m.setMode("square") # create the mosaic mosaic = m.makeMosaic(subImages) # display it with labels in ds9 ds9.mtv(mosaic) m.drawLabels(labels) # make a pyplot png (note: y-axis flipped for pyplot) img = mosaic.getArray()[::-1,:] vmin, vmax = zscale(img) pyplot.imshow(img, interpolation="none", cmap="gray", vmin=vmin, vmax=vmax) pyplot.gcf().savefig("mosaic.png")
def main(rootDir, visit, ccd, n=4): # butler を開き読み込みたいデータの dataId を指定する butler = dafPersist.Butler(rootDir) dataId = {'visit': visit, 'ccd': ccd} # butler から一次処理済データのカタログファイルをとデータを読み込む sources = butler.get('src', dataId) exposure = butler.get("calexp", dataId) # calib.psf.used で flag がついている天体リストを作る psfSources = [s for s in sources if s.get("calib.psf.used")] wx, wy = 25, 25 # bounding box の大きさを指定 subImages = [] labels = [] for psfSrc in psfSources[0:n]: # この天体の bounding box を作る x, y = int(psfSrc.getX()), int(psfSrc.getY()) bbox = afwGeom.Box2I(afwGeom.Point2I(x - wx / 2, y - wy / 2), afwGeom.Extent2I(wx, wy)) # 新しい画像を作り、リストに加える subimg = afwImage.ImageF(exposure.getMaskedImage().getImage(), bbox) subImages.append(subimg) # ラベルつけのために ID を使う label = "ID=" + str(psfSrc.getId()) labels.append(label) # mosaic の天体を作り、仕様をセットする m = dispUtils.Mosaic() m.setGutter(2) # 各 subimage の幅を指定する m.setBackground(0) m.setMode("square") # mosaic を作る mosaic = m.makeMosaic(subImages) # ds9 で表示し、その画像にラベルをつける ds9.mtv(mosaic) m.drawLabels(labels) # pyplot で png 画像を作る(pyplot 用に y-軸 は判定居させている) img = mosaic.getArray()[::-1, :] vmin, vmax = zscale(img) pyplot.imshow(img, interpolation="none", cmap="gray", vmin=vmin, vmax=vmax) pyplot.gcf().savefig("mosaic.png")
def BuildMosaic(filename, gutter=0, background=0): import lsst.afw.display.utils as Util import lsst.afw.image as afwImg m = Util.Mosaic() m.setGutter(gutter) m.setBackground(background) images = [] for i in range(2, 18): m.append(afwImg.ImageF(filename, i), str(i)) # mosaic = m.makeMosaic() return m
def showPsf(da, distort=False, stampSize=19, nx=7, ny=None, gutterLevel=0.05): mos = displayUtils.Mosaic(background=gutterLevel, gutter=1) if ny is None: ny = 2*nx dataId = da.dataId.copy() for ccd in (8, 9, 5, 4, 3, 6, 7, 2, 1, 0): dataId.update(ccd=ccd) calexp = da.getDataset("calexp", dataId)[0] if False: print calexp.getDetector().getId() mos.append(maUtils.showPsfMosaic(calexp, stampSize=stampSize, nx=nx, ny=ny, distort=distort).makeMosaic(mode=nx)) return mos.makeMosaic(mode=5) # Camera is 5x2
def testCandidateList(self): if False and display: ds9.mtv(self.mi) for cell in self.cellSet.getCellList(): x0, y0, x1, y1 = (cell.getBBox().getX0(), cell.getBBox().getY0(), cell.getBBox().getX1(), cell.getBBox().getY1()) print((x0, y0, " ", x1, y1)) x0 -= 0.5 y0 -= 0.5 x1 += 0.5 y1 += 0.5 ds9.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=ds9.RED) self.assertFalse(self.cellSet.getCellList()[0].empty()) self.assertTrue(self.cellSet.getCellList()[1].empty()) self.assertFalse(self.cellSet.getCellList()[2].empty()) stamps = [] for cell in self.cellSet.getCellList(): for cand in cell: # # Swig doesn't know that we PsfCandidate; all it knows is # that we have a SpatialCellCandidate, and # SpatialCellCandidates don't know about getMaskedImage; so # cast the pointer to PsfCandidate<float> and all will be well # cand = algorithms.PsfCandidateF.cast(cell[0]) width, height = 15, 17 cand.setWidth(width) cand.setHeight(height) im = cand.getMaskedImage() stamps.append(im) self.assertEqual(im.getWidth(), width) self.assertEqual(im.getHeight(), height) if display: mos = displayUtils.Mosaic() ds9.mtv(mos.makeMosaic(stamps), frame=1)
def testCandidateList(self): if False and display: ds9.mtv(self.mi) for cell in self.cellSet.getCellList(): x0, y0, x1, y1 = (cell.getBBox().getX0(), cell.getBBox().getY0(), cell.getBBox().getX1(), cell.getBBox().getY1()) print((x0, y0, " ", x1, y1)) x0 -= 0.5 y0 -= 0.5 x1 += 0.5 y1 += 0.5 ds9.line([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)], ctype=ds9.RED) self.assertFalse(self.cellSet.getCellList()[0].empty()) self.assertTrue(self.cellSet.getCellList()[1].empty()) self.assertFalse(self.cellSet.getCellList()[2].empty()) stamps = [] for cell in self.cellSet.getCellList(): for cand in cell: cand = cell[0] width, height = 15, 17 cand.setWidth(width) cand.setHeight(height) im = cand.getMaskedImage() stamps.append(im) self.assertEqual(im.getWidth(), width) self.assertEqual(im.getHeight(), height) if display: mos = displayUtils.Mosaic() ds9.mtv(mos.makeMosaic(stamps), frame=1)
def testKernelPsf(self): """Test creating a Psf from a Kernel.""" x, y = 10.4999, 10.4999 ksize = 15 sigma1 = 1 # # Make a PSF from that kernel # kPsf = measAlg.KernelPsf( afwMath.AnalyticKernel(ksize, ksize, afwMath.GaussianFunction2D(sigma1, sigma1))) kIm = kPsf.computeImage(afwGeom.Point2D(x, y)) # # And now via the dgPsf model # dgPsf = measAlg.DoubleGaussianPsf(ksize, ksize, sigma1) dgIm = dgPsf.computeImage(afwGeom.Point2D(x, y)) # # Check that they're the same # diff = type(kIm)(kIm, True) diff -= dgIm stats = afwMath.makeStatistics(diff, afwMath.MAX | afwMath.MIN) self.assertAlmostEqual(stats.getValue(afwMath.MAX), 0.0, places=16) self.assertAlmostEqual(stats.getValue(afwMath.MIN), 0.0, places=16) for pad in [-2, 4, 0]: resizedKPsf = kPsf.resized(ksize + pad, ksize + pad) self.assertEqual(resizedKPsf.computeBBox().getDimensions(), afwGeom.Extent2I(ksize + pad, ksize + pad)) self.assertEqual(resizedKPsf.getKernel().getKernelParameters(), kPsf.getKernel().getKernelParameters()) self._compareKernelImages(kPsf, resizedKPsf) if display: mos = displayUtils.Mosaic() mos.setBackground(-0.1) ds9.mtv(mos.makeMosaic([kIm, dgIm, diff], mode="x"), frame=1)
def testCandidateList(self): self.assertFalse(self.cellSet.getCellList()[0].empty()) self.assertTrue(self.cellSet.getCellList()[1].empty()) self.assertFalse(self.cellSet.getCellList()[2].empty()) self.assertTrue(self.cellSet.getCellList()[3].empty()) stamps = [] for cell in self.cellSet.getCellList(): for cand in cell: cand = cell[0] width, height = 29, 25 cand.setWidth(width) cand.setHeight(height) im = cand.getMaskedImage() stamps.append(im) self.assertEqual(im.getWidth(), width) self.assertEqual(im.getHeight(), height) if False and display: mos = displayUtils.Mosaic() mos.makeMosaic(stamps, frame=2)
def testPcaNaN(self): """Test calculating PCA when the images can contain NaNs""" width, height = 20, 10 values = (100, 200, 300) for i, val in enumerate(values): im = afwImage.ImageF(afwGeom.Extent2I(width, height)) im.set(val) if i == 1: im.set(width // 2, height // 2, np.nan) self.ImageSet.addImage(im, 1.0) self.ImageSet.analyze() eImages = [] for img in self.ImageSet.getEigenImages(): eImages.append(img) if display: mos = displayUtils.Mosaic(background=-10) ds9.mtv(mos.makeMosaic(eImages), frame=1)
def testGetPcaKernel(self): """Convert our cellSet to a LinearCombinationKernel""" nEigenComponents = 2 spatialOrder = 1 kernelSize = 21 nStarPerCell = 2 nStarPerCellSpatialFit = 2 tolerance = 1e-5 if display: ds9.mtv(self.mi, frame=0) # # Show the candidates we're using # for cell in self.cellSet.getCellList(): i = 0 for cand in cell: i += 1 source = cand.getSource() xc, yc = source.getXAstrom() - self.mi.getX0( ), source.getYAstrom() - self.mi.getY0() if i <= nStarPerCell: ds9.dot("o", xc, yc, ctype=ds9.GREEN) else: ds9.dot("o", xc, yc, ctype=ds9.YELLOW) pair = algorithms.createKernelFromPsfCandidates( self.cellSet, self.exposure.getDimensions(), self.exposure.getXY0(), nEigenComponents, spatialOrder, kernelSize, nStarPerCell) kernel, eigenValues = pair[0], pair[1] del pair print("lambda", " ".join(["%g" % l for l in eigenValues])) pair = algorithms.fitSpatialKernelFromPsfCandidates( kernel, self.cellSet, nStarPerCellSpatialFit, tolerance) status, chi2 = pair[0], pair[1] del pair print("Spatial fit: %s chi^2 = %.2g" % (status, chi2)) psf = roundTripPsf(5, algorithms.PcaPsf(kernel)) # Hurrah! self.assertIsNotNone(psf.getKernel()) self.checkTablePersistence(psf) if display: # print psf.getKernel().toString() eImages = [] for k in psf.getKernel().getKernelList(): im = afwImage.ImageD(k.getDimensions()) k.computeImage(im, False) eImages.append(im) mos = displayUtils.Mosaic() frame = 3 ds9.mtv(mos.makeMosaic(eImages), frame=frame) ds9.dot("Eigen Images", 0, 0, frame=frame) # # Make a mosaic of PSF candidates # stamps = [] stampInfo = [] for cell in self.cellSet.getCellList(): for cand in cell: s = cand.getSource() im = cand.getMaskedImage() stamps.append(im) stampInfo.append("[%d 0x%x]" % (s.getId(), s.getFlagForDetection())) mos = displayUtils.Mosaic() frame = 1 ds9.mtv(mos.makeMosaic(stamps), frame=frame, lowOrderBits=True) for i in range(len(stampInfo)): ds9.dot(stampInfo[i], mos.getBBox(i).getX0(), mos.getBBox(i).getY0(), frame=frame, ctype=ds9.RED) psfImages = [] labels = [] if False: nx, ny = 3, 4 for iy in range(ny): for ix in range(nx): x = int((ix + 0.5) * self.mi.getWidth() / nx) y = int((iy + 0.5) * self.mi.getHeight() / ny) im = psf.getImage(x, y) psfImages.append(im.Factory(im, True)) labels.append("PSF(%d,%d)" % (int(x), int(y))) if True: print((x, y, "PSF parameters:", psf.getKernel().getKernelParameters())) else: nx, ny = 2, 2 for x, y in [(20, 20), (60, 20), (60, 210), (20, 210)]: im = psf.computeImage(afwGeom.PointD(x, y)) psfImages.append(im.Factory(im, True)) labels.append("PSF(%d,%d)" % (int(x), int(y))) if True: print(x, y, "PSF parameters:", psf.getKernel().getKernelParameters()) frame = 2 mos.makeMosaic(psfImages, frame=frame, mode=nx) mos.drawLabels(labels, frame=frame) if display: ds9.mtv(self.mi, frame=0) psfImages = [] labels = [] if False: nx, ny = 3, 4 for iy in range(ny): for ix in range(nx): x = int((ix + 0.5) * self.mi.getWidth() / nx) y = int((iy + 0.5) * self.mi.getHeight() / ny) algorithms.subtractPsf(psf, self.mi, x, y) else: nx, ny = 2, 2 for x, y in [(20, 20), (60, 20), (60, 210), (20, 210)]: if False: # Test subtraction with non-centered psfs x += 0.5 y -= 0.5 #algorithms.subtractPsf(psf, self.mi, x, y) ds9.mtv(self.mi, frame=1)
def showPsfMosaic(exposure, psf=None, nx=7, ny=None, showCenter=True, showEllipticity=False, showFwhm=False, stampSize=0, display=None, title=None): """Show a mosaic of Psf images. exposure may be an Exposure (optionally with PSF), or a tuple (width, height) If stampSize is > 0, the psf images will be trimmed to stampSize*stampSize """ scale = 1.0 if showFwhm: showEllipticity = True scale = 2*math.log(2) # convert sigma^2 to HWHM^2 for a Gaussian mos = displayUtils.Mosaic() try: # maybe it's a real Exposure width, height = exposure.getWidth(), exposure.getHeight() x0, y0 = exposure.getXY0() if not psf: psf = exposure.getPsf() except AttributeError: try: # OK, maybe a list [width, height] width, height = exposure[0], exposure[1] x0, y0 = 0, 0 except TypeError: # I guess not raise RuntimeError("Unable to extract width/height from object of type %s" % type(exposure)) if not ny: ny = int(nx*float(height)/width + 0.5) if not ny: ny = 1 centroidName = "SdssCentroid" shapeName = "base_SdssShape" schema = afwTable.SourceTable.makeMinimalSchema() schema.getAliasMap().set("slot_Centroid", centroidName) schema.getAliasMap().set("slot_Centroid_flag", centroidName+"_flag") control = measBase.SdssCentroidControl() centroider = measBase.SdssCentroidAlgorithm(control, centroidName, schema) sdssShape = measBase.SdssShapeControl() shaper = measBase.SdssShapeAlgorithm(sdssShape, shapeName, schema) table = afwTable.SourceTable.make(schema) table.defineCentroid(centroidName) table.defineShape(shapeName) bbox = None if stampSize > 0: w, h = psf.computeImage(lsst.geom.PointD(0, 0)).getDimensions() if stampSize <= w and stampSize <= h: bbox = lsst.geom.BoxI(lsst.geom.PointI((w - stampSize)//2, (h - stampSize)//2), lsst.geom.ExtentI(stampSize, stampSize)) centers = [] shapes = [] for iy in range(ny): for ix in range(nx): x = int(ix*(width-1)/(nx-1)) + x0 y = int(iy*(height-1)/(ny-1)) + y0 im = psf.computeImage(lsst.geom.PointD(x, y)).convertF() imPeak = psf.computePeak(lsst.geom.PointD(x, y)) im /= imPeak if bbox: im = im.Factory(im, bbox) lab = "PSF(%d,%d)" % (x, y) if False else "" mos.append(im, lab) exp = afwImage.makeExposure(afwImage.makeMaskedImage(im)) exp.setPsf(psf) w, h = im.getWidth(), im.getHeight() centerX = im.getX0() + w//2 centerY = im.getY0() + h//2 src = table.makeRecord() spans = afwGeom.SpanSet(exp.getBBox()) foot = afwDet.Footprint(spans) foot.addPeak(centerX, centerY, 1) src.setFootprint(foot) try: centroider.measure(src, exp) centers.append((src.getX() - im.getX0(), src.getY() - im.getY0())) shaper.measure(src, exp) shapes.append((src.getIxx(), src.getIxy(), src.getIyy())) except Exception: pass if not display: display = afwDisplay.Display() mos.makeMosaic(display=display, title=title if title else "Model Psf", mode=nx) if centers and display: with display.Buffering(): for i, (cen, shape) in enumerate(zip(centers, shapes)): bbox = mos.getBBox(i) xc, yc = cen[0] + bbox.getMinX(), cen[1] + bbox.getMinY() if showCenter: display.dot("+", xc, yc, ctype=afwDisplay.BLUE) if showEllipticity: ixx, ixy, iyy = shape ixx *= scale ixy *= scale iyy *= scale display.dot("@:%g,%g,%g" % (ixx, ixy, iyy), xc, yc, ctype=afwDisplay.RED) return mos
def showPsf(psf, set, ext=None, wcsData=None, trim=0, nspot=5, diagnostics=False, outDir="", frame=None, title=None): """Show a PSF on ds9""" if ext is not None: psf = psf[ext] if wcsData: if ext is not None: wcsData = wcsData[ext] wcs, naxis1, naxis2 = wcsData else: wcs, naxis1, naxis2 = None, None, None naxis = [naxis1, naxis2] for i in range(2): if naxis[i] is None: # cmin, cmax are the range of input star positions cmin, cmax = [set.getContextOffset(i) + d*set.getContextScale(i) for d in (-0.5, 0.5)] naxis[i] = cmax + cmin # a decent guess import lsst.afw.display.utils as ds9Utils if naxis[0] > naxis[1]: nx, ny = int(nspot*naxis[0]/float(naxis[1]) + 0.5), nspot else: nx, ny = nspot, int(nspot*naxis[1]/float(naxis[0]) + 0.5) mos = ds9Utils.Mosaic(gutter=2, background=0.02) xpos, ypos = np.linspace(0, naxis[0], nx), np.linspace(0, naxis[1], ny) for y in ypos: for x in xpos: psf.build(x, y) im = afwImage.ImageF(*psf.getLoc().shape) im.getArray()[:] = psf.getLoc() im /= float(im.getArray().max()) if trim: if trim > im.getHeight()//2: trim = im.getHeight()//2 im = im[trim:-trim, trim:-trim] mos.append(im) mosaic = mos.makeMosaic(mode=nx) if frame is not None: ds9.mtv(mosaic, frame=frame, title=title) # # Figure out the WCS for the mosaic # pos = [] if False: for x, y, i in zip((xpos[0], xpos[-1]), (ypos[0], ypos[-1]), (0, mos.nImage - 1)): bbox = mos.getBBox(i) mosx = bbox.getMinX() + 0.5*(bbox.getWidth() - 1) mosy = bbox.getMinY() + 0.5*(bbox.getHeight() - 1) pos.append([afwGeom.PointD(mosx, mosy), wcs.pixelToSky(afwGeom.PointD(x, y))]) else: pos.append([afwGeom.PointD(0, 0), wcs.pixelToSky(afwGeom.PointD(0, 0))]) pos.append([afwGeom.PointD(*mosaic.getDimensions()), wcs.pixelToSky(afwGeom.PointD(naxis1, naxis2))]) CD = [] for i in range(2): delta = pos[1][1][i].asDegrees() - pos[0][1][i].asDegrees() CD.append(delta/(pos[1][0][i] - pos[0][0][i])) CD = np.array(CD) CD.shape = (2, 2) mosWcs = afwGeom.makeSkyWcs(crval=pos[0][0], crpix=pos[0][1], cdMatrix=CD) if ext is not None: title = "%s-%d" % (title, ext) if frame is not None: ds9.mtv(mosaic, frame=frame, title=title, wcs=mosWcs) if diagnostics: outFile = "%s-mod.fits" % title if outDir: outFile = os.path.join(outDir, outFile) mosaic.writeFits(outFile, mosWcs.getFitsMetadata()) #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- mos = ds9Utils.Mosaic(gutter=4, background=0.002) for i in range(set.getNsample()): s = set.getSample(i) if ext is not None and s.getExtindex() != ext: continue smos = ds9Utils.Mosaic(gutter=2, background=-0.003) for func in [s.getVig, s.getVigResi]: arr = func() if func == s.getVig: norm = float(arr.max()) if True else s.getNorm() arr /= norm im = afwImage.ImageF(*arr.shape) im.getArray()[:] = arr smos.append(im) mos.append(smos.makeMosaic(mode="x")) mosaic = mos.makeMosaic(title=title) if frame is not None: ds9.mtv(mosaic, title=title, frame=frame+1) if diagnostics: outFile = "%s-psfstars.fits" % title if outDir: outFile = os.path.join(outDir, outFile) mosaic.writeFits(outFile)
def showPsfCandidates(exposure, psfCellSet, psf=None, display=None, normalize=True, showBadCandidates=True, fitBasisComponents=False, variance=None, chi=None): """Display the PSF candidates. If psf is provided include PSF model and residuals; if normalize is true normalize the PSFs (and residuals) If chi is True, generate a plot of residuals/sqrt(variance), i.e. chi If fitBasisComponents is true, also find the best linear combination of the PSF's components (if they exist) """ if not display: display = afwDisplay.Display() if chi is None: if variance is not None: # old name for chi chi = variance # # Show us the ccandidates # mos = displayUtils.Mosaic() # candidateCenters = [] candidateCentersBad = [] candidateIndex = 0 for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # include bad candidates rchi2 = cand.getChi2() if rchi2 > 1e100: rchi2 = numpy.nan if not showBadCandidates and cand.isBad(): continue if psf: im_resid = displayUtils.Mosaic(gutter=0, background=-5, mode="x") try: im = cand.getMaskedImage() # copy of this object's image xc, yc = cand.getXCenter(), cand.getYCenter() margin = 0 if True else 5 w, h = im.getDimensions() bbox = lsst.geom.BoxI(lsst.geom.PointI(margin, margin), im.getDimensions()) if margin > 0: bim = im.Factory(w + 2*margin, h + 2*margin) stdev = numpy.sqrt(afwMath.makeStatistics(im.getVariance(), afwMath.MEAN).getValue()) afwMath.randomGaussianImage(bim.getImage(), afwMath.Random()) bim.getVariance().set(stdev**2) bim.assign(im, bbox) im = bim xc += margin yc += margin im = im.Factory(im, True) im.setXY0(cand.getMaskedImage().getXY0()) except Exception: continue if not variance: im_resid.append(im.Factory(im, True)) if True: # tweak up centroids mi = im psfIm = mi.getImage() config = measBase.SingleFrameMeasurementTask.ConfigClass() config.slots.centroid = "base_SdssCentroid" schema = afwTable.SourceTable.makeMinimalSchema() measureSources = measBase.SingleFrameMeasurementTask(schema, config=config) catalog = afwTable.SourceCatalog(schema) extra = 10 # enough margin to run the sdss centroider miBig = mi.Factory(im.getWidth() + 2*extra, im.getHeight() + 2*extra) miBig[extra:-extra, extra:-extra, afwImage.LOCAL] = mi miBig.setXY0(mi.getX0() - extra, mi.getY0() - extra) mi = miBig del miBig exp = afwImage.makeExposure(mi) exp.setPsf(psf) footprintSet = afwDet.FootprintSet(mi, afwDet.Threshold(0.5*numpy.max(psfIm.getArray())), "DETECTED") footprintSet.makeSources(catalog) if len(catalog) == 0: raise RuntimeError("Failed to detect any objects") measureSources.run(catalog, exp) if len(catalog) == 1: source = catalog[0] else: # more than one source; find the once closest to (xc, yc) dmin = None # an invalid value to catch logic errors for i, s in enumerate(catalog): d = numpy.hypot(xc - s.getX(), yc - s.getY()) if i == 0 or d < dmin: source, dmin = s, d xc, yc = source.getCentroid() # residuals using spatial model try: subtractPsf(psf, im, xc, yc) except Exception: continue resid = im if variance: resid = resid.getImage() var = im.getVariance() var = var.Factory(var, True) numpy.sqrt(var.getArray(), var.getArray()) # inplace sqrt resid /= var im_resid.append(resid) # Fit the PSF components directly to the data (i.e. ignoring the spatial model) if fitBasisComponents: im = cand.getMaskedImage() im = im.Factory(im, True) im.setXY0(cand.getMaskedImage().getXY0()) try: noSpatialKernel = psf.getKernel() except Exception: noSpatialKernel = None if noSpatialKernel: candCenter = lsst.geom.PointD(cand.getXCenter(), cand.getYCenter()) fit = fitKernelParamsToImage(noSpatialKernel, im, candCenter) params = fit[0] kernels = afwMath.KernelList(fit[1]) outputKernel = afwMath.LinearCombinationKernel(kernels, params) outImage = afwImage.ImageD(outputKernel.getDimensions()) outputKernel.computeImage(outImage, False) im -= outImage.convertF() resid = im if margin > 0: bim = im.Factory(w + 2*margin, h + 2*margin) afwMath.randomGaussianImage(bim.getImage(), afwMath.Random()) bim *= stdev bim.assign(resid, bbox) resid = bim if variance: resid = resid.getImage() resid /= var im_resid.append(resid) im = im_resid.makeMosaic() else: im = cand.getMaskedImage() if normalize: im /= afwMath.makeStatistics(im, afwMath.MAX).getValue() objId = splitId(cand.getSource().getId(), True)["objId"] if psf: lab = "%d chi^2 %.1f" % (objId, rchi2) ctype = afwDisplay.RED if cand.isBad() else afwDisplay.GREEN else: lab = "%d flux %8.3g" % (objId, cand.getSource().getPsfInstFlux()) ctype = afwDisplay.GREEN mos.append(im, lab, ctype) if False and numpy.isnan(rchi2): display.mtv(cand.getMaskedImage().getImage(), title="showPsfCandidates: candidate") print("amp", cand.getAmplitude()) im = cand.getMaskedImage() center = (candidateIndex, xc - im.getX0(), yc - im.getY0()) candidateIndex += 1 if cand.isBad(): candidateCentersBad.append(center) else: candidateCenters.append(center) if variance: title = "chi(Psf fit)" else: title = "Stars & residuals" mosaicImage = mos.makeMosaic(display=display, title=title) with display.Buffering(): for centers, color in ((candidateCenters, afwDisplay.GREEN), (candidateCentersBad, afwDisplay.RED)): for cen in centers: bbox = mos.getBBox(cen[0]) display.dot("+", cen[1] + bbox.getMinX(), cen[2] + bbox.getMinY(), ctype=color) return mosaicImage
spatialBg = results.backgroundModel kernelCellSet = results.kernelCellSet except Exception as e: print('FAIL') sys.exit(1) if False: print(spatialKernel.getSpatialFunctionList()[0].toString()) print(spatialKernel.getKernelParameters()) print(spatialKernel.getSpatialParameters()) import pdb pdb.set_trace() # Lets see what we got if display: mos = displayUtils.Mosaic() # Inputs for cell in kernelCellSet.getCellList(): for cand in cell.begin(False): # False = include bad candidates rchi2 = cand.getChi2() # No kernels made if cand.getStatus() == afwMath.SpatialCellCandidate.UNKNOWN: continue try: im = cand.getKernelImage(ipDiffim.KernelCandidateF.RECENT) except Exception: continue
def makeDeblendFamilyMosaic(mi, parent, kids, mapperInfo=None, background=-10, maskbit=False, imBbox=None): """Create a mosaic of an object's children """ aa = {} if maskbit: aa.update(mask=True) parent_im = footprintToImage(parent.getFootprint(), mi, **aa) bbox = afwGeom.BoxD(parent.getFootprint().getBBox()) pext = (bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox.getMaxY()) pks = parent.getFootprint().getPeaks() pix = [pk.getIx() for pk in pks] piy = [pk.getIy() for pk in pks] pfx = [pk.getFx() for pk in pks] pfy = [pk.getFy() for pk in pks] N = 1 + len(kids) S = np.ceil(np.sqrt(N)) C = S R = np.ceil(float(N) / C) Rx, Ry = [], [] tts = [] stys = [] xys = [] # # Find how large an image we need to display the parent and all the children # kidImages, kim = {}, None for kid in kids: kim = footprintToImage(kid.getFootprint(), mi, **aa) kidImages[kid] = kim if not kim: kim = parent_im.clone() if not imBbox: imBbox = parent_im.getBBox(afwImage.PARENT) for kid in kids: imBbox.include(kidImages[kid].getBBox(afwImage.PARENT)) mos = displayUtils.Mosaic(background=background) bbox = afwGeom.Box2I( afwGeom.Point2I(kim.getX0() - imBbox.getMinX(), kim.getY0() - imBbox.getMinY()), kim.getDimensions()) kidImages[parent] = parent_im # not strictly a kid for kid in [parent] + kids: kim = kidImages[kid] # # Put the child into the correct place in the parent image. We have to do this for # the parent too if some of the children extended outside its BBox # bbox = afwGeom.Box2I( afwGeom.Point2I(kim.getX0() - imBbox.getMinX(), kim.getY0() - imBbox.getMinY()), kim.getDimensions()) _kim = parent_im.Factory(imBbox) if kid.getFootprint().getBBox().isEmpty(): _kim[:] = 0 pass else: _kim[bbox] = kim mos.append( _kim, '%d%s' % (mapperInfo.getId(kid) if mapperInfo else (kid.getId() & 0xfff), "P" if kid == parent else "C")) del _kim return mos
def showKernelMosaic(bbox, kernel, nx=7, ny=None, frame=None, title=None, showCenter=True, showEllipticity=True): """Show a mosaic of Kernel images. """ mos = displayUtils.Mosaic() x0 = bbox.getBeginX() y0 = bbox.getBeginY() width = bbox.getWidth() height = bbox.getHeight() if not ny: ny = int(nx * float(height) / width + 0.5) if not ny: ny = 1 schema = afwTable.SourceTable.makeMinimalSchema() centroidName = "base_SdssCentroid" shapeName = "base_SdssShape" control = measBase.SdssCentroidControl() schema.getAliasMap().set("slot_Centroid", centroidName) schema.getAliasMap().set("slot_Centroid_flag", centroidName + "_flag") centroider = measBase.SdssCentroidAlgorithm(control, centroidName, schema) sdssShape = measBase.SdssShapeControl() shaper = measBase.SdssShapeAlgorithm(sdssShape, shapeName, schema) table = afwTable.SourceTable.make(schema) table.defineCentroid(centroidName) table.defineShape(shapeName) centers = [] shapes = [] for iy in range(ny): for ix in range(nx): x = int(ix * (width - 1) / (nx - 1)) + x0 y = int(iy * (height - 1) / (ny - 1)) + y0 im = afwImage.ImageD(kernel.getDimensions()) ksum = kernel.computeImage(im, False, x, y) lab = "Kernel(%d,%d)=%.2f" % (x, y, ksum) if False else "" mos.append(im, lab) # SdssCentroidAlgorithm.measure requires an exposure of floats exp = afwImage.makeExposure(afwImage.makeMaskedImage( im.convertF())) w, h = im.getWidth(), im.getHeight() centerX = im.getX0() + w // 2 centerY = im.getY0() + h // 2 src = table.makeRecord() foot = afwDet.Footprint(exp.getBBox()) foot.addPeak(centerX, centerY, 1) src.setFootprint(foot) centroider.measure(src, exp) centers.append((src.getX(), src.getY())) shaper.measure(src, exp) shapes.append((src.getIxx(), src.getIxy(), src.getIyy())) mos.makeMosaic(frame=frame, title=title if title else "Model Kernel", mode=nx) if centers and frame is not None: i = 0 with ds9.Buffering(): for cen, shape in zip(centers, shapes): bbox = mos.getBBox(i) i += 1 xc, yc = cen[0] + bbox.getMinX(), cen[1] + bbox.getMinY() if showCenter: ds9.dot("+", xc, yc, ctype=ds9.BLUE, frame=frame) if showEllipticity: ixx, ixy, iyy = shape ds9.dot("@:%g,%g,%g" % (ixx, ixy, iyy), xc, yc, frame=frame, ctype=ds9.RED) return mos
def showPsfMosaic(exposure, psf=None, nx=7, ny=None, showCenter=True, showEllipticity=False, showFwhm=False, stampSize=0, frame=None, title=None): """Show a mosaic of Psf images. exposure may be an Exposure (optionally with PSF), or a tuple (width, height) If stampSize is > 0, the psf images will be trimmed to stampSize*stampSize """ scale = 1.0 if showFwhm: showEllipticity = True scale = 2*math.log(2) # convert sigma^2 to HWHM^2 for a Gaussian mos = displayUtils.Mosaic() try: # maybe it's a real Exposure width, height = exposure.getWidth(), exposure.getHeight() x0, y0 = exposure.getXY0() if not psf: psf = exposure.getPsf() except AttributeError: try: # OK, maybe a list [width, height] width, height = exposure[0], exposure[1] x0, y0 = 0, 0 except TypeError: # I guess not raise RuntimeError, ("Unable to extract width/height from object of type %s" % type(exposure)) if not ny: ny = int(nx*float(height)/width + 0.5) if not ny: ny = 1 schema = afwTable.SourceTable.makeMinimalSchema() control = algorithmsLib.GaussianCentroidControl() centroider = algorithmsLib.MeasureSourcesBuilder().addAlgorithm(control).build(schema) sdssShape = algorithmsLib.SdssShapeControl() shaper = algorithmsLib.MeasureSourcesBuilder().addAlgorithm(sdssShape).build(schema) table = afwTable.SourceTable.make(schema) table.defineCentroid(control.name) table.defineShape(sdssShape.name) bbox = None if stampSize > 0: w, h = psf.computeImage(afwGeom.PointD(0, 0)).getDimensions() if stampSize <= w and stampSize <= h: bbox = afwGeom.BoxI(afwGeom.PointI((w - stampSize)//2, (h - stampSize)//2), afwGeom.ExtentI(stampSize, stampSize)) centers = [] shapes = [] for iy in range(ny): for ix in range(nx): x = int(ix*(width-1)/(nx-1)) + x0 y = int(iy*(height-1)/(ny-1)) + y0 im = psf.computeImage(afwGeom.PointD(x, y)).convertF() imPeak = psf.computePeak(afwGeom.PointD(x, y)) im /= imPeak if bbox: im = im.Factory(im, bbox) lab = "PSF(%d,%d)" % (x, y) if False else "" mos.append(im, lab) exp = afwImage.makeExposure(afwImage.makeMaskedImage(im)) w, h = im.getWidth(), im.getHeight() cen = afwGeom.PointD(im.getX0() + w//2, im.getY0() + h//2) src = table.makeRecord() foot = afwDet.Footprint(exp.getBBox()) src.setFootprint(foot) centroider.apply(src, exp, cen) centers.append((src.getX() - im.getX0(), src.getY() - im.getY0())) shaper.apply(src, exp, cen) shapes.append((src.getIxx(), src.getIxy(), src.getIyy())) mos.makeMosaic(frame=frame, title=title if title else "Model Psf", mode=nx) if centers and frame is not None: i = 0 with ds9.Buffering(): for cen, shape in zip(centers, shapes): bbox = mos.getBBox(i); i += 1 xc, yc = cen[0] + bbox.getMinX(), cen[1] + bbox.getMinY() if showCenter: ds9.dot("+", xc, yc, ctype=ds9.BLUE, frame=frame) if showEllipticity: ixx, ixy, iyy = shape ixx *= scale; ixy *= scale; iyy *= scale ds9.dot("@:%g,%g,%g" % (ixx, ixy, iyy), xc, yc, frame=frame, ctype=ds9.RED) return mos
class PcaPsfDeterminer(object): """! A measurePsfTask psf estimator """ ConfigClass = PcaPsfDeterminerConfig def __init__(self, config): """!Construct a PCA PSF Fitter \param[in] config instance of PcaPsfDeterminerConfig """ self.config = config # N.b. name of component is meas.algorithms.psfDeterminer so you can turn on psf debugging # independent of which determiner is active self.debugLog = pexLog.Debug("meas.algorithms.psfDeterminer") self.warnLog = pexLog.Log(pexLog.getDefaultLog(), "meas.algorithms.psfDeterminer") def _fitPsf(self, exposure, psfCellSet, kernelSize, nEigenComponents): algorithmsLib.PsfCandidateF.setPixelThreshold( self.config.pixelThreshold) algorithmsLib.PsfCandidateF.setMaskBlends(self.config.doMaskBlends) # # Loop trying to use nEigenComponents, but allowing smaller numbers if necessary # for nEigen in range(nEigenComponents, 0, -1): # Determine KL components try: kernel, eigenValues = algorithmsLib.createKernelFromPsfCandidates( psfCellSet, exposure.getDimensions(), exposure.getXY0(), nEigen, self.config.spatialOrder, kernelSize, self.config.nStarPerCell, bool(self.config.constantWeight)) break # OK, we can get nEigen components except pexExceptions.LengthError as e: if nEigen == 1: # can't go any lower raise IndexError("No viable PSF candidates survive") self.warnLog.log( pexLog.Log.WARN, "%s: reducing number of eigen components" % e.what()) # # We got our eigen decomposition so let's use it # # Express eigenValues in units of reduced chi^2 per star size = kernelSize + 2 * self.config.borderWidth nu = size * size - 1 # number of degrees of freedom/star for chi^2 eigenValues = [ l / float( algorithmsLib.countPsfCandidates( psfCellSet, self.config.nStarPerCell) * nu) for l in eigenValues ] # Fit spatial model status, chi2 = algorithmsLib.fitSpatialKernelFromPsfCandidates( kernel, psfCellSet, bool(self.config.nonLinearSpatialFit), self.config.nStarPerCellSpatialFit, self.config.tolerance, self.config.lam) psf = algorithmsLib.PcaPsf(kernel) return psf, eigenValues, nEigen, chi2 def determinePsf(self, exposure, psfCandidateList, metadata=None, flagKey=None): """!Determine a PCA PSF model for an exposure given a list of PSF candidates \param[in] exposure exposure containing the psf candidates (lsst.afw.image.Exposure) \param[in] psfCandidateList a sequence of PSF candidates (each an lsst.meas.algorithms.PsfCandidate); typically obtained by detecting sources and then running them through a star selector \param[in,out] metadata a home for interesting tidbits of information \param[in] flagKey schema key used to mark sources actually used in PSF determination \return a list of - psf: the measured PSF, an lsst.meas.algorithms.PcaPsf - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates """ import lsstDebug display = lsstDebug.Info(__name__).display displayExposure = lsstDebug.Info( __name__).displayExposure # display the Exposure + spatialCells displayPsfCandidates = lsstDebug.Info( __name__).displayPsfCandidates # show the viable candidates displayIterations = lsstDebug.Info( __name__).displayIterations # display on each PSF iteration displayPsfComponents = lsstDebug.Info( __name__).displayPsfComponents # show the PCA components displayResiduals = lsstDebug.Info( __name__).displayResiduals # show residuals displayPsfMosaic = lsstDebug.Info( __name__).displayPsfMosaic # show mosaic of reconstructed PSF(x,y) matchKernelAmplitudes = lsstDebug.Info( __name__).matchKernelAmplitudes # match Kernel amplitudes # for spatial plots keepMatplotlibPlots = lsstDebug.Info( __name__).keepMatplotlibPlots # Keep matplotlib alive # post mortem displayPsfSpatialModel = lsstDebug.Info( __name__).displayPsfSpatialModel # Plot spatial model? showBadCandidates = lsstDebug.Info( __name__).showBadCandidates # Include bad candidates normalizeResiduals = lsstDebug.Info( __name__).normalizeResiduals # Normalise residuals by # object amplitude pause = lsstDebug.Info( __name__).pause # Prompt user after each iteration? if display > 1: pause = True mi = exposure.getMaskedImage() if len(psfCandidateList) == 0: raise RuntimeError("No PSF candidates supplied.") # construct and populate a spatial cell set bbox = mi.getBBox() psfCellSet = afwMath.SpatialCellSet(bbox, self.config.sizeCellX, self.config.sizeCellY) sizes = [] for i, psfCandidate in enumerate(psfCandidateList): if psfCandidate.getSource().getPsfFluxFlag(): # bad measurement continue try: psfCellSet.insertCandidate(psfCandidate) except Exception, e: self.debugLog.debug( 2, "Skipping PSF candidate %d of %d: %s" % (i, len(psfCandidateList), e)) continue source = psfCandidate.getSource() quad = afwEll.Quadrupole(source.getIxx(), source.getIyy(), source.getIxy()) axes = afwEll.Axes(quad) sizes.append(axes.getA()) if len(sizes) == 0: raise RuntimeError("No usable PSF candidates supplied") nEigenComponents = self.config.nEigenComponents # initial version if self.config.kernelSize >= 15: self.debugLog.debug(1, \ "WARNING: NOT scaling kernelSize by stellar quadrupole moment " + "because config.kernelSize=%s >= 15; using config.kernelSize as as the width, instead" \ % (self.config.kernelSize,) ) actualKernelSize = int(self.config.kernelSize) else: medSize = numpy.median(sizes) actualKernelSize = 2 * int(self.config.kernelSize * math.sqrt(medSize) + 0.5) + 1 if actualKernelSize < self.config.kernelSizeMin: actualKernelSize = self.config.kernelSizeMin if actualKernelSize > self.config.kernelSizeMax: actualKernelSize = self.config.kernelSizeMax if display: print "Median size=%s" % (medSize, ) self.debugLog.debug(3, "Kernel size=%s" % (actualKernelSize, )) # Set size of image returned around candidate psfCandidateList[0].setHeight(actualKernelSize) psfCandidateList[0].setWidth(actualKernelSize) if self.config.doRejectBlends: # Remove blended candidates completely blendedCandidates = [ ] # Candidates to remove; can't do it while iterating for cell, cand in candidatesIter(psfCellSet, False): if len(cand.getSource().getFootprint().getPeaks()) > 1: blendedCandidates.append((cell, cand)) continue if display: print "Removing %d blended Psf candidates" % len( blendedCandidates) for cell, cand in blendedCandidates: cell.removeCandidate(cand) if sum(1 for cand in candidatesIter(psfCellSet, False)) == 0: raise RuntimeError("All PSF candidates removed as blends") if display: frame = 0 if displayExposure: ds9.mtv(exposure, frame=frame, title="psf determination") maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, symb="o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, size=4, frame=frame) # # Do a PCA decomposition of those PSF candidates # reply = "y" # used in interactive mode for iter in range(self.config.nIterForPsf): if display and displayPsfCandidates: # Show a mosaic of usable PSF candidates # import lsst.afw.display.utils as displayUtils stamps = [] for cell in psfCellSet.getCellList(): for cand in cell.begin(not showBadCandidates ): # maybe include bad candidates cand = algorithmsLib.cast_PsfCandidateF(cand) try: im = cand.getMaskedImage() chi2 = cand.getChi2() if chi2 > 1e100: chi2 = numpy.nan stamps.append( (im, "%d%s" % (maUtils.splitId(cand.getSource().getId(), True)["objId"], chi2), cand.getStatus())) except Exception, e: continue if len(stamps) == 0: print "WARNING: No PSF candidates to show; try setting showBadCandidates=True" else: mos = displayUtils.Mosaic() for im, label, status in stamps: im = type(im)(im, True) try: im /= afwMath.makeStatistics( im, afwMath.MAX).getValue() except NotImplementedError: pass mos.append( im, label, ds9.GREEN if status == afwMath.SpatialCellCandidate.GOOD else ds9.YELLOW if status == afwMath.SpatialCellCandidate.UNKNOWN else ds9.RED) mos.makeMosaic(frame=8, title="Psf Candidates") # Re-fit until we don't have any candidates with naughty chi^2 values influencing the fit cleanChi2 = False # Any naughty (negative/NAN) chi^2 values? while not cleanChi2: cleanChi2 = True # # First, estimate the PSF # psf, eigenValues, nEigenComponents, fitChi2 = \ self._fitPsf(exposure, psfCellSet, actualKernelSize, nEigenComponents) # # In clipping, allow all candidates to be innocent until proven guilty on this iteration. # Throw out any prima facie guilty candidates (naughty chi^2 values) # for cell in psfCellSet.getCellList(): awfulCandidates = [] for cand in cell.begin(False): # include bad candidates cand = algorithmsLib.cast_PsfCandidateF(cand) cand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN ) # until proven guilty rchi2 = cand.getChi2() if not numpy.isfinite(rchi2) or rchi2 <= 0: # Guilty prima facie awfulCandidates.append(cand) cleanChi2 = False self.debugLog.debug( 2, "chi^2=%s; id=%s" % (cand.getChi2(), cand.getSource().getId())) for cand in awfulCandidates: if display: print "Removing bad candidate: id=%d, chi^2=%f" % \ (cand.getSource().getId(), cand.getChi2()) cell.removeCandidate(cand) # # Clip out bad fits based on reduced chi^2 # badCandidates = list() for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # include bad candidates cand = algorithmsLib.cast_PsfCandidateF(cand) rchi2 = cand.getChi2( ) # reduced chi^2 when fitting PSF to candidate assert rchi2 > 0 if rchi2 > self.config.reducedChi2ForPsfCandidates: badCandidates.append(cand) badCandidates.sort(key=lambda x: x.getChi2(), reverse=True) numBad = int( len(badCandidates) * (iter + 1) / self.config.nIterForPsf + 0.5) for i, c in zip(range(numBad), badCandidates): if display: chi2 = c.getChi2() if chi2 > 1e100: chi2 = numpy.nan print "Chi^2 clipping %-4d %.2g" % (c.getSource().getId(), chi2) c.setStatus(afwMath.SpatialCellCandidate.BAD) # # Clip out bad fits based on spatial fitting. # # This appears to be better at getting rid of sources that have a single dominant kernel component # (other than the zeroth; e.g., a nearby contaminant) because the surrounding sources (which help # set the spatial model) don't contain that kernel component, and so the spatial modeling # downweights the component. # residuals = list() candidates = list() kernel = psf.getKernel() noSpatialKernel = afwMath.cast_LinearCombinationKernel( psf.getKernel()) for cell in psfCellSet.getCellList(): for cand in cell.begin(False): cand = algorithmsLib.cast_PsfCandidateF(cand) candCenter = afwGeom.PointD(cand.getXCenter(), cand.getYCenter()) try: im = cand.getMaskedImage(kernel.getWidth(), kernel.getHeight()) except Exception, e: continue fit = algorithmsLib.fitKernelParamsToImage( noSpatialKernel, im, candCenter) params = fit[0] kernels = fit[1] amp = 0.0 for p, k in zip(params, kernels): amp += p * afwMath.cast_FixedKernel(k).getSum() predict = [ kernel.getSpatialFunction(k)(candCenter.getX(), candCenter.getY()) for k in range(kernel.getNKernelParameters()) ] #print cand.getSource().getId(), [a / amp for a in params], predict residuals.append( [a / amp - p for a, p in zip(params, predict)]) candidates.append(cand)
def determinePsf(self, exposure, psfCandidateList, metadata=None, flagKey=None): """!Determine a PCA PSF model for an exposure given a list of PSF candidates \param[in] exposure exposure containing the psf candidates (lsst.afw.image.Exposure) \param[in] psfCandidateList a sequence of PSF candidates (each an lsst.meas.algorithms.PsfCandidate); typically obtained by detecting sources and then running them through a star selector \param[in,out] metadata a home for interesting tidbits of information \param[in] flagKey schema key used to mark sources actually used in PSF determination \return a list of - psf: the measured PSF, an lsst.meas.algorithms.PcaPsf - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates """ import lsstDebug display = lsstDebug.Info(__name__).display displayExposure = lsstDebug.Info( __name__).displayExposure # display the Exposure + spatialCells displayPsfCandidates = lsstDebug.Info( __name__).displayPsfCandidates # show the viable candidates displayIterations = lsstDebug.Info( __name__).displayIterations # display on each PSF iteration displayPsfComponents = lsstDebug.Info( __name__).displayPsfComponents # show the PCA components displayResiduals = lsstDebug.Info( __name__).displayResiduals # show residuals displayPsfMosaic = lsstDebug.Info( __name__).displayPsfMosaic # show mosaic of reconstructed PSF(x,y) # match Kernel amplitudes for spatial plots matchKernelAmplitudes = lsstDebug.Info(__name__).matchKernelAmplitudes # Keep matplotlib alive post mortem keepMatplotlibPlots = lsstDebug.Info(__name__).keepMatplotlibPlots displayPsfSpatialModel = lsstDebug.Info( __name__).displayPsfSpatialModel # Plot spatial model? showBadCandidates = lsstDebug.Info( __name__).showBadCandidates # Include bad candidates # Normalize residuals by object amplitude normalizeResiduals = lsstDebug.Info(__name__).normalizeResiduals pause = lsstDebug.Info( __name__).pause # Prompt user after each iteration? if display > 1: pause = True mi = exposure.getMaskedImage() if len(psfCandidateList) == 0: raise RuntimeError("No PSF candidates supplied.") # construct and populate a spatial cell set bbox = mi.getBBox() psfCellSet = afwMath.SpatialCellSet(bbox, self.config.sizeCellX, self.config.sizeCellY) sizes = [] for i, psfCandidate in enumerate(psfCandidateList): if psfCandidate.getSource().getPsfFluxFlag(): # bad measurement continue try: psfCellSet.insertCandidate(psfCandidate) except Exception as e: self.log.debug("Skipping PSF candidate %d of %d: %s", i, len(psfCandidateList), e) continue source = psfCandidate.getSource() quad = afwEll.Quadrupole(source.getIxx(), source.getIyy(), source.getIxy()) axes = afwEll.Axes(quad) sizes.append(axes.getA()) if len(sizes) == 0: raise RuntimeError("No usable PSF candidates supplied") nEigenComponents = self.config.nEigenComponents # initial version if self.config.kernelSize >= 15: self.log.warn( "WARNING: NOT scaling kernelSize by stellar quadrupole moment " + "because config.kernelSize=%s >= 15; using config.kernelSize as as the width, instead", self.config.kernelSize) actualKernelSize = int(self.config.kernelSize) else: medSize = numpy.median(sizes) actualKernelSize = 2 * int(self.config.kernelSize * math.sqrt(medSize) + 0.5) + 1 if actualKernelSize < self.config.kernelSizeMin: actualKernelSize = self.config.kernelSizeMin if actualKernelSize > self.config.kernelSizeMax: actualKernelSize = self.config.kernelSizeMax if display: print("Median size=%s" % (medSize, )) self.log.trace("Kernel size=%s", actualKernelSize) # Set size of image returned around candidate psfCandidateList[0].setHeight(actualKernelSize) psfCandidateList[0].setWidth(actualKernelSize) if self.config.doRejectBlends: # Remove blended candidates completely blendedCandidates = [ ] # Candidates to remove; can't do it while iterating for cell, cand in candidatesIter(psfCellSet, False): if len(cand.getSource().getFootprint().getPeaks()) > 1: blendedCandidates.append((cell, cand)) continue if display: print("Removing %d blended Psf candidates" % len(blendedCandidates)) for cell, cand in blendedCandidates: cell.removeCandidate(cand) if sum(1 for cand in candidatesIter(psfCellSet, False)) == 0: raise RuntimeError("All PSF candidates removed as blends") if display: frame = 0 if displayExposure: ds9.mtv(exposure, frame=frame, title="psf determination") maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, symb="o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, size=4, frame=frame) # # Do a PCA decomposition of those PSF candidates # reply = "y" # used in interactive mode for iterNum in range(self.config.nIterForPsf): if display and displayPsfCandidates: # Show a mosaic of usable PSF candidates # import lsst.afw.display.utils as displayUtils stamps = [] for cell in psfCellSet.getCellList(): for cand in cell.begin(not showBadCandidates ): # maybe include bad candidates try: im = cand.getMaskedImage() chi2 = cand.getChi2() if chi2 > 1e100: chi2 = numpy.nan stamps.append( (im, "%d%s" % (maUtils.splitId(cand.getSource().getId(), True)["objId"], chi2), cand.getStatus())) except Exception as e: continue if len(stamps) == 0: print( "WARNING: No PSF candidates to show; try setting showBadCandidates=True" ) else: mos = displayUtils.Mosaic() for im, label, status in stamps: im = type(im)(im, True) try: im /= afwMath.makeStatistics( im, afwMath.MAX).getValue() except NotImplementedError: pass mos.append( im, label, ds9.GREEN if status == afwMath.SpatialCellCandidate.GOOD else ds9.YELLOW if status == afwMath.SpatialCellCandidate.UNKNOWN else ds9.RED) mos.makeMosaic(frame=8, title="Psf Candidates") # Re-fit until we don't have any candidates with naughty chi^2 values influencing the fit cleanChi2 = False # Any naughty (negative/NAN) chi^2 values? while not cleanChi2: cleanChi2 = True # # First, estimate the PSF # psf, eigenValues, nEigenComponents, fitChi2 = \ self._fitPsf(exposure, psfCellSet, actualKernelSize, nEigenComponents) # # In clipping, allow all candidates to be innocent until proven guilty on this iteration. # Throw out any prima facie guilty candidates (naughty chi^2 values) # for cell in psfCellSet.getCellList(): awfulCandidates = [] for cand in cell.begin(False): # include bad candidates cand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN ) # until proven guilty rchi2 = cand.getChi2() if not numpy.isfinite(rchi2) or rchi2 <= 0: # Guilty prima facie awfulCandidates.append(cand) cleanChi2 = False self.log.debug("chi^2=%s; id=%s", cand.getChi2(), cand.getSource().getId()) for cand in awfulCandidates: if display: print("Removing bad candidate: id=%d, chi^2=%f" % \ (cand.getSource().getId(), cand.getChi2())) cell.removeCandidate(cand) # # Clip out bad fits based on reduced chi^2 # badCandidates = list() for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # include bad candidates rchi2 = cand.getChi2( ) # reduced chi^2 when fitting PSF to candidate assert rchi2 > 0 if rchi2 > self.config.reducedChi2ForPsfCandidates: badCandidates.append(cand) badCandidates.sort(key=lambda x: x.getChi2(), reverse=True) numBad = numCandidatesToReject(len(badCandidates), iterNum, self.config.nIterForPsf) for i, c in zip(range(numBad), badCandidates): if display: chi2 = c.getChi2() if chi2 > 1e100: chi2 = numpy.nan print("Chi^2 clipping %-4d %.2g" % (c.getSource().getId(), chi2)) c.setStatus(afwMath.SpatialCellCandidate.BAD) # # Clip out bad fits based on spatial fitting. # # This appears to be better at getting rid of sources that have a single dominant kernel component # (other than the zeroth; e.g., a nearby contaminant) because the surrounding sources (which help # set the spatial model) don't contain that kernel component, and so the spatial modeling # downweights the component. # residuals = list() candidates = list() kernel = psf.getKernel() noSpatialKernel = psf.getKernel() for cell in psfCellSet.getCellList(): for cand in cell.begin(False): candCenter = afwGeom.PointD(cand.getXCenter(), cand.getYCenter()) try: im = cand.getMaskedImage(kernel.getWidth(), kernel.getHeight()) except Exception as e: continue fit = fitKernelParamsToImage(noSpatialKernel, im, candCenter) params = fit[0] kernels = fit[1] amp = 0.0 for p, k in zip(params, kernels): amp += p * k.getSum() predict = [ kernel.getSpatialFunction(k)(candCenter.getX(), candCenter.getY()) for k in range(kernel.getNKernelParameters()) ] #print cand.getSource().getId(), [a / amp for a in params], predict residuals.append( [a / amp - p for a, p in zip(params, predict)]) candidates.append(cand) residuals = numpy.array(residuals) for k in range(kernel.getNKernelParameters()): if False: # Straight standard deviation mean = residuals[:, k].mean() rms = residuals[:, k].std() elif False: # Using interquartile range sr = numpy.sort(residuals[:, k]) mean = sr[int(0.5*len(sr))] if len(sr) % 2 else \ 0.5 * (sr[int(0.5*len(sr))] + sr[int(0.5*len(sr))+1]) rms = 0.74 * (sr[int(0.75 * len(sr))] - sr[int(0.25 * len(sr))]) else: stats = afwMath.makeStatistics( residuals[:, k], afwMath.MEANCLIP | afwMath.STDEVCLIP) mean = stats.getValue(afwMath.MEANCLIP) rms = stats.getValue(afwMath.STDEVCLIP) rms = max( 1.0e-4, rms) # Don't trust RMS below this due to numerical issues if display: print("Mean for component %d is %f" % (k, mean)) print("RMS for component %d is %f" % (k, rms)) badCandidates = list() for i, cand in enumerate(candidates): if numpy.fabs(residuals[i, k] - mean) > self.config.spatialReject * rms: badCandidates.append(i) badCandidates.sort( key=lambda x: numpy.fabs(residuals[x, k] - mean), reverse=True) numBad = numCandidatesToReject(len(badCandidates), iterNum, self.config.nIterForPsf) for i, c in zip(range(min(len(badCandidates), numBad)), badCandidates): cand = candidates[c] if display: print("Spatial clipping %d (%f,%f) based on %d: %f vs %f" % \ (cand.getSource().getId(), cand.getXCenter(), cand.getYCenter(), k, residuals[badCandidates[i], k], self.config.spatialReject * rms)) cand.setStatus(afwMath.SpatialCellCandidate.BAD) # # Display results # if display and displayIterations: if displayExposure: if iterNum > 0: ds9.erase(frame=frame) maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=True, symb="o", size=8, frame=frame, ctype=ds9.YELLOW, ctypeBad=ds9.RED, ctypeUnused=ds9.MAGENTA) if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell: maUtils.showPsfSpatialCells( exposure, psfCellSet, self.config.nStarPerCellSpatialFit, symb="o", size=10, frame=frame, ctype=ds9.YELLOW, ctypeBad=ds9.RED) if displayResiduals: while True: try: maUtils.showPsfCandidates( exposure, psfCellSet, psf=psf, frame=4, normalize=normalizeResiduals, showBadCandidates=showBadCandidates) maUtils.showPsfCandidates( exposure, psfCellSet, psf=psf, frame=5, normalize=normalizeResiduals, showBadCandidates=showBadCandidates, variance=True) except: if not showBadCandidates: showBadCandidates = True continue break if displayPsfComponents: maUtils.showPsf(psf, eigenValues, frame=6) if displayPsfMosaic: maUtils.showPsfMosaic(exposure, psf, frame=7, showFwhm=True) ds9.scale('linear', 0, 1, frame=7) if displayPsfSpatialModel: maUtils.plotPsfSpatialModel( exposure, psf, psfCellSet, showBadCandidates=True, matchKernelAmplitudes=matchKernelAmplitudes, keepPlots=keepMatplotlibPlots) if pause: while True: try: reply = input( "Next iteration? [ynchpqQs] ").strip() except EOFError: reply = "n" reply = reply.split() if reply: reply, args = reply[0], reply[1:] else: reply = "" if reply in ("", "c", "h", "n", "p", "q", "Q", "s", "y"): if reply == "c": pause = False elif reply == "h": print("c[ontinue without prompting] h[elp] n[o] p[db] q[uit displaying] " \ "s[ave fileName] y[es]") continue elif reply == "p": import pdb pdb.set_trace() elif reply == "q": display = False elif reply == "Q": sys.exit(1) elif reply == "s": fileName = args.pop(0) if not fileName: print("Please provide a filename") continue print("Saving to %s" % fileName) maUtils.saveSpatialCellSet(psfCellSet, fileName=fileName) continue break else: print("Unrecognised response: %s" % reply, file=sys.stderr) if reply == "n": break # One last time, to take advantage of the last iteration psf, eigenValues, nEigenComponents, fitChi2 = \ self._fitPsf(exposure, psfCellSet, actualKernelSize, nEigenComponents) # # Display code for debugging # if display and reply != "n": if displayExposure: maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=True, symb="o", ctype=ds9.YELLOW, ctypeBad=ds9.RED, size=8, frame=frame) if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell: maUtils.showPsfSpatialCells( exposure, psfCellSet, self.config.nStarPerCellSpatialFit, symb="o", ctype=ds9.YELLOW, ctypeBad=ds9.RED, size=10, frame=frame) if displayResiduals: maUtils.showPsfCandidates( exposure, psfCellSet, psf=psf, frame=4, normalize=normalizeResiduals, showBadCandidates=showBadCandidates) if displayPsfComponents: maUtils.showPsf(psf, eigenValues, frame=6) if displayPsfMosaic: maUtils.showPsfMosaic(exposure, psf, frame=7, showFwhm=True) ds9.scale("linear", 0, 1, frame=7) if displayPsfSpatialModel: maUtils.plotPsfSpatialModel( exposure, psf, psfCellSet, showBadCandidates=True, matchKernelAmplitudes=matchKernelAmplitudes, keepPlots=keepMatplotlibPlots) # # Generate some QA information # # Count PSF stars # numGoodStars = 0 numAvailStars = 0 avgX = 0.0 avgY = 0.0 for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # don't ignore BAD stars numAvailStars += 1 for cand in cell.begin(True): # do ignore BAD stars src = cand.getSource() if flagKey is not None: src.set(flagKey, True) avgX += src.getX() avgY += src.getY() numGoodStars += 1 avgX /= numGoodStars avgY /= numGoodStars if metadata is not None: metadata.set("spatialFitChi2", fitChi2) metadata.set("numGoodStars", numGoodStars) metadata.set("numAvailStars", numAvailStars) metadata.set("avgX", avgX) metadata.set("avgY", avgY) psf = PcaPsf(psf.getKernel(), afwGeom.Point2D(avgX, avgY)) return psf, psfCellSet
def showKernelCandidates(kernelCellSet, kernel, background, frame=None, showBadCandidates=True, resids=False, kernels=False): """Display the Kernel candidates. If kernel is provided include spatial model and residuals; If chi is True, generate a plot of residuals/sqrt(variance), i.e. chi """ # # Show us the ccandidates # if kernels: mos = displayUtils.Mosaic(gutter=5, background=0) else: mos = displayUtils.Mosaic(gutter=5, background=-1) # candidateCenters = [] candidateCentersBad = [] candidateIndex = 0 for cell in kernelCellSet.getCellList(): for cand in cell.begin(False): # include bad candidates # Original difference image; if does not exist, skip candidate try: resid = cand.getDifferenceImage( diffimLib.KernelCandidateF.ORIG) except Exception: continue rchi2 = cand.getChi2() if rchi2 > 1e100: rchi2 = np.nan if not showBadCandidates and cand.isBad(): continue im_resid = displayUtils.Mosaic(gutter=1, background=-0.5, mode="x") try: im = cand.getScienceMaskedImage() im = im.Factory(im, True) im.setXY0(cand.getScienceMaskedImage().getXY0()) except Exception: continue if (not resids and not kernels): im_resid.append(im.Factory(im, True)) try: im = cand.getTemplateMaskedImage() im = im.Factory(im, True) im.setXY0(cand.getTemplateMaskedImage().getXY0()) except Exception: continue if (not resids and not kernels): im_resid.append(im.Factory(im, True)) # Difference image with original basis if resids: var = resid.getVariance() var = var.Factory(var, True) np.sqrt(var.getArray(), var.getArray()) # inplace sqrt resid = resid.getImage() resid /= var bbox = kernel.shrinkBBox(resid.getBBox()) resid = resid.Factory(resid, bbox, True) elif kernels: kim = cand.getKernelImage( diffimLib.KernelCandidateF.ORIG).convertF() resid = kim.Factory(kim, True) im_resid.append(resid) # residuals using spatial model ski = afwImage.ImageD(kernel.getDimensions()) kernel.computeImage(ski, False, int(cand.getXCenter()), int(cand.getYCenter())) sk = afwMath.FixedKernel(ski) sbg = 0.0 if background: sbg = background(int(cand.getXCenter()), int(cand.getYCenter())) sresid = cand.getDifferenceImage(sk, sbg) resid = sresid if resids: resid = sresid.getImage() resid /= var bbox = kernel.shrinkBBox(resid.getBBox()) resid = resid.Factory(resid, bbox, True) elif kernels: kim = ski.convertF() resid = kim.Factory(kim, True) im_resid.append(resid) im = im_resid.makeMosaic() lab = "%d chi^2 %.1f" % (cand.getId(), rchi2) ctype = ds9.RED if cand.isBad() else ds9.GREEN mos.append(im, lab, ctype) if False and np.isnan(rchi2): ds9.mtv(cand.getScienceMaskedImage.getImage(), title="candidate", frame=1) print("rating", cand.getCandidateRating()) im = cand.getScienceMaskedImage() center = (candidateIndex, cand.getXCenter() - im.getX0(), cand.getYCenter() - im.getY0()) candidateIndex += 1 if cand.isBad(): candidateCentersBad.append(center) else: candidateCenters.append(center) if resids: title = "chi Diffim" elif kernels: title = "Kernels" else: title = "Candidates & residuals" mosaicImage = mos.makeMosaic(frame=frame, title=title) return mosaicImage