Ejemplo n.º 1
0
 def testArrays(self):
     image, mask, variance = self.mimage.getArrays()
     self.assert_((self.mimage.getImage().getArray() == image).all())
     self.assert_((self.mimage.getMask().getArray() == mask).all())
     self.assert_((self.mimage.getVariance().getArray() == variance).all())
     mimage2 = afwImage.makeMaskedImageFromArrays(image, mask, variance)
     self.assertEqual(type(mimage2), type(self.mimage))
Ejemplo n.º 2
0
 def testArrays(self):
     """
     This method is testing that ``lsst.afw.image.MaskedImageF.getArrays()``
     returns the proper image, mask, and variance.
     """
     image, mask, variance = self.mimage.getArrays()
     self.assertFloatsEqual(self.mimage.image.getArray(), image)
     self.assertFloatsEqual(self.mimage.mask.getArray(), mask)
     self.assertFloatsEqual(self.mimage.variance.getArray(), variance)
     mimage2 = afwImage.makeMaskedImageFromArrays(image, mask, variance)
     self.assertEqual(type(mimage2), type(self.mimage))
Ejemplo n.º 3
0
    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"))
Ejemplo n.º 4
0
def makeGaussianNoiseMaskedImage(dimensions, sigma, variance=1.0):
    """Make a gaussian noise MaskedImageF
    
    Inputs:
    - dimensions: dimensions of output array (cols, rows)
    - sigma; sigma of image plane's noise distribution
    - variance: constant value for variance plane
    """
    npSize = (dimensions[1], dimensions[0])
    image = numpy.random.normal(loc=0.0, scale=sigma, size=npSize).astype(numpy.float32)
    mask = numpy.zeros(npSize, dtype=numpy.uint16)
    variance = numpy.zeros(npSize, dtype=numpy.float32) + variance
    
    return afwImage.makeMaskedImageFromArrays(image, mask, variance)
Ejemplo n.º 5
0
def makeGaussianNoiseMaskedImage(dimensions, sigma, variance=1.0):
    """Make a gaussian noise MaskedImageF
    
    Inputs:
    - dimensions: dimensions of output array (cols, rows)
    - sigma; sigma of image plane's noise distribution
    - variance: constant value for variance plane
    """
    npSize = (dimensions[1], dimensions[0])
    image = numpy.random.normal(loc=0.0, scale=sigma,
                                size=npSize).astype(numpy.float32)
    mask = numpy.zeros(npSize, dtype=numpy.uint16)
    variance = numpy.zeros(npSize, dtype=numpy.float32) + variance

    return afwImage.makeMaskedImageFromArrays(image, mask, variance)
    def setUp(self):
        """Create a sqlite3 database with default tables and schemas.
        """
        # CFHT Filters from the camera mapper.
        self.filter_names = ["u", "g", "r", "i", "z"]
        afwImageUtils.resetFilters()
        afwImageUtils.defineFilter('u', lambdaEff=374, alias="u.MP9301")
        afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401")
        afwImageUtils.defineFilter('r', lambdaEff=628, alias="r.MP9601")
        afwImageUtils.defineFilter('i', lambdaEff=778, alias="i.MP9701")
        afwImageUtils.defineFilter('z', lambdaEff=1170, alias="z.MP9801")

        self.dia_object_schema = make_dia_object_schema()

        # metadata taken from CFHT data
        # v695856-e0/v695856-e0-c000-a00.sci_img.fits

        self.metadata = dafBase.PropertySet()

        self.metadata.set("SIMPLE", "T")
        self.metadata.set("BITPIX", -32)
        self.metadata.set("NAXIS", 2)
        self.metadata.set("NAXIS1", 1024)
        self.metadata.set("NAXIS2", 1153)
        self.metadata.set("RADECSYS", 'FK5')
        self.metadata.set("EQUINOX", 2000.)

        self.metadata.setDouble("CRVAL1", 215.604025685476)
        self.metadata.setDouble("CRVAL2", 53.1595451514076)
        self.metadata.setDouble("CRPIX1", 1109.99981456774)
        self.metadata.setDouble("CRPIX2", 560.018167811613)
        self.metadata.set("CTYPE1", 'RA---SIN')
        self.metadata.set("CTYPE2", 'DEC--SIN')

        self.metadata.setDouble("CD1_1", 5.10808596133527E-05)
        self.metadata.setDouble("CD1_2", 1.85579539217196E-07)
        self.metadata.setDouble("CD2_2", -5.10281493481982E-05)
        self.metadata.setDouble("CD2_1", -8.27440751733828E-07)

        self.wcs = afwGeom.makeSkyWcs(self.metadata)
        self.exposure = afwImage.makeExposure(
            afwImage.makeMaskedImageFromArrays(np.ones((1024, 1153))),
            self.wcs)
        detector = DetectorWrapper(id=23, bbox=self.exposure.getBBox()).detector
        visit = afwImage.VisitInfo(
            exposureId=1234,
            exposureTime=200.,
            date=dafBase.DateTime("2014-05-13T17:00:00.000000000",
                                  dafBase.DateTime.Timescale.TAI))
        self.exposure.setDetector(detector)
        self.exposure.getInfo().setVisitInfo(visit)
        self.exposure.setFilter(afwImage.Filter('g'))
        self.flux0 = 10000
        self.flux0_err = 100
        self.exposure.setPhotoCalib(
            afwImage.PhotoCalib(self.flux0, self.flux0_err))

        bbox = geom.Box2D(self.exposure.getBBox())
        wcs = self.exposure.getWcs()

        self.pixelator = sphgeom.HtmPixelization(20)
        region = sphgeom.ConvexPolygon([wcs.pixelToSky(pp).getVector()
                                        for pp in bbox.getCorners()])

        indices = self.pixelator.envelope(region, 64)
        # Index types must be cast to int to work with dax_apdb.
        self.index_ranges = indices.ranges()
