def checkEdge(self, footprint): """Check that Footprint::findEdgePixels() works""" bbox = footprint.getBBox() bbox.grow(3) def makeImage(footprint): """Make an ImageF with 1 in the footprint, and 0 elsewhere""" ones = afwImage.ImageI(bbox) ones.set(1) image = afwImage.ImageI(bbox) image.set(0) afwDetect.copyWithinFootprintImage(footprint, ones, image) return image edges = self.foot.findEdgePixels() edgeImage = makeImage(edges) # Find edges with an edge-detection kernel image = makeImage(self.foot) kernel = afwImage.ImageD(3, 3) kernel.set(1, 1, 4) for x, y in [(1, 2), (0, 1), (1, 0), (2, 1)]: kernel.set(x, y, -1) kernel.setXY0(1, 1) result = afwImage.ImageI(bbox) result.set(0) afwMath.convolve(result, image, afwMath.FixedKernel(kernel), afwMath.ConvolutionControl(False)) result.getArray().__imul__(image.getArray()) trueEdges = numpy.where(result.getArray() > 0, 1, 0) self.assertTrue(numpy.all(trueEdges == edgeImage.getArray()))
def checkEdge(self, footprint): """Check that Footprint::findEdgePixels() works""" bbox = footprint.getBBox() bbox.grow(3) def makeImage(area): """Make an ImageF with 1 in the footprint, and 0 elsewhere""" ones = afwImage.ImageI(bbox) ones.set(1) image = afwImage.ImageI(bbox) image.set(0) if isinstance(area, afwDetect.Footprint): area.spans.copyImage(ones, image) if isinstance(area, afwGeom.SpanSet): area.copyImage(ones, image) return image edges = self.foot.spans.findEdgePixels() edgeImage = makeImage(edges) # Find edges with an edge-detection kernel image = makeImage(self.foot) kernel = afwImage.ImageD(3, 3) kernel[1, 1, afwImage.LOCAL] = 4 for x, y in [(1, 2), (0, 1), (1, 0), (2, 1)]: kernel[x, y, afwImage.LOCAL] = -1 kernel.setXY0(1, 1) result = afwImage.ImageI(bbox) result.set(0) afwMath.convolve(result, image, afwMath.FixedKernel(kernel), afwMath.ConvolutionControl(False)) result.getArray().__imul__(image.getArray()) trueEdges = np.where(result.getArray() > 0, 1, 0) self.assertTrue(np.all(trueEdges == edgeImage.getArray()))
def setUp(self): self.config = ipDiffim.ImagePsfMatchTask.ConfigClass() self.subconfig = self.config.kernel.active self.policy = pexConfig.makePolicy(self.subconfig) self.kSize = self.policy.getInt('kernelSize') # gaussian reference kernel self.gSize = self.kSize self.gaussFunction = afwMath.GaussianFunction2D(2, 3) self.gaussKernel = afwMath.AnalyticKernel(self.gSize, self.gSize, self.gaussFunction) # known input images try: self.defDataDir = lsst.utils.getPackageDir('afwdata') except Exception: self.defDataDir = None if self.defDataDir: defImagePath = os.path.join(self.defDataDir, "DC3a-Sim", "sci", "v5-e0", "v5-e0-c011-a00.sci.fits") self.templateImage = afwImage.MaskedImageF(defImagePath) self.scienceImage = self.templateImage.Factory( self.templateImage.getDimensions()) afwMath.convolve(self.scienceImage, self.templateImage, self.gaussKernel, False)
def _doConvolve(exposure, kernel): """Convolve an Exposure with a decorrelation convolution kernel. Parameters ---------- exposure : `lsst.afw.image.Exposure` Input exposure to be convolved. kernel : `numpy.array` Input 2-d numpy.array to convolve the image with Returns ------- out : `lsst.afw.image.Exposure` a new Exposure with the convolved pixels and the (possibly re-centered) kernel. Notes ----- We re-center the kernel if necessary and return the possibly re-centered kernel """ kernelImg = afwImage.ImageD(kernel.shape[0], kernel.shape[1]) kernelImg.getArray()[:, :] = kernel kern = afwMath.FixedKernel(kernelImg) maxloc = np.unravel_index(np.argmax(kernel), kernel.shape) kern.setCtrX(maxloc[0]) kern.setCtrY(maxloc[1]) outExp = exposure.clone() # Do this to keep WCS, PSF, masks, etc. convCntrl = afwMath.ConvolutionControl(False, True, 0) afwMath.convolve(outExp.getMaskedImage(), exposure.getMaskedImage(), kern, convCntrl) return outExp, kern
def main(): gaussFunction = afwMath.GaussianFunction2D(3, 2, 0.5) gaussKernel = afwMath.AnalyticKernel(10, 10, gaussFunction) inImage = afwImage.ImageF(afwGeom.Extent2I(100, 100)) inImage.set(1) if disp: ds9.mtv(inImage, frame = 0) # works outImage = afwImage.ImageF(afwGeom.Extent2I(100, 100)) afwMath.convolve(outImage, inImage, gaussKernel, False, True) if disp: ds9.mtv(outImage, frame = 1) print("Should be a number: ", afwMath.makeStatistics( outImage, afwMath.MEAN).getValue()) print("Should be a number: ", afwMath.makeStatistics( outImage, afwMath.STDEV).getValue()) # not works ... now does work outImage = afwImage.ImageF(afwGeom.Extent2I(100, 100)) afwMath.convolve(outImage, inImage, gaussKernel, False, False) if disp: ds9.mtv(outImage, frame = 2) print("Should be a number: ", afwMath.makeStatistics( outImage, afwMath.MEAN).getValue()) print("Should be a number: ", afwMath.makeStatistics( outImage, afwMath.STDEV).getValue()) # This will print nan sctrl = afwMath.StatisticsControl() sctrl.setNanSafe(False) print ("Should be a nan (nanSafe set to False): " + str(afwMath.makeStatistics(outImage, afwMath.MEAN, sctrl).getValue()))
def testUnityConvolution(self): """Verify that convolution with a centered delta function reproduces the original. """ # create a delta function kernel that has 1,1 in the center kFunc = afwMath.IntegerDeltaFunction2D(0.0, 0.0) kernel = afwMath.AnalyticKernel(3, 3, kFunc) doNormalize = False doCopyEdge = False afwMath.convolve(self.cnvImage, self.maskedImage.getImage(), kernel, doNormalize, doCopyEdge) cnvImArr = self.cnvImage.getArray() afwMath.convolve(self.cnvMaskedImage, self.maskedImage, kernel, doNormalize, doCopyEdge) cnvImMaskVarArr = self.cnvMaskedImage.getArrays() refCnvImMaskVarArr = self.maskedImage.getArrays() skipMaskArr = numpy.isnan(cnvImMaskVarArr[0]) kernelDescr = "Centered DeltaFunctionKernel (testing unity convolution)" errStr = imTestUtils.imagesDiffer(cnvImArr, refCnvImMaskVarArr[0], skipMaskArr=skipMaskArr) if errStr: self.fail("convolve(Image, kernel=%s, doNormalize=%s, doCopyEdge=%s) failed:\n%s" % \ (kernelDescr, doNormalize, doCopyEdge, errStr)) errStr = imTestUtils.maskedImagesDiffer(cnvImMaskVarArr, refCnvImMaskVarArr, skipMaskArr=skipMaskArr) if errStr: self.fail("convolve(MaskedImage, kernel=%s, doNormalize=%s, doCopyEdge=%s) failed:\n%s" % \ (kernelDescr, doNormalize, doCopyEdge, errStr)) self.assert_(sameMaskPlaneDicts(self.cnvMaskedImage, self.maskedImage), "convolve(MaskedImage, kernel=%s, doNormalize=%s, doCopyEdge=%s) failed:\n%s" % \ (kernelDescr, doNormalize, doCopyEdge, "convolved mask dictionary does not match input"))
def testGood(self): ti = afwImage.MaskedImageF(afwGeom.Extent2I(100, 100)) ti.getVariance().set(0.1) ti.set(50, 50, (1., 0x0, 1.)) sKernel = self.makeSpatialKernel(2) si = afwImage.MaskedImageF(ti.getDimensions()) afwMath.convolve(si, ti, sKernel, True) bbox = afwGeom.Box2I(afwGeom.Point2I(25, 25), afwGeom.Point2I(75, 75)) si = afwImage.MaskedImageF(si, bbox, afwImage.LOCAL) ti = afwImage.MaskedImageF(ti, bbox, afwImage.LOCAL) kc = ipDiffim.KernelCandidateF(50., 50., ti, si, self.policy) sBg = afwMath.PolynomialFunction2D(1) bgCoeffs = [0., 0., 0.] sBg.setParameters(bgCoeffs) # must be initialized bskv = ipDiffim.BuildSingleKernelVisitorF(self.kList, self.policy) bskv.processCandidate(kc) self.assertEqual(kc.isInitialized(), True) #ds9.mtv(kc.getTemplateMaskedImage(), frame=1) #ds9.mtv(kc.getScienceMaskedImage(), frame=2) #ds9.mtv(kc.getKernelImage(ipDiffim.KernelCandidateF.RECENT), frame=3) #ds9.mtv(kc.getDifferenceImage(ipDiffim.KernelCandidateF.RECENT), frame=4) askv = ipDiffim.AssessSpatialKernelVisitorF(sKernel, sBg, self.policy) askv.processCandidate(kc) self.assertEqual(askv.getNProcessed(), 1) self.assertEqual(askv.getNRejected(), 0) self.assertEqual(kc.getStatus(), afwMath.SpatialCellCandidate.GOOD)
def run(self, exposure, referencePsfModel, kernelSum=1.0): """!Psf-match an exposure to a model Psf @param exposure: Exposure to Psf-match to the reference Psf model; it must return a valid PSF model via exposure.getPsf() @param referencePsfModel: The Psf model to match to (an lsst.afw.detection.Psf) @param kernelSum: A multipicative factor to apply to the kernel sum (default=1.0) @return - psfMatchedExposure: the Psf-matched Exposure. This has the same parent bbox, Wcs, Calib and Filter as the input Exposure but no Psf. In theory the Psf should equal referencePsfModel but the match is likely not exact. - psfMatchingKernel: the spatially varying Psf-matching kernel - kernelCellSet: SpatialCellSet used to solve for the Psf-matching kernel - referencePsfModel: Validated and/or modified reference model used Raise a RuntimeError if the Exposure does not contain a Psf model """ if not exposure.hasPsf(): raise RuntimeError("exposure does not contain a Psf model") maskedImage = exposure.getMaskedImage() self.log.info("compute Psf-matching kernel") result = self._buildCellSet(exposure, referencePsfModel) kernelCellSet = result.kernelCellSet referencePsfModel = result.referencePsfModel fwhmScience = exposure.getPsf().computeShape().getDeterminantRadius() * sigma2fwhm fwhmModel = referencePsfModel.computeShape().getDeterminantRadius() * sigma2fwhm basisList = makeKernelBasisList(self.kConfig, fwhmScience, fwhmModel, metadata=self.metadata) spatialSolution, psfMatchingKernel, backgroundModel = self._solve(kernelCellSet, basisList) if psfMatchingKernel.isSpatiallyVarying(): sParameters = np.array(psfMatchingKernel.getSpatialParameters()) sParameters[0][0] = kernelSum psfMatchingKernel.setSpatialParameters(sParameters) else: kParameters = np.array(psfMatchingKernel.getKernelParameters()) kParameters[0] = kernelSum psfMatchingKernel.setKernelParameters(kParameters) self.log.info("Psf-match science exposure to reference") psfMatchedExposure = afwImage.ExposureF(exposure.getBBox(), exposure.getWcs()) psfMatchedExposure.setFilter(exposure.getFilter()) psfMatchedExposure.setCalib(exposure.getCalib()) psfMatchedExposure.setPsf(referencePsfModel) psfMatchedMaskedImage = psfMatchedExposure.getMaskedImage() # Normalize the psf-matching kernel while convolving since its magnitude is meaningless # when PSF-matching one model to another. doNormalize = True afwMath.convolve(psfMatchedMaskedImage, maskedImage, psfMatchingKernel, doNormalize) self.log.info("done") return pipeBase.Struct(psfMatchedExposure=psfMatchedExposure, psfMatchingKernel=psfMatchingKernel, kernelCellSet=kernelCellSet, metadata=self.metadata, )
def main(): gaussFunction = afwMath.GaussianFunction2D(3, 2, 0.5) gaussKernel = afwMath.AnalyticKernel(10, 10, gaussFunction) inImage = afwImage.ImageF(afwGeom.Extent2I(100, 100)) inImage.set(1) if disp: ds9.mtv(inImage, frame = 0) # works outImage = afwImage.ImageF(afwGeom.Extent2I(100, 100)) afwMath.convolve(outImage, inImage, gaussKernel, False, True) if disp: ds9.mtv(outImage, frame = 1) print "Should be a number: ", afwMath.makeStatistics(outImage, afwMath.MEAN).getValue() print "Should be a number: ", afwMath.makeStatistics(outImage, afwMath.STDEV).getValue() # not works ... now does work outImage = afwImage.ImageF(afwGeom.Extent2I(100, 100)) afwMath.convolve(outImage, inImage, gaussKernel, False, False) if disp: ds9.mtv(outImage, frame = 2) print "Should be a number: ", afwMath.makeStatistics(outImage, afwMath.MEAN).getValue() print "Should be a number: ", afwMath.makeStatistics(outImage, afwMath.STDEV).getValue() # This will print nan sctrl = afwMath.StatisticsControl() sctrl.setNanSafe(False) print ("Should be a nan (nanSafe set to False): " + str(afwMath.makeStatistics(outImage, afwMath.MEAN, sctrl).getValue()))
def testGaussianWithNoise(self): # Convolve a real image with a gaussian and try and recover # it. Add noise and perform the same test. gsize = self.ps["kernelSize"] gaussFunction = afwMath.GaussianFunction2D(2, 3) gaussKernel = afwMath.AnalyticKernel(gsize, gsize, gaussFunction) kImageIn = afwImage.ImageD(geom.Extent2I(gsize, gsize)) kSumIn = gaussKernel.computeImage(kImageIn, False) imX, imY = self.templateExposure2.getMaskedImage().getDimensions() smi = afwImage.MaskedImageF(geom.Extent2I(imX, imY)) afwMath.convolve(smi, self.templateExposure2.getMaskedImage(), gaussKernel, False) bbox = gaussKernel.shrinkBBox(smi.getBBox(afwImage.LOCAL)) tmi2 = afwImage.MaskedImageF(self.templateExposure2.getMaskedImage(), bbox, origin=afwImage.LOCAL) smi2 = afwImage.MaskedImageF(smi, bbox, origin=afwImage.LOCAL) kc = ipDiffim.KernelCandidateF(self.x02, self.y02, tmi2, smi2, self.ps) kList = ipDiffim.makeKernelBasisList(self.subconfig) kc.build(kList) self.assertEqual(kc.isInitialized(), True) kImageOut = kc.getImage() soln = kc.getKernelSolution(ipDiffim.KernelCandidateF.RECENT) self.assertAlmostEqual(soln.getKsum(), kSumIn) # 8.7499380640430563e-06 != 0.0 within 7 places self.assertAlmostEqual(soln.getBackground(), 0.0, 4) for j in range(kImageOut.getHeight()): for i in range(kImageOut.getWidth()): # in the outskirts of the kernel, the ratio can get screwed because of low S/N # e.g. 7.45817359824e-09 vs. 1.18062529402e-08 # in the guts of the kernel it should look closer if kImageIn[i, j, afwImage.LOCAL] > 1e-4: # sigh, too bad this sort of thing fails.. # 0.99941584433815966 != 1.0 within 3 places self.assertAlmostEqual(kImageOut[i, j, afwImage.LOCAL]/kImageIn[i, j, afwImage.LOCAL], 1.0, 2) # now repeat with noise added; decrease precision of comparison self.addNoise(smi2) kc = ipDiffim.KernelCandidateF(self.x02, self.y02, tmi2, smi2, self.ps) kList = ipDiffim.makeKernelBasisList(self.subconfig) kc.build(kList) self.assertEqual(kc.isInitialized(), True) kImageOut = kc.getImage() soln = kc.getKernelSolution(ipDiffim.KernelCandidateF.RECENT) self.assertAlmostEqual(soln.getKsum(), kSumIn, 3) if not self.ps.get("fitForBackground"): self.assertEqual(soln.getBackground(), 0.0) for j in range(kImageOut.getHeight()): for i in range(kImageOut.getWidth()): if kImageIn[i, j, afwImage.LOCAL] > 1e-2: self.assertAlmostEqual(kImageOut[i, j, afwImage.LOCAL], kImageIn[i, j, afwImage.LOCAL], 2)
def doConvolve(exposure, kernel, use_scipy=False, fix_kernel=False): """! Convolve an Exposure with a decorrelation convolution kernel. @param exposure Input afw.image.Exposure to be convolved. @param kernel Input 2-d numpy.array to convolve the image with @param use_scipy Use scipy to do convolution instead of afwMath @return a new Exposure with the convolved pixels and the (possibly re-centered) kernel. @note We use afwMath.convolve() but keep scipy.convolve for debugging. @note We re-center the kernel if necessary and return the possibly re-centered kernel """ outExp = kern = None fkernel = kernel if fix_kernel: fkernel = fixEvenKernel(kernel) if use_scipy: pci = scipy.ndimage.filters.convolve(exposure.getMaskedImage().getImage().getArray(), fkernel, mode='constant', cval=np.nan) outExp = exposure.clone() outExp.getMaskedImage().getImage().getArray()[:, :] = pci kern = fkernel else: kern = arrayToAfwKernel(fkernel) outExp = exposure.clone() # Do this to keep WCS, PSF, masks, etc. convCntrl = afwMath.ConvolutionControl(False, True, 0) afwMath.convolve(outExp.getMaskedImage(), exposure.getMaskedImage(), kern, convCntrl) return outExp, kern
def _makeAndTestUncorrectedDiffim(self): """Create the (un-decorrelated) diffim, and verify that its variance is too low. """ # Create the matching kernel. We used Gaussian PSFs for im1 and im2, so we can compute the "expected" # matching kernel sigma. psf1_sig = self.im1ex.getPsf().computeShape().getDeterminantRadius() psf2_sig = self.im2ex.getPsf().computeShape().getDeterminantRadius() sig_match = np.sqrt((psf1_sig**2. - psf2_sig**2.)) # Sanity check - make sure PSFs are correct. self.assertFloatsAlmostEqual(sig_match, np.sqrt((self.psf1_sigma**2. - self.psf2_sigma**2.)), rtol=2e-5) # mKernel = measAlg.SingleGaussianPsf(31, 31, sig_match) x0 = np.arange(-16, 16, 1) y0 = x0.copy() x0im, y0im = np.meshgrid(x0, y0) matchingKernel = singleGaussian2d(x0im, y0im, -1., -1., sigma_x=sig_match, sigma_y=sig_match) kernelImg = afwImage.ImageD(matchingKernel.shape[0], matchingKernel.shape[1]) kernelImg.getArray()[:, :] = matchingKernel mKernel = afwMath.FixedKernel(kernelImg) # Create the matched template by convolving the template with the matchingKernel matched_im2ex = self.im2ex.clone() convCntrl = afwMath.ConvolutionControl(False, True, 0) afwMath.convolve(matched_im2ex.getMaskedImage(), self.im2ex.getMaskedImage(), mKernel, convCntrl) # Expected (ideal) variance of difference image expected_var = self.svar + self.tvar if verbose: print('EXPECTED VARIANCE:', expected_var) # Create the diffim (uncorrected) # Uncorrected diffim exposure - variance plane is wrong (too low) tmp_diffExp = self.im1ex.getMaskedImage().clone() tmp_diffExp -= matched_im2ex.getMaskedImage() var = self._computeVarianceMean(tmp_diffExp) self.assertLess(var, expected_var) # Uncorrected diffim exposure - variance is wrong (too low) - same as above but on pixels diffExp = self.im1ex.clone() tmp = diffExp.getMaskedImage() tmp -= matched_im2ex.getMaskedImage() var = self._computePixelVariance(diffExp.getMaskedImage()) self.assertLess(var, expected_var) # Uncorrected diffim exposure - variance plane is wrong (too low) mn = self._computeVarianceMean(diffExp.getMaskedImage()) self.assertLess(mn, expected_var) if verbose: print('UNCORRECTED VARIANCE:', var, mn) return diffExp, mKernel, expected_var
def testGood(self): ti = afwImage.MaskedImageF(afwGeom.Extent2I(100, 100)) ti.getVariance().set(0.1) ti.set(50, 50, (1., 0x0, 1.)) sKernel = self.makeSpatialKernel(2) si = afwImage.MaskedImageF(ti.getDimensions()) afwMath.convolve(si, ti, sKernel, True) bbox = afwGeom.Box2I(afwGeom.Point2I(25, 25), afwGeom.Point2I(75, 75)) si = afwImage.MaskedImageF(si, bbox, origin=afwImage.LOCAL) ti = afwImage.MaskedImageF(ti, bbox, origin=afwImage.LOCAL) kc = ipDiffim.KernelCandidateF(50., 50., ti, si, self.policy) sBg = afwMath.PolynomialFunction2D(1) bgCoeffs = [0., 0., 0.] sBg.setParameters(bgCoeffs) # must be initialized bskv = ipDiffim.BuildSingleKernelVisitorF(self.kList, self.policy) bskv.processCandidate(kc) self.assertEqual(kc.isInitialized(), True) #ds9.mtv(kc.getTemplateMaskedImage(), frame=1) #ds9.mtv(kc.getScienceMaskedImage(), frame=2) #ds9.mtv(kc.getKernelImage(ipDiffim.KernelCandidateF.RECENT), frame=3) #ds9.mtv(kc.getDifferenceImage(ipDiffim.KernelCandidateF.RECENT), frame=4) askv = ipDiffim.AssessSpatialKernelVisitorF(sKernel, sBg, self.policy) askv.processCandidate(kc) self.assertEqual(askv.getNProcessed(), 1) self.assertEqual(askv.getNRejected(), 0) self.assertEqual(kc.getStatus(), afwMath.SpatialCellCandidate.GOOD)
def testGaussianWithNoise(self): # Convolve a real image with a gaussian and try and recover # it. Add noise and perform the same test. gsize = self.policy.getInt("kernelSize") gaussFunction = afwMath.GaussianFunction2D(2, 3) gaussKernel = afwMath.AnalyticKernel(gsize, gsize, gaussFunction) kImageIn = afwImage.ImageD(afwGeom.Extent2I(gsize, gsize)) kSumIn = gaussKernel.computeImage(kImageIn, False) imX, imY = self.templateExposure2.getMaskedImage().getDimensions() smi = afwImage.MaskedImageF(afwGeom.Extent2I(imX, imY)) afwMath.convolve(smi, self.templateExposure2.getMaskedImage(), gaussKernel, False) bbox = gaussKernel.shrinkBBox(smi.getBBox(afwImage.LOCAL)) tmi2 = afwImage.MaskedImageF(self.templateExposure2.getMaskedImage(), bbox, origin=afwImage.LOCAL) smi2 = afwImage.MaskedImageF(smi, bbox, origin=afwImage.LOCAL) kc = ipDiffim.KernelCandidateF(self.x02, self.y02, tmi2, smi2, self.policy) kList = ipDiffim.makeKernelBasisList(self.subconfig) kc.build(kList) self.assertEqual(kc.isInitialized(), True) kImageOut = kc.getImage() soln = kc.getKernelSolution(ipDiffim.KernelCandidateF.RECENT) self.assertAlmostEqual(soln.getKsum(), kSumIn) # 8.7499380640430563e-06 != 0.0 within 7 places self.assertAlmostEqual(soln.getBackground(), 0.0, 4) for j in range(kImageOut.getHeight()): for i in range(kImageOut.getWidth()): # in the outskirts of the kernel, the ratio can get screwed because of low S/N # e.g. 7.45817359824e-09 vs. 1.18062529402e-08 # in the guts of the kernel it should look closer if kImageIn[i, j, afwImage.LOCAL] > 1e-4: # sigh, too bad this sort of thing fails.. # 0.99941584433815966 != 1.0 within 3 places self.assertAlmostEqual(kImageOut[i, j, afwImage.LOCAL]/kImageIn[i, j, afwImage.LOCAL], 1.0, 2) # now repeat with noise added; decrease precision of comparison self.addNoise(smi2) kc = ipDiffim.KernelCandidateF(self.x02, self.y02, tmi2, smi2, self.policy) kList = ipDiffim.makeKernelBasisList(self.subconfig) kc.build(kList) self.assertEqual(kc.isInitialized(), True) kImageOut = kc.getImage() soln = kc.getKernelSolution(ipDiffim.KernelCandidateF.RECENT) self.assertAlmostEqual(soln.getKsum(), kSumIn, 3) if not (self.policy.get("fitForBackground")): self.assertEqual(soln.getBackground(), 0.0) for j in range(kImageOut.getHeight()): for i in range(kImageOut.getWidth()): if kImageIn[i, j, afwImage.LOCAL] > 1e-2: self.assertAlmostEqual(kImageOut[i, j, afwImage.LOCAL], kImageIn[i, j, afwImage.LOCAL], 2)
def convolveImage(self, maskedImage, psf, doSmooth=True): """Convolve the image with the PSF We convolve the image with a Gaussian approximation to the PSF, because this is separable and therefore fast. It's technically a correlation rather than a convolution, but since we use a symmetric Gaussian there's no difference. The convolution can be disabled with ``doSmooth=False``. If we do convolve, we mask the edges as ``EDGE`` and return the convolved image with the edges removed. This is because we can't convolve the edges because the kernel would extend off the image. Parameters ---------- maskedImage : `lsst.afw.image.MaskedImage` Image to convolve. psf : `lsst.afw.detection.Psf` PSF to convolve with (actually with a Gaussian approximation to it). doSmooth : `bool` Actually do the convolution? Set to False when running on e.g. a pre-convolved image, or a mask plane. Return Struct contents ---------------------- middle : `lsst.afw.image.MaskedImage` Convolved image, without the edges. sigma : `float` Gaussian sigma used for the convolution. """ self.metadata["doSmooth"] = doSmooth sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius() self.metadata["sigma"] = sigma if not doSmooth: middle = maskedImage.Factory(maskedImage, deep=True) return pipeBase.Struct(middle=middle, sigma=sigma) # Smooth using a Gaussian (which is separable, hence fast) of width sigma # Make a SingleGaussian (separable) kernel with the 'sigma' kWidth = self.calculateKernelSize(sigma) self.metadata["smoothingKernelWidth"] = kWidth gaussFunc = afwMath.GaussianFunction1D(sigma) gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc) convolvedImage = maskedImage.Factory(maskedImage.getBBox()) afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl()) # # Only search psf-smoothed part of frame # goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox()) middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT, False) # # Mark the parts of the image outside goodBBox as EDGE # self.setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask("EDGE")) return pipeBase.Struct(middle=middle, sigma=sigma)
def doConvolve(exposure, kernel, use_scipy=False, fix_kernel=False): """! Convolve an Exposure with a decorrelation convolution kernel. @param exposure Input afw.image.Exposure to be convolved. @param kernel Input 2-d numpy.array to convolve the image with @param use_scipy Use scipy to do convolution instead of afwMath @return a new Exposure with the convolved pixels and the (possibly re-centered) kernel. @note We use afwMath.convolve() but keep scipy.convolve for debugging. @note We re-center the kernel if necessary and return the possibly re-centered kernel """ outExp = kern = None fkernel = kernel if fix_kernel: fkernel = fixEvenKernel(kernel) if use_scipy: pci = scipy.ndimage.filters.convolve( exposure.getMaskedImage().getImage().getArray(), fkernel, mode='constant', cval=np.nan) outExp = exposure.clone() outExp.getMaskedImage().getImage().getArray()[:, :] = pci kern = fkernel else: kern = arrayToAfwKernel(fkernel) outExp = exposure.clone() # Do this to keep WCS, PSF, masks, etc. convCntrl = afwMath.ConvolutionControl(False, True, 0) afwMath.convolve(outExp.getMaskedImage(), exposure.getMaskedImage(), kern, convCntrl) return outExp, kern
def testGood(self): ti = afwImage.MaskedImageF(geom.Extent2I(100, 100)) ti.getVariance().set(0.1) ti[50, 50, afwImage.LOCAL] = (1., 0x0, 1.) sKernel = self.makeSpatialKernel(2) si = afwImage.MaskedImageF(ti.getDimensions()) convolutionControl = afwMath.ConvolutionControl() convolutionControl.setDoNormalize(True) afwMath.convolve(si, ti, sKernel, convolutionControl) bbox = geom.Box2I(geom.Point2I(25, 25), geom.Point2I(75, 75)) si = afwImage.MaskedImageF(si, bbox, origin=afwImage.LOCAL) ti = afwImage.MaskedImageF(ti, bbox, origin=afwImage.LOCAL) kc = ipDiffim.KernelCandidateF(50., 50., ti, si, self.ps) sBg = afwMath.PolynomialFunction2D(1) bgCoeffs = [0., 0., 0.] sBg.setParameters(bgCoeffs) # must be initialized bskv = ipDiffim.BuildSingleKernelVisitorF(self.kList, self.ps) bskv.processCandidate(kc) self.assertEqual(kc.isInitialized(), True) askv = ipDiffim.AssessSpatialKernelVisitorF(sKernel, sBg, self.ps) askv.processCandidate(kc) self.assertEqual(askv.getNProcessed(), 1) self.assertEqual(askv.getNRejected(), 0) self.assertEqual(kc.getStatus(), afwMath.SpatialCellCandidate.GOOD)
def smooth_gauss(masked_image, sigma, nsigma=7.0): """ Smooth image with a Gaussian kernel. Parameters ---------- masked_image : lsst.afw.image.imageLib.MaskedImageF Masked image object to be smoothed sigma : float Standard deviation of Gaussian nsigma : float, optional Number of sigma for kernel width Returns ------- convolved_image : lsst.afw.image.imageLib.MaskedImageF The convolved masked image """ width = (int(sigma*nsigma + 0.5) // 2)*2 + 1 # make sure it is odd gauss_func = afwMath.GaussianFunction1D(sigma) gauss_kern = afwMath.SeparableKernel(width, width, gauss_func, gauss_func) convolved_image = masked_image.Factory(masked_image.getBBox()) afwMath.convolve(convolved_image, masked_image, gauss_kern, afwMath.ConvolutionControl()) return convolved_image
def testUnityConvolution(self): """Verify that convolution with a centered delta function reproduces the original. """ # create a delta function kernel that has 1,1 in the center kFunc = afwMath.IntegerDeltaFunction2D(0.0, 0.0) kernel = afwMath.AnalyticKernel(3, 3, kFunc) doNormalize = False doCopyEdge = False afwMath.convolve(self.cnvImage, self.maskedImage.getImage(), kernel, doNormalize, doCopyEdge) afwMath.convolve(self.cnvMaskedImage, self.maskedImage, kernel, doNormalize, doCopyEdge) cnvImMaskVarArr = self.cnvMaskedImage.getArrays() skipMaskArr = numpy.array(numpy.isnan(cnvImMaskVarArr[0]), dtype=numpy.uint16) kernelDescr = "Centered DeltaFunctionKernel (testing unity convolution)" self.assertImagesAlmostEqual(self.cnvImage, self.maskedImage.getImage(), skipMask=skipMaskArr, msg=kernelDescr) self.assertMaskedImagesAlmostEqual(self.cnvMaskedImage, self.maskedImage, skipMask=skipMaskArr, msg=kernelDescr)
def run(self, exposure, referencePsfModel, kernelSum=1.0): """!Psf-match an exposure to a model Psf @param exposure: Exposure to Psf-match to the reference Psf model; it must return a valid PSF model via exposure.getPsf() @param referencePsfModel: The Psf model to match to (an lsst.afw.detection.Psf) @param kernelSum: A multipicative factor to apply to the kernel sum (default=1.0) @return - psfMatchedExposure: the Psf-matched Exposure. This has the same parent bbox, Wcs, Calib and Filter as the input Exposure but no Psf. In theory the Psf should equal referencePsfModel but the match is likely not exact. - psfMatchingKernel: the spatially varying Psf-matching kernel - kernelCellSet: SpatialCellSet used to solve for the Psf-matching kernel Raise a RuntimeError if the Exposure does not contain a Psf model """ if not exposure.hasPsf(): raise RuntimeError("exposure does not contain a Psf model") maskedImage = exposure.getMaskedImage() self.log.log(pexLog.Log.INFO, "compute Psf-matching kernel") kernelCellSet = self._buildCellSet(exposure, referencePsfModel) width, height = referencePsfModel.getLocalKernel().getDimensions() psfAttr1 = measAlg.PsfAttributes(exposure.getPsf(), width//2, height//2) psfAttr2 = measAlg.PsfAttributes(referencePsfModel, width//2, height//2) s1 = psfAttr1.computeGaussianWidth(psfAttr1.ADAPTIVE_MOMENT) # gaussian sigma in pixels s2 = psfAttr2.computeGaussianWidth(psfAttr2.ADAPTIVE_MOMENT) # gaussian sigma in pixels fwhm1 = s1 * sigma2fwhm # science Psf fwhm2 = s2 * sigma2fwhm # template Psf basisList = makeKernelBasisList(self.kConfig, fwhm1, fwhm2, metadata = self.metadata) spatialSolution, psfMatchingKernel, backgroundModel = self._solve(kernelCellSet, basisList) if psfMatchingKernel.isSpatiallyVarying(): sParameters = num.array(psfMatchingKernel.getSpatialParameters()) sParameters[0][0] = kernelSum psfMatchingKernel.setSpatialParameters(sParameters) else: kParameters = num.array(psfMatchingKernel.getKernelParameters()) kParameters[0] = kernelSum psfMatchingKernel.setKernelParameters(kParameters) self.log.log(pexLog.Log.INFO, "Psf-match science exposure to reference") psfMatchedExposure = afwImage.ExposureF(exposure.getBBox(), exposure.getWcs()) psfMatchedExposure.setFilter(exposure.getFilter()) psfMatchedExposure.setCalib(exposure.getCalib()) psfMatchedMaskedImage = psfMatchedExposure.getMaskedImage() # Normalize the psf-matching kernel while convolving since its magnitude is meaningless # when PSF-matching one model to another. doNormalize = True afwMath.convolve(psfMatchedMaskedImage, maskedImage, psfMatchingKernel, doNormalize) self.log.log(pexLog.Log.INFO, "done") return pipeBase.Struct(psfMatchedExposure=psfMatchedExposure, psfMatchingKernel=psfMatchingKernel, kernelCellSet=kernelCellSet, metadata=self.metadata)
def convolveImage(self, maskedImage, psf, doSmooth=True): """Convolve the image with the PSF This differs from the standard SourceDetectionTask convolveImage in that it allows the user to specify a scaling to the sigma. For GOTO coadds, we found that convolving by the sigma blurred the image too much, meaning that some peaks were missed. By reducing the width of the Gaussian kernel, the smoothing is reduced. Parameters ---------- maskedImage : `lsst.afw.image.MaskedImage` Image to convolve. psf : `lsst.afw.detection.Psf` PSF to convolve with (actually with a Gaussian approximation to it). doSmooth : `bool` Actually do the convolution? Return Struct contents ---------------------- middle : `lsst.afw.image.MaskedImage` Convolved image, without the edges. sigma : `float` Gaussian sigma used for the convolution. """ self.metadata.set("doSmooth", doSmooth) sigma = psf.computeShape().getDeterminantRadius() # This is the only thing that differs from SourceDetectionTask: sigma *= self.config.scaleSigma self.metadata.set("sigma", sigma) if not doSmooth: middle = maskedImage.Factory(maskedImage) return pipeBase.Struct(middle=middle, sigma=sigma) # Smooth using a Gaussian (which is separable, hence fast) of width sigma # Make a SingleGaussian (separable) kernel with the 'sigma' kWidth = self.calculateKernelSize(sigma) self.metadata.set("smoothingKernelWidth", kWidth) gaussFunc = afwMath.GaussianFunction1D(sigma) gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc) convolvedImage = maskedImage.Factory(maskedImage.getBBox()) afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl()) # # Only search psf-smoothed part of frame # goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox()) middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT, False) # # Mark the parts of the image outside goodBBox as EDGE self.setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask("EDGE")) return pipeBase.Struct(middle=middle, sigma=sigma)
def convolveImage(self, maskedImage, psf, doSmooth=True): """Convolve the image with the PSF We convolve the image with a Gaussian approximation to the PSF, because this is separable and therefore fast. It's technically a correlation rather than a convolution, but since we use a symmetric Gaussian there's no difference. The convolution can be disabled with ``doSmooth=False``. If we do convolve, we mask the edges as ``EDGE`` and return the convolved image with the edges removed. This is because we can't convolve the edges because the kernel would extend off the image. Parameters ---------- maskedImage : `lsst.afw.image.MaskedImage` Image to convolve. psf : `lsst.afw.detection.Psf` PSF to convolve with (actually with a Gaussian approximation to it). doSmooth : `bool` Actually do the convolution? Return Struct contents ---------------------- middle : `lsst.afw.image.MaskedImage` Convolved image, without the edges. sigma : `float` Gaussian sigma used for the convolution. """ self.metadata.set("doSmooth", doSmooth) sigma = psf.computeShape().getDeterminantRadius() self.metadata.set("sigma", sigma) if not doSmooth: middle = maskedImage.Factory(maskedImage) return pipeBase.Struct(middle=middle, sigma=sigma) # Smooth using a Gaussian (which is separable, hence fast) of width sigma # Make a SingleGaussian (separable) kernel with the 'sigma' kWidth = self.calculateKernelSize(sigma) self.metadata.set("smoothingKernelWidth", kWidth) gaussFunc = afwMath.GaussianFunction1D(sigma) gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc) convolvedImage = maskedImage.Factory(maskedImage.getBBox()) afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl()) # # Only search psf-smoothed part of frame # goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox()) middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT, False) # # Mark the parts of the image outside goodBBox as EDGE # self.setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask("EDGE")) return pipeBase.Struct(middle=middle, sigma=sigma)
def doConvolve(exposure, kernel, use_scipy=False): """! Convolve an Exposure with a decorrelation convolution kernel. @param exposure Input afw.image.Exposure to be convolved. @param kernel Input 2-d numpy.array to convolve the image with @param use_scipy Use scipy to do convolution instead of afwMath @return a new Exposure with the convolved pixels and the (possibly re-centered) kernel. @note We use afwMath.convolve() but keep scipy.convolve for debugging. @note We re-center the kernel if necessary and return the possibly re-centered kernel """ def _fixEvenKernel(kernel): """! Take a kernel with even dimensions and make them odd, centered correctly. @param kernel a numpy.array @return a fixed kernel numpy.array """ # Make sure the peak (close to a delta-function) is in the center! maxloc = np.unravel_index(np.argmax(kernel), kernel.shape) out = np.roll(kernel, kernel.shape[0] // 2 - maxloc[0], axis=0) out = np.roll(out, out.shape[1] // 2 - maxloc[1], axis=1) # Make sure it is odd-dimensioned by trimming it. if (out.shape[0] % 2) == 0: maxloc = np.unravel_index(np.argmax(out), out.shape) if out.shape[0] - maxloc[0] > maxloc[0]: out = out[:-1, :] else: out = out[1:, :] if out.shape[1] - maxloc[1] > maxloc[1]: out = out[:, :-1] else: out = out[:, 1:] return out outExp = kern = None fkernel = _fixEvenKernel(kernel) if use_scipy: from scipy.ndimage.filters import convolve pci = convolve(exposure.getMaskedImage().getImage().getArray(), fkernel, mode='constant', cval=np.nan) outExp = exposure.clone() outExp.getMaskedImage().getImage().getArray()[:, :] = pci kern = fkernel else: kernelImg = afwImage.ImageD(fkernel.shape[0], fkernel.shape[1]) kernelImg.getArray()[:, :] = fkernel kern = afwMath.FixedKernel(kernelImg) maxloc = np.unravel_index(np.argmax(fkernel), fkernel.shape) kern.setCtrX(maxloc[0]) kern.setCtrY(maxloc[1]) outExp = exposure.clone() # Do this to keep WCS, PSF, masks, etc. convCntrl = afwMath.ConvolutionControl(False, True, 0) afwMath.convolve(outExp.getMaskedImage(), exposure.getMaskedImage(), kern, convCntrl) return outExp, kern
def psf(self, exposure, sources): """Measure the PSF @param exposure Exposure to process @param sources Measured sources on exposure """ assert exposure, "No exposure provided" assert sources, "No sources provided" psfPolicy = self.config['psf'] selName = psfPolicy['selectName'] selPolicy = psfPolicy['select'].getPolicy() algName = psfPolicy['algorithmName'] algPolicy = psfPolicy['algorithm'].getPolicy() self.log.log(self.log.INFO, "Measuring PSF") # # Run an extra detection step to mask out faint stars # if False: print "RHL is cleaning faint sources" import lsst.afw.math as afwMath sigma = 1.0 gaussFunc = afwMath.GaussianFunction1D(sigma) gaussKernel = afwMath.SeparableKernel(15, 15, gaussFunc, gaussFunc) im = exposure.getMaskedImage().getImage() convolvedImage = im.Factory(im.getDimensions()) afwMath.convolve(convolvedImage, im, gaussKernel) del im fs = afwDet.makeFootprintSet(convolvedImage, afwDet.createThreshold(4, "stdev")) fs = afwDet.makeFootprintSet(fs, 3, True) fs.setMask(exposure.getMaskedImage().getMask(), "DETECTED") starSelector = measAlg.makeStarSelector(selName, selPolicy) psfCandidateList = starSelector.selectStars(exposure, sources) psfDeterminer = measAlg.makePsfDeterminer(algName, algPolicy) psf, cellSet = psfDeterminer.determinePsf(exposure, psfCandidateList) # The PSF candidates contain a copy of the source, and so we need to explicitly propagate new flags for cand in psfCandidateList: cand = measAlg.cast_PsfCandidateF(cand) src = cand.getSource() if src.getFlagForDetection() & measAlg.Flags.PSFSTAR: ident = src.getId() src = sources[ident] assert src.getId() == ident src.setFlagForDetection(src.getFlagForDetection() | measAlg.Flags.PSFSTAR) exposure.setPsf(psf) return psf, cellSet
def doConvolve(exposure, kernel, use_scipy=False): """! Convolve an Exposure with a decorrelation convolution kernel. @param exposure Input afw.image.Exposure to be convolved. @param kernel Input 2-d numpy.array to convolve the image with @param use_scipy Use scipy to do convolution instead of afwMath @return a new Exposure with the convolved pixels and the (possibly re-centered) kernel. @note We use afwMath.convolve() but keep scipy.convolve for debugging. @note We re-center the kernel if necessary and return the possibly re-centered kernel """ def _fixEvenKernel(kernel): """! Take a kernel with even dimensions and make them odd, centered correctly. @param kernel a numpy.array @return a fixed kernel numpy.array """ # Make sure the peak (close to a delta-function) is in the center! maxloc = np.unravel_index(np.argmax(kernel), kernel.shape) out = np.roll(kernel, kernel.shape[0]//2 - maxloc[0], axis=0) out = np.roll(out, out.shape[1]//2 - maxloc[1], axis=1) # Make sure it is odd-dimensioned by trimming it. if (out.shape[0] % 2) == 0: maxloc = np.unravel_index(np.argmax(out), out.shape) if out.shape[0] - maxloc[0] > maxloc[0]: out = out[:-1, :] else: out = out[1:, :] if out.shape[1] - maxloc[1] > maxloc[1]: out = out[:, :-1] else: out = out[:, 1:] return out outExp = kern = None fkernel = _fixEvenKernel(kernel) if use_scipy: from scipy.ndimage.filters import convolve pci = convolve(exposure.getMaskedImage().getImage().getArray(), fkernel, mode='constant', cval=np.nan) outExp = exposure.clone() outExp.getMaskedImage().getImage().getArray()[:, :] = pci kern = fkernel else: kernelImg = afwImage.ImageD(fkernel.shape[0], fkernel.shape[1]) kernelImg.getArray()[:, :] = fkernel kern = afwMath.FixedKernel(kernelImg) maxloc = np.unravel_index(np.argmax(fkernel), fkernel.shape) kern.setCtrX(maxloc[0]) kern.setCtrY(maxloc[1]) outExp = exposure.clone() # Do this to keep WCS, PSF, masks, etc. convCntrl = afwMath.ConvolutionControl(False, True, 0) afwMath.convolve(outExp.getMaskedImage(), exposure.getMaskedImage(), kern, convCntrl) return outExp, kern
def testGaussian(self, imsize=50): # Convolve a delta function with a known gaussian; try to # recover using delta-function basis gsize = self.ps["kernelSize"] tsize = imsize + gsize gaussFunction = afwMath.GaussianFunction2D(2, 3) gaussKernel = afwMath.AnalyticKernel(gsize, gsize, gaussFunction) kImageIn = afwImage.ImageD(geom.Extent2I(gsize, gsize)) gaussKernel.computeImage(kImageIn, False) # template image with a single hot pixel in the exact center tmi = afwImage.MaskedImageF(geom.Extent2I(tsize, tsize)) tmi.set(0, 0x0, 1e-4) cpix = tsize // 2 tmi[cpix, cpix, afwImage.LOCAL] = (1, 0x0, 1) # science image smi = afwImage.MaskedImageF(tmi.getDimensions()) convolutionControl = afwMath.ConvolutionControl() convolutionControl.setDoNormalize(False) afwMath.convolve(smi, tmi, gaussKernel, convolutionControl) # get the actual kernel sum (since the image is not infinite) gscaling = afwMath.makeStatistics(smi, afwMath.SUM).getValue(afwMath.SUM) # grab only the non-masked subregion bbox = gaussKernel.shrinkBBox(smi.getBBox(afwImage.LOCAL)) tmi2 = afwImage.MaskedImageF(tmi, bbox, origin=afwImage.LOCAL) smi2 = afwImage.MaskedImageF(smi, bbox, origin=afwImage.LOCAL) # make sure its a valid subregion! for j in range(tmi2.getHeight()): for i in range(tmi2.getWidth()): self.assertEqual(tmi2.mask[i, j, afwImage.LOCAL], 0) self.assertEqual(smi2.mask[i, j, afwImage.LOCAL], 0) kc = ipDiffim.KernelCandidateF(0.0, 0.0, tmi2, smi2, self.ps) kList = ipDiffim.makeKernelBasisList(self.subconfig) kc.build(kList) self.assertEqual(kc.isInitialized(), True) kImageOut = kc.getImage() soln = kc.getKernelSolution(ipDiffim.KernelCandidateF.RECENT) self.assertAlmostEqual(soln.getKsum(), gscaling) self.assertAlmostEqual(soln.getBackground(), 0.0) for j in range(kImageOut.getHeight()): for i in range(kImageOut.getWidth()): self.assertAlmostEqual( kImageOut[i, j, afwImage.LOCAL] / kImageIn[i, j, afwImage.LOCAL], 1.0, 5)
def convolve(self, exposure, kernel): """Convolve image with kernel @param[in] exposure Exposure to convolve @param[in] kernel Kernel with which to convolve @output Convolved exposure """ convolved = exposure.Factory(exposure) afwMath.convolve(convolved.getMaskedImage(), exposure.getMaskedImage(), kernel, false) return convolved
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 runStdTest(self, kernel, refKernel=None, kernelDescr="", rtol=1.0e-05, atol=1e-08, maxInterpDist=10): """Assert that afwMath::convolve gives the same result as reference convolution for a given kernel. Inputs: - kernel: convolution kernel - 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) - maxInterpDist: maximum allowed distance for linear interpolation during convolution 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". """ convControl = afwMath.ConvolutionControl() convControl.setMaxInterpolationDistance(maxInterpDist) # verify dimension assertions: # - output image dimensions = input image dimensions # - input image width and height >= kernel width and height # Note: the assertion kernel size > 0 is tested elsewhere for inWidth in (kernel.getWidth() - 1, self.width - 1, self.width, self.width + 1): for inHeight in (kernel.getHeight() - 1, self.width - 1, self.width, self.width + 1): if (inWidth == self.width) and (inHeight == self.height): continue inMaskedImage = afwImage.MaskedImageF( afwGeom.Extent2I(inWidth, inHeight)) with self.assertRaises(Exception): afwMath.convolve(self.cnvMaskedImage, inMaskedImage, kernel) for doNormalize in (True, ): # (False, True): convControl.setDoNormalize(doNormalize) for doCopyEdge in (False, ): # (False, True): convControl.setDoCopyEdge(doCopyEdge) self.runBasicTest(kernel, convControl=convControl, refKernel=refKernel, kernelDescr=kernelDescr, rtol=rtol, atol=atol) # verify that basicConvolve does not write to edge pixels self.runBasicConvolveEdgeTest(kernel, kernelDescr)
def smoothExp(exp, smoothing, kernelSize=7): """Use for DISPLAY ONLY! Return a smoothed copy of the exposure with the original mask plane in place.""" psf = measAlg.DoubleGaussianPsf( kernelSize, kernelSize, smoothing / (2 * math.sqrt(2 * math.log(2)))) newExp = exp.clone() originalMask = exp.mask kernel = psf.getKernel() afwMath.convolve(newExp.maskedImage, newExp.maskedImage, kernel, afwMath.ConvolutionControl()) newExp.mask = originalMask return newExp
def testGaussian(self, imsize = 50): # Convolve a delta function with a known gaussian; try to # recover using delta-function basis gsize = self.policy.getInt("kernelSize") tsize = imsize + gsize gaussFunction = afwMath.GaussianFunction2D(2, 3) gaussKernel = afwMath.AnalyticKernel(gsize, gsize, gaussFunction) kImageIn = afwImage.ImageD(afwGeom.Extent2I(gsize, gsize)) gaussKernel.computeImage(kImageIn, False) # template image with a single hot pixel in the exact center tmi = afwImage.MaskedImageF(afwGeom.Extent2I(tsize, tsize)) tmi.set(0, 0x0, 1e-4) cpix = tsize // 2 tmi.set(cpix, cpix, (1, 0x0, 1)) # science image smi = afwImage.MaskedImageF(tmi.getDimensions()) afwMath.convolve(smi, tmi, gaussKernel, False) # get the actual kernel sum (since the image is not infinite) gscaling = afwMath.makeStatistics(smi, afwMath.SUM).getValue(afwMath.SUM) # grab only the non-masked subregion bbox = gaussKernel.shrinkBBox(smi.getBBox(afwImage.LOCAL)) tmi2 = afwImage.MaskedImageF(tmi, bbox, afwImage.LOCAL) smi2 = afwImage.MaskedImageF(smi, bbox, afwImage.LOCAL) # make sure its a valid subregion! for j in range(tmi2.getHeight()): for i in range(tmi2.getWidth()): self.assertEqual(tmi2.getMask().get(i, j), 0) self.assertEqual(smi2.getMask().get(i, j), 0) kc = ipDiffim.KernelCandidateF(0.0, 0.0, tmi2, smi2, self.policy) kList = ipDiffim.makeKernelBasisList(self.subconfig) kc.build(kList) self.assertEqual(kc.isInitialized(), True) kImageOut = kc.getImage() soln = kc.getKernelSolution(ipDiffim.KernelCandidateF.RECENT) self.assertAlmostEqual(soln.getKsum(), gscaling) self.assertAlmostEqual(soln.getBackground(), 0.0) for j in range(kImageOut.getHeight()): for i in range(kImageOut.getWidth()): self.assertAlmostEqual(kImageOut.get(i, j)/kImageIn.get(i, j), 1.0, 5)
def findBrightestStarRHL(exp, minArea=100, maxRadialOffset=1500): import lsst.afw.detection as afwDetect import lsst.afw.image as afwImage import lsst.afw.math as afwMath cexp = exp.clone() sigma = 4 ksize = 1 + 4*int(sigma + 1) gauss1d = afwMath.GaussianFunction1D(sigma) kernel = afwMath.SeparableKernel(ksize, ksize, gauss1d, gauss1d) convolvedImage = cexp.maskedImage.Factory(cexp.maskedImage.getBBox()) afwMath.convolve(convolvedImage, cexp.maskedImage, kernel) bbox = geom.BoxI(geom.PointI(ksize//2, ksize//2), geom.PointI(cexp.getWidth() - ksize//2 - 1, cexp.getHeight() - ksize//2 - 1)) tmp = cexp.maskedImage[bbox] cexp.image *= 0 tmp[bbox, afwImage.PARENT] = convolvedImage[bbox] del convolvedImage; del tmp if False: defectBBoxes = [ geom.BoxI(geom.PointI(548, 3562), geom.PointI(642, 3999)), geom.BoxI(geom.PointI(474, 3959), geom.PointI(1056, 3999)), geom.BoxI(geom.PointI(500, 0), geom.PointI(680, 40)), ] for bbox in defectBBoxes: cexp.maskedImage[bbox] = 0 threshold = 1000 feet = afwDetect.FootprintSet(cexp.maskedImage, afwDetect.Threshold(threshold), "DETECTED").getFootprints() maxVal = None centroid = None for foot in feet: peak = foot.peaks[0] if minArea > 0 and foot.getArea() < minArea: continue x, y = peak.getCentroid() if np.hypot(x - 2036, y - 2000) > maxRadialOffset: continue if maxVal is None or peak.getPeakValue() > maxVal: maxVal = peak.getPeakValue() centroid = peak.getCentroid() return centroid
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 == None: refKernel = kernel # strip garbage characters (whitespace and punctuation) to make a short description for saving files shortKernelDescr = kernelDescr.translate(NullTranslator, GarbageChars) 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 createImageAndKernel(sigma, psfSize, image): function = afwMath.GaussianFunction2D(sigma, sigma) kernel = afwMath.AnalyticKernel(psfSize, psfSize, function) psf = measAlg.KernelPsf(kernel) cim = afwImage.ImageF(image.getDimensions()) afwMath.convolve(cim, image, kernel, True) # Trim off the border pixels bbox = kernel.shrinkBBox(cim.getBBox(afwImage.LOCAL)) cim = afwImage.ImageF(cim, bbox, afwImage.LOCAL) cim.setXY0(0, 0) return cim, psf
def smoothExp(exp, fwhm, display=False): import lsst.afw.math as afwMath smoothedScienceExposure = exp.clone() gauss = afwMath.GaussianFunction1D(fwhm) kSize = int(2 * fwhm + 0.5) + 1 kernel = afwMath.SeparableKernel(kSize, kSize, gauss, gauss) afwMath.convolve(smoothedScienceExposure.getMaskedImage(), exp.getMaskedImage(), kernel) if display: import lsst.afw.display.ds9 as ds9 ds9.mtv(exp, title="science", frame=1) ds9.mtv(smoothedScienceExposure, title="smoothed", frame=2) return smoothedScienceExposure
def setUp(self): FWHM = 5 psf = algorithms.DoubleGaussianPsf(15, 15, FWHM/(2*math.sqrt(2*math.log(2)))) mi = afwImage.MaskedImageF(afwGeom.ExtentI(100, 100)) self.xc, self.yc, self.flux = 45, 55, 1000.0 mi.getImage().set(self.xc, self.yc, self.flux) cnvImage = mi.Factory(mi.getDimensions()) afwMath.convolve(cnvImage, mi, psf.getKernel(), afwMath.ConvolutionControl()) self.exp = afwImage.makeExposure(cnvImage) self.exp.setPsf(psf) if display and False: ds9.mtv(self.exp)
def setUp(self): FWHM = 5 psf = algorithms.DoubleGaussianPsf(15, 15, FWHM/(2*math.sqrt(2*math.log(2)))) mi = afwImage.MaskedImageF(lsst.geom.ExtentI(100, 100)) self.xc, self.yc, self.instFlux = 45, 55, 1000.0 mi.image[self.xc, self.yc, afwImage.LOCAL] = self.instFlux cnvImage = mi.Factory(mi.getDimensions()) afwMath.convolve(cnvImage, mi, psf.getKernel(), afwMath.ConvolutionControl()) self.exp = afwImage.makeExposure(cnvImage) self.exp.setPsf(psf) if display and False: afwDisplay.Display(frame=0).mtv(self.exp, title=self._testMethodName + ": image")
def testZeroWidthKernel(self): """Convolution by a 0x0 kernel should raise an exception. The only way to produce a 0x0 kernel is to use the default constructor (which exists only to support persistence; it does not produce a useful kernel). """ kernelList = [ afwMath.FixedKernel(), afwMath.AnalyticKernel(), afwMath.SeparableKernel(), # afwMath.DeltaFunctionKernel(), # DeltaFunctionKernel has no default constructor afwMath.LinearCombinationKernel(), ] convolutionControl = afwMath.ConvolutionControl() for kernel in kernelList: with self.assertRaises(Exception): afwMath.convolve(self.cnvMaskedImage, self.maskedImage, kernel, convolutionControl)
def smooth_gauss(masked_image, sigma=2.0, nsigma=7.0, inplace=False, use_scipy=False, **kwargs): """ Smooth image with a Gaussian kernel. Parameters ---------- masked_image : lsst.afw.image.imageLib.MaskedImageF Masked image object to be smoothed sigma : float Standard deviation of Gaussian nsigma : float, optional Number of sigma for kernel width inplace : bool, optional If True, smooth the image inplace. Else, return a clone of the masked image. use_scipy : bool, optional If True, perform smoothing using scipy.ndimage. Returns ------- convolved_image : lsst.afw.image.imageLib.MaskedImageF The convolved masked image """ if use_scipy: img_arr = get_image_ndarray(masked_image) img_arr_smooth = ndi.gaussian_filter( img_arr, sigma, truncate=nsigma, **kwargs) if inplace: convolved_image = masked_image else: convolved_image = masked_image.Factory(masked_image.getBBox()) convolved_image.getImage().getArray()[:] = img_arr_smooth else: width = (int(sigma*nsigma + 0.5) // 2)*2 + 1 # make sure it is odd gauss_func = afwMath.GaussianFunction1D(sigma) gauss_kern = afwMath.SeparableKernel( width, width, gauss_func, gauss_func) convolved_image = masked_image.Factory(masked_image.getBBox()) afwMath.convolve(convolved_image, masked_image, gauss_kern, afwMath.ConvolutionControl()) if inplace: img_arr_smooth = convolved_image.getImage().getArray() masked_image.getImage().getArray()[:] = img_arr_smooth return convolved_image
def setUp(self): self.config = ipDiffim.ImagePsfMatchTask.ConfigClass() self.subconfig = self.config.kernel.active self.policy = pexConfig.makePolicy(self.subconfig) self.kSize = self.policy.getInt('kernelSize') # gaussian reference kernel self.gSize = self.kSize self.gaussFunction = afwMath.GaussianFunction2D(2, 3) self.gaussKernel = afwMath.AnalyticKernel(self.gSize, self.gSize, self.gaussFunction) if defDataDir: defImagePath = os.path.join(defDataDir, "DC3a-Sim", "sci", "v5-e0", "v5-e0-c011-a00.sci.fits") self.templateImage = afwImage.MaskedImageF(defImagePath) self.scienceImage = self.templateImage.Factory(self.templateImage.getDimensions()) afwMath.convolve(self.scienceImage, self.templateImage, self.gaussKernel, False)
def testUnityConvolution(self): """Verify that convolution with a centered delta function reproduces the original. """ # create a delta function kernel that has 1,1 in the center kFunc = afwMath.IntegerDeltaFunction2D(0.0, 0.0) kernel = afwMath.AnalyticKernel(3, 3, kFunc) doNormalize = False doCopyEdge = False afwMath.convolve(self.cnvImage, self.maskedImage.getImage(), kernel, doNormalize, doCopyEdge) afwMath.convolve(self.cnvMaskedImage, self.maskedImage, kernel, doNormalize, doCopyEdge) cnvImMaskVarArr = self.cnvMaskedImage.getArrays() skipMaskArr = numpy.array(numpy.isnan(cnvImMaskVarArr[0]), dtype=numpy.uint16) kernelDescr = "Centered DeltaFunctionKernel (testing unity convolution)" self.assertImagesNearlyEqual(self.cnvImage, self.maskedImage.getImage(), skipMask=skipMaskArr, msg=kernelDescr) self.assertMaskedImagesNearlyEqual(self.cnvMaskedImage, self.maskedImage, skipMask=skipMaskArr, msg=kernelDescr)
def timeConvolution(outImage, inImage, kernel, convControl): """Time convolution @param outImage: output image or masked image (must be the same size as inImage) @param inImage: input image or masked image @param kernel: convolution kernel @param convControl: convolution control parameters (afwMath.ConvolutionControl) @return (elapsed time in seconds, number of iterations) """ startTime = time.time(); for nIter in range(1, MaxIter + 1): # mathDetail.convolveWithInterpolation(outImage, inImage, kernel, convControl) afwMath.convolve(outImage, inImage, kernel, convControl) endTime = time.time() if endTime - startTime > MaxTime: break return (endTime - startTime, nIter)
def _doConvolve(exposure, kernel): """! Convolve an Exposure with a decorrelation convolution kernel. @param exposure Input afw.image.Exposure to be convolved. @param kernel Input 2-d numpy.array to convolve the image with @return a new Exposure with the convolved pixels and the (possibly re-centered) kernel. @note We use afwMath.convolve() but keep scipy.convolve for debugging. @note We re-center the kernel if necessary and return the possibly re-centered kernel """ kernelImg = afwImage.ImageD(kernel.shape[0], kernel.shape[1]) kernelImg.getArray()[:, :] = kernel kern = afwMath.FixedKernel(kernelImg) maxloc = np.unravel_index(np.argmax(kernel), kernel.shape) kern.setCtrX(maxloc[0]) kern.setCtrY(maxloc[1]) outExp = exposure.clone() # Do this to keep WCS, PSF, masks, etc. convCntrl = afwMath.ConvolutionControl(False, True, 0) afwMath.convolve(outExp.getMaskedImage(), exposure.getMaskedImage(), kern, convCntrl) return outExp, kern
def setUp(self): self.min, self.range, self.Q = 0, 5, 20 # asinh width, height = 85, 75 self.images = [] self.images.append(afwImage.ImageF(lsst.geom.ExtentI(width, height))) self.images.append(afwImage.ImageF(lsst.geom.ExtentI(width, height))) self.images.append(afwImage.ImageF(lsst.geom.ExtentI(width, height))) for (x, y, A, g_r, r_i) in [(15, 15, 1000, 1.0, 2.0), (50, 45, 5500, -1.0, -0.5), (30, 30, 600, 1.0, 2.5), (45, 15, 20000, 1.0, 1.0), ]: for i in range(len(self.images)): if i == B: amp = A elif i == G: amp = A*math.pow(10, 0.4*g_r) elif i == R: amp = A*math.pow(10, 0.4*r_i) self.images[i][x, y, afwImage.LOCAL] = amp psf = afwMath.AnalyticKernel( 15, 15, afwMath.GaussianFunction2D(2.5, 1.5, 0.5)) convolvedImage = type(self.images[0])(self.images[0].getDimensions()) randomImage = type(self.images[0])(self.images[0].getDimensions()) rand = afwMath.Random("MT19937", 666) for i in range(len(self.images)): afwMath.convolve(convolvedImage, self.images[i], psf, True, True) afwMath.randomGaussianImage(randomImage, rand) randomImage *= 2 convolvedImage += randomImage self.images[i][:] = convolvedImage del convolvedImage del randomImage
def testBad(self): ti = afwImage.MaskedImageF(afwGeom.Extent2I(100, 100)) ti.getVariance().set(0.1) ti.set(50, 50, (1., 0x0, 1.)) sKernel = self.makeSpatialKernel(2) si = afwImage.MaskedImageF(ti.getDimensions()) afwMath.convolve(si, ti, sKernel, True) bbox = afwGeom.Box2I(afwGeom.Point2I(25, 25), afwGeom.Point2I(75, 75)) si = afwImage.MaskedImageF(si, bbox, afwImage.LOCAL) ti = afwImage.MaskedImageF(ti, bbox, afwImage.LOCAL) kc = ipDiffim.KernelCandidateF(50., 50., ti, si, self.policy) badGaussian = afwMath.GaussianFunction2D(1., 1., 0.) badKernel = afwMath.AnalyticKernel(self.ksize, self.ksize, badGaussian) basisList = afwMath.KernelList() basisList.append(badKernel) badSpatialKernelFunction = afwMath.PolynomialFunction2D(0) badSpatialKernel = afwMath.LinearCombinationKernel(basisList, badSpatialKernelFunction) badSpatialKernel.setSpatialParameters([[1,]]) sBg = afwMath.PolynomialFunction2D(1) bgCoeffs = [10., 10., 10.] sBg.setParameters(bgCoeffs) # must be initialized bskv = ipDiffim.BuildSingleKernelVisitorF(self.kList, self.policy) bskv.processCandidate(kc) self.assertEqual(kc.isInitialized(), True) askv = ipDiffim.AssessSpatialKernelVisitorF(badSpatialKernel, sBg, self.policy) askv.processCandidate(kc) self.assertEqual(askv.getNProcessed(), 1) self.assertEqual(askv.getNRejected(), 1) self.assertEqual(kc.getStatus(), afwMath.SpatialCellCandidate.BAD)
def setUp(self): self.config = ipDiffim.ImagePsfMatchTask.ConfigClass() self.config.kernel.name = "AL" self.subconfig = self.config.kernel.active self.kSize = self.subconfig.kernelSize # gaussian reference kernel self.gSize = self.kSize self.gaussFunction = afwMath.GaussianFunction2D(2, 3) self.gaussKernel = afwMath.AnalyticKernel(self.gSize, self.gSize, self.gaussFunction) # known input images try: self.defDataDir = lsst.utils.getPackageDir('afwdata') except Exception: self.defDataDir = None if self.defDataDir: defImagePath = os.path.join(self.defDataDir, "DC3a-Sim", "sci", "v5-e0", "v5-e0-c011-a00.sci.fits") self.templateImage = afwImage.MaskedImageF(defImagePath) self.scienceImage = self.templateImage.Factory( self.templateImage.getDimensions() ) afwMath.convolve(self.scienceImage, self.templateImage, self.gaussKernel, False)
def matchMaskedImages(self, templateMaskedImage, scienceMaskedImage, candidateList, templateFwhmPix=None, scienceFwhmPix=None): """PSF-match a MaskedImage (templateMaskedImage) to a reference MaskedImage (scienceMaskedImage). Do the following, in order: - Determine a PSF matching kernel and differential background model that matches templateMaskedImage to scienceMaskedImage - Convolve templateMaskedImage by the PSF matching kernel Parameters ---------- templateMaskedImage : `lsst.afw.image.MaskedImage` masked image to PSF-match to the reference masked image; must be warped to match the reference masked image scienceMaskedImage : `lsst.afw.image.MaskedImage` maskedImage whose PSF is to be matched to templateFwhmPix : `float` FWHM (in pixels) of the Psf in the template image (image to convolve) scienceFwhmPix : `float` FWHM (in pixels) of the Psf in the science image candidateList : `list`, optional A list of footprints/maskedImages for kernel candidates; if `None` then source detection is run. - Currently supported: list of Footprints or measAlg.PsfCandidateF Returns ------- result : `callable` An `lsst.pipe.base.Struct` containing these fields: - psfMatchedMaskedImage: the PSF-matched masked image = ``templateMaskedImage`` convolved with psfMatchingKernel. This has the same xy0, dimensions and wcs as ``scienceMaskedImage``. - psfMatchingKernel: the PSF matching kernel - backgroundModel: differential background model - kernelCellSet: SpatialCellSet used to solve for the PSF matching kernel Raises ------ RuntimeError Raised if input images have different dimensions """ import lsstDebug display = lsstDebug.Info(__name__).display displayTemplate = lsstDebug.Info(__name__).displayTemplate displaySciIm = lsstDebug.Info(__name__).displaySciIm displaySpatialCells = lsstDebug.Info(__name__).displaySpatialCells maskTransparency = lsstDebug.Info(__name__).maskTransparency if not maskTransparency: maskTransparency = 0 if display: afwDisplay.setDefaultMaskTransparency(maskTransparency) if not candidateList: raise RuntimeError("Candidate list must be populated by makeCandidateList") if not self._validateSize(templateMaskedImage, scienceMaskedImage): self.log.error("ERROR: Input images different size") raise RuntimeError("Input images different size") if display and displayTemplate: disp = afwDisplay.Display(frame=lsstDebug.frame) disp.mtv(templateMaskedImage, title="Image to convolve") lsstDebug.frame += 1 if display and displaySciIm: disp = afwDisplay.Display(frame=lsstDebug.frame) disp.mtv(scienceMaskedImage, title="Image to not convolve") lsstDebug.frame += 1 kernelCellSet = self._buildCellSet(templateMaskedImage, scienceMaskedImage, candidateList) if display and displaySpatialCells: diffimUtils.showKernelSpatialCells(scienceMaskedImage, kernelCellSet, symb="o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW, ctypeBad=afwDisplay.RED, size=4, frame=lsstDebug.frame, title="Image to not convolve") lsstDebug.frame += 1 if templateFwhmPix and scienceFwhmPix: self.log.info("Matching Psf FWHM %.2f -> %.2f pix", templateFwhmPix, scienceFwhmPix) if self.kConfig.useBicForKernelBasis: tmpKernelCellSet = self._buildCellSet(templateMaskedImage, scienceMaskedImage, candidateList) nbe = diffimTools.NbasisEvaluator(self.kConfig, templateFwhmPix, scienceFwhmPix) bicDegrees = nbe(tmpKernelCellSet, self.log) basisList = makeKernelBasisList(self.kConfig, templateFwhmPix, scienceFwhmPix, alardDegGauss=bicDegrees[0], metadata=self.metadata) del tmpKernelCellSet else: basisList = makeKernelBasisList(self.kConfig, templateFwhmPix, scienceFwhmPix, metadata=self.metadata) spatialSolution, psfMatchingKernel, backgroundModel = self._solve(kernelCellSet, basisList) psfMatchedMaskedImage = afwImage.MaskedImageF(templateMaskedImage.getBBox()) doNormalize = False afwMath.convolve(psfMatchedMaskedImage, templateMaskedImage, psfMatchingKernel, doNormalize) return pipeBase.Struct( matchedImage=psfMatchedMaskedImage, psfMatchingKernel=psfMatchingKernel, backgroundModel=backgroundModel, kernelCellSet=kernelCellSet, )
def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True): """!Detect footprints. \param exposure Exposure to process; DETECTED{,_NEGATIVE} mask plane will be set in-place. \param doSmooth if True, smooth the image before detection using a Gaussian of width sigma \param sigma sigma of PSF (pixels); used for smoothing and to grow detections; if None then measure the sigma of the PSF of the exposure \param clearMask Clear both DETECTED and DETECTED_NEGATIVE planes before running detection \return a lsst.pipe.base.Struct with fields: - positive: lsst.afw.detection.FootprintSet with positive polarity footprints (may be None) - negative: lsst.afw.detection.FootprintSet with negative polarity footprints (may be None) - numPos: number of footprints in positive or 0 if detection polarity was negative - numNeg: number of footprints in negative or 0 if detection polarity was positive - background: re-estimated background. None if reEstimateBackground==False \throws lsst.pipe.base.TaskError if sigma=None and the exposure has no PSF """ try: import lsstDebug display = lsstDebug.Info(__name__).display except ImportError: try: display except NameError: display = False if exposure is None: raise RuntimeError("No exposure for detection") maskedImage = exposure.getMaskedImage() region = maskedImage.getBBox() if clearMask: mask = maskedImage.getMask() mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask("DETECTED_NEGATIVE")) del mask if self.config.doTempLocalBackground: tempBgRes = self.tempLocalBackground.run(maskedImage) tempLocalBkgdImage = tempBgRes.background.getImage() if sigma is None: psf = exposure.getPsf() if psf is None: raise pipeBase.TaskError("exposure has no PSF; must specify sigma") shape = psf.computeShape() sigma = shape.getDeterminantRadius() self.metadata.set("sigma", sigma) self.metadata.set("doSmooth", doSmooth) if not doSmooth: convolvedImage = maskedImage.Factory(maskedImage) middle = convolvedImage else: # smooth using a Gaussian (which is separate, hence fast) of width sigma # make a SingleGaussian (separable) kernel with the 'sigma' psf = exposure.getPsf() kWidth = (int(sigma * 7 + 0.5) // 2) * 2 + 1 # make sure it is odd self.metadata.set("smoothingKernelWidth", kWidth) gaussFunc = afwMath.GaussianFunction1D(sigma) gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc) convolvedImage = maskedImage.Factory(maskedImage.getBBox()) afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl()) # # Only search psf-smooth part of frame # goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox()) middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT, False) # # Mark the parts of the image outside goodBBox as EDGE # self.setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask("EDGE")) fpSets = pipeBase.Struct(positive=None, negative=None) if self.config.thresholdPolarity != "negative": fpSets.positive = self.thresholdImage(middle, "positive") if self.config.reEstimateBackground or self.config.thresholdPolarity != "positive": fpSets.negative = self.thresholdImage(middle, "negative") for polarity, maskName in (("positive", "DETECTED"), ("negative", "DETECTED_NEGATIVE")): fpSet = getattr(fpSets, polarity) if fpSet is None: continue fpSet.setRegion(region) if self.config.nSigmaToGrow > 0: nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5) self.metadata.set("nGrow", nGrow) fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow) fpSet.setMask(maskedImage.getMask(), maskName) if not self.config.returnOriginalFootprints: setattr(fpSets, polarity, fpSet) fpSets.numPos = len(fpSets.positive.getFootprints()) if fpSets.positive is not None else 0 fpSets.numNeg = len(fpSets.negative.getFootprints()) if fpSets.negative is not None else 0 if self.config.thresholdPolarity != "negative": self.log.log(self.log.INFO, "Detected %d positive sources to %g sigma." % (fpSets.numPos, self.config.thresholdValue*self.config.includeThresholdMultiplier)) if self.config.doTempLocalBackground: maskedImage += tempLocalBkgdImage fpSets.background = None if self.config.reEstimateBackground: mi = exposure.getMaskedImage() bkgd = self.background.fitBackground(mi) if self.config.adjustBackground: self.log.log(self.log.WARN, "Fiddling the background by %g" % self.config.adjustBackground) bkgd += self.config.adjustBackground fpSets.background = bkgd self.log.log(self.log.INFO, "Resubtracting the background after object detection") mi -= bkgd.getImageF() del mi if self.config.thresholdPolarity == "positive": if self.config.reEstimateBackground: mask = maskedImage.getMask() mask &= ~mask.getPlaneBitMask("DETECTED_NEGATIVE") del mask fpSets.negative = None else: self.log.log(self.log.INFO, "Detected %d negative sources to %g %s" % (fpSets.numNeg, self.config.thresholdValue, ("DN" if self.config.thresholdType == "value" else "sigma"))) if display: ds9.mtv(exposure, frame=0, title="detection") x0, y0 = exposure.getXY0() def plotPeaks(fps, ctype): if fps is None: return with ds9.Buffering(): for fp in fps.getFootprints(): for pp in fp.getPeaks(): ds9.dot("+", pp.getFx() - x0, pp.getFy() - y0, ctype=ctype) plotPeaks(fpSets.positive, "yellow") plotPeaks(fpSets.negative, "red") if convolvedImage and display and display > 1: ds9.mtv(convolvedImage, frame=1, title="PSF smoothed") return fpSets
def doit(args): dataId = args print "# Running", dataId # I need to create a separate instance in each thread mapper = Mapper(root = "/lsst7/stripe82/dr7/runs/", calibRoot = None, outputRoot = None) butler = dafPersist.ButlerFactory(mapper = mapper).create() # Grab science pixels im = butler.get(datasetType="fpC", dataId = dataId).convertF() # Remove the 128 pixel duplicate overlap between fields # See python/lsst/obs/sdss/processCcdSdss.py for guidance bbox = im.getBBox() begin = bbox.getBegin() extent = bbox.getDimensions() extent -= afwGeom.Extent2I(0, 128) tbbox = afwGeom.BoxI(begin, extent) im = afwImage.ImageF(im, tbbox, True) # Remove 1000 count pedestal im -= 1000.0 # Create image variance from gain calib, gain = butler.get(datasetType="tsField", dataId = dataId) var = afwImage.ImageF(im, True) var /= gain # Note I need to do a bit extra for the mask; I actually need to call # convertfpM with allPlanes = True to get all the SDSS info # # mask = butler.get(datasetType="fpM", dataId = dataId) fpMFile = butler.mapper.map_fpM(dataId = dataId).getLocations()[0] mask = convertfpM(fpMFile, True) # Remove the 128 pixel duplicate overlap... mask = afwImage.MaskU(mask, tbbox, True) # We need this for the background estimation exp = afwImage.ExposureF(afwImage.MaskedImageF(im, mask, var)) # Subtract off background, and scale by stdev # This will turn the image into "sigma" if False: ctrl = afwMath.StatisticsControl(5.0, 5) bitPlanes = mask.getMaskPlaneDict().values() bitMask = 2**bitPlanes[0] for plane in bitPlanes[1:]: bitMask |= 2**bitPlanes[plane] ctrl.setAndMask(bitMask) stat = afwMath.makeStatistics(im, mask, afwMath.STDEVCLIP | afwMath.MEDIAN | afwMath.NPOINT, ctrl) stdev = stat.getValue(afwMath.STDEVCLIP) bg = stat.getValue(afwMath.MEDIAN) elif False: # It should be that afwMath.NPOINT = len(idx[0]) # Not the case, exactly, so go with what you know idx = np.where(mask.getArray() == 0) gdata = im.getArray()[idx] bg = np.median(gdata) stdev = 0.741 * (np.percentile(gdata, 75) - np.percentile(gdata, 25)) else: # Do full-on background subtraction bgctrl = measAlg.BackgroundConfig(binSize=512, statisticsProperty="MEANCLIP", ignoredPixelMask=mask.getMaskPlaneDict().keys()) bg, bgsubexp = measAlg.estimateBackground(exp, bgctrl, subtract=True) im = bgsubexp.getMaskedImage().getImage() sctrl = afwMath.StatisticsControl() sctrl.setAndMask(reduce(lambda x, y, mask=mask: x | mask.getPlaneBitMask(y), bgctrl.ignoredPixelMask, 0x0)) stdev = afwMath.makeStatistics(im, mask, afwMath.STDEVCLIP, sctrl).getValue(afwMath.STDEVCLIP) im /= stdev # Decision point: do I send the convolution a MaskedImage, in which # case the mask is also spread, or just an Image, and not spread # the mask... # # I think for now I will not spread the mask so that it represents the # condition of the underlying pixels, not the Psf-filtered ones psf = butler.get(datasetType="psField", dataId = dataId) wcs = butler.get(datasetType="asTrans", dataId = dataId) # Image convolved with the Psf, i.e. maximum point source likelihood image cim = afwImage.ImageF(im, True) afwMath.convolve(cim, im, psf.getKernel(), True) # The pixels that are "good" in the image, i.e. ignore borders cBBox = psf.getKernel().shrinkBBox(cim.getBBox()) cim = afwImage.ImageF(cim, cBBox) mask = afwImage.MaskU(mask, cBBox) # Create an ra,decl map for the good pixels raIm = afwImage.ImageF(cim.getDimensions()) decIm = afwImage.ImageF(cim.getDimensions()) nx, ny = cim.getDimensions() # But note that the Wcs expects their coordinates in the non-shrunk image x0 = cBBox.getBeginX() y0 = cBBox.getBeginY() x1 = cBBox.getEndX() y1 = cBBox.getEndY() for y in range(ny): for x in range(nx): ra, decl = getRaDecl(wcs, x+x0, y+y0) raIm.set(x, y, ra) decIm.set(x, y, decl) run = dataId["run"] camcol = dataId["camcol"] field = dataId["field"] filterName = dataId["filter"] if doWriteSql: # Make the table inputs xll, yll = getRaDecl(wcs, 0 +x0, 0+ y0) xlr, ylr = getRaDecl(wcs, x1+x0, 0+ y0) xur, yur = getRaDecl(wcs, x1+x0, y1+y0) xul, yul = getRaDecl(wcs, 0 +x0, y1+y0) tc = calib.getMidTime() t0 = dafBase.DateTime(tc.nsecs() - int(0.5 * calib.getExptime() * 1e9), dafBase.DateTime.TAI) t1 = dafBase.DateTime(tc.nsecs() + int(0.5 * calib.getExptime() * 1e9), dafBase.DateTime.TAI) # Magic for the day; 2**63 because BIGINT is signed fieldId = int(md5.new(" ".join(map(str, [run, filterName, camcol, field]))).hexdigest(), 16) % 2**63 pfile = "pixel-%06d-%s%s-%04d.csv" % (run, filterName, camcol, field) ffile = "field-%06d-%s%s-%04d.pgsql" % (run, filterName, camcol, field) pbuff = open(pfile, "w") fbuff = open(ffile, "w") fbuff.write("INSERT INTO fields (fieldId, run, camcol, field, filter, bbox, tmid, trange) VALUES\n") fbuff.write(" (%d, %d, %d, %d, '%s', ST_GeomFromText('POLYGON((\n" % (fieldId, run, camcol, field, filterName)) fbuff.write(" %.9f %.9f, %.9f %.9f,\n" % (xll, yll, xlr, ylr)) fbuff.write(" %.9f %.9f, %.9f %.9f,\n" % (xur, yur, xul, yul)) fbuff.write(" %.9f %.9f))',3786),\n" % (xll, yll)) fbuff.write(" '%s',\n" % (re.sub("T", " ", tc.toString()))) fbuff.write(" '[%s, %s]');\n" % (re.sub("T", " ", t0.toString()), re.sub("T", " ", t1.toString()))) for y in range(ny): for x in range(nx): # Note the different orders of raIm,decIm and cim,mask pbuff.write("%d, %.9f, %.9f, %f, %d\n" % (fieldId, raIm.get(x,y), decIm.get(x,y), cim.get(x,y), mask.get(x,y))) pbuff.close() fbuff.close() if False: cim.writeFits("image-%06d-%s%s-%04d.fits" % (run, filterName, camcol, field)) mask.writeFits("mask-%06d-%s%s-%04d.fits" % (run, filterName, camcol, field)) raIm.writeFits("ra-%06d-%s%s-%04d.fits" % (run, filterName, camcol, field)) decIm.writeFits("dec-%06d-%s%s-%04d.fits" % (run, filterName, camcol, field))
t_start = time.time() image = makeImageFromArray(data) dt = time.time() - t0 print "DM image made in = %.2f us" %(dt*1e6) t_total += dt t0 = time.time() sigma = 0.75 # kWidth = (int(sigma * 7 + 0.5) // 2) * 2 + 1 # make sure it is odd kWidth = 5 ####### discrete method gaussFunc = math.GaussianFunction1D(sigma) gaussKernel = math.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc) math.convolve(image, image, gaussKernel, math.ConvolutionControl()) ####### analytical method # kernel = math.AnalyticKernel(kWidth, kWidth, math.GaussianFunction2D(sigma, sigma, 0)) # math.convolve(image, image, kernel, True, True) dt = time.time() - t0 print "image smoothed in = %.2f us" %(dt*1e6) t_total += dt ds9.mtv(image) #calculate basic stats t0 = time.time() basic_mean = np.std(data) basic_stddev = np.mean(data)
def brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain): """Apply brighter fatter correction in place for the image. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure to have brighter-fatter correction applied. Modified by this method. kernel : `numpy.ndarray` Brighter-fatter kernel to apply. maxIter : scalar Number of correction iterations to run. threshold : scalar Convergence threshold in terms of the sum of absolute deviations between an iteration and the previous one. applyGain : `Bool` If True, then the exposure values are scaled by the gain prior to correction. Notes ----- This correction takes a kernel that has been derived from flat field images to redistribute the charge. The gradient of the kernel is the deflection field due to the accumulated charge. Given the original image I(x) and the kernel K(x) we can compute the corrected image Ic(x) using the following equation: Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y)))) To evaluate the derivative term we expand it as follows: 0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y))) + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) ) Because we use the measured counts instead of the incident counts we apply the correction iteratively to reconstruct the original counts and the correction. We stop iterating when the summed difference between the current corrected image and the one from the previous iteration is below the threshold. We do not require convergence because the number of iterations is too large a computational cost. How we define the threshold still needs to be evaluated, the current default was shown to work reasonably well on a small set of images. For more information on the method see DocuShare Document-19407. The edges as defined by the kernel are not corrected because they have spurious values due to the convolution. """ image = exposure.getMaskedImage().getImage() # The image needs to be units of electrons/holes with gainContext(exposure, image, applyGain): kLx = numpy.shape(kernel)[0] kLy = numpy.shape(kernel)[1] kernelImage = afwImage.ImageD(kLx, kLy) kernelImage.getArray()[:, :] = kernel tempImage = image.clone() nanIndex = numpy.isnan(tempImage.getArray()) tempImage.getArray()[nanIndex] = 0. outImage = afwImage.ImageF(image.getDimensions()) corr = numpy.zeros_like(image.getArray()) prev_image = numpy.zeros_like(image.getArray()) convCntrl = afwMath.ConvolutionControl(False, True, 1) fixedKernel = afwMath.FixedKernel(kernelImage) # Define boundary by convolution region. The region that the correction will be # calculated for is one fewer in each dimension because of the second derivative terms. # NOTE: these need to use integer math, as we're using start:end as numpy index ranges. startX = kLx//2 endX = -kLx//2 startY = kLy//2 endY = -kLy//2 for iteration in range(maxIter): afwMath.convolve(outImage, tempImage, fixedKernel, convCntrl) tmpArray = tempImage.getArray() outArray = outImage.getArray() with numpy.errstate(invalid="ignore", over="ignore"): # First derivative term gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX]) gradOut = numpy.gradient(outArray[startY:endY, startX:endX]) first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1] # Second derivative term diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1] diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX] second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21) corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second) tmpArray[:, :] = image.getArray()[:, :] tmpArray[nanIndex] = 0. tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX] if iteration > 0: diff = numpy.sum(numpy.abs(prev_image - tmpArray)) if diff < threshold: break prev_image[:, :] = tmpArray[:, :] # if iteration == maxIter - 1: # self.log.warn("Brighter fatter correction did not converge, # final difference %f" % diff) # self.log.info("Finished brighter fatter in %d iterations" % (iteration + 1)) image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \ corr[startY + 1:endY - 1, startX + 1:endX - 1]
def testDetection(self): """Test object detection""" # # Fix defects # # Mask known bad pixels # measAlgorithmsDir = lsst.utils.getPackageDir('meas_algorithms') badPixels = defects.policyToBadRegionList(os.path.join(measAlgorithmsDir, "policy/BadPixels.paf")) # did someone lie about the origin of the maskedImage? If so, adjust bad pixel list if self.XY0.getX() != self.mi.getX0() or self.XY0.getY() != self.mi.getY0(): dx = self.XY0.getX() - self.mi.getX0() dy = self.XY0.getY() - self.mi.getY0() for bp in badPixels: bp.shift(-dx, -dy) algorithms.interpolateOverDefects(self.mi, self.psf, badPixels) # # Subtract background # bgGridSize = 64 # was 256 ... but that gives only one region and the spline breaks bctrl = afwMath.BackgroundControl(afwMath.Interpolate.NATURAL_SPLINE) bctrl.setNxSample(int(self.mi.getWidth()/bgGridSize) + 1) bctrl.setNySample(int(self.mi.getHeight()/bgGridSize) + 1) backobj = afwMath.makeBackground(self.mi.getImage(), bctrl) self.mi.getImage()[:] -= backobj.getImageF() # # Remove CRs # crConfig = algorithms.FindCosmicRaysConfig() algorithms.findCosmicRays(self.mi, self.psf, 0, pexConfig.makePolicy(crConfig)) # # We do a pretty good job of interpolating, so don't propagagate the convolved CR/INTRP bits # (we'll keep them for the original CR/INTRP pixels) # savedMask = self.mi.getMask().Factory(self.mi.getMask(), True) saveBits = savedMask.getPlaneBitMask("CR") | \ savedMask.getPlaneBitMask("BAD") | \ savedMask.getPlaneBitMask("INTRP") # Bits to not convolve savedMask &= saveBits msk = self.mi.getMask() msk &= ~saveBits # Clear the saved bits del msk # # Smooth image # psf = algorithms.DoubleGaussianPsf(15, 15, self.FWHM/(2*math.sqrt(2*math.log(2)))) cnvImage = self.mi.Factory(self.mi.getBBox()) kernel = psf.getKernel() afwMath.convolve(cnvImage, self.mi, kernel, afwMath.ConvolutionControl()) msk = cnvImage.getMask() msk |= savedMask # restore the saved bits del msk threshold = afwDetection.Threshold(3, afwDetection.Threshold.STDEV) # # Only search the part of the frame that was PSF-smoothed # llc = lsst.geom.PointI(psf.getKernel().getWidth()//2, psf.getKernel().getHeight()//2) urc = lsst.geom.PointI(cnvImage.getWidth() - llc[0] - 1, cnvImage.getHeight() - llc[1] - 1) middle = cnvImage.Factory(cnvImage, lsst.geom.BoxI(llc, urc), afwImage.LOCAL) ds = afwDetection.FootprintSet(middle, threshold, "DETECTED") del middle # # Reinstate the saved (e.g. BAD) (and also the DETECTED | EDGE) bits in the unsmoothed image # savedMask[:] = cnvImage.getMask() msk = self.mi.getMask() msk |= savedMask del msk del savedMask if display: disp = afwDisplay.Display(frame=2) disp.mtv(self.mi, title=self._testMethodName + ": image") afwDisplay.Display(frame=3).mtv(cnvImage, title=self._testMethodName + ": cnvImage") # # Time to actually measure # schema = afwTable.SourceTable.makeMinimalSchema() sfm_config = measBase.SingleFrameMeasurementConfig() sfm_config.plugins = ["base_SdssCentroid", "base_CircularApertureFlux", "base_PsfFlux", "base_SdssShape", "base_GaussianFlux", "base_PixelFlags"] sfm_config.slots.centroid = "base_SdssCentroid" sfm_config.slots.shape = "base_SdssShape" sfm_config.slots.psfFlux = "base_PsfFlux" sfm_config.slots.gaussianFlux = None sfm_config.slots.apFlux = "base_CircularApertureFlux_3_0" sfm_config.slots.modelFlux = "base_GaussianFlux" sfm_config.slots.calibFlux = None sfm_config.plugins["base_SdssShape"].maxShift = 10.0 sfm_config.plugins["base_CircularApertureFlux"].radii = [3.0] task = measBase.SingleFrameMeasurementTask(schema, config=sfm_config) measCat = afwTable.SourceCatalog(schema) # detect the sources and run with the measurement task ds.makeSources(measCat) self.exposure.setPsf(self.psf) task.run(measCat, self.exposure) self.assertGreater(len(measCat), 0) for source in measCat: if source.get("base_PixelFlags_flag_edge"): continue if display: disp.dot("+", source.getX(), source.getY())