Exemple #1
0
 def warped_from_fits(self, fitsfile):
     """Return a warped exposure computed from an LSST exposure"""
     exp = afwImage.ExposureF(fitsfile)
     wcs_in = exp.getWcs()
     wcs_out = self.make_wcs()
     warper = afwMath.Warper(self.kernel)
     warpedExposure = warper.warpExposure(destWcs=wcs_out, srcExposure=exp)
     return warpedExposure
Exemple #2
0
    def compareToSwarp(self,
                       kernelName,
                       useSubregion=False,
                       useDeepCopy=False,
                       interpLength=10,
                       cacheSize=100000,
                       rtol=4e-05,
                       atol=1e-2):
        """Compare warpExposure to swarp for given warping kernel.
        
        Note that swarp only warps the image plane, so only test that plane.
        
        Inputs:
        - kernelName: name of kernel in the form used by afwImage.makeKernel
        - useSubregion: if True then the original source exposure (from which the usual
            test exposure was extracted) is read and the correct subregion extracted
        - useDeepCopy: if True then the copy of the subimage is a deep copy,
            else it is a shallow copy; ignored if useSubregion is False
        - interpLength: interpLength argument for lsst.afw.math.warpExposure
        - cacheSize: cacheSize argument for lsst.afw.math.SeparableKernel.computeCache;
            0 disables the cache
            10000 gives some speed improvement but less accurate results (atol must be increased)
            100000 gives better accuracy but no speed improvement in this test
        - rtol: relative tolerance as used by numpy.allclose
        - atol: absolute tolerance as used by numpy.allclose
        """
        warper = afwMath.Warper(kernelName)

        originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage(
            kernelName=kernelName,
            useSubregion=useSubregion,
            useDeepCopy=useDeepCopy)
        maxBBox = afwGeom.Box2I(
            afwGeom.Point2I(swarpedImage.getX0(), swarpedImage.getY0()),
            afwGeom.Extent2I(swarpedImage.getWidth(),
                             swarpedImage.getHeight()))

        # warning: this test assumes that the swarped image is smaller than it needs to be
        # to hold all of the warped pixels
        afwWarpedExposure = warper.warpExposure(
            destWcs=swarpedWcs,
            srcExposure=originalExposure,
            maxBBox=maxBBox,
        )
        afwWarpedMaskedImage = afwWarpedExposure.getMaskedImage()

        afwWarpedMask = afwWarpedMaskedImage.getMask()
        noDataBitMask = afwImage.MaskU.getPlaneBitMask("NO_DATA")
        noDataMask = afwWarpedMask.getArray() & noDataBitMask

        msg = "afw and swarp %s-warped %s (ignoring bad pixels)"
        self.assertImagesNearlyEqual(afwWarpedMaskedImage.getImage(),
                                     swarpedImage,
                                     skipMask=noDataMask,
                                     rtol=rtol,
                                     atol=atol,
                                     msg=msg)
