Ejemplo n.º 1
0
    def getNoise(self, i, rng=None, gsparams=None):
        """Returns the noise cf at index `i` as a CorrelatedNoise object.
        """
        if self.noise_file_name is None:
            cf = galsim.UncorrelatedNoise(rng, self.pixel_scale[i], self.variance[i], gsparams)

        else:

            if i >= len(self.noise_file_name):
                raise IndexError(
                    'index %d given to getNoise is out of range (0..%d)'%(
                        i,len(self.noise_file_name)-1))

            if self.noise_file_name[i] in self.saved_noise_im:
                im = self.saved_noise_im[self.noise_file_name[i]]
            else:
                import pyfits
                import os
                import numpy

                file_name = os.path.join(self.noise_dir,self.noise_file_name[i])
                array = pyfits.getdata(file_name)
                im = galsim.ImageViewD(numpy.ascontiguousarray(array.astype(numpy.float64)))
                self.saved_noise_im[self.noise_file_name[i]] = im

            cf = galsim.correlatednoise._BaseCorrelatedNoise(
                rng, galsim.InterpolatedImage(im, dx=self.pixel_scale[i], normalization="sb",
                                                calculate_stepk=False, calculate_maxk=False,
                                                x_interpolant='linear', gsparams=gsparams))
            cf.setVariance(self.variance[i])

        return cf
Ejemplo n.º 2
0
    def applyTo(self, image):
        """Apply this correlated Gaussian random noise field to an input Image.

        Calling
        -------
        To add deviates to every element of an image, the syntax 

            >>> image.addNoise(correlated_noise)

        is preferred.  However, this is equivalent to calling this instance's .applyTo() method as
        follows

            >>> correlated_noise.applyTo(image)

        On output the Image instance `image` will have been given additional noise according to the
        given CorrelatedNoise instance `correlated_noise`.  image.getScale() is used to determine
        the input image pixel separation, and if image.getScale() <= 0 a pixel scale of 1 is
        assumed.

        Note that the correlated noise field in `image` will be periodic across its boundaries: this
        is due to the fact that the internals of the CorrelatedNoise currently use a relatively
        simple implementation of noise generation using the Fast Fourier Transform.  If you wish to
        avoid this property being present in your final `image` you should .applyTo() an `image` of
        greater extent than you need, and take a subset.

        @param image The input Image object.
        """
        # Note that this uses the (fast) method of going via the power spectrum and FFTs to generate
        # noise according to the correlation function represented by this instance.  An alternative
        # would be to use the covariance matrices and eigendecomposition.  However, it is O(N^6)
        # operations for an NxN image!  FFT-based noise realization is O(2 N^2 log[N]) so we use it
        # for noise generation applications.

        # Check that the input has defined bounds
        if not hasattr(image, "bounds"):
            raise ValueError(
                "Input image argument does not have a bounds attribute, it must be a galsim.Image "+
                "or galsim.ImageView-type object with defined bounds.")

        # If the profile has changed since last time (or if we have never been here before),
        # clear out the stored values.
        if self._profile_for_stored is not self._profile:
            self._rootps_store = []
            self._rootps_whitening_store = []
            self._variance_stored = None
        # Set profile_for_stored for next time.
        self._profile_for_stored = self._profile

        # Then retrieve or redraw the sqrt(power spectrum) needed for making the noise field
        rootps = self._get_update_rootps(image.array.shape, image.getScale())

        # Finally generate a random field in Fourier space with the right PS
        noise_array = _generate_noise_from_rootps(self.getRNG(), rootps)
        # Add it to the image
        image += galsim.ImageViewD(noise_array)
        return image
