示例#1
0
def test_Quintic_ref():
    """Test use of Quintic interpolant against some reference values
    """
    import time
    t1 = time.time()
    interp = galsim.Quintic(tol=1.e-4)
    testobj = galsim.InterpolatedImage(ref_image.view(),
                                       x_interpolant=interp,
                                       dx=dx,
                                       normalization='sb')
    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"))
    print 'ref = ', refKvals
    print 'test = ', testKvals
    np.testing.assert_array_almost_equal(
        refKvals / testKvals,
        1.,
        5,
        err_msg="kValues do not match reference values for Quintic interpolant."
    )
    t2 = time.time()
    print 'time for %s = %.2f' % (funcname(), t2 - t1)
示例#2
0
    def __init__(self, real_kimage, imag_kimage, k_interpolant=None, stepk=None,
                 gsparams=None):

        # make sure real_kimage, imag_kimage are really `Image`s, are floats, and are congruent.
        if not isinstance(real_kimage, galsim.Image) or not isinstance(imag_kimage, galsim.Image):
            raise ValueError("Supplied kimage is not an Image instance")
        if ((real_kimage.dtype != np.float32 and real_kimage.dtype != np.float64)
            or (imag_kimage.dtype != np.float32 and imag_kimage.dtype != np.float64)):
            raise ValueError("Supplied image does not have dtype of float32 or float64!")
        if real_kimage.bounds != imag_kimage.bounds:
            raise ValueError("Real and Imag kimages must have same bounds.")
        if real_kimage.scale != imag_kimage.scale:
            raise ValueError("Real and Imag kimages must have same scale.")

        # Make sure any `wcs`s are `PixelScale`s.
        if ((real_kimage.wcs is not None
             and not real_kimage.wcs.isPixelScale())
            or (imag_kimage.wcs is not None
                and not imag_kimage.wcs.isPixelScale())):
            raise ValueError("Real and Imag kimage wcs's must be PixelScale's or None.")

        # Check for Hermitian symmetry properties of real_kimage and imag_kimage
        shape = real_kimage.array.shape
        # If image is even-sized, ignore first row/column since in this case not every pixel has
        # a symmetric partner to which to compare.
        bd = galsim.BoundsI(real_kimage.xmin + (1 if shape[1]%2==0 else 0),
                            real_kimage.xmax,
                            real_kimage.ymin + (1 if shape[0]%2==0 else 0),
                            real_kimage.ymax)
        if not (np.allclose(real_kimage[bd].array,
                            real_kimage[bd].array[::-1,::-1]) and
                np.allclose(imag_kimage[bd].array,
                            -imag_kimage[bd].array[::-1,::-1])):
            raise ValueError("Real and Imag kimages must form a Hermitian complex matrix.")

        if stepk is None:
            stepk = real_kimage.scale
        else:
            if stepk < real_kimage.scale:
                import warnings
                warnings.warn(
                    "Provided stepk is smaller than kimage.scale; overriding with kimage.scale.")
                stepk = real_kimage.scale

        self._real_kimage = real_kimage
        self._imag_kimage = imag_kimage
        self._stepk = stepk
        self._gsparams = gsparams

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

        GSObject.__init__(self, galsim._galsim.SBInterpolatedKImage(
            self._real_kimage.image, self._imag_kimage.image,
            self._real_kimage.scale, self._stepk, self.k_interpolant, gsparams))
示例#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()
示例#4
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)
示例#5
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)
示例#6
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)
示例#7
0
文件: real.py 项目: maxmen/GalSim
    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))
