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)
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()
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)
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
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.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, 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 __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()
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)
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] # Make the bulge: use a Sersic rather than a DeVauc specifically, because we want to