Example #1
0
def test_sbinterpolatedimage():
    """Test that we can make SBInterpolatedImages from Images of various types, and convert back.
    """
    import time
    t1 = time.time()
    # for each type, try to make an SBInterpolatedImage, and check that when we draw an image from
    # that SBInterpolatedImage that it is the same as the original
    lan3 = galsim.Lanczos(3, True, 1.E-4)
    lan3_2d = galsim.InterpolantXY(lan3)

    ftypes = [np.float32, np.float64]
    ref_array = np.array([[0.01, 0.08, 0.07, 0.02], [0.13, 0.38, 0.52, 0.06],
                          [0.09, 0.41, 0.44, 0.09], [0.04, 0.11, 0.10, 0.01]])

    for array_type in ftypes:
        image_in = galsim.ImageView[array_type](ref_array.astype(array_type))
        np.testing.assert_array_equal(
            ref_array.astype(array_type),
            image_in.array,
            err_msg=
            "Array from input Image differs from reference array for type %s" %
            array_type)
        sbinterp = galsim.SBInterpolatedImage(image_in, lan3_2d, dx=1.0)
        test_array = np.zeros(ref_array.shape, dtype=array_type)
        image_out = galsim.ImageView[array_type](test_array, scale=1.0)
        sbinterp.draw(image_out.view())
        np.testing.assert_array_equal(
            ref_array.astype(array_type),
            image_out.array,
            err_msg=
            "Array from output Image differs from reference array for type %s"
            % array_type)

        # Lanczos doesn't quite get the flux right.  Wrong at the 5th decimal place.
        # Gary says that's expected -- Lanczos isn't technically flux conserving.
        # He applied the 1st order correction to the flux, but expect to be wrong at around
        # the 10^-5 level.
        # Anyway, Quintic seems to be accurate enough.
        quint = galsim.Quintic(1.e-4)
        quint_2d = galsim.InterpolantXY(quint)
        sbinterp = galsim.SBInterpolatedImage(image_in, quint_2d, dx=1.0)
        sbinterp.setFlux(1.)
        do_shoot(galsim.GSObject(sbinterp), image_out, "InterpolatedImage")

        # Test kvalues
        do_kvalue(galsim.GSObject(sbinterp), "InterpolatedImage")

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Example #2
0
def main(argv):

    # Try different interpolants!
    #interp1d = galsim.Linear();
    #interp1d = galsim.Delta();
    interp1d = galsim.Lanczos(5, conserve_dc=True, tol=1.e-4)
    #interp1d = galsim.Quintic();
    interp2d = galsim.InterpolantXY(interp1d)

    try:
        inname = argv[1]
        outname = argv[2]
        dxOut = float(argv[3])
        dim = int(argv[4])
        nPhotons = int(argv[5])
        g1 = float(argv[6]) if len(argv) > 6 else 0.
        g2 = float(argv[7]) if len(argv) > 7 else 0.
    except Exception as err:
        print __doc__
        raise err

    galaxyImg = galsim.fits.read(inname)
    galaxy = galsim.InterpolatedImage(galaxyImg, x_interpolant=interp2d, dx=1.)
    galaxy.applyShear(g1=g1, g2=g2)

    rng = galsim.UniformDeviate(1534225)
    bounds = galsim.BoundsI(-dim / 2, dim / 2 + 1, -dim / 2, dim / 2 + 1)
    img = galsim.ImageF(bounds, scale=dxOut)
    galaxy.drawShoot(image=img, n_photons=nPhotons, rng=rng)
    img.write(outname)
Example #3
0
    def __init__(self,
                 lam_over_r0=None,
                 fwhm=None,
                 interpolant=None,
                 oversampling=1.5,
                 flux=1.,
                 gsparams=None):

        # The FWHM of the Kolmogorov PSF is ~0.976 lambda/r0 (e.g., Racine 1996, PASP 699, 108).
        if lam_over_r0 is None:
            if fwhm is not None:
                lam_over_r0 = fwhm / 0.976
            else:
                raise TypeError(
                    "Either lam_over_r0 or fwhm must be specified for AtmosphericPSF"
                )
        else:
            if fwhm is None:
                fwhm = 0.976 * lam_over_r0
            else:
                raise TypeError(
                    "Only one of lam_over_r0 and fwhm may be specified for AtmosphericPSF"
                )
        # Set the lookup table sample rate via FWHM / 2 / oversampling (BARNEY: is this enough??)
        dx_lookup = .5 * fwhm / oversampling

        # Fold at 10 times the FWHM
        stepk_kolmogorov = np.pi / (10. * fwhm)

        # Odd array to center the interpolant on the centroid. Might want to pad this later to
        # make a nice size array for FFT, but for typical seeing, arrays will be very small.
        npix = 1 + 2 * (np.ceil(np.pi / stepk_kolmogorov)).astype(int)
        atmoimage = kolmogorov_psf_image(array_shape=(npix, npix),
                                         dx=dx_lookup,
                                         lam_over_r0=lam_over_r0,
                                         flux=flux)

        # Run checks on the interpolant and build default if None
        if interpolant is None:
            quintic = galsim.Quintic(tol=1e-4)
            self.interpolant = galsim.InterpolantXY(quintic)
        else:
            self.interpolant = galsim.utilities.convert_interpolant_to_2d(
                interpolant)

        # Then initialize the SBProfile
        GSObject.__init__(
            self,
            galsim.SBInterpolatedImage(atmoimage,
                                       xInterp=self.interpolant,
                                       dx=dx_lookup,
                                       gsparams=gsparams))

        # The above procedure ends up with a larger image than we really need, which
        # means that the default stepK value will be smaller than we need.
        # Thus, we call the function calculateStepK() to refine the value.
        self.SBProfile.calculateStepK()
        self.SBProfile.calculateMaxK()
Example #4
0
def main(argv):

    # translation from C++ by Jim; comments after this one are in Gary's voice

    l3 = galsim.Lanczos(3, True, 1.0E-4)
    l32d = galsim.InterpolantXY(l3)

    dxHST = 0.03
    dxSDSS = 0.396
    g1 = 0.02
    g2 = 0.0
    psfSky = 1000.0

    rootname = argv[1]
    xshift = float(argv[2]) if len(argv) > 2 else 0.
    yshift = float(argv[3]) if len(argv) > 3 else 0.
    s = galsim.Shear(g1=g1, g2=g2)

    # Rachel is probably using the (1+g, 1-g) form of shear matrix,
    # which means there is some (de)magnification, by my definition:
    #e = galsim.Ellipse(s, -(g1*g1+g2*g2), galsim.PositionD(xshift,yshift));

    galaxyImg = galsim.fits.read(rootname + "_masknoise.fits")
    galaxy = galsim.InterpolatedImage(galaxyImg,
                                      x_interpolant=l32d,
                                      dx=dxHST,
                                      flux=0.804 * 1000. * dxSDSS * dxSDSS)

    psf1Img = galsim.fits.read(rootname + ".psf.fits")
    psf1 = galsim.InterpolatedImage(psf1Img,
                                    x_interpolant=l32d,
                                    dx=dxHST,
                                    flux=1.)

    psf2Img = galsim.fits.read(rootname + ".sdsspsf.fits")
    psf2 = galsim.InterpolatedImage(psf2Img,
                                    x_interpolant=l32d,
                                    dx=dxSDSS,
                                    flux=1.)

    outImg = galsim.fits.read(rootname + ".g1_0.02.g2_0.00.fits")
    result = outImg.copy()

    psfInv = galsim.Deconvolve(psf1)
    deconv = galsim.Convolve(galaxy, psfInv)
    sheared = deconv.createTransformed(e)
    out = galsim.Convolve(sheared, psf2)

    test_outImg = out.draw(result, dx=dxSDSS)
    test_outImg.write(rootname + ".gary.fits")
    result += psfSky
    result -= test_outImg
    result.write(rootname + ".diff.fits")
Example #5
0
def convert_interpolant_to_2d(interpolant):
    """Convert a given interpolant to an Interpolant2d if it is given as a string or 1-d.
    """
    if interpolant == None:
        return None  # caller is responsible for setting a default if desired.
    elif isinstance(interpolant, galsim.Interpolant2d):
        return interpolant
    elif isinstance(interpolant, galsim.Interpolant):
        return galsim.InterpolantXY(interpolant)
    else:
        # Will raise an appropriate exception if this is invalid.
        return galsim.Interpolant2d(interpolant)
Example #6
0
def test_roundtrip():
    """Test round trip from Image to InterpolatedImage back to Image.
    """
    # Based heavily on test_sbinterpolatedimage() in test_SBProfile.py!
    import time
    t1 = time.time()

    # for each type, try to make an SBInterpolatedImage, and check that when we draw an image from
    # that SBInterpolatedImage that it is the same as the original
    ftypes = [np.float32, np.float64]
    ref_array = np.array([[0.01, 0.08, 0.07, 0.02], [0.13, 0.38, 0.52, 0.06],
                          [0.09, 0.41, 0.44, 0.09], [0.04, 0.11, 0.10, 0.01]])

    for array_type in ftypes:
        image_in = galsim.ImageView[array_type](ref_array.astype(array_type))
        np.testing.assert_array_equal(
            ref_array.astype(array_type),
            image_in.array,
            err_msg=
            "Array from input Image differs from reference array for type %s" %
            array_type)
        interp = galsim.InterpolatedImage(image_in, dx=test_dx)
        test_array = np.zeros(ref_array.shape, dtype=array_type)
        image_out = galsim.ImageView[array_type](test_array)
        image_out.setScale(test_dx)
        interp.draw(image_out)
        np.testing.assert_array_equal(
            ref_array.astype(array_type),
            image_out.array,
            err_msg=
            "Array from output Image differs from reference array for type %s"
            % array_type)

        # Lanczos doesn't quite get the flux right.  Wrong at the 5th decimal place.
        # Gary says that's expected -- Lanczos isn't technically flux conserving.
        # He applied the 1st order correction to the flux, but expect to be wrong at around
        # the 10^-5 level.
        # Anyway, Quintic seems to be accurate enough.
        quint = galsim.Quintic(1.e-4)
        quint_2d = galsim.InterpolantXY(quint)
        interp = galsim.InterpolatedImage(image_in,
                                          x_interpolant=quint_2d,
                                          dx=test_dx,
                                          flux=1.)
        do_shoot(interp, image_out, "InterpolatedImage")

    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Example #7