Ejemplo n.º 3
0
 def getPSF(self, i):
     """Returns the PSF at index `i` as an ImageViewD object.
     """
     if i >= len(self.PSF_file_name):
         raise IndexError(
             'index %d given to getPSF is out of range (0..%d)'%(i,len(self.PSF_file_name)-1))
     import pyfits
     import os
     import numpy
     if self.do_preload and not self.preloaded:
         self.preload()
     if self.preloaded:
         array = self.loaded_files[self.PSF_file_name[i]][self.PSF_hdu[i]].data
     else:
         file_name = os.path.join(self.image_dir,self.PSF_file_name[i])
         array = pyfits.getdata(file_name,self.PSF_hdu[i])
     return galsim.ImageViewD(numpy.ascontiguousarray(array.astype(numpy.float64)))
Ejemplo n.º 4
0
def kolmogorov_psf_image(
        array_shape=(256, 256), dx=1., lam_over_r0=1., flux=1.):
    """Return long exposure Kolmogorov PSF as an ImageViewD.

    The ImageView output can be used to directly instantiate an SBInterpolatedImage, and its 
    scale will reflect the spacing of the output grid in the system of units adopted for 
    lam_over_diam.

    @param array_shape     the NumPy array shape desired for the array view of the ImageViewD.
    @param dx              grid spacing of PSF in real space units
    @param lam_over_r0     lambda / r0 in the physical units adopted for dx (user responsible for 
                           consistency). r0 is the Fried parameter. Typical values for the 
                           Fried parameter are on the order of 10 cm for most observatories.
    @param flux            total flux of the profile [default flux=1.]
    """
    array = kolmogorov_psf(array_shape=array_shape,
                           dx=dx,
                           lam_over_r0=lam_over_r0,
                           flux=flux)
    return galsim.ImageViewD(array.astype(np.float64))
Ejemplo n.º 5
0
def kolmogorov_mtf_image(array_shape=(256, 256), dx=1., lam_over_r0=1.):
    """Return the atmospheric modulation transfer function for long exposures with Kolmogorov 
    turbulence as an ImageViewD.

    The ImageView output can be used to directly instantiate an SBInterpolatedImage, and its 
    scale will reflect the spacing of the output grid in the system of units adopted for 
    lam_over_r0.

    @param array_shape     the NumPy array shape desired for the array view of the ImageViewD.
    @param dx              grid spacing of PSF in real space units
    @param lam_over_r0     lambda / r0 in the physical units adopted for dx (user responsible for 
                           consistency), where r0 is the Fried parameter. The FWHM of the Kolmogorov
                           PSF is ~0.976 lambda/r0 (e.g., Racine 1996, PASP 699, 108). Typical 
                           values for the Fried parameter are on the order of 10 cm for most 
                           observatories and up to 20 cm for excellent sites. The values are 
                           usually quoted at lambda = 500 nm and r0 depends weakly on wavelength
                           [r0 ~ lambda^(-6/5)].
    """
    amtf = kolmogorov_mtf(array_shape=array_shape,
                          dx=dx,
                          lam_over_r0=lam_over_r0)
    return galsim.ImageViewD(amtf.astype(np.float64))
Ejemplo n.º 6
0
# function that is NPIX by NPIX

if not os.path.isfile(
        CFIMFILE
):  # If the CFIMFILE already exists skip straight through to the plots
    # Read in the pickled images
    noiseims = cPickle.load(open(NOISEIMFILE, 'rb'))
    # Loop through the images and sum the correlation functions
    hst_ncf = None
    bd = galsim.BaseDeviate(12345)  # Seed is basically unimportant here
    for noiseim in noiseims:
        noiseim = noiseim.astype(np.float64)
        if hst_ncf is None:
            # Initialize the HST noise correlation function using the first image
            hst_ncf = galsim.CorrelatedNoise(bd,
                                             galsim.ImageViewD(noiseim),
                                             correct_periodicity=True,
                                             subtract_mean=SUBTRACT_MEAN)
        else:
            hst_ncf += galsim.CorrelatedNoise(bd,
                                              galsim.ImageViewD(noiseim),
                                              correct_periodicity=True,
                                              subtract_mean=SUBTRACT_MEAN)
    hst_ncf /= float(len(noiseims))
    # Draw and plot an output image of the resulting correlation function
    cfimage = galsim.ImageD(NPIX, NPIX)
    hst_ncf.draw(cfimage, dx=1.)
    # Save this to the output filename specified in the script header
    cfimage.write(CFIMFILE)