示例#8
0
    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,
                 _force_stepk=0.,
                 _force_maxk=0.,
                 _serialize_stepk=None,
                 _serialize_maxk=None,
                 hdu=None):

        # Check for obsolete dx parameter
        if dx is not None and scale is None:
            from galsim.deprecated import depr
            depr('dx', 1.1, 'scale')
            scale = dx

        # If the "image" is not actually an image, try to read the image as a file.
        if not isinstance(image, galsim.Image):
            image = galsim.fits.read(image, hdu=hdu)

        # make sure image is really an image and has a float type
        if image.dtype != np.float32 and image.dtype != np.float64:
            raise ValueError(
                "Supplied image does not have dtype of float32 or float64!")

        # 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.Quintic(tol=1e-4)
        else:
            self.x_interpolant = galsim.utilities.convert_interpolant(
                x_interpolant)
        if k_interpolant is None:
            self.k_interpolant = galsim.Quintic(tol=1e-4)
        else:
            self.k_interpolant = galsim.utilities.convert_interpolant(
                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:
            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 != np.float32 and pad_image.dtype != np.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)
        self.min_scale = local_wcs.minLinearScale()
        self.max_scale = local_wcs.maxLinearScale()

        # 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.
            noise_pad_size = int(math.ceil(noise_pad_size / self.min_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
                pad_image.wcs = self.image.wcs
            else:
                # If padding was smaller than original image, just use the original image.
                pad_image = self.image
        else:
            pad_image = self.image

        # 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 (below).
        #
        # However, there is also a hidden option to force it to use specific values of stepK and
        # maxK (caveat user!).  The values of _force_stepk and _force_maxk should be provided in
        # terms of physical scale, e.g., for images that have a scale length of 0.1 arcsec, the
        # stepK and maxK should be provided in units of 1/arcsec.  Then we convert to the 1/pixel
        # units required by the C++ layer below.  Also note that profile recentering for even-sized
        # images (see the ._fix_center step below) leads to automatic reduction of stepK slightly
        # below what is provided here, while maxK is preserved.
        if _force_stepk > 0.:
            calculate_stepk = False
            _force_stepk *= self.min_scale
        if _force_maxk > 0.:
            calculate_maxk = False
            _force_maxk *= self.max_scale

        # Due to floating point rounding errors, for pickling it's necessary to store the exact
        # _force_maxk and _force_stepk used to create the SBInterpolatedImage, as opposed to the
        # values before being scaled by self.min_scale and self.max_scale.  So we do that via the
        # _serialize_maxk and _serialize_stepk hidden kwargs, which should only get used during
        # pickling.
        if _serialize_stepk is not None:
            calculate_stepk = False
            _force_stepk = _serialize_stepk
        if _serialize_maxk is not None:
            calculate_maxk = False
            _force_maxk = _serialize_maxk

        # Save these values for pickling
        self._pad_image = pad_image
        self._pad_factor = pad_factor
        self._gsparams = gsparams

        # Make the SBInterpolatedImage out of the image.
        sbii = galsim._galsim.SBInterpolatedImage(pad_image.image,
                                                  self.x_interpolant,
                                                  self.k_interpolant,
                                                  pad_factor, _force_stepk,
                                                  _force_maxk, gsparams)

        # I think the only things that will mess up if getFlux() == 0 are the
        # calculateStepK and calculateMaxK functions, and rescaling the flux to some value.
        if (calculate_stepk or calculate_maxk
                or flux is not None) and sbii.getFlux() == 0.:
            raise RuntimeError(
                "This input image has zero total flux. "
                "It does not define a valid surface brightness profile.")

        if calculate_stepk:
            if calculate_stepk is True:
                sbii.calculateStepK()
            else:
                # If not a bool, then value is max_stepk
                sbii.calculateStepK(max_stepk=calculate_stepk)
        if calculate_maxk:
            if calculate_maxk is True:
                sbii.calculateMaxK()
            else:
                # If not a bool, then value is max_maxk
                sbii.calculateMaxK(max_maxk=calculate_maxk)

        # 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.
        if flux is None and normalization.lower() in [
                'surface brightness', 'sb'
        ]:
            flux = sbii.getFlux() * local_wcs.pixelArea()

        # Save this intermediate profile
        self._sbii = sbii
        self._stepk = sbii.stepK() / self.min_scale
        self._maxk = sbii.maxK() / self.max_scale
        self._flux = flux

        self._serialize_stepk = sbii.stepK()
        self._serialize_maxk = sbii.maxK()

        prof = GSObject(sbii)

        # Make sure offset is a PositionD
        offset = prof._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 = prof._fix_center(self.image.array.shape,
                                offset,
                                use_true_center,
                                reverse=True)

        # Save the offset we will need when pickling.
        if hasattr(prof, 'offset'):
            self._offset = -prof.offset
        else:
            self._offset = None

        # 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))

        # Now, in order for these to pickle correctly if they are the "original" object in a
        # Transform object, we need to hide the current transformation.  An easy way to do that
        # is to hide the SBProfile in an SBAdd object.
        sbp = galsim._galsim.SBAdd([prof.SBProfile])

        GSObject.__init__(self, sbp)
示例#9
0
def test_log():
    """Some simple tests of interpolation using logs."""
    # Set up some test vectors that are strictly positive, and others that are negative.
    x = 0.01 * np.arange(1000) + 0.01
    y = 1. * x
    x_neg = -1. * x
    y_neg = 1. * x_neg

    # Check that interpolation agrees for the positive ones when using log interpolation (for some
    # reasonable tolerance).
    tab_1 = galsim.LookupTable(x=x, f=y)
    tab_2 = galsim.LookupTable(x=x, f=y, x_log=True, f_log=True)
    tab_3 = galsim.LookupTable(x=x, f=y, x_log=True)
    tab_4 = galsim.LookupTable(x=x, f=y, f_log=True)
    test_x_vals = [2.641, 3.985, 8.123125]
    for test_val in test_x_vals:
        result_1 = tab_1(test_val)
        result_2 = tab_2(test_val)
        result_3 = tab_3(test_val)
        result_4 = tab_4(test_val)
        print(result_1, result_2, result_3, result_4)
        np.testing.assert_almost_equal(
            result_2,
            result_1,
            decimal=3,
            err_msg='Disagreement when interpolating in log(f) and log(x)')
        np.testing.assert_almost_equal(
            result_3,
            result_1,
            decimal=3,
            err_msg='Disagreement when interpolating in log(x)')
        np.testing.assert_almost_equal(
            result_4,
            result_1,
            decimal=3,
            err_msg='Disagreement when interpolating in log(f)')

    with assert_raises(galsim.GalSimRangeError):
        tab_2(-1)
    with assert_raises(galsim.GalSimRangeError):
        tab_3(-1)
    with assert_raises(galsim.GalSimRangeError):
        tab_2(x_neg)
    with assert_raises(galsim.GalSimRangeError):
        tab_3(x_neg)

    # Check picklability
    do_pickle(tab_1)
    do_pickle(tab_2)
    do_pickle(tab_3)
    do_pickle(tab_4)

    # Check storage of args and vals for log vs. linear, which should be the same to high precision.
    np.testing.assert_array_almost_equal(
        tab_1.getArgs(),
        tab_3.getArgs(),
        decimal=12,
        err_msg='Args differ for linear vs. log storage')
    np.testing.assert_array_almost_equal(
        tab_1.getVals(),
        tab_4.getVals(),
        decimal=12,
        err_msg='Vals differ for linear vs. log storage')
    # Check other properties
    assert not tab_1.x_log
    assert not tab_1.f_log
    assert tab_2.x_log
    assert tab_2.f_log
    assert tab_3.x_log
    assert not tab_3.f_log
    assert not tab_1.isLogX()
    assert not tab_1.isLogF()
    assert tab_2.isLogX()
    assert tab_2.isLogF()
    assert tab_3.isLogX()
    assert not tab_3.isLogF()

    # Check that an appropriate exception is thrown when trying to do interpolation using negative
    # ones.
    assert_raises(ValueError, galsim.LookupTable, x=x_neg, f=y_neg, x_log=True)
    assert_raises(ValueError, galsim.LookupTable, x=x_neg, f=y_neg, f_log=True)
    assert_raises(ValueError,
                  galsim.LookupTable,
                  x=x_neg,
                  f=y_neg,
                  x_log=True,
                  f_log=True)

    # Check that doing log transform explicitly matches expected behavior of x_log and f_log.
    expx = np.exp(x)
    expy = np.exp(y)
    for interpolant in ['linear', 'spline', galsim.Quintic()]:
        tab_1 = galsim.LookupTable(x, y, interpolant=interpolant)
        tab_2 = galsim.LookupTable(expx,
                                   y,
                                   x_log=True,
                                   interpolant=interpolant)
        tab_3 = galsim.LookupTable(x,
                                   expy,
                                   f_log=True,
                                   interpolant=interpolant)
        tab_4 = galsim.LookupTable(expx,
                                   expy,
                                   x_log=True,
                                   f_log=True,
                                   interpolant=interpolant)
        for test_val in test_x_vals:
            result_1 = tab_1(test_val)
            result_2 = tab_2(np.exp(test_val))
            result_3 = np.log(tab_3(test_val))
            result_4 = np.log(tab_4(np.exp(test_val)))
            np.testing.assert_almost_equal(
                result_2,
                result_1,
                decimal=10,
                err_msg='Disagreement when interpolating in log(x)')
            np.testing.assert_almost_equal(
                result_3,
                result_1,
                decimal=10,
                err_msg='Disagreement when interpolating in log(f)')
            np.testing.assert_almost_equal(
                result_4,
                result_1,
                decimal=10,
                err_msg='Disagreement when interpolating in log(f) and log(x)')

    # Verify for exception when using x_log with non-equal-spaced galsim.Interpolant
    galsim.LookupTable(x, y, x_log=True, interpolant='linear')  # works fine
    assert_raises(ValueError,
                  galsim.LookupTable,
                  x,
                  y,
                  x_log=True,
                  interpolant=galsim.Linear())
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)

# Then make a Lanczos5 interpolant
interp = galsim.InterpolantXY(galsim.Lanczos(5, conserve_flux=False, 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,'absfKLanczos5_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)
示例#12
0
"""

import cPickle
import numpy as np
import galsim
import test_interpolants

SERSIC_IMAGE_SIZE = 512  # For initial image of the Sersic at Hubble resolution, make nice and large
TEST_IMAGE_SIZE = SERSIC_IMAGE_SIZE  # For speed could make this smaller
# Dictionary for parsing the test_interpolants.interpolant_list into galsim Interpolants
INTERPOLANT_DICT = {
    "nearest": galsim.Nearest(),
    "sinc": galsim.SincInterpolant(),
    "linear": galsim.Linear(),
    "cubic": galsim.Cubic(),
    "quintic": galsim.Quintic(),
    "lanczos3": galsim.Lanczos(3),
    "lanczos4": galsim.Lanczos(4),
    "lanczos5": galsim.Lanczos(5),
    "lanczos7": galsim.Lanczos(7)
}

# Output filenames
DELTA_FILENAME = 'interpolant_test_parametric_output_delta.dat'
ORIGINAL_FILENAME = 'interpolant_test_parametric_output_original.dat'

NITEMS = 30  # For now, look at a few only

LAM_OVER_DIAM_COSMOS = 814.e-9 / 2.4  # All the original images in Melanie's tests were from COSMOS
# F814W, so this is a crude approximation to the PSF scale in
# radians, ~0.07 arcsec
示例#13
0
    def __init__(self,
                 kimage=None,
                 k_interpolant=None,
                 stepk=None,
                 gsparams=None,
                 real_kimage=None,
                 imag_kimage=None,
                 real_hdu=None,
                 imag_hdu=None):
        if isinstance(kimage, galsim.Image) and isinstance(
                k_interpolant, galsim.Image):
            from .deprecated import depr
            depr(
                'InterpolatedKImage(re,im,...)', 1.5,
                'either InterpolatedKImage(re + 1j * im, ...) or '
                'InterpolatedKImage(real_kimage=re, imag_kimage=im)')
            # This won't work if they call InterpolatedKImage(re,im, k_interpolant=kinterp)
            # But I don't see an easy way around that, so I guess that use case is not
            # backwards compatible.  Sorry..
            real_kimage = kimage
            imag_kimage = k_interpolant
            kimage = None
            k_interpolant = None

        if kimage is None:
            if real_kimage is None or imag_kimage is None:
                raise ValueError(
                    "Must provide either kimage or real_kimage/imag_kimage")

            # If the "image" is not actually an image, try to read the image as a file.
            if not isinstance(real_kimage, galsim.Image):
                real_kimage = galsim.fits.read(real_kimage, hdu=real_hdu)
            if not isinstance(imag_kimage, galsim.Image):
                imag_kimage = galsim.fits.read(imag_kimage, hdu=imag_hdu)

            # make sure real_kimage, imag_kimage are really `Image`s, are floats, and are
            # congruent.
            if not isinstance(real_kimage, galsim.Image):
                raise ValueError(
                    "Supplied real_kimage is not an Image instance")
            if not isinstance(imag_kimage, galsim.Image):
                raise ValueError(
                    "Supplied imag_kimage is not an Image instance")
            if real_kimage.bounds != imag_kimage.bounds:
                raise ValueError(
                    "Real and Imag kimages must have same bounds.")
            if real_kimage.wcs != imag_kimage.wcs:
                raise ValueError(
                    "Real and Imag kimages must have same scale/wcs.")

            kimage = real_kimage + 1j * imag_kimage
        else:
            if real_kimage is not None or imag_kimage is not None:
                raise ValueError(
                    "Cannot provide both kimage and real_kimage/imag_kimage")
            if not kimage.iscomplex:
                raise ValueError("Supplied kimage is not an ImageC")

        # Make sure wcs is a PixelScale.
        if kimage.wcs is not None and not kimage.wcs.isPixelScale():
            raise ValueError("kimage wcs must be PixelScale or None.")

        # Check for Hermitian symmetry properties of kimage
        shape = kimage.array.shape
        # If image is even-sized, ignore first row/column since in this case not every pixel has
        # a symmetric partner to which to compare.
        bd = galsim.BoundsI(kimage.xmin + (1 if shape[1] % 2 == 0 else 0),
                            kimage.xmax,
                            kimage.ymin + (1 if shape[0] % 2 == 0 else 0),
                            kimage.ymax)
        if not (np.allclose(kimage[bd].real.array,
                            kimage[bd].real.array[::-1, ::-1])
                and np.allclose(kimage[bd].imag.array,
                                -kimage[bd].imag.array[::-1, ::-1])):
            raise ValueError(
                "Real and Imag kimages must form a Hermitian complex matrix.")

        if stepk is None:
            stepk = kimage.scale
        else:
            if stepk < kimage.scale:
                import warnings
                warnings.warn(
                    "Provided stepk is smaller than kimage.scale; overriding with kimage.scale."
                )
                stepk = kimage.scale

        self._kimage = kimage
        self._stepk = stepk
        self._gsparams = gsparams

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

        GSObject.__init__(
            self,
            galsim._galsim.SBInterpolatedKImage(self._kimage.image,
                                                self._kimage.scale,
                                                self._stepk,
                                                self.k_interpolant, gsparams))
示例#14
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)
示例#15
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()
示例#16
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()
示例#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)
示例#18
0
    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)