0
def test_Quintic_spline():
    """Test the spline tabulation of the k space Quintic interpolant.
    """
    import time
    t1 = time.time()
    interp = galsim.InterpolantXY(galsim.Quintic(tol=1.e-4))
    testobj = galsim.SBInterpolatedImage(image.view(), interp, dx=dx)
    testKvals = np.zeros(len(KXVALS))
    # Make test kValues
    for i in xrange(len(KXVALS)):
        posk = galsim.PositionD(KXVALS[i], KYVALS[i])
        testKvals[i] = np.abs(testobj.kValue(posk))
    # Compare with saved array
    refKvals = np.loadtxt(os.path.join(TESTDIR, "absfKQuintic_test.txt"))
    np.testing.assert_array_almost_equal(
        refKvals,
        testKvals,
        DECIMAL,
        err_msg="Spline-interpolated kValues do not match saved " +
        "data for k space Quintic interpolant.")
    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
Example #8
0
    def __init__(self,
                 lam_over_diam,
                 defocus=0.,
                 astig1=0.,
                 astig2=0.,
                 coma1=0.,
                 coma2=0.,
                 trefoil1=0.,
                 trefoil2=0.,
                 spher=0.,
                 circular_pupil=True,
                 obscuration=0.,
                 interpolant=None,
                 oversampling=1.5,
                 pad_factor=1.5,
                 flux=1.,
                 gsparams=None):

        # Currently we load optics, noise etc in galsim/__init__.py, but this might change (???)
        import galsim.optics

        # Choose dx for lookup table using Nyquist for optical aperture and the specified
        # oversampling factor
        dx_lookup = .5 * lam_over_diam / oversampling

        # We need alias_threshold here, so don't wait to make this a default GSParams instance
        # if the user didn't specify anything else.
        if not gsparams:
            gsparams = galsim.GSParams()

        # Use a similar prescription as SBAiry to set Airy stepK and thus reference unpadded image
        # size in physical units
        stepk_airy = min(
            gsparams.alias_threshold * .5 * np.pi**3 * (1. - obscuration) /
            lam_over_diam, np.pi / 5. / lam_over_diam)

        # Boost Airy image size by a user-specifed pad_factor to allow for larger, aberrated PSFs,
        # also make npix always *odd* so that opticalPSF lookup table array is correctly centred:
        npix = 1 + 2 * (np.ceil(pad_factor *
                                (np.pi / stepk_airy) / dx_lookup)).astype(int)

        # Make the psf image using this dx and array shape
        optimage = galsim.optics.psf_image(lam_over_diam=lam_over_diam,
                                           dx=dx_lookup,
                                           array_shape=(npix, npix),
                                           defocus=defocus,
                                           astig1=astig1,
                                           astig2=astig2,
                                           coma1=coma1,
                                           coma2=coma2,
                                           trefoil1=trefoil1,
                                           trefoil2=trefoil2,
                                           spher=spher,
                                           circular_pupil=circular_pupil,
                                           obscuration=obscuration,
                                           flux=flux)

        # If interpolant not specified on input, use a Quintic interpolant
        if interpolant is None:
            quintic = galsim.Quintic(tol=1e-4)
            self.interpolant = galsim.InterpolantXY(quintic)
        else:
            self.interpolant = galsim.utilities.convert_interpolant_to_2d(
                interpolant)

        # Initialize the SBProfile
        GSObject.__init__(
            self,
            galsim.SBInterpolatedImage(optimage,
                                       xInterp=self.interpolant,
                                       dx=dx_lookup,
                                       gsparams=gsparams))

        # The above procedure ends up with a larger image than we really need, which
        # means that the default stepK value will be smaller than we need.
        # Thus, we call the function calculateStepK() to refine the value.
        self.SBProfile.calculateStepK()
        self.SBProfile.calculateMaxK()
Example #9
0
def simReal(real_galaxy,
            target_PSF,
            target_pixel_scale,
            g1=0.0,
            g2=0.0,
            rotation_angle=None,
            rand_rotate=True,
            rng=None,
            target_flux=1000.0,
            image=None):
    """Function to simulate images (no added noise) from real galaxy training data.

    This function takes a RealGalaxy from some training set, and manipulates it as needed to 
    simulate a (no-noise-added) image from some lower-resolution telescope.  It thus requires a
    target PSF (which could be an image, or one of our base classes) that represents all PSF 
    components including the pixel response, and a target pixel scale.  

    The default rotation option is to impose a random rotation to make irrelevant any real shears 
    in the galaxy training data (optionally, the RNG can be supplied).  This default can be turned 
    off by setting `rand_rotate = False` or by requesting a specific rotation angle using the
    `rotation_angle` keyword, in which case `rand_rotate` is ignored.

    Optionally, the user can specify a shear (default 0).  Finally, they can specify a flux 
    normalization for the final image, default 1000.

    @param real_galaxy         The RealGalaxy object to use, not modified in generating the
                               simulated image.
    @param target_PSF          The target PSF, either one of our base classes or an ImageView/Image.
    @param target_pixel_scale  The pixel scale for the final image, in arcsec.
    @param g1                  First component of shear to impose (components defined with respect
                               to pixel coordinates), default `g1 = 0.`
    @param g2                  Second component of shear to impose, default `g2 = 0.`
    @param rotation_angle      Angle by which to rotate the galaxy (must be a galsim.Angle 
                               instance).
    @param rand_rotate         If `rand_rotate = True` (default) then impose a random rotation on 
                               the training galaxy; this is ignored if `rotation_angle` is set.
    @param rng                 A random number generator to use for selection of the random 
                               rotation angle. (optional, may be any kind of galsim.BaseDeviate 
                               or None)
    @param target_flux         The target flux in the output galaxy image, default 
                               `target_flux = 1000.`
    @param image               As with the GSObject.draw() function, if an image is provided,
                               then it will be used and returned.
                               If `image=None`, then an appropriately sized image will be created.
    @return A simulated galaxy image.  The input RealGalaxy is unmodified. 
    """
    # do some checking of arguments
    if not isinstance(real_galaxy, galsim.RealGalaxy):
        raise RuntimeError("Error: simReal requires a RealGalaxy!")
    for Class in galsim.Image.itervalues():
        if isinstance(target_PSF, Class):
            lan5 = galsim.Lanczos(5, conserve_flux=True, tol=1.e-4)
            interp2d = galsim.InterpolantXY(lan5)
            target_PSF = galsim.SBInterpolatedImage(target_PSF.view(),
                                                    xInterp=interp2d,
                                                    dx=target_pixel_scale)
            break
    for Class in galsim.ImageView.itervalues():
        if isinstance(target_PSF, Class):
            lan5 = galsim.Lanczos(5, conserve_flux=True, tol=1.e-4)
            interp2d = galsim.InterpolantXY(lan5)
            target_PSF = galsim.SBInterpolatedImage(target_PSF,
                                                    xInterp=interp2d,
                                                    dx=target_pixel_scale)
            break
    if isinstance(target_PSF, galsim.GSObject):
        target_PSF = target_PSF.SBProfile
    if not isinstance(target_PSF, galsim.SBProfile):
        raise RuntimeError(
            "Error: target PSF is not an Image, ImageView, SBProfile, or GSObject!"
        )
    if rotation_angle != None and not isinstance(rotation_angle, galsim.Angle):
        raise RuntimeError(
            "Error: specified rotation angle is not an Angle instance!")
    if (target_pixel_scale < real_galaxy.pixel_scale):
        import warnings
        message = "Warning: requested pixel scale is higher resolution than original!"
        warnings.warn(message)
    import math  # needed for pi, sqrt below
    g = math.sqrt(g1**2 + g2**2)
    if g > 1:
        raise RuntimeError("Error: requested shear is >1!")

    # make sure target PSF is normalized
    target_PSF.setFlux(1.0)

    real_galaxy_copy = real_galaxy.copy()

    # rotate
    if rotation_angle != None:
        real_galaxy_copy.applyRotation(rotation_angle)
    elif rotation_angle == None and rand_rotate == True:
        if rng == None:
            uniform_deviate = galsim.UniformDeviate()
        elif isinstance(rng, galsim.BaseDeviate):
            uniform_deviate = galsim.UniformDeviate(rng)
        else:
            raise TypeError(
                "The rng provided to drawShoot is not a BaseDeviate")
        rand_angle = galsim.Angle(math.pi * uniform_deviate(), galsim.radians)
        real_galaxy_copy.applyRotation(rand_angle)

    # set fluxes
    real_galaxy_copy.setFlux(target_flux)

    # shear
    if (g1 != 0.0 or g2 != 0.0):
        real_galaxy_copy.applyShear(g1=g1, g2=g2)

    # convolve, resample
    out_gal = galsim.Convolve([real_galaxy_copy, galsim.GSObject(target_PSF)])
    image = out_gal.draw(image=image, dx=target_pixel_scale)

    # return simulated image
    return image