else:
    cfimage = galsim.fits.read(CFIMFILE)
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
test_image2 = galsim.ImageD(TESTDIM, TESTDIM)

failed1 = 0
failed2 = 0
failed3 = 0
failed4 = 0
failed5 = 0
failed6 = 0
failed7 = 0

TESTSTART = 25
TESTEND = 125

for dim in range(TESTSTART, TESTEND):
    noise_array = np.random.randn(dim, dim)
    noise_image = galsim.ImageViewD(noise_array)
    cn1 = galsim.CorrelatedNoise(rng, noise_image)
    cn2 = galsim.CorrelatedNoise(rng, noise_image, correct_periodicity=False)
    # First try with (default), then without, periodicity correction
    test_image1.setZero()
    try:
        cn1.applyTo(test_image1)
    except RuntimeError:
        failed1 += 1
    test_image2.setZero()
    try:
        cn2.applyTo(test_image2)
    except RuntimeError:
        failed2 += 1
    # Then try calculating the PS by hand, in the same manner as the CorrelatedNoise internals
    noiseft = np.fft.fft2(noise_array)
Ejemplo n.º 9
0
def ptf_image(array_shape=(256, 256),
              dx=1.,
              lam_over_diam=2.,
              defocus=0.,
              astig1=0.,
              astig2=0.,
              coma1=0.,
              coma2=0.,
              trefoil1=0.,
              trefoil2=0.,
              spher=0.,
              circular_pupil=True,
              obscuration=0.):
    """Return the PTF [radians] of a circular (default) or square pupil with low-order aberrations
    as an ImageViewD.

    PTF array element ordering follows the DFT standard of kxky(array_shape), and has
    ptf[0, 0] = 0. by default.  The scale of the output ImageViewD is correct in k space units.

    The ImageView output can be used to directly instantiate an SBInterpolatedImage, and its 
    .getScale() method will reflect the spacing of the output grid in the system of units adopted
    for lam_over_diam.

    To ensure properly Nyquist sampled output any user should set lam_over_diam >= 2. * dx.

    @param array_shape     the NumPy array shape desired for the array view of the ImageViewD.
    @param dx              grid spacing of PSF in real space units
    @param lam_over_diam   lambda / telescope diameter in the physical units adopted for dx 
                           (user responsible for consistency).
    @param defocus         defocus in units of incident light wavelength.
    @param astig1          astigmatism (like e2) in units of incident light wavelength.
    @param astig2          astigmatism (like e1) in units of incident light wavelength.
    @param coma1           coma along y in units of incident light wavelength.
    @param coma2           coma along x in units of incident light wavelength.
    @param trefoil1        trefoil (one of the arrows along y) in units of incident light
                           wavelength.
    @param trefoil2        trefoil (one of the arrows along x) in units of incident light
                           wavelength.
    @param spher           spherical aberration in units of incident light wavelength.
    @param circular_pupil  adopt a circular pupil?
    @param obscuration     linear dimension of central obscuration as fraction of pupil linear
                           dimension, [0., 1.)
    """
    array = ptf(array_shape=array_shape,
                dx=dx,
                lam_over_diam=lam_over_diam,
                defocus=defocus,
                astig1=astig1,
                astig2=astig2,
                coma1=coma1,
                coma2=coma2,
                trefoil1=trefoil1,
                trefoil2=trefoil2,
                spher=spher,
                circular_pupil=circular_pupil,
                obscuration=obscuration)
    im = galsim.ImageViewD(array.astype(np.float64))
    if array_shape[0] != array_shape[1]:
        import warnings
        warnings.warn(
            "PTF Image scale will not be correct in both directions for non-square arrays, only "
            + "square grids currently supported by galsim.Images.")
    im.setScale(2. * np.pi / array_shape[0])
    return im
