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 main(argv): # Try different interpolants! #interp1d = galsim.Linear(); #interp1d = galsim.Delta(); interp1d = galsim.Lanczos(5, conserve_dc=True, tol=1.e-4) #interp1d = galsim.Quintic(); interp2d = galsim.InterpolantXY(interp1d) try: inname = argv[1] outname = argv[2] dxOut = float(argv[3]) dim = int(argv[4]) nPhotons = int(argv[5]) g1 = float(argv[6]) if len(argv) > 6 else 0. g2 = float(argv[7]) if len(argv) > 7 else 0. except Exception as err: print __doc__ raise err galaxyImg = galsim.fits.read(inname) galaxy = galsim.InterpolatedImage(galaxyImg, x_interpolant=interp2d, dx=1.) galaxy.applyShear(g1=g1, g2=g2) rng = galsim.UniformDeviate(1534225) bounds = galsim.BoundsI(-dim / 2, dim / 2 + 1, -dim / 2, dim / 2 + 1) img = galsim.ImageF(bounds, scale=dxOut) galaxy.drawShoot(image=img, n_photons=nPhotons, rng=rng) img.write(outname)
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 main(argv): # translation from C++ by Jim; comments after this one are in Gary's voice l3 = galsim.Lanczos(3, True, 1.0E-4) l32d = galsim.InterpolantXY(l3) dxHST = 0.03 dxSDSS = 0.396 g1 = 0.02 g2 = 0.0 psfSky = 1000.0 rootname = argv[1] xshift = float(argv[2]) if len(argv) > 2 else 0. yshift = float(argv[3]) if len(argv) > 3 else 0. s = galsim.Shear(g1=g1, g2=g2) # Rachel is probably using the (1+g, 1-g) form of shear matrix, # which means there is some (de)magnification, by my definition: #e = galsim.Ellipse(s, -(g1*g1+g2*g2), galsim.PositionD(xshift,yshift)); galaxyImg = galsim.fits.read(rootname + "_masknoise.fits") galaxy = galsim.InterpolatedImage(galaxyImg, x_interpolant=l32d, dx=dxHST, flux=0.804 * 1000. * dxSDSS * dxSDSS) psf1Img = galsim.fits.read(rootname + ".psf.fits") psf1 = galsim.InterpolatedImage(psf1Img, x_interpolant=l32d, dx=dxHST, flux=1.) psf2Img = galsim.fits.read(rootname + ".sdsspsf.fits") psf2 = galsim.InterpolatedImage(psf2Img, x_interpolant=l32d, dx=dxSDSS, flux=1.) outImg = galsim.fits.read(rootname + ".g1_0.02.g2_0.00.fits") result = outImg.copy() psfInv = galsim.Deconvolve(psf1) deconv = galsim.Convolve(galaxy, psfInv) sheared = deconv.createTransformed(e) out = galsim.Convolve(sheared, psf2) test_outImg = out.draw(result, dx=dxSDSS) test_outImg.write(rootname + ".gary.fits") result += psfSky result -= test_outImg result.write(rootname + ".diff.fits")
def convert_interpolant_to_2d(interpolant): """Convert a given interpolant to an Interpolant2d if it is given as a string or 1-d. """ if interpolant == None: return None # caller is responsible for setting a default if desired. elif isinstance(interpolant, galsim.Interpolant2d): return interpolant elif isinstance(interpolant, galsim.Interpolant): return galsim.InterpolantXY(interpolant) else: # Will raise an appropriate exception if this is invalid. return galsim.Interpolant2d(interpolant)
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)
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 __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 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 = galsim.Gaussian(sigma = 1.9, flux=3.1) g2.applyShear(-0.4,0.3) g2.applyShift(-0.3,0.5) g3 = galsim.Gaussian(sigma = 4.1, flux=1.6) g3.applyShear(0.1,-0.1) g3.applyShift(0.7,-0.2) final = g1 + g2 + g3 image = galsim.ImageD(128,128) dx = 0.4 final.draw(image=image, dx=dx) dir = '../../../tests/interpolant_comparison_files' # First make a Cubic interpolant interp = galsim.InterpolantXY(galsim.Cubic(tol=1.e-4)) testobj = galsim.SBInterpolatedImage(image.view(), interp, dx=dx) for i in xrange(len(kxvals)): posk = galsim.PositionD(kxvals[i], kyvals[i]) absoutk[i] = np.abs(testobj.kValue(posk)) print absoutk np.savetxt(os.path.join(dir,'absfKCubic_test.txt'), absoutk) # Then make a Quintic interpolant interp = galsim.InterpolantXY(galsim.Quintic(tol=1.e-4)) testobj = galsim.SBInterpolatedImage(image.view(), interp, dx=dx) for i in xrange(len(kxvals)): posk = galsim.PositionD(kxvals[i], kyvals[i]) absoutk[i] = np.abs(testobj.kValue(posk)) print absoutk np.savetxt(os.path.join(dir,'absfKQuintic_test.txt'), absoutk)
def __init__( self, position_list_filename="ground_optical_psf_zernike_coefficients_41x41/ZEMAXInput.dat", lam=800., diameter=4.0, obscuration=0.35, nstruts=0, strut_thick=0.01, strut_angle=0. * galsim.degrees, pad_factor=None, dz=0., dx=0., dy=0., tx=0., ty=0., dz0=0.05, interpolant2d=None): """ Inputs - position_list_filename: filename of position list used for reading ZEMAX output files. - lam: wavelength [nm] - diameter: diameter of telescope [m] - obscuration: central obscuration [ratio between mirror diameter and obscuration diameter] - nstruts: number of radial support struts to add to the central obscuration - strut_thick: thickness of support struts as a fraction of pupil diameter - strut_angle: angle made between the vertical and the strut starting closest to it, defined to be positive in the counter-clockwise direction; must be a galsim.Angle instance - pad_factor: optional padding specification if 1.5 is not good enough - dz: defocus [mm] - dx: decenter along x axis [mm] - dy: decenter along y axis [mm] - tx: tilt about x [arcsec] - tx: tilt about y [arcsec] - dz0: offset to defocus [mm] - interpolant2d: galsim._galsim.InterpolantXY If None, galsim.InterpolantXY(galsim.Quintic()) Notes of system convention (dx, dy, dz): right-handed system where z-axis is along the light coming into mirror. (tx, ty): + is the rotation by which a right screw goes into the direction of an axis. """ self.lam = lam * 1e-9 # meters self.lam_over_diam = self.lam / diameter * 206265 # arcsec self.obscuration = obscuration self.nstruts = nstruts self.strut_thick = strut_thick self.strut_angle = strut_angle self.pad_factor = pad_factor # read position information from the list data = np.loadtxt(position_list_filename) x = data[:, 0] y = data[:, 1] n = int(np.sqrt(len(x))) d = y[1] - y[0] self.xmin = x.min() self.xmax = x.max() self.ymin = y.min() self.ymax = y.max() # read coefficients from ZEMAX file self.ncoefs = 8 # defocus, a1, a2, c1, c2, t1, t2, spher coefs = np.zeros((self.ncoefs, n, n)) for i, (_x, _y) in enumerate(zip(x, y)): zernike_filename = os.path.join( os.path.dirname(position_list_filename), "%.4f_%.4f.txt" % (_x, _y)) wavelength, coefs_tmp = loadZernikeCoefficients(zernike_filename) i_x = int(i / n) i_y = i % n # need to flip sign of x and y, since definition of axes in ZEMAX and GalSim seem #different. # We have to convert Zernike coefficients in units of wavelength we want. coefs[:, n - i_y - 1, n - i_x - 1] = coefs_tmp * wavelength * 1e-6 / self.lam # get interpolated images self.interpolated_coefficients = list() for coef in coefs: im_coef = galsim.ImageViewD(coef) im_coef.setScale(d) if interpolant2d == None: interpolant2d = galsim.InterpolantXY(galsim.Quintic()) self.interpolated_coefficients.append( galsim.InterpolatedImage( im_coef, x_interpolant=interpolant2d, normalization="sb", calculate_stepk=False, calculate_maxk=False, )) # prepare for misalignment self.optical_psf_misalignment = OpticalPSFMisalignment( self.lam * 1e9, dz + dz0, dx, dy, tx, ty)
def __init__(self, image, x_interpolant = None, k_interpolant = None, normalization = 'flux', dx = None, flux = None, pad_factor = 0., noise_pad = 0., rng = None, pad_image = None, calculate_stepk=True, calculate_maxk=True, use_cache=True, use_true_center=True, gsparams=None): import numpy as np # first try to read the image as a file. If it's not either a string or a valid # pyfits hdu or hdulist, then an exception will be raised, which we ignore and move on. try: image = galsim.fits.read(image) except: pass # make sure image is really an image and has a float type if not isinstance(image, galsim.BaseImageF) and not isinstance(image, galsim.BaseImageD): raise ValueError("Supplied image is not an image of floats or doubles!") # it must have well-defined bounds, otherwise seg fault in SBInterpolatedImage constructor if not image.getBounds().isDefined(): raise ValueError("Supplied image does not have bounds defined!") # check what normalization was specified for the image: is it an image of surface # brightness, or flux? if not normalization.lower() in ("flux", "f", "surface brightness", "sb"): raise ValueError(("Invalid normalization requested: '%s'. Expecting one of 'flux', "+ "'f', 'surface brightness', or 'sb'.") % normalization) # set up the interpolants if none was provided by user, or check that the user-provided ones # are of a valid type if x_interpolant is None: self.x_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4)) else: self.x_interpolant = galsim.utilities.convert_interpolant_to_2d(x_interpolant) if k_interpolant is None: self.k_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4)) else: self.k_interpolant = galsim.utilities.convert_interpolant_to_2d(k_interpolant) # Check for input dx, and check whether Image already has one set. At the end of this # code block, either an exception will have been raised, or the input image will have a # valid scale set. if dx is None: dx = image.scale if dx == 0: raise ValueError("No information given with Image or keywords about pixel scale!") else: if type(dx) != float: dx = float(dx) # Don't change the original image. Make a new view if we need to set the scale. image = image.view() image.setScale(dx) if dx == 0.0: raise ValueError("dx may not be 0.0") # Set up the GaussianDeviate if not provided one, or check that the user-provided one is # of a valid type. if rng is None: gaussian_deviate = galsim.GaussianDeviate() elif isinstance(rng, galsim.BaseDeviate): # Even if it's already a GaussianDeviate, we still want to make a new Gaussian deviate # that would generate the same sequence, because later we change the sigma and we don't # want to change it for the original one that was passed in. So don't distinguish # between GaussianDeviate and the other BaseDeviates here. gaussian_deviate = galsim.GaussianDeviate(rng) else: raise TypeError("rng provided to InterpolatedImage constructor is not a BaseDeviate") # decide about deterministic image padding specify_size = False padded_size = image.getPaddedSize(pad_factor) if pad_image: specify_size = True if isinstance(pad_image, str): pad_image = galsim.fits.read(pad_image) if ( not isinstance(pad_image, galsim.BaseImageF) and not isinstance(pad_image, galsim.BaseImageD) ): raise ValueError("Supplied pad_image is not one of the allowed types!") # If an image was supplied directly or from a file, check its size: # Cannot use if too small. # Use to define the final image size otherwise. deltax = (1+pad_image.getXMax()-pad_image.getXMin())-(1+image.getXMax()-image.getXMin()) deltay = (1+pad_image.getYMax()-pad_image.getYMin())-(1+image.getYMax()-image.getYMin()) if deltax < 0 or deltay < 0: raise RuntimeError("Image supplied for padding is too small!") if pad_factor != 1. and pad_factor != 0.: import warnings msg = "Warning: ignoring specified pad_factor because user also specified\n" msg += " an image to use directly for the padding." warnings.warn(msg) elif noise_pad: if isinstance(image, galsim.BaseImageF): pad_image = galsim.ImageF(padded_size, padded_size) if isinstance(image, galsim.BaseImageD): pad_image = galsim.ImageD(padded_size, padded_size) # now decide about noise padding # First, see if the input is consistent with a float. # i.e. it could be an int, or a str that converts to a number. try: noise_pad = float(noise_pad) except: pass if isinstance(noise_pad, float): if noise_pad < 0.: raise ValueError("Noise variance cannot be negative!") elif noise_pad > 0.: # Note: make sure the sigma is properly set to sqrt(noise_pad). gaussian_deviate.setSigma(np.sqrt(noise_pad)) pad_image.addNoise(galsim.DeviateNoise(gaussian_deviate)) else: if isinstance(noise_pad, galsim.correlatednoise._BaseCorrelatedNoise): cn = noise_pad.copy() if rng: # Let a user supplied RNG take precedence over that in user CN cn.setRNG(gaussian_deviate) elif isinstance(noise_pad,galsim.BaseImageF) or isinstance(noise_pad,galsim.BaseImageD): cn = galsim.CorrelatedNoise(gaussian_deviate, noise_pad) elif use_cache and noise_pad in InterpolatedImage._cache_noise_pad: cn = InterpolatedImage._cache_noise_pad[noise_pad] if rng: # Make sure that we are using a specified RNG by resetting that in this cached # CorrelatedNoise instance, otherwise preserve the cached RNG cn.setRNG(gaussian_deviate) elif isinstance(noise_pad, str): cn = galsim.CorrelatedNoise(gaussian_deviate, galsim.fits.read(noise_pad)) if use_cache: InterpolatedImage._cache_noise_pad[noise_pad] = cn else: raise ValueError( "Input noise_pad must be a float/int, a CorrelatedNoise, Image, or filename "+ "containing an image to use to make a CorrelatedNoise!") pad_image.addNoise(cn) # Now we have to check: was the padding determined using pad_factor? Or by passing in an # image for padding? Treat these cases differently: # (1) If the former, then we can simply have the C++ handle the padding process. # (2) If the latter, then we have to do the padding ourselves, and pass the resulting image # to the C++ with pad_factor explicitly set to 1. if specify_size is False: # Make the SBInterpolatedImage out of the image. sbinterpolatedimage = galsim.SBInterpolatedImage( image, xInterp=self.x_interpolant, kInterp=self.k_interpolant, dx=dx, pad_factor=pad_factor, pad_image=pad_image, gsparams=gsparams) self.x_size = padded_size self.y_size = padded_size else: # Leave the original image as-is. Instead, we shift around the image to be used for # padding. Find out how much x and y margin there should be on lower end: x_marg = int(np.round(0.5*deltax)) y_marg = int(np.round(0.5*deltay)) # Now reset the pad_image to contain the original image in an even way pad_image = pad_image.view() pad_image.setScale(dx) pad_image.setOrigin(image.getXMin()-x_marg, image.getYMin()-y_marg) # Set the central values of pad_image to be equal to the input image pad_image[image.bounds] = image sbinterpolatedimage = galsim.SBInterpolatedImage( pad_image, xInterp=self.x_interpolant, kInterp=self.k_interpolant, dx=dx, pad_factor=1., gsparams=gsparams) self.x_size = 1+pad_image.getXMax()-pad_image.getXMin() self.y_size = 1+pad_image.getYMax()-pad_image.getYMin() # GalSim cannot automatically know what stepK and maxK are appropriate for the # input image. So it is usually worth it to do a manual calculation here. if calculate_stepk: sbinterpolatedimage.calculateStepK() if calculate_maxk: sbinterpolatedimage.calculateMaxK() # If the user specified a flux, then set to that flux value. if flux != None: if type(flux) != flux: flux = float(flux) sbinterpolatedimage.setFlux(flux) # If the user specified a flux normalization for the input Image, then since # SBInterpolatedImage works in terms of surface brightness, have to rescale the values to # get proper normalization. elif flux is None and normalization.lower() in ['flux','f'] and dx != 1.: sbinterpolatedimage.scaleFlux(1./(dx**2)) # If the input Image normalization is 'sb' then since that is the SBInterpolated default # assumption, no rescaling is needed. # Initialize the SBProfile GSObject.__init__(self, sbinterpolatedimage) # Fix the center to be in the right place. # Note the minus sign in front of image.scale, since we want to fix the center in the # opposite sense of what the draw function does. if use_true_center: prof = self._fix_center(image, -image.scale) GSObject.__init__(self, prof.SBProfile)
def Script4(): pixel_scale = 0.28 # arcsec nx = 64 ny = 64 psf_fwhm = 0.65 # arcsec pix = galsim.Pixel(xw=pixel_scale) atmos = galsim.Gaussian(fwhm=psf_fwhm) i1d_list = [ "galsim.Linear(tol=1.e-4)", "galsim.Cubic(tol=1.e-4)", "galsim.Quintic(tol=1.e-4)", "galsim.Lanczos(3, conserve_flux=False, tol=1.e-4)", "galsim.Lanczos(5, conserve_flux=False, tol=1.e-4)", "galsim.Lanczos(7, conserve_flux=False, tol=1.e-4)", "galsim.Lanczos(3, conserve_flux=True, tol=1.e-4)", "galsim.Lanczos(5, conserve_flux=True, tol=1.e-4)", "galsim.Lanczos(7, conserve_flux=True, tol=1.e-4)" ] for i1d_name in i1d_list: print 'i1d = ', i1d_name # A workaround for the fact that the interpolants don't have good __repr__'s yet. i1d = eval(i1d_name) #print 'i1d = ',i1d # Make the PSF profile: i2d = galsim.InterpolantXY(i1d) optics = galsim.OpticalPSF(interpolantxy=i2d, lam_over_diam=0.6 * psf_fwhm, obscuration=0.4, defocus=0.1, astig1=0.3, astig2=-0.2, coma1=0.2, coma2=0.1, spher=-0.3) psf1 = galsim.Convolve([atmos, optics]) #print 'Made psf1' # Also make the same thing using oversampling = 5 optics = galsim.OpticalPSF(interpolantxy=i2d, oversampling=5, lam_over_diam=0.6 * psf_fwhm, obscuration=0.4, defocus=0.1, astig1=0.3, astig2=-0.2, coma1=0.2, coma2=0.1, spher=-0.3) psf2 = galsim.Convolve([atmos, optics]) #print 'Made psf2' # build final profile epsf1 = galsim.Convolve([psf1, pix]) epsf2 = galsim.Convolve([psf2, pix]) #print 'Made epsf1, epsf2' # Create the large, double width output image image1 = galsim.ImageF(nx, ny) image2 = galsim.ImageF(nx, ny) image1.setScale(pixel_scale) image2.setScale(pixel_scale) #print 'Made images' # Draw the profile epsf1.draw(image1) epsf2.draw(image2) #print 'Done drawing images for i1d = ',i1d print 'rms diff = ', math.sqrt( np.mean((image1.array - image2.array)**2))
bulgeRe = [0.25, 0.5] if len(diskRe) is not len(bulgeRe): raise RuntimeError( "Grids in bulge and disk scale lengths do not have same size!") nBulge = 4.0 nDisk = 1.0 pixelScale = 0.03 # we are simulating ACS data totFlux = 1000.0 # total flux for galaxy #### note: the following parameters were not used #### sigmaBulge = 1.0 # dispersion in Sersic n for bulge sigmaDisk = 0.2 # dispersion in Sersic n for disk # read in ePSF and normalize (note: already includes pixel response, don't have to do separately) l3 = galsim.Lanczos(3, True, 1.0E-4) l32d = galsim.InterpolantXY(l3) PSFImage = galsim.fits.read(PSFFile) PSF = galsim.SBInterpolatedImage(PSFImage, l32d, pixelScale, 2.) PSF.setFlux(1.0) # Loop over the various grids: values of bulge-to-total ratio, bulge ellipticity, disk ellipticity, # S/N and number of noise realizations, radii rng = galsim.UniformDeviate() for bt in bulge2Total: print "Beginning bulge-to-total ratio of ", bt for bell in bulgeEllip: print "Beginning bulge ellipticity of ", bell for dell in diskEllip: print "Beginning disk ellipticity of ", dell for dreind in range(len(diskRe)): print "Beginning disk half-light radius of ", diskRe[dreind]
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()
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)
def getCOSMOSNoise(rng, file_name, dx_cosmos=0.03, variance=0., x_interpolant=None): """Returns a representation of correlated noise in the HST COSMOS F814W unrotated science coadd images. See http://cosmos.astro.caltech.edu/astronomer/hst.html for information about the COSMOS survey, and Leauthaud et al (2007) for detailed information about the unrotated F814W coadds used for weak lensing science. This function uses a stacked estimate of the correlation function in COSMOS noise fields, the location of which should be input to this function via the `file_name` argument. This image is stored in FITS format, and is generated as described in `YOUR/REPO/PATH/GalSim/devel/external/hst/make_cosmos_cfimage.py`. The image itself can also be found within the GalSim repo, located at: /YOUR/REPO/PATH/GalSim/examples/data/acs_I_unrot_sci_20_cf.fits @param rng Must be a galsim.BaseDeviate or derived class instance, provides the random number generator used by the returned _BaseCorrelatedNoise instance. @param file_name String containing the path and filename above but modified to match the location of the GalSim repository on your system. @param dx_cosmos COSMOS ACS F814W coadd image pixel scale in the units you are using to describe GSObjects and image scales in GalSim: defaults to 0.03 arcsec, see below for more information. @param variance Scales the correlation function so that its point variance, equivalent to its value at zero separation distance, matches this value. The default `variance = 0.` uses the variance in the original COSMOS noise fields. @param x_interpolant Forces use of a non-default interpolant for interpolation of the internal lookup table in real space. Supplied kwarg must be an InterpolantXY instance or an Interpolant instance (from which an InterpolantXY will be automatically generated). Defaults to use of the bilinear interpolant `galsim.InterpolantXY(galsim.Linear(tol=1.e-4))`, see below. @return A _BaseCorrelatedNoise instance representing correlated noise in F814W COSMOS images. The interpolation used if `x_interpolant=None` (default) is a galsim.InterpolantXY(galsim.Linear(tol=1.e-4)), which uses bilinear interpolation. Initial tests indicate the favourable performance of this interpolant in applications involving correlated noise. Important note regarding units ------------------------------ The ACS coadd images in COSMOS have a pixel scale of 0.03 arcsec, and so the pixel scale `dx_cosmos` adopted in the representation of of the correlation function takes a default value dx_cosmos = 0.03 If you wish to use other units, ensure that the input keyword `dx_cosmos` takes the value corresponding to 0.03 arcsec in your chosen system. Example usage ------------- The following commands use this function to generate a 300 pixel x 300 pixel image of noise with HST COSMOS correlation properties (substitute in your own file and path for the `filestring`). >>> filestring='/YOUR/REPO/PATH/GalSim/devel/external/hst/acs_I_unrot_sci_20_cf.fits' >>> import galsim >>> rng = galsim.UniformDeviate(123456) >>> cf = galsim.correlatednoise.getCOSMOSNoise(rng, filestring) >>> im = galsim.ImageD(300, 300) >>> im.setScale(0.03) >>> cf.applyTo(im) >>> im.write('out.fits') The FITS file `out.fits` should then contain an image of randomly-generated, COSMOS-like noise. """ # First try to read in the image of the COSMOS correlation function stored in the repository import os if not os.path.isfile(file_name): raise IOError("The input file_name '"+str(file_name)+"' does not exist.") try: cfimage = galsim.fits.read(file_name) except Exception: # Give a vaguely helpful warning, then raise the original exception for extra diagnostics import warnings warnings.warn( "Function getCOSMOSNoise() unable to read FITS image from "+str(file_name)+", "+ "more information on the error in the following Exception...") raise # Then check for negative variance before doing anything time consuming if variance < 0: raise ValueError("Input keyword variance must be zero or positive.") # If x_interpolant not specified on input, use bilinear if x_interpolant == None: linear = galsim.Linear(tol=1.e-4) x_interpolant = galsim.InterpolantXY(linear) else: x_interpolant = utilities.convert_interpolant_to_2d(x_interpolant) # Use this info to then generate a correlated noise model DIRECTLY: note this is non-standard # usage, but tolerated since we can be sure that the input cfimage is appropriately symmetric # and peaked at the origin ret = _BaseCorrelatedNoise(rng, galsim.InterpolatedImage( cfimage, dx=dx_cosmos, normalization="sb", calculate_stepk=False, calculate_maxk=False, x_interpolant=x_interpolant)) # If the input keyword variance is non-zero, scale the correlation function to have this # variance if variance > 0.: ret.setVariance(variance) return ret
def __init__(self, rng, image, dx=0., x_interpolant=None, correct_periodicity=True, subtract_mean=False): # Check that the input image is in fact a galsim.ImageSIFD class instance if not isinstance(image, ( galsim.BaseImageD, galsim.BaseImageF, galsim.BaseImageS, galsim.BaseImageI)): raise TypeError( "Input image not a galsim.Image class object (e.g. ImageD, ImageViewS etc.)") # Build a noise correlation function (CF) from the input image, using DFTs # Calculate the power spectrum then a (preliminary) CF ft_array = np.fft.fft2(image.array) ps_array = (ft_array * ft_array.conj()).real if subtract_mean: # Quickest non-destructive way to make the PS correspond to the # mean-subtracted case ps_array[0, 0] = 0. # Note need to normalize due to one-directional 1/N^2 in FFT conventions cf_array_prelim = (np.fft.ifft2(ps_array)).real / np.product(image.array.shape) store_rootps = True # Currently the ps_array above corresponds to cf, but this may change... # Apply a correction for the DFT assumption of periodicity unless user requests otherwise if correct_periodicity: cf_array_prelim *= _cf_periodicity_dilution_correction(cf_array_prelim.shape) store_rootps = False # Roll CF array to put the centre in image centre. Remember that numpy stores data [y,x] cf_array_prelim = utilities.roll2d( cf_array_prelim, (cf_array_prelim.shape[0] / 2, cf_array_prelim.shape[1] / 2)) # The underlying C++ object is expecting the CF to be represented by an odd-dimensioned # array with the central pixel denoting the zero-distance correlation (variance), even # even if the input image was even-dimensioned on one or both sides. # We therefore copy-paste and zero pad the CF calculated above to ensure that these # expectations are met. # # Determine the largest dimension of the input image, and use it to generate an empty CF # array for final output, padding by one to make odd if necessary: cf_array = np.zeros(( 1 + 2 * (cf_array_prelim.shape[0] / 2), 1 + 2 * (cf_array_prelim.shape[1] / 2))) # using integer division # Then put the data from the prelim CF into this array cf_array[0:cf_array_prelim.shape[0], 0:cf_array_prelim.shape[1]] = cf_array_prelim # Then copy-invert-paste data from the leftmost column to the rightmost column, and lowest # row to the uppermost row, if the original CF had even dimensions in the x and y # directions, respectively (remembering again that NumPy stores data [y,x] in arrays) if cf_array_prelim.shape[1] % 2 == 0: # first do x lhs_column = cf_array[:, 0] cf_array[:, cf_array_prelim.shape[1]] = lhs_column[::-1] # inverts order as required if cf_array_prelim.shape[0] % 2 == 0: # then do y bottom_row = cf_array[0, :] cf_array[cf_array_prelim.shape[0], :] = bottom_row[::-1] # inverts order as required # Wrap correlation function in an image cf_image = galsim.ImageViewD(np.ascontiguousarray(cf_array)) # Correctly record the original image scale if set if dx > 0.: cf_image.setScale(dx) elif image.getScale() > 0.: cf_image.setScale(image.getScale()) else: # sometimes Images are instantiated with scale=0, in which case we will assume unit # pixel scale cf_image.setScale(1.) # If x_interpolant not specified on input, use bilinear if x_interpolant == None: linear = galsim.Linear(tol=1.e-4) x_interpolant = galsim.InterpolantXY(linear) else: x_interpolant = utilities.convert_interpolant_to_2d(x_interpolant) # Then initialize... cf_object = galsim.InterpolatedImage( cf_image, x_interpolant=x_interpolant, dx=cf_image.getScale(), normalization="sb", calculate_stepk=False, calculate_maxk=False) # these internal calculations do not seem # to do very well with often sharp-peaked # correlation function images... _BaseCorrelatedNoise.__init__(self, rng, cf_object) if store_rootps: # If it corresponds to the CF above, store useful data as a (rootps, dx) tuple for # efficient later use: self._profile_for_stored = self._profile self._rootps_store.append((np.sqrt(ps_array), cf_image.getScale()))
def __init__(self, image, x_interpolant=None, k_interpolant=None, normalization='flux', scale=None, wcs=None, flux=None, pad_factor=4., noise_pad_size=0, noise_pad=0., rng=None, pad_image=None, calculate_stepk=True, calculate_maxk=True, use_cache=True, use_true_center=True, offset=None, gsparams=None, dx=None): # Check for obsolete dx parameter if dx is not None and scale is None: scale = dx import numpy # first try to read the image as a file. If it's not either a string or a valid # pyfits hdu or hdulist, then an exception will be raised, which we ignore and move on. try: image = galsim.fits.read(image) except: pass # make sure image is really an image and has a float type if not isinstance(image, galsim.Image): raise ValueError("Supplied image is not an Image instance") if image.dtype != numpy.float32 and image.dtype != numpy.float64: raise ValueError( "Supplied image is not an image of floats or doubles!") # it must have well-defined bounds, otherwise seg fault in SBInterpolatedImage constructor if not image.bounds.isDefined(): raise ValueError("Supplied image does not have bounds defined!") # check what normalization was specified for the image: is it an image of surface # brightness, or flux? if not normalization.lower() in ("flux", "f", "surface brightness", "sb"): raise ValueError(( "Invalid normalization requested: '%s'. Expecting one of 'flux', " + "'f', 'surface brightness', or 'sb'.") % normalization) # set up the interpolants if none was provided by user, or check that the user-provided ones # are of a valid type if x_interpolant is None: self.x_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4)) else: self.x_interpolant = galsim.utilities.convert_interpolant_to_2d( x_interpolant) if k_interpolant is None: self.k_interpolant = galsim.InterpolantXY(galsim.Quintic(tol=1e-4)) else: self.k_interpolant = galsim.utilities.convert_interpolant_to_2d( k_interpolant) # Store the image as an attribute and make sure we don't change the original image # in anything we do here. (e.g. set scale, etc.) self.image = image.view() self.use_cache = use_cache # Set the wcs if necessary if scale is not None: if wcs is not None: raise TypeError( "Cannot provide both scale and wcs to InterpolatedImage") self.image.wcs = galsim.PixelScale(scale) elif wcs is not None: if not isinstance(wcs, galsim.BaseWCS): raise TypeError( "wcs parameter is not a galsim.BaseWCS instance") self.image.wcs = wcs elif self.image.wcs is None: raise ValueError( "No information given with Image or keywords about pixel scale!" ) # Set up the GaussianDeviate if not provided one, or check that the user-provided one is # of a valid type. if rng is None: if noise_pad: rng = galsim.BaseDeviate() elif not isinstance(rng, galsim.BaseDeviate): raise TypeError( "rng provided to InterpolatedImage constructor is not a BaseDeviate" ) # Check that given pad_image is valid: if pad_image: import numpy if isinstance(pad_image, str): pad_image = galsim.fits.read(pad_image) if not isinstance(pad_image, galsim.Image): raise ValueError("Supplied pad_image is not an Image!") if pad_image.dtype != numpy.float32 and pad_image.dtype != numpy.float64: raise ValueError( "Supplied pad_image is not one of the allowed types!") # Check that the given noise_pad is valid: try: noise_pad = float(noise_pad) except: pass if isinstance(noise_pad, float): if noise_pad < 0.: raise ValueError("Noise variance cannot be negative!") # There are other options for noise_pad, the validity of which will be checked in # the helper function self.buildNoisePadImage() # This will be passed to SBInterpolatedImage, so make sure it is the right type. pad_factor = float(pad_factor) if pad_factor <= 0.: raise ValueError("Invalid pad_factor <= 0 in InterpolatedImage") if use_true_center: im_cen = self.image.bounds.trueCenter() else: im_cen = self.image.bounds.center() local_wcs = self.image.wcs.local(image_pos=im_cen) # Make sure the image fits in the noise pad image: if noise_pad_size: import math # Convert from arcsec to pixels according to the local wcs. # Use the minimum scale, since we want to make sure noise_pad_size is # as large as we need in any direction. scale = local_wcs.minLinearScale() noise_pad_size = int(math.ceil(noise_pad_size / scale)) # Round up to a good size for doing FFTs noise_pad_size = galsim._galsim.goodFFTSize(noise_pad_size) if noise_pad_size <= min(self.image.array.shape): # Don't need any noise padding in this case. noise_pad_size = 0 elif noise_pad_size < max(self.image.array.shape): noise_pad_size = max(self.image.array.shape) # See if we need to pad out the image with either a pad_image or noise_pad if noise_pad_size: new_pad_image = self.buildNoisePadImage(noise_pad_size, noise_pad, rng) if pad_image: # if both noise_pad and pad_image are set, then we need to build up a larger # pad_image and place the given pad_image in the center. # We will change the bounds here, so make a new view to avoid modifying the # input pad_image. pad_image = pad_image.view() pad_image.setCenter(0, 0) new_pad_image.setCenter(0, 0) if new_pad_image.bounds.includes(pad_image.bounds): new_pad_image[pad_image.bounds] = pad_image else: new_pad_image = pad_image pad_image = new_pad_image elif pad_image: # Just make sure pad_image is the right type pad_image = galsim.Image(pad_image, dtype=image.dtype) # Now place the given image in the center of the padding image: if pad_image: pad_image.setCenter(0, 0) self.image.setCenter(0, 0) if pad_image.bounds.includes(self.image.bounds): pad_image[self.image.bounds] = self.image else: # If padding was smaller than original image, just use the original image. pad_image = self.image else: pad_image = self.image # Make the SBInterpolatedImage out of the image. sbinterpolatedimage = galsim._galsim.SBInterpolatedImage( pad_image.image, xInterp=self.x_interpolant, kInterp=self.k_interpolant, pad_factor=pad_factor, gsparams=gsparams) # GalSim cannot automatically know what stepK and maxK are appropriate for the # input image. So it is usually worth it to do a manual calculation here. if calculate_stepk: if calculate_stepk is True: sbinterpolatedimage.calculateStepK() else: # If not a bool, then value is max_stepk sbinterpolatedimage.calculateStepK(max_stepk=calculate_stepk) if calculate_maxk: if calculate_maxk is True: sbinterpolatedimage.calculateMaxK() else: # If not a bool, then value is max_maxk sbinterpolatedimage.calculateMaxK(max_maxk=calculate_maxk) # Initialize the SBProfile GSObject.__init__(self, sbinterpolatedimage) # Make sure offset is a PositionD offset = self._parse_offset(offset) # Apply the offset, and possibly fix the centering for even-sized images # Note reverse=True, since we want to fix the center in the opposite sense of what the # draw function does. prof = self._fix_center(self.image, offset, use_true_center, reverse=True) # Bring the profile from image coordinates into world coordinates prof = local_wcs.toWorld(prof) # If the user specified a flux, then set to that flux value. if flux is not None: prof = prof.withFlux(float(flux)) # If the user specified a surface brightness normalization for the input Image, then # need to rescale flux by the pixel area to get proper normalization. elif normalization.lower() in ['surface brightness', 'sb']: prof *= local_wcs.pixelArea() GSObject.__init__(self, prof)