Example #10
0
    def __init__(self,
                 real_galaxy_catalog,
                 index=None,
                 id=None,
                 random=False,
                 rng=None,
                 x_interpolant=None,
                 k_interpolant=None,
                 flux=None,
                 pad_factor=0,
                 noise_pad=False,
                 pad_image=None,
                 use_cache=True,
                 gsparams=None):

        import pyfits
        import numpy as np

        # Code block below will be for galaxy selection; not all are currently implemented.  Each
        # option must return an index within the real_galaxy_catalog.
        if index is not None:
            if id is not None or random is True:
                raise AttributeError(
                    'Too many methods for selecting a galaxy!')
            use_index = index
        elif id is not None:
            if random is True:
                raise AttributeError(
                    'Too many methods for selecting a galaxy!')
            use_index = real_galaxy_catalog._get_index_for_id(id)
        elif random is True:
            if rng is None:
                uniform_deviate = galsim.UniformDeviate()
            elif isinstance(rng, galsim.BaseDeviate):
                uniform_deviate = galsim.UniformDeviate(rng)
            else:
                raise TypeError(
                    "The rng provided to RealGalaxy constructor is not a BaseDeviate"
                )
            use_index = int(real_galaxy_catalog.nobjects * uniform_deviate())
        else:
            raise AttributeError('No method specified for selecting a galaxy!')

        # read in the galaxy, PSF images; for now, rely on pyfits to make I/O errors. Should
        # consider exporting this code into fits.py in some function that takes a filename and HDU,
        # and returns an ImageView

        gal_image = real_galaxy_catalog.getGal(use_index)
        PSF_image = real_galaxy_catalog.getPSF(use_index)

        # choose proper interpolant
        if x_interpolant is None:
            lan5 = galsim.Lanczos(5, conserve_flux=True, tol=1.e-4)
            self.x_interpolant = galsim.InterpolantXY(lan5)
        else:
            self.x_interpolant = galsim.utilities.convert_interpolant_to_2d(
                x_interpolant)
        if k_interpolant is None:
            self.k_interpolant = galsim.InterpolantXY(
                galsim.Quintic(tol=1.e-4))
        else:
            self.k_interpolant = galsim.utilities.convert_interpolant_to_2d(
                k_interpolant)

        # read in data about galaxy from FITS binary table; store as normal attributes of RealGalaxy

        # save any other relevant information as instance attributes
        self.catalog_file = real_galaxy_catalog.file_name
        self.index = use_index
        self.pixel_scale = float(real_galaxy_catalog.pixel_scale[use_index])

        # handle padding by an image
        specify_size = False
        padded_size = gal_image.getPaddedSize(pad_factor)
        if pad_image is not None:
            specify_size = True
            if isinstance(pad_image, str):
                pad_image = galsim.fits.read(pad_image)
            if (not isinstance(pad_image, galsim.BaseImageF)
                    and not isinstance(pad_image, galsim.BaseImageD)):
                raise ValueError(
                    "Supplied pad_image is not one of the allowed types!")
            # If an image was supplied directly or from a file, check its size:
            #    Cannot use if too small.
            #    Use to define the final image size otherwise.
            deltax = ((1 + pad_image.getXMax() - pad_image.getXMin()) -
                      (1 + gal_image.getXMax() - gal_image.getXMin()))
            deltay = ((1 + pad_image.getYMax() - pad_image.getYMin()) -
                      (1 + gal_image.getYMax() - gal_image.getYMin()))
            if deltax < 0 or deltay < 0:
                raise RuntimeError("Image supplied for padding is too small!")
            if pad_factor != 1. and pad_factor != 0.:
                import warnings
                msg = "Warning: ignoring specified pad_factor because user also specified\n"
                msg += "         an image to use directly for the padding."
                warnings.warn(msg)
        else:
            if isinstance(gal_image, galsim.BaseImageF):
                pad_image = galsim.ImageF(padded_size, padded_size)
            if isinstance(gal_image, galsim.BaseImageD):
                pad_image = galsim.ImageD(padded_size, padded_size)

        # Set up the GaussianDeviate if not provided one, or check that the user-provided one
        # is of a valid type.  Note if random was selected, we can use that uniform_deviate safely.
        if random is True:
            gaussian_deviate = galsim.GaussianDeviate(uniform_deviate)
        else:
            if rng is None:
                gaussian_deviate = galsim.GaussianDeviate()
            elif isinstance(rng, galsim.BaseDeviate):
                # Even if it's already a GaussianDeviate, we still want to make a new Gaussian
                # deviate that would generate the same sequence, because later we change the sigma
                # and we don't want to change it for the original one that was passed in.  So don't
                # distinguish between GaussianDeviate and the other BaseDeviates here.
                gaussian_deviate = galsim.GaussianDeviate(rng)
            else:
                raise TypeError(
                    "rng provided to RealGalaxy constructor is not a BaseDeviate"
                )

        # handle noise-padding options
        try:
            noise_pad = galsim.config.value._GetBoolValue(noise_pad, '')
        except:
            pass
        if noise_pad:
            self.pad_variance = float(real_galaxy_catalog.variance[use_index])

            # Check, is it "True" or something else?  If True, we use Gaussian uncorrelated noise
            # using the stored variance in the catalog.  Otherwise, if it's a CorrelatedNoise we use
            # it directly; if it's an Image of some sort we use it to make a CorrelatedNoise; if
            # it's a string, we read in the image from file and make a CorrelatedNoise.
            if type(noise_pad) is not bool:
                if isinstance(noise_pad,
                              galsim.correlatednoise._BaseCorrelatedNoise):
                    cn = noise_pad.copy()
                    if rng:  # Let user supplied RNG take precedence over that in user CN
                        cn.setRNG(gaussian_deviate)
                    # This small patch may have different overall variance, so rescale while
                    # preserving the correlation structure by default
                    cn.setVariance(self.pad_variance)
                elif (isinstance(noise_pad, galsim.BaseImageF)
                      or isinstance(noise_pad, galsim.BaseImageD)):
                    cn = galsim.CorrelatedNoise(gaussian_deviate, noise_pad)
                elif use_cache and noise_pad in RealGalaxy._cache_noise_pad:
                    cn = RealGalaxy._cache_noise_pad[noise_pad]
                    # Make sure that we are using the desired RNG by resetting that in this cached
                    # CorrelatedNoise instance
                    if rng:
                        cn.setRNG(gaussian_deviate)
                    # This small patch may have different overall variance, so rescale while
                    # preserving the correlation structure
                    cn.setVariance(self.pad_variance)
                elif isinstance(noise_pad, str):
                    tmp_img = galsim.fits.read(noise_pad)
                    cn = galsim.CorrelatedNoise(gaussian_deviate, tmp_img)
                    if use_cache:
                        RealGalaxy._cache_noise_pad[noise_pad] = cn
                    # This small patch may have different overall variance, so rescale while
                    # preserving the correlation structure
                    cn.setVariance(self.pad_variance)
                else:
                    raise RuntimeError(
                        "noise_pad must be either a bool, CorrelatedNoise, Image, "
                        + "or a filename for reading in an Image")

            # Set the GaussianDeviate sigma
            gaussian_deviate.setSigma(np.sqrt(self.pad_variance))

            # populate padding image with noise field
            if type(noise_pad) is bool:
                pad_image.addNoise(galsim.DeviateNoise(gaussian_deviate))
            else:
                pad_image.addNoise(cn)
        else:
            self.pad_variance = 0.

        # Now we have to check: was the padding determined using pad_factor?  Or by passing in an
        # image for padding?  Treat these cases differently:
        # (1) If the former, then we can simply have the C++ handle the padding process.
        # (2) If the latter, then we have to do the padding ourselves, and pass the resulting image
        # to the C++ with pad_factor explicitly set to 1.
        if specify_size is False:
            # Make the SBInterpolatedImage out of the image.
            self.original_image = galsim.SBInterpolatedImage(
                gal_image,
                xInterp=self.x_interpolant,
                kInterp=self.k_interpolant,
                dx=self.pixel_scale,
                pad_factor=pad_factor,
                pad_image=pad_image,
                gsparams=gsparams)
        else:
            # Leave the original image as-is.  Instead, we shift around the image to be used for
            # padding.  Find out how much x and y margin there should be on lower end:
            x_marg = int(np.round(0.5 * deltax))
            y_marg = int(np.round(0.5 * deltay))
            # Now reset the pad_image to contain the original image in an even way
            pad_image = pad_image.view()
            pad_image.setScale(self.pixel_scale)
            pad_image.setOrigin(gal_image.getXMin() - x_marg,
                                gal_image.getYMin() - y_marg)
            # Set the central values of pad_image to be equal to the input image
            pad_image[gal_image.bounds] = gal_image
            self.original_image = galsim.SBInterpolatedImage(
                pad_image,
                xInterp=self.x_interpolant,
                kInterp=self.k_interpolant,
                dx=self.pixel_scale,
                pad_factor=1.,
                gsparams=gsparams)

        # also make the original PSF image, with far less fanfare: we don't need to pad with
        # anything interesting.
        self.original_PSF = galsim.SBInterpolatedImage(
            PSF_image,
            xInterp=self.x_interpolant,
            kInterp=self.k_interpolant,
            dx=self.pixel_scale,
            gsparams=gsparams)

        # recalculate Fourier-space attributes rather than using overly-conservative defaults
        self.original_image.calculateStepK()
        self.original_image.calculateMaxK()
        self.original_PSF.calculateStepK()
        self.original_PSF.calculateMaxK()

        if flux != None:
            self.original_image.setFlux(flux)
            self.original_image.__class__ = galsim.SBTransform  # correctly reflect SBProfile change
        self.original_PSF.setFlux(1.0)
        self.original_PSF.__class__ = galsim.SBTransform  # correctly reflect SBProfile change

        # Calculate the PSF "deconvolution" kernel
        psf_inv = galsim.SBDeconvolve(self.original_PSF, gsparams=gsparams)
        # Initialize the SBProfile attribute
        GSObject.__init__(
            self,
            galsim.SBConvolve([self.original_image, psf_inv],
                              gsparams=gsparams))