Ejemplo n.º 10
0
CFLOGPLOTFILE = "acs_I_unrot_sci_20_log10cf.png"    # Plot (log) of the output CF
NPIX = 81                                           # Make an image of the final correlation
                                                    # function that is NPIX by NPIX

if not os.path.isfile(CFIMFILE): # If the CFIMFILE already exists skip straight through to the plots
    # Read in the pickled images
    noiseims = cPickle.load(open(NOISEIMFILE, 'rb'))
    # Loop through the images and sum the correlation functions
    hst_ncf = None
    bd = galsim.BaseDeviate(12345) # Seed is basically unimportant here
    for noiseim in noiseims:
        noiseim = noiseim.astype(np.float64)
        if hst_ncf is None:
            # Initialize the HST noise correlation function using the first image
            hst_ncf = galsim.CorrelatedNoise(
                bd, galsim.ImageViewD(noiseim), correct_periodicity=True,
                subtract_mean=SUBTRACT_MEAN)
        else:
            hst_ncf += galsim.CorrelatedNoise(
                bd, galsim.ImageViewD(noiseim), correct_periodicity=True,
                subtract_mean=SUBTRACT_MEAN)
    hst_ncf /= float(len(noiseims))
    # Draw and plot an output image of the resulting correlation function
    cfimage = galsim.ImageD(NPIX, NPIX)
    hst_ncf.draw(cfimage, dx=1.)
    # Save this to the output filename specified in the script header
    cfimage.write(CFIMFILE)
else:
    cfimage = galsim.fits.read(CFIMFILE)

# Then make nice plots
Ejemplo n.º 11
0
def wavefront_image(array_shape=(256, 256),
                    dx=1.,
                    lam_over_diam=2.,
                    defocus=0.,
                    astig1=0.,
                    astig2=0.,
                    coma1=0.,
                    coma2=0.,
                    trefoil1=0.,
                    trefoil2=0.,
                    spher=0.,
                    circular_pupil=True,
                    obscuration=0.):
    """Return wavefront as a (real, imag) tuple of ImageViewD objects rather than complex NumPy
    array.

    Outputs a circular pupil wavefront of unit amplitude that can be easily transformed to produce
    an optical PSF with lambda/diam = lam_over_diam on an output grid of spacing dx.

    The ImageView output can be used to directly instantiate an SBInterpolatedImage, and its 
    .getScale() method will reflect the spacing of the output grid in the system of units adopted
    for lam_over_diam.

    To ensure properly Nyquist sampled output any user should set lam_over_diam >= 2. * dx.
    
    The pupil sample locations are arranged in standard DFT element ordering format, so that
    (kx, ky) = (0, 0) is the [0, 0] array element.  The scale of the output ImageViewD is correct in
    k space units.

    Input aberration coefficients are assumed to be supplied in units of wavelength, and correspond
    to the Zernike polynomials in the Noll convention definined in
    Noll, J. Opt. Soc. Am. 66, 207-211(1976). For a brief summary of the polynomials, refer to
    http://en.wikipedia.org/wiki/Zernike_polynomials#Zernike_polynomials.

    @param array_shape     the NumPy array shape desired for the output array.
    @param dx              grid spacing of PSF in real space units
    @param lam_over_diam   lambda / telescope diameter in the physical units adopted for dx 
                           (user responsible for consistency).
    @param defocus         defocus in units of incident light wavelength.
    @param astig1          astigmatism (like e2) in units of incident light wavelength.
    @param astig2          astigmatism (like e1) in units of incident light wavelength.
    @param coma1           coma along y in units of incident light wavelength.
    @param coma2           coma along x in units of incident light wavelength.
    @param trefoil1        trefoil (one of the arrows along y) in units of incident light
                           wavelength.
    @param trefoil2        trefoil (one of the arrows along x) in units of incident light
                           wavelength.
    @param spher           spherical aberration in units of incident light wavelength.
    @param circular_pupil  adopt a circular pupil?
    @param obscuration     linear dimension of central obscuration as fraction of pupil linear
                           dimension, [0., 1.)
    """
    array = wavefront(array_shape=array_shape,
                      dx=dx,
                      lam_over_diam=lam_over_diam,
                      defocus=defocus,
                      astig1=astig1,
                      astig2=astig2,
                      coma1=coma1,
                      coma2=coma2,
                      trefoil1=trefoil1,
                      trefoil2=trefoil2,
                      spher=spher,
                      circular_pupil=circular_pupil,
                      obscuration=obscuration)
    imreal = galsim.ImageViewD(
        np.ascontiguousarray(array.real.astype(np.float64)))
    imimag = galsim.ImageViewD(
        np.ascontiguousarray(array.imag.astype(np.float64)))
    if array_shape[0] != array_shape[1]:
        import warnings
        warnings.warn(
            "Wavefront Images' scales will not be correct in both directions for non-square "
            +
            "arrays, only square grids currently supported by galsim.Images.")
    imreal.setScale(2. * np.pi / array_shape[0])
    imimag.setScale(2. * np.pi / array_shape[0])
    return (imreal, imimag)