Ejemplo n.º 7
0
    def verifyMaskWarp(self,
                       kernelName,
                       maskKernelName,
                       growFullMask,
                       interpLength=10,
                       cacheSize=100000,
                       rtol=4e-05,
                       atol=1e-2):
        """Verify that using a separate mask warping kernel produces the correct results

        Inputs:
        - kernelName: name of warping kernel in the form used by afwImage.makeKernel
        - maskKernelName: name of mask warping kernel in the form used by afwImage.makeKernel
        - interpLength: interpLength argument for lsst.afw.math.WarpingControl
        - cacheSize: cacheSize argument for lsst.afw.math.WarpingControl;
            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 np.allclose
        - atol: absolute tolerance as used by np.allclose
        """
        srcWcs = afwGeom.makeSkyWcs(
            crpix=lsst.geom.Point2D(10, 11),
            crval=lsst.geom.SpherePoint(41.7, 32.9, lsst.geom.degrees),
            cdMatrix=afwGeom.makeCdMatrix(scale=0.2 * lsst.geom.degrees),
        )
        destWcs = afwGeom.makeSkyWcs(
            crpix=lsst.geom.Point2D(9, 10),
            crval=lsst.geom.SpherePoint(41.65, 32.95, lsst.geom.degrees),
            cdMatrix=afwGeom.makeCdMatrix(scale=0.17 * lsst.geom.degrees),
        )

        srcMaskedImage = afwImage.MaskedImageF(100, 101)
        srcExposure = afwImage.ExposureF(srcMaskedImage, srcWcs)

        srcArrays = srcMaskedImage.getArrays()
        shape = srcArrays[0].shape
        srcArrays[0][:] = np.random.normal(10000, 1000, size=shape)
        srcArrays[2][:] = np.random.normal(9000, 900, size=shape)
        srcArrays[1][:] = np.reshape(
            np.arange(0, shape[0] * shape[1], 1, dtype=np.uint16), shape)

        warpControl = afwMath.WarpingControl(kernelName, maskKernelName,
                                             cacheSize, interpLength,
                                             growFullMask)
        destMaskedImage = afwImage.MaskedImageF(110, 121)
        destExposure = afwImage.ExposureF(destMaskedImage, destWcs)
        afwMath.warpExposure(destExposure, srcExposure, warpControl)

        # now compute with two separate mask planes
        warpControl.setGrowFullMask(0)
        narrowMaskedImage = afwImage.MaskedImageF(110, 121)
        narrowExposure = afwImage.ExposureF(narrowMaskedImage, destWcs)
        afwMath.warpExposure(narrowExposure, srcExposure, warpControl)
        narrowArrays = narrowExposure.getMaskedImage().getArrays()

        warpControl.setMaskWarpingKernelName("")
        broadMaskedImage = afwImage.MaskedImageF(110, 121)
        broadExposure = afwImage.ExposureF(broadMaskedImage, destWcs)
        afwMath.warpExposure(broadExposure, srcExposure, warpControl)
        broadArrays = broadExposure.getMaskedImage().getArrays()

        if (kernelName != maskKernelName) and (growFullMask != 0xFFFF):
            # we expect the mask planes to differ
            if np.all(narrowArrays[1] == broadArrays[1]):
                self.fail("No difference between broad and narrow mask")

        predMask = (broadArrays[1] & growFullMask) | (
            narrowArrays[1] & ~growFullMask).astype(np.uint16)
        predArraySet = (broadArrays[0], predMask, broadArrays[2])
        predExposure = afwImage.makeMaskedImageFromArrays(*predArraySet)

        msg = f"Separate mask warping failed; warpingKernel={kernelName}; maskWarpingKernel={maskKernelName}"
        self.assertMaskedImagesAlmostEqual(destExposure.getMaskedImage(),
                                           predExposure,
                                           doImage=True,
                                           doMask=True,
                                           doVariance=True,
                                           rtol=rtol,
                                           atol=atol,
                                           msg=msg)