g2 = galsim.Gaussian(sigma = 1.9, flux=3.1)
g2.applyShear(-0.4,0.3)
g2.applyShift(-0.3,0.5)
g3 = galsim.Gaussian(sigma = 4.1, flux=1.6)
g3.applyShear(0.1,-0.1)
g3.applyShift(0.7,-0.2)

final = g1 + g2 + g3
image = galsim.ImageD(128,128)
dx = 0.4
final.draw(image=image, dx=dx)

dir = '../../../tests/interpolant_comparison_files'

# First make a Cubic interpolant
interp = galsim.InterpolantXY(galsim.Cubic(tol=1.e-4))
testobj = galsim.SBInterpolatedImage(image.view(), interp, dx=dx)
for i in xrange(len(kxvals)):
    posk = galsim.PositionD(kxvals[i], kyvals[i])
    absoutk[i] = np.abs(testobj.kValue(posk))
print absoutk
np.savetxt(os.path.join(dir,'absfKCubic_test.txt'), absoutk)

# Then make a Quintic interpolant
interp = galsim.InterpolantXY(galsim.Quintic(tol=1.e-4))
testobj = galsim.SBInterpolatedImage(image.view(), interp, dx=dx)
for i in xrange(len(kxvals)):
    posk = galsim.PositionD(kxvals[i], kyvals[i])
    absoutk[i] = np.abs(testobj.kValue(posk))
print absoutk
np.savetxt(os.path.join(dir,'absfKQuintic_test.txt'), absoutk)
    def __init__(
            self,
            position_list_filename="ground_optical_psf_zernike_coefficients_41x41/ZEMAXInput.dat",
            lam=800.,
            diameter=4.0,
            obscuration=0.35,
            nstruts=0,
            strut_thick=0.01,
            strut_angle=0. * galsim.degrees,
            pad_factor=None,
            dz=0.,
            dx=0.,
            dy=0.,
            tx=0.,
            ty=0.,
            dz0=0.05,
            interpolant2d=None):
        """
        Inputs
        - position_list_filename: filename of position list used for reading ZEMAX output files.
        - lam: wavelength [nm]
        - diameter: diameter of telescope [m]
        - obscuration: central obscuration [ratio between mirror diameter and obscuration diameter]
        - nstruts: number of radial support struts to add to the central obscuration
        - strut_thick: thickness of support struts as a fraction of pupil diameter
        - strut_angle: angle made between the vertical and the strut starting closest to it,
                       defined to be positive in the counter-clockwise direction; must be a
                       galsim.Angle instance
        - pad_factor: optional padding specification if 1.5 is not good enough
        - dz: defocus [mm]
        - dx: decenter along x axis [mm]
        - dy: decenter along y axis [mm]
        - tx: tilt about x [arcsec]
        - tx: tilt about y [arcsec]
        - dz0: offset to defocus [mm]
        - interpolant2d: galsim._galsim.InterpolantXY
                         If None, galsim.InterpolantXY(galsim.Quintic())

        Notes of system convention
        (dx, dy, dz): right-handed system where z-axis is along the light coming into mirror.
        (tx, ty): + is the rotation by which a right screw goes into the direction of an axis.
        """
        self.lam = lam * 1e-9  # meters
        self.lam_over_diam = self.lam / diameter * 206265  # arcsec
        self.obscuration = obscuration
        self.nstruts = nstruts
        self.strut_thick = strut_thick
        self.strut_angle = strut_angle
        self.pad_factor = pad_factor

        # read position information from the list
        data = np.loadtxt(position_list_filename)
        x = data[:, 0]
        y = data[:, 1]
        n = int(np.sqrt(len(x)))
        d = y[1] - y[0]
        self.xmin = x.min()
        self.xmax = x.max()
        self.ymin = y.min()
        self.ymax = y.max()

        # read coefficients from ZEMAX file
        self.ncoefs = 8  # defocus, a1, a2, c1, c2, t1, t2, spher
        coefs = np.zeros((self.ncoefs, n, n))
        for i, (_x, _y) in enumerate(zip(x, y)):
            zernike_filename = os.path.join(
                os.path.dirname(position_list_filename),
                "%.4f_%.4f.txt" % (_x, _y))
            wavelength, coefs_tmp = loadZernikeCoefficients(zernike_filename)
            i_x = int(i / n)
            i_y = i % n
            # need to flip sign of x and y, since definition of axes in ZEMAX and GalSim seem
            #different.
            # We have to convert Zernike coefficients in units of wavelength we want.
            coefs[:, n - i_y - 1,
                  n - i_x - 1] = coefs_tmp * wavelength * 1e-6 / self.lam

        # get interpolated images
        self.interpolated_coefficients = list()
        for coef in coefs:
            im_coef = galsim.ImageViewD(coef)
            im_coef.setScale(d)
            if interpolant2d == None:
                interpolant2d = galsim.InterpolantXY(galsim.Quintic())
            self.interpolated_coefficients.append(
                galsim.InterpolatedImage(
                    im_coef,
                    x_interpolant=interpolant2d,
                    normalization="sb",
                    calculate_stepk=False,
                    calculate_maxk=False,
                ))
        # prepare for misalignment
        self.optical_psf_misalignment = OpticalPSFMisalignment(
            self.lam * 1e9, dz + dz0, dx, dy, tx, ty)
