def testTicket2872(self): """Test that CoaddPsf.getAveragePosition() is always a position at which we can call computeImage(). """ cdelt = (0.2 * afwGeom.arcseconds).asDegrees() wcs = afwImage.makeWcs( afwCoord.IcrsCoord(afwGeom.Point2D(45.0, 45.0), afwGeom.degrees), afwGeom.Point2D(50, 50), cdelt, 0.0, 0.0, cdelt) kernel = measAlg.DoubleGaussianPsf(7, 7, 2.0).getKernel() psf1 = measAlg.KernelPsf(kernel, afwGeom.Point2D(0, 50)) psf2 = measAlg.KernelPsf(kernel, afwGeom.Point2D(100, 50)) record1 = self.mycatalog.addNew() record1.setPsf(psf1) record1.setWcs(wcs) record1.setD(self.weightKey, 1.0) record1.setBBox( afwGeom.Box2I(afwGeom.Point2I(-40, 0), afwGeom.Point2I(40, 100))) record2 = self.mycatalog.addNew() record2.setPsf(psf2) record2.setWcs(wcs) record2.setD(self.weightKey, 1.0) record2.setBBox( afwGeom.Box2I(afwGeom.Point2I(60, 0), afwGeom.Point2I(140, 100))) coaddPsf = measAlg.CoaddPsf(self.mycatalog, wcs) naiveAvgPos = afwGeom.Point2D(50, 50) with self.assertRaises(pexExceptions.InvalidParameterError): coaddPsf.computeKernelImage(naiveAvgPos) # important test is that this doesn't throw: coaddPsf.computeKernelImage()
def testTicket2872(self): """Test that CoaddPsf.getAveragePosition() is always a position at which we can call computeImage(). """ schema = afwTable.ExposureTable.makeMinimalSchema() weightKey = schema.addField("weight", type=float, doc="photometric weight") catalog = afwTable.ExposureCatalog(schema) cdelt = (0.2 * afwGeom.arcseconds).asDegrees() wcs = afwImage.makeWcs( afwCoord.IcrsCoord(afwGeom.Point2D(45.0, 45.0), afwGeom.degrees), afwGeom.Point2D(50, 50), cdelt, 0.0, 0.0, cdelt) kernel = measAlg.DoubleGaussianPsf(7, 7, 2.0).getKernel() psf1 = measAlg.KernelPsf(kernel, afwGeom.Point2D(0, 50)) psf2 = measAlg.KernelPsf(kernel, afwGeom.Point2D(100, 50)) record1 = catalog.addNew() record1.setPsf(psf1) record1.setWcs(wcs) record1.setD(weightKey, 1.0) record1.setBBox( afwGeom.Box2I(afwGeom.Point2I(-40, 0), afwGeom.Point2I(40, 100))) record2 = catalog.addNew() record2.setPsf(psf2) record2.setWcs(wcs) record2.setD(weightKey, 1.0) record2.setBBox( afwGeom.Box2I(afwGeom.Point2I(60, 0), afwGeom.Point2I(140, 100))) coaddPsf = measAlg.CoaddPsf(catalog, wcs) naiveAvgPos = afwGeom.Point2D(50, 50) self.assertRaises(pexExceptions.InvalidParameterError, coaddPsf.computeKernelImage, naiveAvgPos) # important test is that this doesn't throw: coaddPsf.computeKernelImage()
def testTicket2872(self): """Test that CoaddPsf.getAveragePosition() is always a position at which we can call computeImage(). """ scale = 0.2 * lsst.geom.arcseconds cdMatrix = afwGeom.makeCdMatrix(scale=scale) wcs = afwGeom.makeSkyWcs( crpix=lsst.geom.Point2D(50, 50), crval=lsst.geom.SpherePoint(45.0, 45.0, lsst.geom.degrees), cdMatrix=cdMatrix, ) kernel = measAlg.DoubleGaussianPsf(7, 7, 2.0).getKernel() psf1 = measAlg.KernelPsf(kernel, lsst.geom.Point2D(0, 50)) psf2 = measAlg.KernelPsf(kernel, lsst.geom.Point2D(100, 50)) record1 = self.mycatalog.addNew() record1.setPsf(psf1) record1.setWcs(wcs) record1.setD(self.weightKey, 1.0) record1.setBBox( lsst.geom.Box2I(lsst.geom.Point2I(-40, 0), lsst.geom.Point2I(40, 100))) record2 = self.mycatalog.addNew() record2.setPsf(psf2) record2.setWcs(wcs) record2.setD(self.weightKey, 1.0) record2.setBBox( lsst.geom.Box2I(lsst.geom.Point2I(60, 0), lsst.geom.Point2I(140, 100))) coaddPsf = measAlg.CoaddPsf(self.mycatalog, wcs) naiveAvgPos = lsst.geom.Point2D(50, 50) with self.assertRaises(pexExceptions.InvalidParameterError): coaddPsf.computeKernelImage(naiveAvgPos) # important test is that this doesn't throw: coaddPsf.computeKernelImage(coaddPsf.getAveragePosition())
def performALZCExposureCorrection(templateExposure, exposure, subtractedExposure, psfMatchingKernel, log): kimg = alPsfMatchingKernelToArray(psfMatchingKernel, subtractedExposure) # Compute the images' sigmas (sqrt of variance) sig1 = templateExposure.getMaskedImage().getVariance().getArray() sig2 = exposure.getMaskedImage().getVariance().getArray() sig1squared, _, _, _ = computeClippedImageStats(sig1) sig2squared, _, _, _ = computeClippedImageStats(sig2) corrKernel = computeDecorrelationKernel(kimg, sig1squared, sig2squared) # Eventually, use afwMath.convolve(), but for now just use scipy. log.info("ALZC: Convolving.") pci, _ = doConvolve( subtractedExposure.getMaskedImage().getImage().getArray(), corrKernel) subtractedExposure.getMaskedImage().getImage().getArray()[:, :] = pci log.info("ALZC: Finished with convolution.") # Compute the subtracted exposure's updated psf psf = afwPsfToArray(subtractedExposure.getPsf(), subtractedExposure) # .computeImage().getArray() psfc = computeCorrectedDiffimPsf(corrKernel, psf, svar=sig1squared, tvar=sig2squared) psfcI = afwImage.ImageD( subtractedExposure.getPsf().computeKernelImage().getBBox()) psfcI.getArray()[:, :] = psfc psfcK = afwMath.FixedKernel(psfcI) psfNew = measAlg.KernelPsf(psfcK) subtractedExposure.setPsf(psfNew) return subtractedExposure, corrKernel
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 makeHSCExposure(self, galData, psfData, pixScale, variance): ny, nx = galData.shape exposure = afwImg.ExposureF(nx, ny) exposure.getMaskedImage().getImage().getArray()[:, :] = galData exposure.getMaskedImage().getVariance().getArray()[:, :] = variance #Set the PSF ngridPsf = psfData.shape[0] psfLsst = afwImg.ImageF(ngridPsf, ngridPsf) psfLsst.getArray()[:, :] = psfData psfLsst = psfLsst.convertD() kernel = afwMath.FixedKernel(psfLsst) kernelPSF = meaAlg.KernelPsf(kernel) exposure.setPsf(kernelPSF) #prepare the wcs #Rotation cdelt = (pixScale * afwGeom.arcseconds) CD = afwGeom.makeCdMatrix(cdelt, afwGeom.Angle(0.)) #no rotation #wcs crval = afwGeom.SpherePoint(afwGeom.Angle(0., afwGeom.degrees), afwGeom.Angle(0., afwGeom.degrees)) #crval = afwCoord.IcrsCoord(0.*afwGeom.degrees, 0.*afwGeom.degrees) # hscpipe6 crpix = afwGeom.Point2D(0.0, 0.0) dataWcs = afwGeom.makeSkyWcs(crpix, crval, CD) exposure.setWcs(dataWcs) #prepare the frc dataCalib = afwImg.makePhotoCalibFromCalibZeroPoint(63095734448.0194) exposure.setPhotoCalib(dataCalib) return exposure
def makeExposure(imgArray, psfArray, imgVariance): """! Convert an image numpy.array and corresponding PSF numpy.array into an exposure. Add the (constant) variance plane equal to `imgVariance`. @param imgArray 2-d numpy.array containing the image @param psfArray 2-d numpy.array containing the PSF image @param imgVariance variance of input image @return a new exposure containing the image, PSF and desired variance plane """ # All this code to convert the template image array/psf array into an exposure. bbox = geom.Box2I( geom.Point2I(0, 0), geom.Point2I(imgArray.shape[1] - 1, imgArray.shape[0] - 1)) im1ex = afwImage.ExposureD(bbox) im1ex.getMaskedImage().getImage().getArray()[:, :] = imgArray im1ex.getMaskedImage().getVariance().getArray()[:, :] = imgVariance psfBox = geom.Box2I(geom.Point2I(-12, -12), geom.Point2I(12, 12)) # a 25x25 pixel psf psf = afwImage.ImageD(psfBox) psfBox.shift(geom.Extent2I(size[0] // 2, size[1] // 2)) im1_psf_sub = psfArray[psfBox.getMinX():psfBox.getMaxX() + 1, psfBox.getMinY():psfBox.getMaxY() + 1] psf.getArray()[:, :] = im1_psf_sub psfK = afwMath.FixedKernel(psf) psfNew = measAlg.KernelPsf(psfK) im1ex.setPsf(psfNew) wcs = makeWcs() im1ex.setWcs(wcs) return im1ex
def makeExposure(imgArray, psfArray, imgVariance): """Convert an image and corresponding PSF into an exposure. Set the (constant) variance plane equal to ``imgVariance``. Parameters ---------- imgArray : `numpy.ndarray` 2D array containing the image. psfArray : `numpy.ndarray` 2D array containing the PSF image. imgVariance : `float` or `numpy.ndarray` Set the variance plane to this value. If an array, must be broadcastable to ``imgArray.shape``. Returns ------- im1ex : `lsst.afw.image.Exposure` The new exposure. """ # All this code to convert the template image array/psf array into an exposure. bbox = geom.Box2I(geom.Point2I(0, 0), geom.Point2I(imgArray.shape[1] - 1, imgArray.shape[0] - 1)) im1ex = afwImage.ExposureD(bbox) im1ex.image.array[:, :] = imgArray im1ex.variance.array[:, :] = imgVariance psfBox = geom.Box2I(geom.Point2I(-12, -12), geom.Point2I(12, 12)) # a 25x25 pixel psf psf = afwImage.ImageD(psfBox) psfBox.shift(geom.Extent2I(-(-size[0]//2), -(-size[1]//2))) # -N//2 != -(N//2) for odd numbers im1_psf_sub = psfArray[psfBox.getMinY():psfBox.getMaxY() + 1, psfBox.getMinX():psfBox.getMaxX() + 1] psf.getArray()[:, :] = im1_psf_sub psfK = afwMath.FixedKernel(psf) psfNew = measAlg.KernelPsf(psfK) im1ex.setPsf(psfNew) wcs = makeWcs() im1ex.setWcs(wcs) return im1ex
def doALdecorrelation(alTaskResult, sig1squared=None, sig2squared=None, preConvKernel=None): kimg = alPsfMatchingKernelToArray(alTaskResult.psfMatchingKernel, alTaskResult.subtractedExposure) if preConvKernel is not None and kimg.shape[0] < preConvKernel.shape[0]: # This is likely brittle and may only work if both kernels are odd-shaped. #kimg[np.abs(kimg) < 1e-4] = np.sign(kimg)[np.abs(kimg) < 1e-4] * 1e-8 #kimg -= kimg[0, 0] padSize0 = preConvKernel.shape[0] // 2 - kimg.shape[0] // 2 padSize1 = preConvKernel.shape[1] // 2 - kimg.shape[1] // 2 kimg = np.pad(kimg, ((padSize0, padSize0), (padSize1, padSize1)), mode='constant', constant_values=0) #kimg /= kimg.sum() #preConvKernel = preConvKernel[padSize0:-padSize0, padSize1:-padSize1] if sig1squared is None: sig1squared = computeVarianceMean(im1) if sig2squared is None: sig2squared = computeVarianceMean(im2) pck = computeDecorrelationKernel(kimg, sig1squared, sig2squared, preConvKernel=preConvKernel, delta=0.) #return kimg, preConvKernel, pck diffim, _ = doConvolve(alTaskResult.subtractedExposure, pck, use_scipy=False) # For some reason, border areas of img and variance planes can become infinite. Fix it. img = diffim.getMaskedImage().getImage().getArray() img[~np.isfinite(img)] = np.nan img = diffim.getMaskedImage().getVariance().getArray() img[~np.isfinite(img)] = np.nan # TBD: also need to update the mask as it is not (apparently) set correctly. psf = afwPsfToArray(alTaskResult.subtractedExposure.getPsf(), img=alTaskResult.subtractedExposure) # NOTE! Need to compute the updated PSF including preConvKernel !!! This doesn't do it: psfc = computeCorrectedDiffimPsf(kimg, psf, tvar=sig1squared, svar=sig2squared) psfcI = afwImage.ImageD(psfc.shape[0], psfc.shape[1]) psfcI.getArray()[:, :] = psfc psfcK = afwMath.FixedKernel(psfcI) psfNew = measAlg.KernelPsf(psfcK) diffim.setPsf(psfNew) alTaskResult.decorrelatedDiffim = diffim alTaskResult.preConvKernel = preConvKernel alTaskResult.decorrelationKernel = pck alTaskResult.kappaImg = kimg return alTaskResult
def _setNewPsf(self, exposure, psfArr): """Utility method to set an exposure's PSF when provided as a 2-d numpy.array """ psfI = afwImage.ImageD(psfArr.shape[0], psfArr.shape[1]) psfI.getArray()[:, :] = psfArr psfK = afwMath.FixedKernel(psfI) psfNew = measAlg.KernelPsf(psfK) exposure.setPsf(psfNew) return exposure
def makePsf(size, sigma1, mult1, sigma2, mult2): """ make a Gaussian with one or two components. Always square of dimensions size x size """ array0 = makeGaussianArray(size, sigma1) array0 *= mult1 array1 = makeGaussianArray(size, sigma2) array1 *= mult2 kernel = lsst.afw.math.FixedKernel(lsst.afw.image.ImageD(array0 + array1)) return measAlg.KernelPsf(kernel)
def runMeasurement(self, algorithmName, imageid, x, y, v): """Run the measurement algorithm on an image""" # load the test image imgFile = os.path.join(self.dataDir, "image.%d.fits" % imageid) img = afwImage.ImageF(imgFile) img -= self.bkgd nx, ny = img.getWidth(), img.getHeight() msk = afwImage.Mask(geom.Extent2I(nx, ny), 0x0) var = afwImage.ImageF(geom.Extent2I(nx, ny), v) mimg = afwImage.MaskedImageF(img, msk, var) msk.getArray()[:] = np.where(np.fabs(img.getArray()) < 1.0e-8, msk.getPlaneBitMask("BAD"), 0) # Put it in a bigger image, in case it matters big = afwImage.MaskedImageF(self.offset + mimg.getDimensions()) big.getImage().set(0) big.getMask().set(0) big.getVariance().set(v) subBig = afwImage.MaskedImageF(big, geom.Box2I(big.getXY0() + self.offset, mimg.getDimensions())) subBig <<= mimg mimg = big mimg.setXY0(self.xy0) exposure = afwImage.makeExposure(mimg) cdMatrix = np.array([1.0/(2.53*3600.0), 0.0, 0.0, 1.0/(2.53*3600.0)]) cdMatrix.shape = (2, 2) exposure.setWcs(afwGeom.makeSkyWcs(crpix=geom.Point2D(1.0, 1.0), crval=geom.SpherePoint(0, 0, geom.degrees), cdMatrix=cdMatrix)) # load the corresponding test psf psfFile = os.path.join(self.dataDir, "psf.%d.fits" % imageid) psfImg = afwImage.ImageD(psfFile) psfImg -= self.bkgd kernel = afwMath.FixedKernel(psfImg) kernelPsf = algorithms.KernelPsf(kernel) exposure.setPsf(kernelPsf) # perform the shape measurement msConfig = base.SingleFrameMeasurementConfig() alg = base.SingleFramePlugin.registry[algorithmName].PluginClass.AlgClass control = base.SingleFramePlugin.registry[algorithmName].PluginClass.ConfigClass().makeControl() msConfig.algorithms.names = [algorithmName] # Note: It is essential to remove the floating point part of the position for the # Algorithm._apply. Otherwise, when the PSF is realised it will have been warped # to account for the sub-pixel offset and we won't get *exactly* this PSF. plugin, table = makePluginAndCat(alg, algorithmName, control, centroid="centroid") center = geom.Point2D(int(x), int(y)) + geom.Extent2D(self.offset + geom.Extent2I(self.xy0)) source = table.makeRecord() source.set("centroid_x", center.getX()) source.set("centroid_y", center.getY()) source.setFootprint(afwDetection.Footprint(afwGeom.SpanSet(exposure.getBBox(afwImage.PARENT)))) plugin.measure(source, exposure) return source
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 _growPsf(exp, extraPix=(2, 3)): bbox = exp.getBBox() center = ((bbox.getBeginX() + bbox.getEndX()) // 2., (bbox.getBeginY() + bbox.getEndY()) // 2.) center = geom.Point2D(center[0], center[1]) kern = exp.getPsf().computeKernelImage(center).convertF() kernSize = kern.getDimensions() paddedKern = afwImage.ImageF(kernSize[0] + extraPix[0], kernSize[1] + extraPix[1]) bboxToPlace = geom.Box2I(geom.Point2I((kernSize[0] + extraPix[0] - kern.getWidth()) // 2, (kernSize[1] + extraPix[1] - kern.getHeight()) // 2), kern.getDimensions()) paddedKern.assign(kern, bboxToPlace) fixedKern = afwMath.FixedKernel(paddedKern.convertD()) psfNew = measAlg.KernelPsf(fixedKern, center) exp.setPsf(psfNew) return exp
def makeKernelPsfFromArray(A): """Create a non spatially varying PSF from a `numpy.ndarray`. Parameters ---------- A : `numpy.ndarray` 2D array to use as the new psf image. The pixels are copied. Returns ------- psfNew : `lsst.meas.algorithms.KernelPsf` The constructed PSF. """ psfImg = afwImage.ImageD(A.astype(np.float64, copy=True), deep=False) psfNew = measAlg.KernelPsf(afwMath.FixedKernel(psfImg)) return psfNew
def getCoaddPsf(self, exposure): import lsst.afw.table as afwTable import lsst.afw.image as afwImage import lsst.afw.math as afwMath import lsst.afw.geom as afwGeom import lsst.meas.algorithms as measAlg schema = afwTable.ExposureTable.makeMinimalSchema() schema.addField("weight", type="D", doc="Coadd weight") mycatalog = afwTable.ExposureCatalog(schema) wcsref = exposure.getWcs() extentX = int(exposure.getWidth() * 0.05) extentY = int(exposure.getHeight() * 0.05) ind = 0 for x in np.linspace(extentX, exposure.getWidth() - extentX, 10): for y in np.linspace(extentY, exposure.getHeight() - extentY, 10): x = int(x) y = int(y) image = self.getImage(x, y) psf = afwImage.ImageD(image.shape[0], image.shape[1]) psf.getArray()[:, :] = image psfK = afwMath.FixedKernel(psf) psf = measAlg.KernelPsf(psfK) record = mycatalog.getTable().makeRecord() record.setPsf(psf) record.setWcs(wcsref) bbox = afwGeom.Box2I( afwGeom.Point2I( int(np.floor(x - extentX)) - 5, int(np.floor(y - extentY)) - 5), afwGeom.Point2I( int(np.floor(x + extentX)) + 5, int(np.floor(y + extentY)) + 5)) record.setBBox(bbox) record['weight'] = 1.0 record['id'] = ind ind += 1 mycatalog.append(record) # create the coaddpsf psf = measAlg.CoaddPsf(mycatalog, wcsref, 'weight') return psf
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(lsst.geom.Point2D(x, y)) # # And now via the dgPsf model # dgPsf = measAlg.DoubleGaussianPsf(ksize, ksize, sigma1) dgIm = dgPsf.computeImage(lsst.geom.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(), lsst.geom.Extent2I(ksize + pad, ksize + pad)) self.assertEqual(resizedKPsf.getKernel().getKernelParameters(), kPsf.getKernel().getKernelParameters()) self._compareKernelImages(kPsf, resizedKPsf) if display: mos = afwDisplay.utils.Mosaic() mos.setBackground(-0.1) afwDisplay.Display(frame=1).mtv(mos.makeMosaic([kIm, dgIm, diff], mode="x"), title=self._testMethodName + ": mosaic")
def makeLsstExposure(galData, psfData, pixScale, variance): """ make an LSST exposure object Parameters: galData (ndarray): array of galaxy image psfData (ndarray): array of PSF image pixScale (float): pixel scale variance (float): noise variance Returns: exposure: LSST exposure object """ if not with_lsst: raise ImportError('Do not have lsstpipe!') ny, nx = galData.shape exposure = afwImg.ExposureF(nx, ny) exposure.getMaskedImage().getImage().getArray()[:, :] = galData exposure.getMaskedImage().getVariance().getArray()[:, :] = variance #Set the PSF ngridPsf = psfData.shape[0] psfLsst = afwImg.ImageF(ngridPsf, ngridPsf) psfLsst.getArray()[:, :] = psfData psfLsst = psfLsst.convertD() kernel = afwMath.FixedKernel(psfLsst) kernelPSF = meaAlg.KernelPsf(kernel) exposure.setPsf(kernelPSF) #prepare the wcs #Rotation cdelt = (pixScale * afwGeom.arcseconds) CD = afwGeom.makeCdMatrix(cdelt, afwGeom.Angle(0.)) #no rotation #wcs crval = afwGeom.SpherePoint(afwGeom.Angle(0., afwGeom.degrees), afwGeom.Angle(0., afwGeom.degrees)) #crval = afwCoord.IcrsCoord(0.*afwGeom.degrees, 0.*afwGeom.degrees) # hscpipe6 crpix = afwGeom.Point2D(0.0, 0.0) dataWcs = afwGeom.makeSkyWcs(crpix, crval, CD) exposure.setWcs(dataWcs) #prepare the frc dataCalib = afwImg.makePhotoCalibFromCalibZeroPoint(63095734448.0194) exposure.setPhotoCalib(dataCalib) return exposure
def performALZCExposureCorrection(templateExposure, exposure, subtractedExposure, psfMatchingKernel, log): import lsst.afw.image as afwImage import lsst.meas.algorithms as measAlg import lsst.afw.math as afwMath spatialKernel = psfMatchingKernel kimg = afwImage.ImageD(spatialKernel.getDimensions()) bbox = subtractedExposure.getBBox() xcen = (bbox.getBeginX() + bbox.getEndX()) / 2. ycen = (bbox.getBeginY() + bbox.getEndY()) / 2. spatialKernel.computeImage(kimg, True, xcen, ycen) # Compute the images' sigmas (sqrt of variance) sig1 = templateExposure.getMaskedImage().getVariance().getArray() sig2 = exposure.getMaskedImage().getVariance().getArray() sig1squared, _ = computeClippedImageStats(sig1) sig2squared, _ = computeClippedImageStats(sig2) sig1 = np.sqrt(sig1squared) sig2 = np.sqrt(sig2squared) corrKernel = computeCorrectionKernelALZC(kimg.getArray(), sig1=sig1, sig2=sig2) # Eventually, use afwMath.convolve(), but for now just use scipy. log.info("ALZC: Convolving.") pci, _ = doConvolve( subtractedExposure.getMaskedImage().getImage().getArray(), corrKernel) subtractedExposure.getMaskedImage().getImage().getArray()[:, :] = pci log.info("ALZC: Finished with convolution.") # Compute the subtracted exposure's updated psf psf = subtractedExposure.getPsf().computeImage().getArray() psfc = computeCorrectedDiffimPsfALZC(corrKernel, psf, sig1=sig1, sig2=sig2) psfcI = afwImage.ImageD( subtractedExposure.getPsf().computeImage().getBBox()) psfcI.getArray()[:, :] = psfc psfcK = afwMath.FixedKernel(psfcI) psfNew = measAlg.KernelPsf(psfcK) subtractedExposure.setPsf(psfNew) return subtractedExposure, corrKernel
def asAfwExposure(self): import lsst.afw.image as afwImage import lsst.afw.math as afwMath import lsst.afw.geom as afwGeom import lsst.meas.algorithms as measAlg bbox = afwGeom.Box2I( afwGeom.Point2I(0, 0), afwGeom.Point2I(self.im.shape[0] - 1, self.im.shape[1] - 1)) im1ex = afwImage.ExposureF(bbox) im1ex.getMaskedImage().getImage().getArray()[:, :] = self.im im1ex.getMaskedImage().getVariance().getArray()[:, :] = self.var psfShape = self.psf.shape[0] // 2 psfBox = afwGeom.Box2I(afwGeom.Point2I(-psfShape, -psfShape), afwGeom.Point2I(psfShape, psfShape)) psf = afwImage.ImageD(psfBox) psf.getArray()[:, :] = self.psf psfK = afwMath.FixedKernel(psf) psfNew = measAlg.KernelPsf(psfK) im1ex.setPsf(psfNew) wcs = makeWcs(naxis1=self.im.shape[0], naxis2=self.im.shape[1]) im1ex.setWcs(wcs) return im1ex
def run(self, subExposure, expandedSubExposure, fullBBox, template, science, alTaskResult=None, psfMatchingKernel=None, preConvKernel=None, returnDiffimPsf=False, **kwargs): """Perform decorrelation operation on `subExposure`, using `expandedSubExposure` to allow for invalid edge pixels arising from convolutions. This method performs A&L decorrelation on `subExposure` using local measures for image variances and PSF. `subExposure` is a sub-exposure of the non-decorrelated A&L diffim. It also requires the corresponding sub-exposures of the template (`template`) and science (`science`) exposures. Parameters ---------- subExposure : afw.Exposure the sub-exposure of the diffim expandedSubExposure : afw.Exposure the expanded sub-exposure upon which to operate fullBBox : afwGeom.BoundingBox the bounding box of the original exposure template : afw.Exposure the corresponding sub-exposure of the template exposure science : afw.Exposure the corresponding sub-exposure of the science exposure alTaskResult : pipeBase.Struct the result of A&L image differencing on `science` and `template`, importantly containing the resulting `psfMatchingKernel`. Can be `None`, only if `psfMatchingKernel` is not `None`. psfMatchingKernel : Alternative parameter for passing the A&L `psfMatchingKernel` directly. kwargs : additional keyword arguments propagated from `ImageMapReduceTask.run`. Returns ------- A `pipeBase.Struct containing the result of the `subExposure` processing, labelled 'subExposure'. It also returns the 'decorrelationKernel', although that currently is not used. Notes ----- This `run` method accepts parameters identical to those of `ImageMapperSubtask.run`, since it is called from the `ImageMapperTask`. See that class for more information. """ templateExposure = template # input template scienceExposure = science # input science image if alTaskResult is None and psfMatchingKernel is None: raise ValueError( 'Both alTaskResult and psfMatchingKernel cannot be None') psfMatchingKernel = alTaskResult.psfMatchingKernel if alTaskResult is not None else psfMatchingKernel # subExp and expandedSubExp are subimages of the (un-decorrelated) diffim! # So here we compute corresponding subimages of templateExposure and scienceExposure subExp2 = scienceExposure.Factory(scienceExposure, expandedSubExposure.getBBox()) subExp1 = templateExposure.Factory(templateExposure, expandedSubExposure.getBBox()) # Prevent too much log INFO verbosity from DecorrelateALKernelTask.run #logLevel = self.log.getLevel() #self.log.setLevel(lsst.log.WARN) #res = ipDiffim.DecorrelateALKernelTask.run(self, subExp2, subExp1, expandedSubExposure, # psfMatchingKernel) svar = ipDiffim.DecorrelateALKernelTask.computeVarianceMean( self, subExp2) tvar = ipDiffim.DecorrelateALKernelTask.computeVarianceMean( self, subExp1) kimg = afwImage.ImageD(psfMatchingKernel.getDimensions()) bbox = subExposure.getBBox() xcen = (bbox.getBeginX() + bbox.getEndX()) / 2. ycen = (bbox.getBeginY() + bbox.getEndY()) / 2. psfMatchingKernel.computeImage(kimg, True, xcen, ycen) kernel = ipDiffim.DecorrelateALKernelTask._computeDecorrelationKernel( kappa=kimg.getArray(), svar=svar, tvar=tvar) if not returnDiffimPsf: 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]) else: # Compute the subtracted exposure's updated psf psf = subExposure.getPsf().computeImage(afwGeom.Point2D( xcen, ycen)).getArray() psfc = ipDiffim.DecorrelateALKernelTask.computeCorrectedDiffimPsf( kernel, psf, svar=svar, tvar=tvar) psfcI = afwImage.ImageD(psfc.shape[0], psfc.shape[1]) psfcI.getArray()[:, :] = psfc kern = afwMath.FixedKernel(psfcI) psf = measAlg.KernelPsf(kern) out = pipeBase.Struct(psf=psf, bbox=subExposure.getBBox()) return out
def arrayToAfwPsf(array): psfcK = arrayToAfwKernel(array) psfNew = measAlg.KernelPsf(psfcK) return psfNew
def run(self, exposure, templateExposure, subtractedExposure, psfMatchingKernel, preConvKernel=None, xcen=None, ycen=None, svar=None, tvar=None): """Perform decorrelation of an image difference exposure. Decorrelates the diffim due to the convolution of the templateExposure with the A&L PSF matching kernel. Currently can accept a spatially varying matching kernel but in this case it simply uses a static kernel from the center of the exposure. The decorrelation is described in [DMTN-021, Equation 1](http://dmtn-021.lsst.io/#equation-1), where `exposure` is I_1; templateExposure is I_2; `subtractedExposure` is D(k); `psfMatchingKernel` is kappa; and svar and tvar are their respective variances (see below). Parameters ---------- exposure : `lsst.afw.image.Exposure` The original science exposure (before `preConvKernel` applied) used for PSF matching. templateExposure : `lsst.afw.image.Exposure` The original template exposure (before matched to the science exposure by `psfMatchingKernel`) warped into the science exposure dimensions. Always the PSF of the `templateExposure` should be matched to the PSF of `exposure`, see notes below. subtractedExposure : the subtracted exposure produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()`. The `subtractedExposure` must inherit its PSF from `exposure`, see notes below. psfMatchingKernel : An (optionally spatially-varying) PSF matching kernel produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()` preConvKernel : if not None, then the `exposure` was pre-convolved with this kernel xcen : `float`, optional X-pixel coordinate to use for computing constant matching kernel to use If `None` (default), then use the center of the image. ycen : `float`, optional Y-pixel coordinate to use for computing constant matching kernel to use If `None` (default), then use the center of the image. svar : `float`, optional Image variance for science image If `None` (default) then compute the variance over the entire input science image. tvar : `float`, optional Image variance for template image If `None` (default) then compute the variance over the entire input template image. Returns ------- result : `lsst.pipe.base.Struct` - ``correctedExposure`` : the decorrelated diffim Notes ----- The `subtractedExposure` is NOT updated. The returned `correctedExposure` has an updated but spatially fixed PSF. It is calculated as the center of image PSF corrected by the center of image matching kernel. In this task, it is _always_ the `templateExposure` that was matched to the `exposure` by `psfMatchingKernel`. Swap arguments accordingly if actually the science exposure was matched to a co-added template. In this case, tvar > svar typically occurs. The `templateExposure` and `exposure` image dimensions must be the same. Here we currently convert a spatially-varying matching kernel into a constant kernel, just by computing it at the center of the image (tickets DM-6243, DM-6244). We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute the decorrelation kernel. TODO DM-23857 As part of the spatially varying correction implementation consider whether returning a Struct is still necessary. """ spatialKernel = psfMatchingKernel kimg = afwImage.ImageD(spatialKernel.getDimensions()) bbox = subtractedExposure.getBBox() if xcen is None: xcen = (bbox.getBeginX() + bbox.getEndX()) / 2. if ycen is None: ycen = (bbox.getBeginY() + bbox.getEndY()) / 2. self.log.info("Using matching kernel computed at (%d, %d)", xcen, ycen) spatialKernel.computeImage(kimg, True, xcen, ycen) if svar is None: svar = self.computeVarianceMean(exposure) if tvar is None: tvar = self.computeVarianceMean(templateExposure) self.log.info("Variance (science, template): (%f, %f)", svar, tvar) # Should not happen unless entire image has been masked, which could happen # if this is a small subimage of the main exposure. In this case, just return a full NaN # exposure if np.isnan(svar) or np.isnan(tvar): # Double check that one of the exposures is all NaNs if (np.all(np.isnan(exposure.image.array)) or np.all(np.isnan(templateExposure.image.array))): self.log.warn('Template or science image is entirely NaNs: skipping decorrelation.') outExposure = subtractedExposure.clone() return pipeBase.Struct(correctedExposure=outExposure, ) # The maximal correction value converges to sqrt(tvar/svar). # Correction divergence warning if the correction exceeds 4 orders of magnitude. tOverSVar = tvar/svar if tOverSVar > 1e8: self.log.warn("Diverging correction: science image variance is much smaller than template" f", tvar/svar:{tOverSVar:.2e}") oldVarMean = self.computeVarianceMean(subtractedExposure) self.log.info("Variance (uncorrected diffim): %f", oldVarMean) if preConvKernel is not None: self.log.info('Using a pre-convolution kernel as part of decorrelation correction.') kimg2 = afwImage.ImageD(preConvKernel.getDimensions()) preConvKernel.computeImage(kimg2, False) pckArr = kimg2.array kArr = kimg.array diffExpArr = subtractedExposure.image.array psfImg = subtractedExposure.getPsf().computeKernelImage(geom.Point2D(xcen, ycen)) psfDim = psfImg.getDimensions() psfArr = psfImg.array # Determine the common shape if preConvKernel is None: self.computeCommonShape(kArr.shape, psfArr.shape, diffExpArr.shape) corrft = self.computeCorrection(kArr, svar, tvar) else: self.computeCommonShape(pckArr.shape, kArr.shape, psfArr.shape, diffExpArr.shape) corrft = self.computeCorrection(kArr, svar, tvar, preConvArr=pckArr) diffExpArr = self.computeCorrectedImage(corrft, diffExpArr) corrPsfArr = self.computeCorrectedDiffimPsf(corrft, psfArr) psfcI = afwImage.ImageD(psfDim) psfcI.array = corrPsfArr psfcK = afwMath.FixedKernel(psfcI) psfNew = measAlg.KernelPsf(psfcK) correctedExposure = subtractedExposure.clone() correctedExposure.image.array[...] = diffExpArr # Allow for numpy type casting # The subtracted exposure variance plane is already correlated, we cannot propagate # it through another convolution; instead we need to use the uncorrelated originals # The whitening should scale it to svar + tvar on average varImg = correctedExposure.variance.array # Allow for numpy type casting varImg[...] = exposure.variance.array + templateExposure.variance.array correctedExposure.setPsf(psfNew) newVarMean = self.computeVarianceMean(correctedExposure) self.log.info(f"Variance (corrected diffim): {newVarMean:.2e}") # TODO DM-23857 As part of the spatially varying correction implementation # consider whether returning a Struct is still necessary. return pipeBase.Struct(correctedExposure=correctedExposure, )
def run(self, exposure, templateExposure, subtractedExposure, psfMatchingKernel, xcen=None, ycen=None, svar=None, tvar=None): """! Perform decorrelation of an image difference exposure. Decorrelates the diffim due to the convolution of the templateExposure with the A&L PSF matching kernel. Currently can accept a spatially varying matching kernel but in this case it simply uses a static kernel from the center of the exposure. The decorrelation is described in [DMTN-021, Equation 1](http://dmtn-021.lsst.io/#equation-1), where `exposure` is I_1; templateExposure is I_2; `subtractedExposure` is D(k); `psfMatchingKernel` is kappa; and svar and tvar are their respective variances (see below). @param[in] exposure the science afwImage.Exposure used for PSF matching @param[in] templateExposure the template afwImage.Exposure used for PSF matching @param[in] subtractedExposure the subtracted exposure produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()` @param[in] psfMatchingKernel an (optionally spatially-varying) PSF matching kernel produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()` @param[in] xcen X-pixel coordinate to use for computing constant matching kernel to use If `None` (default), then use the center of the image. @param[in] ycen Y-pixel coordinate to use for computing constant matching kernel to use If `None` (default), then use the center of the image. @param[in] svar image variance for science image If `None` (default) then compute the variance over the entire input science image. @param[in] tvar image variance for template image If `None` (default) then compute the variance over the entire input template image. @return a `pipeBase.Struct` containing: * `correctedExposure`: the decorrelated diffim * `correctionKernel`: the decorrelation correction kernel (which may be ignored) @note The `subtractedExposure` is NOT updated @note The returned `correctedExposure` has an updated PSF as well. @note Here we currently convert a spatially-varying matching kernel into a constant kernel, just by computing it at the center of the image (tickets DM-6243, DM-6244). @note We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute the decorrelation kernel. @note Still TBD (ticket DM-6580): understand whether the convolution is correctly modifying the variance plane of the new subtractedExposure. """ self.log.info("Starting.") kimg = None spatialKernel = psfMatchingKernel kimg = afwImage.ImageD(spatialKernel.getDimensions()) bbox = subtractedExposure.getBBox() if xcen is None: xcen = (bbox.getBeginX() + bbox.getEndX()) / 2. if ycen is None: ycen = (bbox.getBeginY() + bbox.getEndY()) / 2. self.log.info("Using matching kernel computed at (%d, %d)" % (xcen, ycen)) spatialKernel.computeImage(kimg, True, xcen, ycen) if False: # debug code to save spatially varying kernel for analysis import pickle import gzip pickle.dump(spatialKernel, gzip.GzipFile('spatialKernel.p.gz', 'wb')) if svar is None: svar = self.computeVarianceMean(exposure) self.log.info("Variance (science): %f" % svar) if tvar is None: tvar = self.computeVarianceMean(templateExposure) self.log.info("Variance (template): %f" % tvar) var = self.computeVarianceMean(subtractedExposure) self.log.info("Variance (uncorrected diffim): %f" % var) corrKernel = DecorrelateALKernelTask._computeDecorrelationKernel( kimg.getArray(), svar, tvar) self.log.info("Convolving.") correctedExposure, corrKern = DecorrelateALKernelTask._doConvolve( subtractedExposure, corrKernel) self.log.info("Updating correctedExposure and its PSF.") # Compute the subtracted exposure's updated psf psf = subtractedExposure.getPsf().computeImage().getArray() psfc = DecorrelateALKernelTask.computeCorrectedDiffimPsf(corrKernel, psf, svar=svar, tvar=tvar) psfcI = afwImage.ImageD(psfc.shape[0], psfc.shape[1]) psfcI.getArray()[:, :] = psfc psfcK = afwMath.FixedKernel(psfcI) psfNew = measAlg.KernelPsf(psfcK) correctedExposure.setPsf(psfNew) self.log.info("Complete.") var = self.computeVarianceMean(correctedExposure) self.log.info("Variance (corrected diffim): %f" % var) return pipeBase.Struct(correctedExposure=correctedExposure, correctionKernel=corrKern)
def makeBiaxialGaussianPsf(sizex, sizey, sigma1, sigma2, theta): kernel = afwMath.AnalyticKernel( sizex, sizey, afwMath.GaussianFunction2D(sigma1, sigma2, theta)) return measAlg.KernelPsf(kernel)
tot_images[filter_name].addNoise(noise) tot_images[filter_name].write('%s/image_%s.fits' % (output_dir, filter_name)) psf_bounds = galsim.BoundsI(0, 41, 0, 41) psf_image = galsim.ImageF(psf_bounds, scale=scale) psf.drawImage(image=psf_image) psf_image.write('%s/psf_image.fits' % output_dir) # Read in PSF lsst_psf_image = afwImage.ImageF('%s/psf_image.fits' % output_dir) bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(41, 41)) lsst_psf_image = lsst_psf_image[bbox].convertD() lsst_psf_image = afwMath.offsetImage(lsst_psf_image, -0.5, -0.5) kernel = afwMath.FixedKernel(lsst_psf_image) kernelPsf = measAlg.KernelPsf(kernel) # Exposure setup wcs = afwImage.makeWcs( afwCoord.Coord(0. * afwGeom.degrees, 0. * afwGeom.degrees), afwGeom.Point2D(0.0, 0.0), 0.168 * 3600, 0.0, 0.0, 0.168 * 3600) calib = afwImage.Calib() calib.setFluxMag0(1e12) # Config and task setup measure_config = SingleFrameMeasurementTask.ConfigClass() measure_config.load( '/tigress/rea3/lsst/DM-8059/obs_subaru/config/apertures.py') measure_config.load('/tigress/rea3/lsst/DM-8059/obs_subaru/config/kron.py') measure_config.plugins.names |= [
def run(self, exposure, templateExposure, subtractedExposure, psfMatchingKernel, preConvKernel=None, xcen=None, ycen=None, svar=None, tvar=None): """Perform decorrelation of an image difference exposure. Decorrelates the diffim due to the convolution of the templateExposure with the A&L PSF matching kernel. Currently can accept a spatially varying matching kernel but in this case it simply uses a static kernel from the center of the exposure. The decorrelation is described in [DMTN-021, Equation 1](http://dmtn-021.lsst.io/#equation-1), where `exposure` is I_1; templateExposure is I_2; `subtractedExposure` is D(k); `psfMatchingKernel` is kappa; and svar and tvar are their respective variances (see below). Parameters ---------- exposure : `lsst.afw.image.Exposure` The science afwImage.Exposure used for PSF matching templateExposure : `lsst.afw.image.Exposure` The template exposure used for PSF matching subtractedExposure : the subtracted exposure produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()` psfMatchingKernel : An (optionally spatially-varying) PSF matching kernel produced by `ip_diffim.ImagePsfMatchTask.subtractExposures()` preConvKernel : if not None, then the `exposure` was pre-convolved with this kernel xcen : `float`, optional X-pixel coordinate to use for computing constant matching kernel to use If `None` (default), then use the center of the image. ycen : `float`, optional Y-pixel coordinate to use for computing constant matching kernel to use If `None` (default), then use the center of the image. svar : `float`, optional image variance for science image If `None` (default) then compute the variance over the entire input science image. tvar : `float`, optional Image variance for template image If `None` (default) then compute the variance over the entire input template image. Returns ------- result : `Struct` a `lsst.pipe.base.Struct` containing: - ``correctedExposure`` : the decorrelated diffim - ``correctionKernel`` : the decorrelation correction kernel (which may be ignored) Notes ----- The `subtractedExposure` is NOT updated The returned `correctedExposure` has an updated PSF as well. Here we currently convert a spatially-varying matching kernel into a constant kernel, just by computing it at the center of the image (tickets DM-6243, DM-6244). We are also using a constant accross-the-image measure of sigma (sqrt(variance)) to compute the decorrelation kernel. Still TBD (ticket DM-6580): understand whether the convolution is correctly modifying the variance plane of the new subtractedExposure. """ spatialKernel = psfMatchingKernel kimg = afwImage.ImageD(spatialKernel.getDimensions()) bbox = subtractedExposure.getBBox() if xcen is None: xcen = (bbox.getBeginX() + bbox.getEndX()) / 2. if ycen is None: ycen = (bbox.getBeginY() + bbox.getEndY()) / 2. self.log.info("Using matching kernel computed at (%d, %d)", xcen, ycen) spatialKernel.computeImage(kimg, True, xcen, ycen) if svar is None: svar = self.computeVarianceMean(exposure) if tvar is None: tvar = self.computeVarianceMean(templateExposure) self.log.info("Variance (science, template): (%f, %f)", svar, tvar) # Should not happen unless entire image has been masked, which could happen # if this is a small subimage of the main exposure. In this case, just return a full NaN # exposure if np.isnan(svar) or np.isnan(tvar): # Double check that one of the exposures is all NaNs if (np.all(np.isnan(exposure.getMaskedImage().getImage().getArray())) or np.all(np.isnan(templateExposure.getMaskedImage().getImage().getArray()))): self.log.warn('Template or science image is entirely NaNs: skipping decorrelation.') outExposure = subtractedExposure.clone() return pipeBase.Struct(correctedExposure=outExposure, correctionKernel=None) var = self.computeVarianceMean(subtractedExposure) self.log.info("Variance (uncorrected diffim): %f", var) pck = None if preConvKernel is not None: self.log.info('Using a pre-convolution kernel as part of decorrelation.') kimg2 = afwImage.ImageD(preConvKernel.getDimensions()) preConvKernel.computeImage(kimg2, False) pck = kimg2.getArray() corrKernel = DecorrelateALKernelTask._computeDecorrelationKernel(kimg.getArray(), svar, tvar, pck) correctedExposure, corrKern = DecorrelateALKernelTask._doConvolve(subtractedExposure, corrKernel) # Compute the subtracted exposure's updated psf psf = subtractedExposure.getPsf().computeKernelImage(afwGeom.Point2D(xcen, ycen)).getArray() psfc = DecorrelateALKernelTask.computeCorrectedDiffimPsf(corrKernel, psf, svar=svar, tvar=tvar) psfcI = afwImage.ImageD(psfc.shape[0], psfc.shape[1]) psfcI.getArray()[:, :] = psfc psfcK = afwMath.FixedKernel(psfcI) psfNew = measAlg.KernelPsf(psfcK) correctedExposure.setPsf(psfNew) var = self.computeVarianceMean(correctedExposure) self.log.info("Variance (corrected diffim): %f", var) return pipeBase.Struct(correctedExposure=correctedExposure, correctionKernel=corrKern)