Exemple #3
0
    def testBBox(self):
        """Test that the default bounding box includes all warped pixels
        """
        kernelName = "lanczos2"
        warper = afwMath.Warper(kernelName)
        originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage(
            kernelName=kernelName, useSubregion=True, useDeepCopy=False)

        filterPolicyFile = pexPolicy.DefaultPolicyFile("afw",
                                                       "SdssFilters.paf",
                                                       "tests")
        filterPolicy = pexPolicy.Policy.createPolicy(
            filterPolicyFile, filterPolicyFile.getRepositoryPath(), True)
        imageUtils.defineFiltersFromPolicy(filterPolicy, reset=True)

        originalFilter = afwImage.Filter("i")
        originalCalib = afwImage.Calib()
        originalCalib.setFluxMag0(1.0e5, 1.0e3)
        originalExposure.setFilter(originalFilter)
        originalExposure.setCalib(originalCalib)

        warpedExposure1 = warper.warpExposure(destWcs=swarpedWcs,
                                              srcExposure=originalExposure)
        # the default size must include all good pixels, so growing the bbox
        # should not add any
        warpedExposure2 = warper.warpExposure(destWcs=swarpedWcs,
                                              srcExposure=originalExposure,
                                              border=1)
        # a bit of excess border is allowed, but surely not as much as 10 (in
        # fact it is approx. 5)
        warpedExposure3 = warper.warpExposure(destWcs=swarpedWcs,
                                              srcExposure=originalExposure,
                                              border=-10)
        # assert that warpedExposure and warpedExposure2 have the same number of non-no_data pixels
        # and that warpedExposure3 has fewer
        noDataBitMask = afwImage.Mask.getPlaneBitMask("NO_DATA")
        mask1Arr = warpedExposure1.getMaskedImage().getMask().getArray()
        mask2Arr = warpedExposure2.getMaskedImage().getMask().getArray()
        mask3Arr = warpedExposure3.getMaskedImage().getMask().getArray()
        nGood1 = (mask1Arr & noDataBitMask == 0).sum()
        nGood2 = (mask2Arr & noDataBitMask == 0).sum()
        nGood3 = (mask3Arr & noDataBitMask == 0).sum()
        self.assertEqual(nGood1, nGood2)
        self.assertLess(nGood3, nGood1)

        self.assertEqual(warpedExposure1.getFilter().getName(),
                         originalFilter.getName())
        self.assertEqual(warpedExposure1.getCalib().getFluxMag0(),
                         originalCalib.getFluxMag0())
Exemple #4
0
def rotateExposure(exp, nDegrees, kernelName='lanczos4', logger=None):
    """Rotate an exposure by nDegrees clockwise.

    Parameters
    ----------
    exp : `lsst.afw.image.exposure.Exposure`
        The exposure to rotate
    nDegrees : `float`
        Number of degrees clockwise to rotate by
    kernelName : `str`
        Name of the warping kernel, used to instantiate the warper.
    logger : `lsst.log.Log`
        Logger for logging warnings

    Returns
    -------
    rotatedExp : `lsst.afw.image.exposure.Exposure`
        A copy of the input exposure, rotated by nDegrees
    """
    nDegrees = nDegrees % 360

    if not logger:
        logger = lsstLog.getLogger('atmospec.utils')

    wcs = exp.getWcs()
    if not wcs:
        logger.warn(
            "Can't rotate exposure without a wcs - returning exp unrotated")
        return exp.clone(
        )  # return a clone so it's always returning a copy as this is what default does

    warper = afwMath.Warper(kernelName)
    if isinstance(exp, afwImage.ExposureU):
        # TODO: remove once this bug is fixed - DM-20258
        logger.info('Converting ExposureU to ExposureF due to bug')
        logger.info('Remove this workaround after DM-20258')
        exp = afwImage.ExposureF(exp, deep=True)

    affineRotTransform = geom.AffineTransform.makeRotation(nDegrees *
                                                           geom.degrees)
    transformP2toP2 = afwGeom.makeTransform(affineRotTransform)
    rotatedWcs = afwGeom.makeModifiedWcs(transformP2toP2, wcs, False)

    rotatedExp = warper.warpExposure(rotatedWcs, exp)
    # rotatedExp.setXY0(geom.Point2I(0, 0))  # TODO: check no longer required
    return rotatedExp
Exemple #5
0
    def testDestBBox(self):
        """Test that the destBBox argument works
        """
        kernelName = "lanczos2"
        warper = afwMath.Warper(kernelName)
        originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage(
            kernelName=kernelName, useSubregion=True, useDeepCopy=False)

        bbox = afwGeom.Box2I(afwGeom.Point2I(100, 25), afwGeom.Extent2I(3, 7))
        warpedExposure = warper.warpExposure(
            destWcs=swarpedWcs,
            srcExposure=originalExposure,
            destBBox=bbox,
            border=-2,  # should be ignored
            maxBBox=afwGeom.Box2I(afwGeom.Point2I(1, 2),
                                  afwGeom.Extent2I(8, 9)),  # should be ignored
        )
        self.assertTrue(bbox == warpedExposure.getBBox(afwImage.PARENT))