Example #13
0
    def __init__(self, image, x_interpolant = None, k_interpolant = None, normalization = 'flux',
                 dx = None, flux = None, pad_factor = 0., noise_pad = 0., rng = None,
                 pad_image = None, calculate_stepk=True, calculate_maxk=True,
                 use_cache=True, use_true_center=True, gsparams=None):

        import numpy as np

        # first try to read the image as a file.  If it's not either a string or a valid
        # pyfits hdu or hdulist, then an exception will be raised, which we ignore and move on.
        try:
            image = galsim.fits.read(image)
        except:
            pass

        # make sure image is really an image and has a float type
        if not isinstance(image, galsim.BaseImageF) and not isinstance(image, galsim.BaseImageD):
            raise ValueError("Supplied image is not an image of floats or doubles!")

        # it must have well-defined bounds, otherwise seg fault in SBInterpolatedImage constructor
        if not image.getBounds().isDefined():
            raise ValueError("Supplied image does not have bounds defined!")

        # check what normalization was specified for the image: is it an image of surface
        # brightness, or flux?
        if not normalization.lower() in ("flux", "f", "surface brightness", "sb"):
            raise ValueError(("Invalid normalization requested: '%s'. Expecting one of 'flux', "+
                              "'f', 'surface brightness', or 'sb'.") % normalization)

        # set up the interpolants if none was provided by user, or check that the user-provided ones
        # are of a valid type
        if x_interpolant is None:
            self.x_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4))
        else:
            self.x_interpolant = galsim.utilities.convert_interpolant_to_2d(x_interpolant)
        if k_interpolant is None:
            self.k_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4))
        else:
            self.k_interpolant = galsim.utilities.convert_interpolant_to_2d(k_interpolant)

        # Check for input dx, and check whether Image already has one set.  At the end of this
        # code block, either an exception will have been raised, or the input image will have a
        # valid scale set.
        if dx is None:
            dx = image.scale
            if dx == 0:
                raise ValueError("No information given with Image or keywords about pixel scale!")
        else:
            if type(dx) != float:
                dx = float(dx)
            # Don't change the original image.  Make a new view if we need to set the scale.
            image = image.view()
            image.setScale(dx)
            if dx == 0.0:
                raise ValueError("dx may not be 0.0")

        # Set up the GaussianDeviate if not provided one, or check that the user-provided one is
        # of a valid type.
        if rng is None:
            gaussian_deviate = galsim.GaussianDeviate()
        elif isinstance(rng, galsim.BaseDeviate):
            # Even if it's already a GaussianDeviate, we still want to make a new Gaussian deviate
            # that would generate the same sequence, because later we change the sigma and we don't
            # want to change it for the original one that was passed in.  So don't distinguish
            # between GaussianDeviate and the other BaseDeviates here.
            gaussian_deviate = galsim.GaussianDeviate(rng)
        else:
            raise TypeError("rng provided to InterpolatedImage constructor is not a BaseDeviate")

        # decide about deterministic image padding
        specify_size = False
        padded_size = image.getPaddedSize(pad_factor)
        if pad_image:
            specify_size = True
            if isinstance(pad_image, str):
                pad_image = galsim.fits.read(pad_image)
            if ( not isinstance(pad_image, galsim.BaseImageF) and 
                 not isinstance(pad_image, galsim.BaseImageD) ):
                raise ValueError("Supplied pad_image is not one of the allowed types!")

            # If an image was supplied directly or from a file, check its size:
            #    Cannot use if too small.
            #    Use to define the final image size otherwise.
            deltax = (1+pad_image.getXMax()-pad_image.getXMin())-(1+image.getXMax()-image.getXMin())
            deltay = (1+pad_image.getYMax()-pad_image.getYMin())-(1+image.getYMax()-image.getYMin())
            if deltax < 0 or deltay < 0:
                raise RuntimeError("Image supplied for padding is too small!")
            if pad_factor != 1. and pad_factor != 0.:
                import warnings
                msg =  "Warning: ignoring specified pad_factor because user also specified\n"
                msg += "         an image to use directly for the padding."
                warnings.warn(msg)
        elif noise_pad:
            if isinstance(image, galsim.BaseImageF):
                pad_image = galsim.ImageF(padded_size, padded_size)
            if isinstance(image, galsim.BaseImageD):
                pad_image = galsim.ImageD(padded_size, padded_size)

        # now decide about noise padding
        # First, see if the input is consistent with a float.
        # i.e. it could be an int, or a str that converts to a number.
        try:
            noise_pad = float(noise_pad)
        except:
            pass
        if isinstance(noise_pad, float):
            if noise_pad < 0.:
                raise ValueError("Noise variance cannot be negative!")
            elif noise_pad > 0.:
                # Note: make sure the sigma is properly set to sqrt(noise_pad).
                gaussian_deviate.setSigma(np.sqrt(noise_pad))
                pad_image.addNoise(galsim.DeviateNoise(gaussian_deviate))
        else:
            if isinstance(noise_pad, galsim.correlatednoise._BaseCorrelatedNoise):
                cn = noise_pad.copy()
                if rng: # Let a user supplied RNG take precedence over that in user CN
                    cn.setRNG(gaussian_deviate)
            elif isinstance(noise_pad,galsim.BaseImageF) or isinstance(noise_pad,galsim.BaseImageD):
                cn = galsim.CorrelatedNoise(gaussian_deviate, noise_pad)
            elif use_cache and noise_pad in InterpolatedImage._cache_noise_pad:
                cn = InterpolatedImage._cache_noise_pad[noise_pad]
                if rng:
                    # Make sure that we are using a specified RNG by resetting that in this cached
                    # CorrelatedNoise instance, otherwise preserve the cached RNG
                    cn.setRNG(gaussian_deviate)
            elif isinstance(noise_pad, str):
                cn = galsim.CorrelatedNoise(gaussian_deviate, galsim.fits.read(noise_pad))
                if use_cache: 
                    InterpolatedImage._cache_noise_pad[noise_pad] = cn
            else:
                raise ValueError(
                    "Input noise_pad must be a float/int, a CorrelatedNoise, Image, or filename "+
                    "containing an image to use to make a CorrelatedNoise!")
            pad_image.addNoise(cn)

        # Now we have to check: was the padding determined using pad_factor?  Or by passing in an
        # image for padding?  Treat these cases differently:
        # (1) If the former, then we can simply have the C++ handle the padding process.
        # (2) If the latter, then we have to do the padding ourselves, and pass the resulting image
        # to the C++ with pad_factor explicitly set to 1.
        if specify_size is False:
            # Make the SBInterpolatedImage out of the image.
            sbinterpolatedimage = galsim.SBInterpolatedImage(
                    image, xInterp=self.x_interpolant, kInterp=self.k_interpolant,
                    dx=dx, pad_factor=pad_factor, pad_image=pad_image, gsparams=gsparams)
            self.x_size = padded_size
            self.y_size = padded_size
        else:
            # Leave the original image as-is.  Instead, we shift around the image to be used for
            # padding.  Find out how much x and y margin there should be on lower end:
            x_marg = int(np.round(0.5*deltax))
            y_marg = int(np.round(0.5*deltay))
            # Now reset the pad_image to contain the original image in an even way
            pad_image = pad_image.view()
            pad_image.setScale(dx)
            pad_image.setOrigin(image.getXMin()-x_marg, image.getYMin()-y_marg)
            # Set the central values of pad_image to be equal to the input image
            pad_image[image.bounds] = image
            sbinterpolatedimage = galsim.SBInterpolatedImage(
                    pad_image, xInterp=self.x_interpolant, kInterp=self.k_interpolant,
                    dx=dx, pad_factor=1., gsparams=gsparams)
            self.x_size = 1+pad_image.getXMax()-pad_image.getXMin()
            self.y_size = 1+pad_image.getYMax()-pad_image.getYMin()

        # GalSim cannot automatically know what stepK and maxK are appropriate for the 
        # input image.  So it is usually worth it to do a manual calculation here.
        if calculate_stepk:
            sbinterpolatedimage.calculateStepK()
        if calculate_maxk:
            sbinterpolatedimage.calculateMaxK()

        # If the user specified a flux, then set to that flux value.
        if flux != None:
            if type(flux) != flux:
                flux = float(flux)
            sbinterpolatedimage.setFlux(flux)
        # If the user specified a flux normalization for the input Image, then since
        # SBInterpolatedImage works in terms of surface brightness, have to rescale the values to
        # get proper normalization.
        elif flux is None and normalization.lower() in ['flux','f'] and dx != 1.:
            sbinterpolatedimage.scaleFlux(1./(dx**2))
        # If the input Image normalization is 'sb' then since that is the SBInterpolated default
        # assumption, no rescaling is needed.

        # Initialize the SBProfile
        GSObject.__init__(self, sbinterpolatedimage)

        # Fix the center to be in the right place.
        # Note the minus sign in front of image.scale, since we want to fix the center in the 
        # opposite sense of what the draw function does.
        if use_true_center:
            prof = self._fix_center(image, -image.scale)
            GSObject.__init__(self, prof.SBProfile)
def Script4():

    pixel_scale = 0.28  # arcsec
    nx = 64
    ny = 64

    psf_fwhm = 0.65  # arcsec

    pix = galsim.Pixel(xw=pixel_scale)

    atmos = galsim.Gaussian(fwhm=psf_fwhm)

    i1d_list = [
        "galsim.Linear(tol=1.e-4)", "galsim.Cubic(tol=1.e-4)",
        "galsim.Quintic(tol=1.e-4)",
        "galsim.Lanczos(3, conserve_flux=False, tol=1.e-4)",
        "galsim.Lanczos(5, conserve_flux=False, tol=1.e-4)",
        "galsim.Lanczos(7, conserve_flux=False, tol=1.e-4)",
        "galsim.Lanczos(3, conserve_flux=True, tol=1.e-4)",
        "galsim.Lanczos(5, conserve_flux=True, tol=1.e-4)",
        "galsim.Lanczos(7, conserve_flux=True, tol=1.e-4)"
    ]

    for i1d_name in i1d_list:
        print 'i1d = ', i1d_name
        # A workaround for the fact that the interpolants don't have good __repr__'s yet.
        i1d = eval(i1d_name)
        #print 'i1d = ',i1d

        # Make the PSF profile:
        i2d = galsim.InterpolantXY(i1d)
        optics = galsim.OpticalPSF(interpolantxy=i2d,
                                   lam_over_diam=0.6 * psf_fwhm,
                                   obscuration=0.4,
                                   defocus=0.1,
                                   astig1=0.3,
                                   astig2=-0.2,
                                   coma1=0.2,
                                   coma2=0.1,
                                   spher=-0.3)
        psf1 = galsim.Convolve([atmos, optics])
        #print 'Made psf1'

        # Also make the same thing using oversampling = 5
        optics = galsim.OpticalPSF(interpolantxy=i2d,
                                   oversampling=5,
                                   lam_over_diam=0.6 * psf_fwhm,
                                   obscuration=0.4,
                                   defocus=0.1,
                                   astig1=0.3,
                                   astig2=-0.2,
                                   coma1=0.2,
                                   coma2=0.1,
                                   spher=-0.3)
        psf2 = galsim.Convolve([atmos, optics])
        #print 'Made psf2'

        # build final profile
        epsf1 = galsim.Convolve([psf1, pix])
        epsf2 = galsim.Convolve([psf2, pix])
        #print 'Made epsf1, epsf2'

        # Create the large, double width output image
        image1 = galsim.ImageF(nx, ny)
        image2 = galsim.ImageF(nx, ny)
        image1.setScale(pixel_scale)
        image2.setScale(pixel_scale)
        #print 'Made images'

        # Draw the profile
        epsf1.draw(image1)
        epsf2.draw(image2)
        #print 'Done drawing images for i1d = ',i1d

        print 'rms diff = ', math.sqrt(
            np.mean((image1.array - image2.array)**2))
Example #15
0
bulgeRe = [0.25, 0.5]
if len(diskRe) is not len(bulgeRe):
    raise RuntimeError(
        "Grids in bulge and disk scale lengths do not have same size!")
nBulge = 4.0
nDisk = 1.0
pixelScale = 0.03  # we are simulating ACS data
totFlux = 1000.0  # total flux for galaxy

#### note: the following parameters were not used ####
sigmaBulge = 1.0  # dispersion in Sersic n for bulge
sigmaDisk = 0.2  # dispersion in Sersic n for disk