Ejemplo n.º 12
0
    def __init__(self, rng, image, dx=0., x_interpolant=None, correct_periodicity=True,
        subtract_mean=False):

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

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

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

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

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

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

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

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

        if store_rootps:
            # If it corresponds to the CF above, store useful data as a (rootps, dx) tuple for
            # efficient later use:
            self._profile_for_stored = self._profile
            self._rootps_store.append((np.sqrt(ps_array), cf_image.getScale()))
Ejemplo n.º 13
0
    def applyWhiteningTo(self, image):
        """Apply noise designed to whiten correlated Gaussian random noise in an input Image.

        On output the Image instance `image` will have been given additional noise according to 
        a specified CorrelatedNoise instance, designed to whiten any correlated noise that may have
        originally existed in `image`.

        Calling
        -------

            >>> correlated_noise.applyWhiteningTo(image)

        If the `image` originally contained noise with a correlation function described by the 
        `correlated_noise` instance, the combined noise after using the applyWhiteningTo() method
        will be approximately uncorrelated.  Tests using COSMOS noise fields suggest ~0.3% residual
        off-diagonal covariances after whitening, relative to the variance, although results may
        vary depending on the precise correlation function of the noise field.
        (See `devel/external/hst/compare_whitening_subtraction.py` for the COSMOS tests.)

        Note that the code doesn't check that the "if" above is true: the user MUST make sure this 
        is the case for the final noise to be uncorrelated.

        image.getScale() is used to determine the input image pixel separation, and if 
        image.getScale() <= 0 a pixel scale of 1 is assumed.

        If you are interested in a theoretical calculation of the variance in the final noise field
        after whitening, the applyWhiteningTo() method in fact returns this variance.  For example:

            >>> variance = correlated_noise.applyWhiteningTo(image)

        Example
        -------
        To see noise whitening in action, let us use a model of the correlated noise in COSMOS 
        as returned by the getCOSMOSNoise() function.  Let's initialize and add noise to an image:

            >>> cosmos_file='YOUR/REPO/PATH/GalSim/examples/data/acs_I_unrot_sci_20_cf.fits'
            >>> cn = galsim.getCOSMOSNoise(galsim.BaseDeviate(), cosmos_file)
            >>> image = galsim.ImageD(256, 256)
            >>> image.setScale(0.03) # Should match the COSMOS default since didn't specify another
            >>> image.addNoise(cn)

        The `image` will then contain a realization of a random noise field with COSMOS-like
        correlation.  Using the applyWhiteningTo() method, we can now add more noise to `image`
        with a power spectrum specifically designed to make the combined noise fields uncorrelated:

            >>> cn.applyWhiteningTo(image)

        Of course, this whitening comes at the cost of adding further noise to the image, but 
        the algorithm is designed to make this additional noise (nearly) as small as possible.

        @param image The input Image object.

        @return variance  A float containing the theoretically calculated variance of the combined
                          noise fields in the updated image.
        """
        # Note that this uses the (fast) method of going via the power spectrum and FFTs to generate
        # noise according to the correlation function represented by this instance.  An alternative
        # would be to use the covariance matrices and eigendecomposition.  However, it is O(N^6)
        # operations for an NxN image!  FFT-based noise realization is O(2 N^2 log[N]) so we use it
        # for noise generation applications.

        # Check that the input has defined bounds
        if not hasattr(image, "bounds"):
            raise ValueError(
                "Input image argument does not have a bounds attribute, it must be a galsim.Image "+
                "or galsim.ImageView-type object with defined bounds.")

        # If the profile has changed since last time (or if we have never been here before),
        # clear out the stored values.
        if self._profile_for_stored is not self._profile:
            self._rootps_store = []
            self._rootps_whitening_store = []
            self._variance_stored = None
        # Set profile_for_stored for next time.
        self._profile_for_stored = self._profile

        # Then retrieve or redraw the sqrt(power spectrum) needed for making the whitening noise,
        # and the total variance of the combination
        rootps_whitening, variance = self._get_update_rootps_whitening(
            image.array.shape, image.getScale())

        # Finally generate a random field in Fourier space with the right PS and add to image
        noise_array = _generate_noise_from_rootps(self.getRNG(), rootps_whitening)
        image += galsim.ImageViewD(noise_array)

        # Return the variance to the interested user
        return variance