Exemple #6
0
    def testBBox(self):
        """Test that the default bounding box includes all warped pixels
        """
        kernelName = "lanczos2"
        warper = afwMath.Warper(kernelName)
        originalExposure, swarpedImage, swarpedWcs = self.getSwarpedImage(
            kernelName=kernelName, useSubregion=True, useDeepCopy=False)

        originalFilterLabel = afwImage.FilterLabel(band="i")
        originalPhotoCalib = afwImage.PhotoCalib(1.0e5, 1.0e3)
        originalExposure.setFilterLabel(originalFilterLabel)
        originalExposure.setPhotoCalib(originalPhotoCalib)

        warpedExposure1 = warper.warpExposure(destWcs=swarpedWcs,
                                              srcExposure=originalExposure)
        # the default size must include all good pixels, so growing the bbox
        # should not add any
        warpedExposure2 = warper.warpExposure(destWcs=swarpedWcs,
                                              srcExposure=originalExposure,
                                              border=1)
        # a bit of excess border is allowed, but surely not as much as 10 (in
        # fact it is approx. 5)
        warpedExposure3 = warper.warpExposure(destWcs=swarpedWcs,
                                              srcExposure=originalExposure,
                                              border=-10)
        # assert that warpedExposure and warpedExposure2 have the same number of non-no_data pixels
        # and that warpedExposure3 has fewer
        noDataBitMask = afwImage.Mask.getPlaneBitMask("NO_DATA")
        mask1Arr = warpedExposure1.getMaskedImage().getMask().getArray()
        mask2Arr = warpedExposure2.getMaskedImage().getMask().getArray()
        mask3Arr = warpedExposure3.getMaskedImage().getMask().getArray()
        nGood1 = (mask1Arr & noDataBitMask == 0).sum()
        nGood2 = (mask2Arr & noDataBitMask == 0).sum()
        nGood3 = (mask3Arr & noDataBitMask == 0).sum()
        self.assertEqual(nGood1, nGood2)
        self.assertLess(nGood3, nGood1)

        self.assertEqual(warpedExposure1.getFilterLabel().bandLabel,
                         originalFilterLabel.bandLabel)
        self.assertEqual(warpedExposure1.getPhotoCalib(), originalPhotoCalib)