Ejemplo n.º 8
0
def makeExposure(flipX=False, flipY=False):
    """Create an exposure and flip the x or y (or both) coordinates.

    Returns bounding boxes that are right or left handed around the bounding
    polygon.

    Parameters
    ----------
    flipX : `bool`
        Flip the x coordinate in the WCS.
    flipY : `bool`
        Flip the y coordinate in the WCS.

    Returns
    -------
    exposure : `lsst.afw.image.Exposure`
        Exposure with a valid bounding box and wcs.
    """
    metadata = dafBase.PropertySet()

    metadata.set("SIMPLE", "T")
    metadata.set("BITPIX", -32)
    metadata.set("NAXIS", 2)
    metadata.set("NAXIS1", 1024)
    metadata.set("NAXIS2", 1153)
    metadata.set("RADECSYS", 'FK5')
    metadata.set("EQUINOX", 2000.)

    metadata.setDouble("CRVAL1", 215.604025685476)
    metadata.setDouble("CRVAL2", 53.1595451514076)
    metadata.setDouble("CRPIX1", 1109.99981456774)
    metadata.setDouble("CRPIX2", 560.018167811613)
    metadata.set("CTYPE1", 'RA---SIN')
    metadata.set("CTYPE2", 'DEC--SIN')

    xFlip = 1
    if flipX:
        xFlip = -1
    yFlip = 1
    if flipY:
        yFlip = -1
    metadata.setDouble("CD1_1", xFlip * 5.10808596133527E-05)
    metadata.setDouble("CD1_2", yFlip * 1.85579539217196E-07)
    metadata.setDouble("CD2_2", yFlip * -5.10281493481982E-05)
    metadata.setDouble("CD2_1", xFlip * -8.27440751733828E-07)

    wcs = afwGeom.makeSkyWcs(metadata)
    exposure = afwImage.makeExposure(
        afwImage.makeMaskedImageFromArrays(np.ones((1024, 1153))), wcs)
    detector = DetectorWrapper(id=23, bbox=exposure.getBBox()).detector
    visit = afwImage.VisitInfo(exposureId=1234,
                               exposureTime=200.,
                               date=dafBase.DateTime(
                                   "2014-05-13T17:00:00.000000000",
                                   dafBase.DateTime.Timescale.TAI))
    exposure.info.id = 1234
    exposure.setDetector(detector)
    exposure.getInfo().setVisitInfo(visit)
    exposure.setFilter(afwImage.FilterLabel(band='g'))

    return exposure