# read in ePSF and normalize (note: already includes pixel response, don't have to do separately)
l3 = galsim.Lanczos(3, True, 1.0E-4)
l32d = galsim.InterpolantXY(l3)
PSFImage = galsim.fits.read(PSFFile)
PSF = galsim.SBInterpolatedImage(PSFImage, l32d, pixelScale, 2.)
PSF.setFlux(1.0)

# Loop over the various grids: values of bulge-to-total ratio, bulge ellipticity, disk ellipticity,
# S/N and number of noise realizations, radii
rng = galsim.UniformDeviate()
for bt in bulge2Total:
    print "Beginning bulge-to-total ratio of ", bt
    for bell in bulgeEllip:
        print "Beginning bulge ellipticity of ", bell
        for dell in diskEllip:
            print "Beginning disk ellipticity of ", dell
            for dreind in range(len(diskRe)):
                print "Beginning disk half-light radius of ", diskRe[dreind]
Example #16
0
    def __init__(
            self,
            filename="afta_wfirst_example_psf_exaggerated.fields_and_coefs.fits",
            lam=1000.,
            diameter=2.4,
            obscuration=0.28,
            nstruts=6,
            strut_thick=0.01,
            strut_angle=0. * galsim.degrees,
            pad_factor=None,
            rms=0.075,
            interpolant2d=None,
            seed=None):
        """
        Inputs
        - filename: filename of fits file with information of optics.
        - lam: wavelength [nm]
        - diameter: diameter of telescope [m]
        - obscuration: central obscuration [ratio between mirror diameter and obscuration diameter]
        - nstruts: number of radial support struts to add to the central obscuration
        - strut_thick: thickness of support struts as a fraction of pupil diameter
        - strut_angle: angle made between the vertical and the strut starting closest to it,
                       defined to be positive in the counter-clockwise direction; must be a
                       galsim.Angle instance
        - pad_factor: optional padding specification if 1.5 is not good enough
        - rms: total rms of the random Zernike coefficients [wavelength]
        - interpolant2d: galsim._galsim.InterpolantXY
                         If None, galsim.InterpolantXY(galsim.Quintic())
        - seed: random seed to use for numpy routines that make random additional aberrations (if
                None, then let numpy seed routines based on time)
        """

        self.lam = lam * 1e-9  # meters
        self.lam_over_diam = self.lam / diameter * 206265  # arcsec
        self.obscuration = obscuration
        self.nstruts = nstruts
        self.strut_thick = strut_thick
        self.strut_angle = strut_angle
        self.pad_factor = pad_factor

        # read file
        hdulist = pyfits.open(filename)
        primary_hdu, fields, coefs = hdulist

        # note that fields.data['x'] is actually y-axis and fields.data['y'] is x-axis
        fields_1d = np.array(zip(fields.data['x'], fields.data['y']))

        n = int(np.sqrt(fields_1d.shape[0]))
        self.dy = sorted(fields.data['x'])[n] - sorted(fields.data['x'])[n - 1]
        self.dx = sorted(fields.data['y'])[n] - sorted(fields.data['y'])[n - 1]

        self.xmin = fields.data['y'].min()
        self.xmax = fields.data['y'].max()
        self.ymin = fields.data['x'].min()
        self.ymax = fields.data['x'].max()

        sorted_coordinates_raster = np.array([
            row[2] for row in sorted([(r[0], r[1], i)
                                      for i, r in enumerate(fields_1d)])
        ])
        field_mapping_index = sorted_coordinates_raster.reshape(n, n)
        # Zernike coefficients in the input file is 10 times larger than actual ones
        mapped_coefs = coefs.data[field_mapping_index] / 10.

        # interpolate coefficients
        self.n_coefs = 8  # defocus, a1, a2, c1, c2, t1, t2, spher
        self.interpolated_coefficients = list()
        for i_coefs in range(self.n_coefs):
            im_coef = galsim.ImageViewD(
                np.ascontiguousarray(mapped_coefs[:, :, i_coefs + 3]))
            im_coef.setScale(1.)
            if interpolant2d == None:
                interpolant2d = galsim.InterpolantXY(galsim.Quintic())
            self.interpolated_coefficients.append(
                galsim.InterpolatedImage(
                    im_coef,
                    x_interpolant=interpolant2d,
                    normalization="sb",
                    calculate_stepk=False,
                    calculate_maxk=False,
                ))
        # generate aberration errors
        if rms != 0.:
            if seed is not None:
                np.random.seed(seed)
            self.aberration_errors = np.random.normal(
                0., rms / np.sqrt(self.n_coefs), self.n_coefs)
        else:
            self.aberration_errors = np.zeros(self.n_coefs)

        hdulist.close()
Example #17
0
    def __init__(self, image, x_interpolant = None, k_interpolant = None, normalization = 'flux',
                 dx = None, flux = None, pad_factor = 0., noise_pad = 0., rng = None,
                 pad_image = None, calculate_stepk=True, calculate_maxk=True,
                 use_cache=True, use_true_center=True, offset=None, gsparams=None):

        # first try to read the image as a file.  If it's not either a string or a valid
        # pyfits hdu or hdulist, then an exception will be raised, which we ignore and move on.
        try:
            image = galsim.fits.read(image)
        except:
            pass

        # make sure image is really an image and has a float type
        if not isinstance(image, galsim.BaseImageF) and not isinstance(image, galsim.BaseImageD):
            raise ValueError("Supplied image is not an image of floats or doubles!")

        # it must have well-defined bounds, otherwise seg fault in SBInterpolatedImage constructor
        if not image.bounds.isDefined():
            raise ValueError("Supplied image does not have bounds defined!")

        # check what normalization was specified for the image: is it an image of surface
        # brightness, or flux?
        if not normalization.lower() in ("flux", "f", "surface brightness", "sb"):
            raise ValueError(("Invalid normalization requested: '%s'. Expecting one of 'flux', "+
                              "'f', 'surface brightness', or 'sb'.") % normalization)

        # set up the interpolants if none was provided by user, or check that the user-provided ones
        # are of a valid type
        if x_interpolant is None:
            self.x_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4))
        else:
            self.x_interpolant = galsim.utilities.convert_interpolant_to_2d(x_interpolant)
        if k_interpolant is None:
            self.k_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4))
        else:
            self.k_interpolant = galsim.utilities.convert_interpolant_to_2d(k_interpolant)

        # Check for input dx, and check whether Image already has one set.  At the end of this
        # code block, either an exception will have been raised, or the input image will have a
        # valid scale set.
        if dx is None:
            dx = image.scale
            if dx == 0:
                raise ValueError("No information given with Image or keywords about pixel scale!")
        else:
            if type(dx) != float:
                dx = float(dx)
            if dx <= 0.0:
                raise ValueError("dx may not be <= 0.0")
            # Don't change the original image.  Make a new view if we need to set the scale.
            image = image.view()
            image.scale = dx

        # Set up the GaussianDeviate if not provided one, or check that the user-provided one is
        # of a valid type.
        if rng is None:
            if noise_pad: rng = galsim.BaseDeviate()
        elif not isinstance(rng, galsim.BaseDeviate):
            raise TypeError("rng provided to InterpolatedImage constructor is not a BaseDeviate")

        # Check that given pad_image is valid:
        if pad_image:
            if isinstance(pad_image, str):
                pad_image = galsim.fits.read(pad_image)
            if ( not isinstance(pad_image, galsim.BaseImageF) and 
                 not isinstance(pad_image, galsim.BaseImageD) ):
                raise ValueError("Supplied pad_image is not one of the allowed types!")

        # Check that the given noise_pad is valid:
        try:
            noise_pad = float(noise_pad)
        except:
            pass
        if isinstance(noise_pad, float):
            if noise_pad < 0.:
                raise ValueError("Noise variance cannot be negative!")
        # There are other options for noise_pad, the validity of which will be checked in
        # the helper function self.buildNoisePadImage()

        # This will be passed to SBInterpolatedImage, so make sure it is the right type.
        pad_factor = float(pad_factor)

        # Store the image as an attribute
        self.orig_image = image
        self.use_cache = use_cache

        # See if we need to build a pad_image
        if noise_pad and pad_image:
            # if both noise_pad and pad_image are set, then we need to build up a larger
            # pad_image and place the given pad_image in the center.
            new_pad_image = self.buildNoisePadImage(pad_factor, noise_pad, rng)

            # We will change the bounds here, so make a new view to avoid modifying the 
            # input pad_image.
            pad_image = pad_image.view()  
            pad_image.setCenter(0,0)
            new_pad_image.setCenter(0,0)
            if not new_pad_image.bounds.includes(pad_image.bounds):
                raise ValueError("pad_factor is too small to fit the provided pad_image.")
            new_pad_image[pad_image.bounds] = pad_image
            pad_image = new_pad_image
        elif noise_pad:
            # Just build the noise image
            pad_image = self.buildNoisePadImage(pad_factor, noise_pad, rng)
        elif pad_image:
            # Just make sure pad_image is the right type
            if ( isinstance(image, galsim.BaseImageF) and 
                 not isinstance(pad_image, galsim.BaseImageF) ):
                pad_image = galsim.ImageF(pad_image)
            elif ( isinstance(image, galsim.BaseImageD) and 
                   not isinstance(pad_image, galsim.BaseImageD) ):
                pad_image = galsim.ImageD(pad_image)

        # Make the SBInterpolatedImage out of the image.
        sbinterpolatedimage = galsim.SBInterpolatedImage(
                image, xInterp=self.x_interpolant, kInterp=self.k_interpolant,
                dx=dx, pad_factor=pad_factor, pad_image=pad_image, gsparams=gsparams)

        # GalSim cannot automatically know what stepK and maxK are appropriate for the 
        # input image.  So it is usually worth it to do a manual calculation here.
        if calculate_stepk:
            sbinterpolatedimage.calculateStepK()
        if calculate_maxk:
            sbinterpolatedimage.calculateMaxK()

        # If the user specified a flux, then set to that flux value.
        if flux != None:
            if type(flux) != flux:
                flux = float(flux)
            sbinterpolatedimage.setFlux(flux)
        # If the user specified a flux normalization for the input Image, then since
        # SBInterpolatedImage works in terms of surface brightness, have to rescale the values to
        # get proper normalization.
        elif flux is None and normalization.lower() in ['flux','f'] and dx != 1.:
            sbinterpolatedimage.scaleFlux(1./(dx**2))
        # If the input Image normalization is 'sb' then since that is the SBInterpolated default
        # assumption, no rescaling is needed.

        # Initialize the SBProfile
        GSObject.__init__(self, sbinterpolatedimage)

        # Apply the offset, and possibly fix the centering for even-sized images
        # Note reverse=True, since we want to fix the center in the opposite sense of what the 
        # draw function does.
        prof = self._fix_center(image, dx, offset, use_true_center, reverse=True)
        GSObject.__init__(self, prof.SBProfile)