Exemple #7
0
    # Use one of the input images as a base for dimensions.
    d_ang = d_angs[0]
    nx, ny = ghostIms[d_ang].getDimensions()
    # pixel scale in ghost images is 10" per pixel
    pixScale = afwGeom.Angle(10.0/60./60./1.)
    # Create the destination image.
    destIm, destWcs = makeDest(nx, ny, pixScale, stars['raCen'], stars['decCen'])
    destArr = destIm.getArray()

    # Calculate star positions.
    stars = calcStarPos(stars)
    #print stars['d_ang'].min(), stars['d_ang'].max(), stars['rotAng'].min(), stars['rotAng'].max()

    # Set up warper to map input image to output WCS.
    # There are other warping kernels, but lanczos2 is the standard one
    warper = afwMath.Warper("lanczos2")

    idx = numpy.argsort(stars['mag'])
    if args.nStars > 0:
        idx = idx[0:args.nStars]
    for i in (idx):
        ## For each star, choose appropriate ghost & rotate. Add.
        # Find the closest ghost image.
        d_ang = stars['d_ang'][i]
        # Skip star if too far outside the last ghost image.
        if d_ang > d_ang_max_use:
            continue
        d = abs(d_angs - d_ang)
        didx = numpy.where(d == d.min())
        d_ang_closest = d_angs[didx][0]
        # Calculate scale for output image (assuming ghost source = 0 mag)
    def testForced(self):
        """Check that forced photometry works in the presence of rotations and translations.
        """
        kfac = 2.5
        warper = afwMath.Warper("lanczos4")
        a = 13
        for axisRatio in (0.25, 1.0):
            b = a * axisRatio
            for theta in (0, 30, 45):
                width, height = 256, 256
                center = afwGeom.Point2D(0.5 * width, 0.5 * height)
                original = makeGalaxy(width, height, 1000.0, a, b, theta)
                msConfig = makeMeasurementConfig(forced=False, kfac=kfac)
                source = measureFree(original, center, msConfig)
                algMeta = source.getTable().getMetadata()
                self.assertTrue(
                    algMeta.exists(
                        'ext_photometryKron_KronFlux_nRadiusForFlux'))
                if source.get("ext_photometryKron_KronFlux_flag"):
                    continue

                angleList = [val * afwGeom.degrees for val in (45, 90)]
                scaleList = [1.0, 0.5]
                offsetList = [(1.23, 4.56), (12.3, 45.6)]

                for angle, scale, offset in itertools.product(
                        angleList, scaleList, offsetList):
                    dx, dy = offset
                    pixelScale = original.getWcs().getPixelScale() * scale
                    cdMatrix = afwGeom.makeCdMatrix(scale=pixelScale,
                                                    orientation=angle,
                                                    flipX=True)
                    wcs = afwGeom.makeSkyWcs(crpix=afwGeom.Point2D(dx, dy),
                                             crval=afwGeom.SpherePoint(
                                                 0.0, 0.0, afwGeom.degrees),
                                             cdMatrix=cdMatrix)

                    warped = warper.warpExposure(wcs, original)
                    # add a Psf if there is none.  The new SdssCentroid needs a Psf.
                    if warped.getPsf() is None:
                        warped.setPsf(afwDetection.GaussianPsf(11, 11, 0.01))
                    msConfig = makeMeasurementConfig(kfac=kfac, forced=True)
                    forced = measureForced(warped, source, original.getWcs(),
                                           msConfig)
                    algMeta = source.getTable().getMetadata()
                    self.assertTrue(
                        algMeta.exists(
                            'ext_photometryKron_KronFlux_nRadiusForFlux'))

                    if display:
                        disp1 = afwDisplay.Display(frame=1)
                        disp1.mtv(original,
                                  title=self._testMethodName +
                                  ": original image")
                        shape = source.getShape().clone()
                        xc, yc = source.getCentroid()
                        radius = source.get(
                            "ext_photometryKron_KronFlux_radius")
                        for r, ct in [
                            (radius, afwDisplay.BLUE),
                            (radius * kfac, afwDisplay.CYAN),
                        ]:
                            shape.scale(r / shape.getDeterminantRadius())
                            disp1.dot(shape, xc, yc, ctype=ct)
                        disp2 = afwDisplay.Display(frame=2)
                        disp2.mtv(warped,
                                  title=self._testMethodName +
                                  ": warped image")
                        transform = (wcs.linearizeSkyToPixel(
                            source.getCoord(), lsst.geom.degrees) *
                                     original.getWcs().linearizePixelToSky(
                                         source.getCoord(), lsst.geom.degrees))
                        shape = shape.transform(transform.getLinear())
                        radius = source.get(
                            "ext_photometryKron_KronFlux_radius")
                        xc, yc = wcs.skyToPixel(source.getCoord())
                        for r, ct in [
                            (radius, afwDisplay.BLUE),
                            (radius * kfac, afwDisplay.CYAN),
                        ]:
                            shape.scale(r / shape.getDeterminantRadius())
                            disp2.dot(shape, xc, yc, ctype=ct)
                    try:
                        self.assertFloatsAlmostEqual(
                            source.get("ext_photometryKron_KronFlux_instFlux"),
                            forced.get("ext_photometryKron_KronFlux_instFlux"),
                            rtol=1.0e-3)
                        self.assertFloatsAlmostEqual(
                            source.get("ext_photometryKron_KronFlux_radius"),
                            scale *
                            forced.get("ext_photometryKron_KronFlux_radius"),
                            rtol=1.0e-3)
                        self.assertEqual(
                            source.get("ext_photometryKron_KronFlux_flag"),
                            forced.get("ext_photometryKron_KronFlux_flag"))
                    except Exception:
                        print(("Failed:", angle, scale, offset, [
                            (source.get(f), forced.get(f))
                            for f in ("ext_photometryKron_KronFlux_instFlux",
                                      "ext_photometryKron_KronFlux_radius",
                                      "ext_photometryKron_KronFlux_flag")
                        ]))
                        raise