Ejemplo n.º 9
0
    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.assertImagesAlmostEqual(self.cnvImage,
                                     refMaskedImage.getImage(),
                                     atol=atol,
                                     rtol=rtol)
        self.assertMaskedImagesAlmostEqual(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"))
Ejemplo n.º 10
0
    def verifyMaskWarp(self, kernelName, maskKernelName, growFullMask, interpLength=10, cacheSize=100000,
                       rtol=4e-05, atol=1e-2):
        """Verify that using a separate mask warping kernel produces the correct results

        Inputs:
        - kernelName: name of warping kernel in the form used by afwImage.makeKernel
        - maskKernelName: name of mask warping kernel in the form used by afwImage.makeKernel
        - interpLength: interpLength argument for lsst.afw.math.WarpingControl
        - cacheSize: cacheSize argument for lsst.afw.math.WarpingControl;
            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 np.allclose
        - atol: absolute tolerance as used by np.allclose
        """
        srcWcs = afwGeom.makeSkyWcs(
            crpix=lsst.geom.Point2D(10, 11),
            crval=lsst.geom.SpherePoint(41.7, 32.9, lsst.geom.degrees),
            cdMatrix=afwGeom.makeCdMatrix(scale=0.2*lsst.geom.degrees),
        )
        destWcs = afwGeom.makeSkyWcs(
            crpix=lsst.geom.Point2D(9, 10),
            crval=lsst.geom.SpherePoint(41.65, 32.95, lsst.geom.degrees),
            cdMatrix=afwGeom.makeCdMatrix(scale=0.17*lsst.geom.degrees),
        )

        srcMaskedImage = afwImage.MaskedImageF(100, 101)
        srcExposure = afwImage.ExposureF(srcMaskedImage, srcWcs)

        srcArrays = srcMaskedImage.getArrays()
        shape = srcArrays[0].shape
        srcArrays[0][:] = np.random.normal(10000, 1000, size=shape)
        srcArrays[2][:] = np.random.normal(9000, 900, size=shape)
        srcArrays[1][:] = np.reshape(
            np.arange(0, shape[0] * shape[1], 1, dtype=np.uint16), shape)

        warpControl = afwMath.WarpingControl(
            kernelName,
            maskKernelName,
            cacheSize,
            interpLength,
            growFullMask
        )
        destMaskedImage = afwImage.MaskedImageF(110, 121)
        destExposure = afwImage.ExposureF(destMaskedImage, destWcs)
        afwMath.warpExposure(destExposure, srcExposure, warpControl)

        # now compute with two separate mask planes
        warpControl.setGrowFullMask(0)
        narrowMaskedImage = afwImage.MaskedImageF(110, 121)
        narrowExposure = afwImage.ExposureF(narrowMaskedImage, destWcs)
        afwMath.warpExposure(narrowExposure, srcExposure, warpControl)
        narrowArrays = narrowExposure.getMaskedImage().getArrays()

        warpControl.setMaskWarpingKernelName("")
        broadMaskedImage = afwImage.MaskedImageF(110, 121)
        broadExposure = afwImage.ExposureF(broadMaskedImage, destWcs)
        afwMath.warpExposure(broadExposure, srcExposure, warpControl)
        broadArrays = broadExposure.getMaskedImage().getArrays()

        if (kernelName != maskKernelName) and (growFullMask != 0xFFFF):
            # we expect the mask planes to differ
            if np.all(narrowArrays[1] == broadArrays[1]):
                self.fail("No difference between broad and narrow mask")

        predMask = (broadArrays[1] & growFullMask) | (
            narrowArrays[1] & ~growFullMask).astype(np.uint16)
        predArraySet = (broadArrays[0], predMask, broadArrays[2])
        predExposure = afwImage.makeMaskedImageFromArrays(*predArraySet)

        msg = "Separate mask warping failed; warpingKernel=%s; maskWarpingKernel=%s" % \
            (kernelName, maskKernelName)
        self.assertMaskedImagesAlmostEqual(destExposure.getMaskedImage(), predExposure,
                                           doImage=True, doMask=True, doVariance=True,
                                           rtol=rtol, atol=atol, msg=msg)
Ejemplo n.º 11
0
    def verifyMaskWarp(self,
                       kernelName,
                       maskKernelName,
                       growFullMask,
                       interpLength=10,
                       cacheSize=100000,
                       rtol=4e-05,
                       atol=1e-2):
        """Verify that using a separate mask warping kernel produces the correct results
        
        Inputs:
        - kernelName: name of warping kernel in the form used by afwImage.makeKernel
        - maskKernelName: name of mask warping kernel in the form used by afwImage.makeKernel
        - interpLength: interpLength argument for lsst.afw.math.WarpingControl
        - cacheSize: cacheSize argument for lsst.afw.math.WarpingControl;
            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
        """
        srcWcs = makeWcs(
            pixelScale=afwGeom.Angle(0.2, afwGeom.degrees),
            crPixPos=(10.0, 11.0),
            crValCoord=afwCoord.IcrsCoord(afwGeom.Point2D(41.7, 32.9),
                                          afwGeom.degrees),
        )
        destWcs = makeWcs(
            pixelScale=afwGeom.Angle(0.17, afwGeom.degrees),
            crPixPos=(9.0, 10.0),
            crValCoord=afwCoord.IcrsCoord(afwGeom.Point2D(41.65, 32.95),
                                          afwGeom.degrees),
            posAng=afwGeom.Angle(31, afwGeom.degrees),
        )

        srcMaskedImage = afwImage.MaskedImageF(100, 101)
        srcExposure = afwImage.ExposureF(srcMaskedImage, srcWcs)

        srcArrays = srcMaskedImage.getArrays()
        shape = srcArrays[0].shape
        numpy.random.seed(0)
        srcArrays[0][:] = numpy.random.normal(10000, 1000, size=shape)
        srcArrays[2][:] = numpy.random.normal(9000, 900, size=shape)
        srcArrays[1][:] = numpy.reshape(
            numpy.arange(0, shape[0] * shape[1], 1, dtype=numpy.uint16), shape)

        warpControl = afwMath.WarpingControl(kernelName, maskKernelName,
                                             cacheSize, interpLength,
                                             afwGpu.DEFAULT_DEVICE_PREFERENCE,
                                             growFullMask)
        destMaskedImage = afwImage.MaskedImageF(110, 121)
        destExposure = afwImage.ExposureF(destMaskedImage, destWcs)
        afwMath.warpExposure(destExposure, srcExposure, warpControl)

        # now compute with two separate mask planes
        warpControl.setGrowFullMask(0)
        narrowMaskedImage = afwImage.MaskedImageF(110, 121)
        narrowExposure = afwImage.ExposureF(narrowMaskedImage, destWcs)
        afwMath.warpExposure(narrowExposure, srcExposure, warpControl)
        narrowArrays = narrowExposure.getMaskedImage().getArrays()

        warpControl.setMaskWarpingKernelName("")
        broadMaskedImage = afwImage.MaskedImageF(110, 121)
        broadExposure = afwImage.ExposureF(broadMaskedImage, destWcs)
        afwMath.warpExposure(broadExposure, srcExposure, warpControl)
        broadArrays = broadExposure.getMaskedImage().getArrays()

        if (kernelName != maskKernelName) and (growFullMask != 0xFFFF):
            # we expect the mask planes to differ
            if numpy.all(narrowArrays[1] == broadArrays[1]):
                self.fail("No difference between broad and narrow mask")

        predMask = (broadArrays[1] & growFullMask) | (
            narrowArrays[1] & ~growFullMask).astype(numpy.uint16)
        predArraySet = (broadArrays[0], predMask, broadArrays[2])
        predExposure = afwImage.makeMaskedImageFromArrays(*predArraySet)

        msg = "Separate mask warping failed; warpingKernel=%s; maskWarpingKernel=%s" % \
                (kernelName, maskKernelName)
        self.assertMaskedImagesNearlyEqual(destExposure.getMaskedImage(),
                                           predExposure,
                                           doImage=True,
                                           doMask=True,
                                           doVariance=True,
                                           rtol=rtol,
                                           atol=atol,
                                           msg=msg)