Example #18
0
def getCOSMOSNoise(rng, file_name, dx_cosmos=0.03, variance=0., x_interpolant=None):
    """Returns a representation of correlated noise in the HST COSMOS F814W unrotated science coadd
    images.

    See http://cosmos.astro.caltech.edu/astronomer/hst.html for information about the COSMOS survey,
    and Leauthaud et al (2007) for detailed information about the unrotated F814W coadds used for
    weak lensing science.

    This function uses a stacked estimate of the correlation function in COSMOS noise fields, the
    location of which should be input to this function via the `file_name` argument.  This image is
    stored in FITS format, and is generated as described in
    `YOUR/REPO/PATH/GalSim/devel/external/hst/make_cosmos_cfimage.py`.  The image itself can also be
    found within the GalSim repo, located at:

        /YOUR/REPO/PATH/GalSim/examples/data/acs_I_unrot_sci_20_cf.fits

    @param rng            Must be a galsim.BaseDeviate or derived class instance, provides the
                          random number generator used by the returned _BaseCorrelatedNoise
                          instance.
    @param file_name      String containing the path and filename above but modified to match the
                          location of the GalSim repository on your system.
    @param dx_cosmos      COSMOS ACS F814W coadd image pixel scale in the units you are using to
                          describe GSObjects and image scales in GalSim: defaults to 0.03 arcsec,
                          see below for more information.
    @param variance       Scales the correlation function so that its point variance, equivalent to
                          its value at zero separation distance, matches this value.  The default
                          `variance = 0.` uses the variance in the original COSMOS noise fields.
    @param x_interpolant  Forces use of a non-default interpolant for interpolation of the internal
                          lookup table in real space.  Supplied kwarg must be an InterpolantXY
                          instance or an Interpolant instance (from which an InterpolantXY will be
                          automatically generated).  Defaults to use of the bilinear interpolant
                          `galsim.InterpolantXY(galsim.Linear(tol=1.e-4))`, see below.

    @return A _BaseCorrelatedNoise instance representing correlated noise in F814W COSMOS images.

    The interpolation used if `x_interpolant=None` (default) is a
    galsim.InterpolantXY(galsim.Linear(tol=1.e-4)), which uses bilinear interpolation.  Initial
    tests indicate the favourable performance of this interpolant in applications involving
    correlated noise.

    Important note regarding units
    ------------------------------
    The ACS coadd images in COSMOS have a pixel scale of 0.03 arcsec, and so the pixel scale
    `dx_cosmos` adopted in the representation of of the correlation function takes a default value

        dx_cosmos = 0.03

    If you wish to use other units, ensure that the input keyword `dx_cosmos` takes the value
    corresponding to 0.03 arcsec in your chosen system.

    Example usage
    -------------
    The following commands use this function to generate a 300 pixel x 300 pixel image of noise with
    HST COSMOS correlation properties (substitute in your own file and path for the `filestring`).

        >>> filestring='/YOUR/REPO/PATH/GalSim/devel/external/hst/acs_I_unrot_sci_20_cf.fits'
        >>> import galsim
        >>> rng = galsim.UniformDeviate(123456)
        >>> cf = galsim.correlatednoise.getCOSMOSNoise(rng, filestring)
        >>> im = galsim.ImageD(300, 300)
        >>> im.setScale(0.03)
        >>> cf.applyTo(im)
        >>> im.write('out.fits')

    The FITS file `out.fits` should then contain an image of randomly-generated, COSMOS-like noise.
    """
    # First try to read in the image of the COSMOS correlation function stored in the repository
    import os
    if not os.path.isfile(file_name):
        raise IOError("The input file_name '"+str(file_name)+"' does not exist.")
    try:
        cfimage = galsim.fits.read(file_name)
    except Exception:
        # Give a vaguely helpful warning, then raise the original exception for extra diagnostics
        import warnings
        warnings.warn(
            "Function getCOSMOSNoise() unable to read FITS image from "+str(file_name)+", "+
            "more information on the error in the following Exception...")
        raise

    # Then check for negative variance before doing anything time consuming
    if variance < 0:
        raise ValueError("Input keyword variance must be zero or positive.")

    # If x_interpolant not specified on input, use bilinear
    if x_interpolant == None:
        linear = galsim.Linear(tol=1.e-4)
        x_interpolant = galsim.InterpolantXY(linear)
    else:
        x_interpolant = utilities.convert_interpolant_to_2d(x_interpolant)

    # Use this info to then generate a correlated noise model DIRECTLY: note this is non-standard
    # usage, but tolerated since we can be sure that the input cfimage is appropriately symmetric
    # and peaked at the origin
    ret = _BaseCorrelatedNoise(rng, galsim.InterpolatedImage(
        cfimage, dx=dx_cosmos, normalization="sb", calculate_stepk=False, calculate_maxk=False,
        x_interpolant=x_interpolant))
    # If the input keyword variance is non-zero, scale the correlation function to have this
    # variance
    if variance > 0.:
        ret.setVariance(variance)
    return ret