Exemple #9
0
    def testForced(self):
        """Check that forced photometry works in the presence of rotations and translations"""
        kfac = 2.5
        msConfig = makeSourceMeasurementConfig(kfac=kfac)
        warper = afwMath.Warper("lanczos4")
        a = 13
        for axisRatio in (0.25, 1.0):
            b = a*axisRatio
            for theta in (0, 30, 45):
                width, height = 256, 256
                center = afwGeom.Point2D(0.5*width, 0.5*height)
                original = makeGalaxy(width, height, 1000.0, a, b, theta)
                source = measureFree(original, center, msConfig)
                if source.get("flux.kron.flags"):
                    continue

                angleList = [45, 90,]
                scaleList = [1.0, 0.5]
                offsetList = [(1.23, 4.56), (12.3, 45.6)]

                for angle, scale, offset in itertools.product(angleList, scaleList, offsetList):
                    cosAngle = math.cos(math.radians(angle))
                    sinAngle = math.sin(math.radians(angle))
                    dx, dy = offset
                    pixelScale = original.getWcs().pixelScale().asDegrees()*scale
                    wcs = afwImage.makeWcs(afwCoord.Coord(0.0*afwGeom.degrees, 0.0*afwGeom.degrees),
                                           afwGeom.Point2D(dx, dy), pixelScale*cosAngle,
                                           pixelScale*sinAngle, -pixelScale*sinAngle, pixelScale*cosAngle)

                    warped = warper.warpExposure(wcs, original)
                    forced = measureForced(warped, source, original.getWcs(), msConfig)

                    if display:
                        ds9.mtv(original, frame=1)
                        shape = source.getShape().clone()
                        xc, yc = source.getCentroid()
                        radius = source.get("flux.kron.radius")
                        for r, ct in [(radius, ds9.BLUE), (radius*kfac, ds9.CYAN),]:
                            shape.scale(r/shape.getDeterminantRadius())
                            ds9.dot(shape, xc, yc, ctype=ct, frame=1)
                        ds9.mtv(warped, frame=2)
                        transform = (wcs.linearizeSkyToPixel(source.getCoord())*
                                     original.getWcs().linearizePixelToSky(source.getCoord()))
                        shape = shape.transform(transform.getLinear())
                        radius = forced.get("flux.kron.radius")
                        xc, yc = wcs.skyToPixel(source.getCoord()) - afwGeom.Extent2D(warped.getXY0())
                        for r, ct in [(radius, ds9.BLUE), (radius*kfac, ds9.CYAN),]:
                            shape.scale(r/shape.getDeterminantRadius())
                            ds9.dot(shape, xc, yc, ctype=ct, frame=2)

                    try:
                        self.assertClose(source.get("flux.kron"), forced.get("flux.kron"),
                                         rtol=2.0e-4, atol=None)
                        self.assertClose(source.get("flux.kron.radius"), scale*forced.get("flux.kron.radius"),
                                         rtol=None, atol=1.0e-12)
                        self.assertEqual(source.get("flux.kron.flags"), forced.get("flux.kron.flags"))
                    except:
                        print ("Failed:", angle, scale, offset,
                               [(source.get(f), forced.get(f)) for f in
                                ("flux.kron", "flux.kron.radius", "flux.kron.flags")])
                        raise