Ejemplo n.º 14
0
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions, and the disclaimer given in the documentation
#    and/or other materials provided with the distribution.
#

# Just some temporary testing stuff
if __name__ == "__main__":

    import numpy as np
    import matplotlib.pyplot as plt
    import galsim

    IMSIZE = 40

    # First make a noise field with an even number of elements
    enoise = galsim.ImageViewD(np.random.randn(IMSIZE, IMSIZE))
    encf = galsim.correlatednoise.CorrFunc(enoise)
    print encf.SBProfile.xValue(galsim.PositionD(0, 0))

    # Then make a noise field with an odd number of elements
    onoise = galsim.ImageViewD(np.random.randn(IMSIZE + 1, IMSIZE + 1))
    oncf = galsim.correlatednoise.CorrFunc(onoise)
    print oncf.SBProfile.xValue(galsim.PositionD(0, 0))

    testim = galsim.ImageD(10, 10)
    cv = encf.SBProfile.getCovarianceMatrix(testim.view(), dx=1.)
    #plt.pcolor(cv.array); plt.colorbar()

    # Construct an image with noise correlated in the y direction
    plt.figure()
    ynoise = galsim.ImageViewD(enoise.array[:, :] +
Ejemplo n.º 15
0
def psf_image(array_shape=(256, 256),
              dx=1.,
              lam_over_diam=2.,
              defocus=0.,
              astig1=0.,
              astig2=0.,
              coma1=0.,
              coma2=0.,
              trefoil1=0.,
              trefoil2=0.,
              spher=0.,
              circular_pupil=True,
              obscuration=0.,
              flux=1.):
    """Return circular (default) or square pupil PSF with low-order aberrations as an ImageViewD.

    The PSF is centred on the array[array_shape[0] / 2, array_shape[1] / 2] pixel by default, and
    uses surface brightness rather than flux units for pixel values, matching SBProfile.

    The ImageView output can be used to directly instantiate an SBInterpolatedImage, and its 
    .getScale() method will reflect the spacing of the output grid in the system of units adopted
    for lam_over_diam.

    To ensure properly Nyquist sampled output any user should set lam_over_diam >= 2. * dx.

    @param array_shape     the NumPy array shape desired for the array view of the ImageViewD.
    @param dx              grid spacing of PSF in real space units
    @param lam_over_diam   lambda / telescope diameter in the physical units adopted for dx 
                           (user responsible for consistency).
    @param defocus         defocus in units of incident light wavelength.
    @param astig1          astigmatism (like e2) in units of incident light wavelength.
    @param astig2          astigmatism (like e1) in units of incident light wavelength.
    @param coma1           coma along y in units of incident light wavelength.
    @param coma2           coma along x in units of incident light wavelength.
    @param trefoil1        trefoil (one of the arrows along y) in units of incident light
                           wavelength.
    @param trefoil2        trefoil (one of the arrows along x) in units of incident light
                           wavelength.
    @param spher           spherical aberration in units of incident light wavelength.
    @param circular_pupil  adopt a circular pupil?
    @param obscuration     linear dimension of central obscuration as fraction of pupil linear
                           dimension, [0., 1.)
    @param flux            total flux of the profile [default flux=1.]
    """
    array = psf(array_shape=array_shape,
                dx=dx,
                lam_over_diam=lam_over_diam,
                defocus=defocus,
                astig1=astig1,
                astig2=astig2,
                coma1=coma1,
                coma2=coma2,
                trefoil1=trefoil1,
                trefoil2=trefoil2,
                spher=spher,
                circular_pupil=circular_pupil,
                obscuration=obscuration,
                flux=flux)
    im = galsim.ImageViewD(array.astype(np.float64))
    im.setScale(dx)
    return im
Ejemplo n.º 16
0
RSEED = 12334566

ud = galsim.UniformDeviate(RSEED)

# Case 1: subtract_mean=True; Case 2: subtract_mean=False
cn1 = galsim.getCOSMOSNoise(ud, CFIMFILE_SUB, dx_cosmos=1.)
cn2 = galsim.getCOSMOSNoise(ud, CFIMFILE_UNS, dx_cosmos=1.)

testim1 = galsim.ImageD(7, 7)
testim2 = galsim.ImageD(7, 7)
var1 = 0.
var2 = 0.

noisearrays = cPickle.load(open(NOISEIMFILE, 'rb'))
for noisearray, i in zip(noisearrays, range(len(noisearrays))):
    noise1 = galsim.ImageViewD((noisearray.copy()).astype(np.float64), scale=1.)
    noise2 = galsim.ImageViewD((noisearray.copy()).astype(np.float64), scale=1.)
    cn1.applyWhiteningTo(noise1)
    cn2.applyWhiteningTo(noise2)
    var1 += noise1.array.var()
    var2 += noise2.array.var()
    cntest1 = galsim.CorrelatedNoise(ud, noise1)
    cntest2 = galsim.CorrelatedNoise(ud, noise2)
    cntest1.draw(testim1, dx=1., add_to_image=True)
    cntest2.draw(testim2, dx=1., add_to_image=True)
    print "Done "+str(i + 1)+"/"+str(len(noisearrays))

testim1 /= len(noisearrays)
testim2 /= len(noisearrays)
var1 /= len(noisearrays)
var2 /= len(noisearrays)
Ejemplo n.º 17
0
    def __init__(
            self,
            filename="afta_wfirst_example_psf_exaggerated.fields_and_coefs.fits",
            lam=1000.,
            diameter=2.4,
            obscuration=0.28,
            nstruts=6,
            strut_thick=0.01,
            strut_angle=0. * galsim.degrees,
            pad_factor=None,
            rms=0.075,
            interpolant2d=None,
            seed=None):
        """
        Inputs
        - filename: filename of fits file with information of optics.
        - lam: wavelength [nm]
        - diameter: diameter of telescope [m]
        - obscuration: central obscuration [ratio between mirror diameter and obscuration diameter]
        - nstruts: number of radial support struts to add to the central obscuration
        - strut_thick: thickness of support struts as a fraction of pupil diameter
        - strut_angle: angle made between the vertical and the strut starting closest to it,
                       defined to be positive in the counter-clockwise direction; must be a
                       galsim.Angle instance
        - pad_factor: optional padding specification if 1.5 is not good enough
        - rms: total rms of the random Zernike coefficients [wavelength]
        - interpolant2d: galsim._galsim.InterpolantXY
                         If None, galsim.InterpolantXY(galsim.Quintic())
        - seed: random seed to use for numpy routines that make random additional aberrations (if
                None, then let numpy seed routines based on time)
        """

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

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

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

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

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

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

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

        hdulist.close()