Example #19
0
    def __init__(self, rng, image, dx=0., x_interpolant=None, correct_periodicity=True,
        subtract_mean=False):

        # Check that the input image is in fact a galsim.ImageSIFD class instance
        if not isinstance(image, (
            galsim.BaseImageD, galsim.BaseImageF, galsim.BaseImageS, galsim.BaseImageI)):
            raise TypeError(
                "Input image not a galsim.Image class object (e.g. ImageD, ImageViewS etc.)")
        # Build a noise correlation function (CF) from the input image, using DFTs
        # Calculate the power spectrum then a (preliminary) CF 
        ft_array = np.fft.fft2(image.array)
        ps_array = (ft_array * ft_array.conj()).real
        if subtract_mean: # Quickest non-destructive way to make the PS correspond to the
                          # mean-subtracted case
            ps_array[0, 0] = 0.
        # Note need to normalize due to one-directional 1/N^2 in FFT conventions
        cf_array_prelim = (np.fft.ifft2(ps_array)).real / np.product(image.array.shape)

        store_rootps = True # Currently the ps_array above corresponds to cf, but this may change...

        # Apply a correction for the DFT assumption of periodicity unless user requests otherwise
        if correct_periodicity:
            cf_array_prelim *= _cf_periodicity_dilution_correction(cf_array_prelim.shape)
            store_rootps = False

        # Roll CF array to put the centre in image centre.  Remember that numpy stores data [y,x]
        cf_array_prelim = utilities.roll2d(
            cf_array_prelim, (cf_array_prelim.shape[0] / 2, cf_array_prelim.shape[1] / 2))

        # The underlying C++ object is expecting the CF to be represented by an odd-dimensioned 
        # array with the central pixel denoting the zero-distance correlation (variance), even 
        # even if the input image was even-dimensioned on one or both sides.
        # We therefore copy-paste and zero pad the CF calculated above to ensure that these
        # expectations are met. 
        #
        # Determine the largest dimension of the input image, and use it to generate an empty CF 
        # array for final output, padding by one to make odd if necessary:
        cf_array = np.zeros((
            1 + 2 * (cf_array_prelim.shape[0] / 2), 
            1 + 2 * (cf_array_prelim.shape[1] / 2))) # using integer division
        # Then put the data from the prelim CF into this array
        cf_array[0:cf_array_prelim.shape[0], 0:cf_array_prelim.shape[1]] = cf_array_prelim
        # Then copy-invert-paste data from the leftmost column to the rightmost column, and lowest
        # row to the uppermost row, if the original CF had even dimensions in the x and y 
        # directions, respectively (remembering again that NumPy stores data [y,x] in arrays)
        if cf_array_prelim.shape[1] % 2 == 0: # first do x
            lhs_column = cf_array[:, 0]
            cf_array[:, cf_array_prelim.shape[1]] = lhs_column[::-1] # inverts order as required
        if cf_array_prelim.shape[0] % 2 == 0: # then do y
            bottom_row = cf_array[0, :]
            cf_array[cf_array_prelim.shape[0], :] = bottom_row[::-1] # inverts order as required
  
        # Wrap correlation function in an image 
        cf_image = galsim.ImageViewD(np.ascontiguousarray(cf_array))

        # Correctly record the original image scale if set
        if dx > 0.:
            cf_image.setScale(dx)
        elif image.getScale() > 0.:
            cf_image.setScale(image.getScale())
        else: # sometimes Images are instantiated with scale=0, in which case we will assume unit
              # pixel scale
            cf_image.setScale(1.)

        # If x_interpolant not specified on input, use bilinear
        if x_interpolant == None:
            linear = galsim.Linear(tol=1.e-4)
            x_interpolant = galsim.InterpolantXY(linear)
        else:
            x_interpolant = utilities.convert_interpolant_to_2d(x_interpolant)

        # Then initialize...
        cf_object = galsim.InterpolatedImage(
            cf_image, x_interpolant=x_interpolant, dx=cf_image.getScale(), normalization="sb",
            calculate_stepk=False, calculate_maxk=False) # these internal calculations do not seem
                                                         # to do very well with often sharp-peaked
                                                         # correlation function images...
        _BaseCorrelatedNoise.__init__(self, rng, cf_object)

        if store_rootps:
            # If it corresponds to the CF above, store useful data as a (rootps, dx) tuple for
            # efficient later use:
            self._profile_for_stored = self._profile
            self._rootps_store.append((np.sqrt(ps_array), cf_image.getScale()))
    def __init__(self,
                 image,
                 x_interpolant=None,
                 k_interpolant=None,
                 normalization='flux',
                 scale=None,
                 wcs=None,
                 flux=None,
                 pad_factor=4.,
                 noise_pad_size=0,
                 noise_pad=0.,
                 rng=None,
                 pad_image=None,
                 calculate_stepk=True,
                 calculate_maxk=True,
                 use_cache=True,
                 use_true_center=True,
                 offset=None,
                 gsparams=None,
                 dx=None):
        # Check for obsolete dx parameter
        if dx is not None and scale is None: scale = dx

        import numpy

        # first try to read the image as a file.  If it's not either a string or a valid
        # pyfits hdu or hdulist, then an exception will be raised, which we ignore and move on.
        try:
            image = galsim.fits.read(image)
        except:
            pass

        # make sure image is really an image and has a float type
        if not isinstance(image, galsim.Image):
            raise ValueError("Supplied image is not an Image instance")
        if image.dtype != numpy.float32 and image.dtype != numpy.float64:
            raise ValueError(
                "Supplied image is not an image of floats or doubles!")

        # it must have well-defined bounds, otherwise seg fault in SBInterpolatedImage constructor
        if not image.bounds.isDefined():
            raise ValueError("Supplied image does not have bounds defined!")

        # check what normalization was specified for the image: is it an image of surface
        # brightness, or flux?
        if not normalization.lower() in ("flux", "f", "surface brightness",
                                         "sb"):
            raise ValueError((
                "Invalid normalization requested: '%s'. Expecting one of 'flux', "
                + "'f', 'surface brightness', or 'sb'.") % normalization)

        # set up the interpolants if none was provided by user, or check that the user-provided ones
        # are of a valid type
        if x_interpolant is None:
            self.x_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4))
        else:
            self.x_interpolant = galsim.utilities.convert_interpolant_to_2d(
                x_interpolant)
        if k_interpolant is None:
            self.k_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4))
        else:
            self.k_interpolant = galsim.utilities.convert_interpolant_to_2d(
                k_interpolant)

        # Store the image as an attribute and make sure we don't change the original image
        # in anything we do here.  (e.g. set scale, etc.)
        self.image = image.view()
        self.use_cache = use_cache

        # Set the wcs if necessary
        if scale is not None:
            if wcs is not None:
                raise TypeError(
                    "Cannot provide both scale and wcs to InterpolatedImage")
            self.image.wcs = galsim.PixelScale(scale)
        elif wcs is not None:
            if not isinstance(wcs, galsim.BaseWCS):
                raise TypeError(
                    "wcs parameter is not a galsim.BaseWCS instance")
            self.image.wcs = wcs
        elif self.image.wcs is None:
            raise ValueError(
                "No information given with Image or keywords about pixel scale!"
            )

        # Set up the GaussianDeviate if not provided one, or check that the user-provided one is
        # of a valid type.
        if rng is None:
            if noise_pad: rng = galsim.BaseDeviate()
        elif not isinstance(rng, galsim.BaseDeviate):
            raise TypeError(
                "rng provided to InterpolatedImage constructor is not a BaseDeviate"
            )

        # Check that given pad_image is valid:
        if pad_image:
            import numpy
            if isinstance(pad_image, str):
                pad_image = galsim.fits.read(pad_image)
            if not isinstance(pad_image, galsim.Image):
                raise ValueError("Supplied pad_image is not an Image!")
            if pad_image.dtype != numpy.float32 and pad_image.dtype != numpy.float64:
                raise ValueError(
                    "Supplied pad_image is not one of the allowed types!")

        # Check that the given noise_pad is valid:
        try:
            noise_pad = float(noise_pad)
        except:
            pass
        if isinstance(noise_pad, float):
            if noise_pad < 0.:
                raise ValueError("Noise variance cannot be negative!")
        # There are other options for noise_pad, the validity of which will be checked in
        # the helper function self.buildNoisePadImage()

        # This will be passed to SBInterpolatedImage, so make sure it is the right type.
        pad_factor = float(pad_factor)
        if pad_factor <= 0.:
            raise ValueError("Invalid pad_factor <= 0 in InterpolatedImage")

        if use_true_center:
            im_cen = self.image.bounds.trueCenter()
        else:
            im_cen = self.image.bounds.center()

        local_wcs = self.image.wcs.local(image_pos=im_cen)

        # Make sure the image fits in the noise pad image:
        if noise_pad_size:
            import math
            # Convert from arcsec to pixels according to the local wcs.
            # Use the minimum scale, since we want to make sure noise_pad_size is
            # as large as we need in any direction.
            scale = local_wcs.minLinearScale()
            noise_pad_size = int(math.ceil(noise_pad_size / scale))
            # Round up to a good size for doing FFTs
            noise_pad_size = galsim._galsim.goodFFTSize(noise_pad_size)
            if noise_pad_size <= min(self.image.array.shape):
                # Don't need any noise padding in this case.
                noise_pad_size = 0
            elif noise_pad_size < max(self.image.array.shape):
                noise_pad_size = max(self.image.array.shape)

        # See if we need to pad out the image with either a pad_image or noise_pad
        if noise_pad_size:
            new_pad_image = self.buildNoisePadImage(noise_pad_size, noise_pad,
                                                    rng)

            if pad_image:
                # if both noise_pad and pad_image are set, then we need to build up a larger
                # pad_image and place the given pad_image in the center.

                # We will change the bounds here, so make a new view to avoid modifying the
                # input pad_image.
                pad_image = pad_image.view()
                pad_image.setCenter(0, 0)
                new_pad_image.setCenter(0, 0)
                if new_pad_image.bounds.includes(pad_image.bounds):
                    new_pad_image[pad_image.bounds] = pad_image
                else:
                    new_pad_image = pad_image

            pad_image = new_pad_image

        elif pad_image:
            # Just make sure pad_image is the right type
            pad_image = galsim.Image(pad_image, dtype=image.dtype)

        # Now place the given image in the center of the padding image:
        if pad_image:
            pad_image.setCenter(0, 0)
            self.image.setCenter(0, 0)
            if pad_image.bounds.includes(self.image.bounds):
                pad_image[self.image.bounds] = self.image
            else:
                # If padding was smaller than original image, just use the original image.
                pad_image = self.image
        else:
            pad_image = self.image

        # Make the SBInterpolatedImage out of the image.
        sbinterpolatedimage = galsim._galsim.SBInterpolatedImage(
            pad_image.image,
            xInterp=self.x_interpolant,
            kInterp=self.k_interpolant,
            pad_factor=pad_factor,
            gsparams=gsparams)

        # GalSim cannot automatically know what stepK and maxK are appropriate for the
        # input image.  So it is usually worth it to do a manual calculation here.
        if calculate_stepk:
            if calculate_stepk is True:
                sbinterpolatedimage.calculateStepK()
            else:
                # If not a bool, then value is max_stepk
                sbinterpolatedimage.calculateStepK(max_stepk=calculate_stepk)
        if calculate_maxk:
            if calculate_maxk is True:
                sbinterpolatedimage.calculateMaxK()
            else:
                # If not a bool, then value is max_maxk
                sbinterpolatedimage.calculateMaxK(max_maxk=calculate_maxk)

        # Initialize the SBProfile
        GSObject.__init__(self, sbinterpolatedimage)

        # Make sure offset is a PositionD
        offset = self._parse_offset(offset)

        # Apply the offset, and possibly fix the centering for even-sized images
        # Note reverse=True, since we want to fix the center in the opposite sense of what the
        # draw function does.
        prof = self._fix_center(self.image,
                                offset,
                                use_true_center,
                                reverse=True)

        # Bring the profile from image coordinates into world coordinates
        prof = local_wcs.toWorld(prof)

        # If the user specified a flux, then set to that flux value.
        if flux is not None:
            prof = prof.withFlux(float(flux))
        # If the user specified a surface brightness normalization for the input Image, then
        # need to rescale flux by the pixel area to get proper normalization.
        elif normalization.lower() in ['surface brightness', 'sb']:
            prof *= local_wcs.pixelArea()

        GSObject.__init__(self, prof)